Compare commits

..

1 commit

Author SHA1 Message Date
Matthew Exon
03f5e7a952 Leave event type alone even if description includes image 2023-09-23 20:22:08 +02:00
1416 changed files with 64009 additions and 95546 deletions

View file

@ -1,7 +1,7 @@
name: my-friendica
type: php
docroot: ""
php_version: "7.4"
php_version: "7.3"
webserver_type: apache-fpm
router_http_port: "80"
router_https_port: "443"
@ -17,7 +17,7 @@ use_dns_when_possible: true
composer_version: "1"
web_environment: []
nodejs_version: "16"
webimage_extra_packages: [php7.4-gmp]
webimage_extra_packages: [php7.3-gmp]
# Key features of ddev's config.yaml:

View file

@ -1,13 +1,15 @@
matrix:
include:
- PHP_MAJOR_VERSION: 7.3
PHP_VERSION: 7.3.33
- PHP_MAJOR_VERSION: 7.4
PHP_VERSION: 7.4.33
- PHP_MAJOR_VERSION: 8.0
PHP_VERSION: 8.0.30
PHP_VERSION: 8.0.29
- PHP_MAJOR_VERSION: 8.1
PHP_VERSION: 8.1.23
PHP_VERSION: 8.1.21
- PHP_MAJOR_VERSION: 8.2
PHP_VERSION: 8.2.11
PHP_VERSION: 8.2.8
# This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...)
labels:
@ -67,9 +69,9 @@ steps:
- if [ "${PHP_MAJOR_VERSION}" = "7.4" -a "${CI_REPO}" = "friendica/friendica" ]; then
phpenmod xdebug;
export XDEBUG_MODE=coverage;
phpunit --configuration tests/phpunit.xml -d memory_limit=-1 --coverage-clover clover.xml;
phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml;
else
phpunit --configuration tests/phpunit.xml -d memory_limit=-1;
phpunit --configuration tests/phpunit.xml;
fi
codecov:
image: friendicaci/codecov

121
CHANGELOG
View file

@ -1,129 +1,10 @@
Version 2024.03 (unreleased)
Version 2023.09 (unreleased)
Friendica Core
Friendica Addons
Closed Issues
Version 2023.12 (2023-12-24)
Friendica Core
Raised minimal PHP version to 7.4
Updates to the translations AR, BG, CA, CS, DE, EN GB, EN US, EO, ES, ET, FI, FR, GD, HU, IS, IT, JA, NL, PL, RO, RU, SV
Updates to the themes (frio, vier) [AlfredSK, annando, anubis2814, haheute, foss-, MrPetovan, Raroun, xundeenergie]
Updates to the documentation [AndyHee, BirdboyBolu, foss-, MrPetovan]
Fixed a bug in notification links [annando, keithhacks]
Fixed a bug in content negotiation [GidiKroon]
Fixed the link in invitations [Raroun]
Fixed 0Auth connection with Buffer and redirection problems [annando, HankG]
Fixed a visibility bug with Calendar entries [MrPetovan]
Fixed a JsonLD parsing issue [annando]
Fixed confusion about nickname and username [MrPetovan, Raroun]
Fixed a problem with viewing remote contact profiles [MrPetovan]
Fixed a bug with previewing linked postings [annando]
Fixed a problem parsing Mastodon WebFingers [MrPetovan]
Fixed a bug that could lead to deleting your own account unintended [MrPetovan]
Fixed redirection in 2FA settings page [MrPetovan]
Fixed a problem with distributing some comments [annando]
Fixed a bug that caused public groups not being able to hide members [annando]
Fixed RSS/Atom feed of group accounts [MrPetovan]
Fixed a bug that prevented bi-directional delivery of DMs [annando]
Fixed a bug in privacy settings for remote-self [annando]
Fixed a bug that prevented the selection of photos with captions [Raroun]
Fixed a bug in Magic Auth [git-marijus]
Fixed a bug convertig local tags to external on receiving updates to the post [annando]
Improved Bluesky connector core [annando]
Improved SSL handling of reverse http proxies [MrPetovan]
Improved performance [annando]
Improved language detection [annando]
Improved display of contact relationships [annando]
Improved display of notifications [annando, MrPetovan]
Improved Mastodon compatible API [annando, gudzpoz, HankG, MrPetovan, Quix0r]
Improved federation with Diaspora, Firefish, GoToSocial, Lemmy, Pixelfed, Threads [annando]
Improved display of quoted posts [annando]
Improved BBCode conversion [annando]
Improved addon hook loading [annando, nupplaphil]
Improved handling of relay servers [annando]
Improved blocked users feature [MrPetovan]
Improved robots.txt to include ChatGPT and Google Extended [annando, haheute]
Improved handling of resizing images [annando]
Improved the image grid at the end of postings [MrPetovan]
Improved raw content output [annando]
Improved handling of smileys [gudzpoz]
Improved the admin panel [annando]
Improved handling of the background worker [annando]
Improved the processing of relayed posts [annando]
Improved compatibility with Mastodon Clients [annando]
Improved nodeinfo [MrPetovan]
Improved the federation statistics in the admin panel [annando]
General code cleanup [AlfredSK, annando, jlamothe, mexon, MrPetovan, nupplaphil, Quix0r, toddy15]
Renamed "group" to "circle"
Renamed "forum" to "group"
Added possibility to hide the server block list [MrPetovan, Quix0r]
Added creation of moderation reports [AlfredSK, annando, MrPetovan]
Added user controlled ignore server functionality [MrPetovan]
Added support for Unix domain sockets to Redis [MrPetovan]
Added user controlled channels for the network stream [annando]
Added support of MacOS emoji picker in text fields [MrPetovan]
Added lazy loading for images [FarisKarim, xundeenergie]
Added image caption display in Fancybox gallery [MrPetovan]
Added shortcut (ctrl+enter) to send postings using Frio theme [xundeenergie]
Added support for CORS requests to nodeinfo [palant]
Added setting for servers that should only receive limited profile information [annando]
Friendica Addons
Updates to the translation AR, CS, DE, ES, ET, FR, HU, IT, JA, NL, PL, RU, SV, ZH CN
Advancedcontentfilter
General code cleanup [MrPetovan]
Audon
Added audon addon [loma-one]
Blockem
Deprecated the addon [AndyHee]
Bluesky
Added bidirectional communication to the Bluesky connector [annando]
Added support for pinned postings [annando]
Added support for transmitted language [annando]
Improved thread handling [annando]
Improved handling of hashtags [annando]
Improved image uploading [annando]
CLD
Added addon for detection of the language of postings using CLD2 library [annando]
invidious
Added addon [loma-one]
langfilter
Use two letter codes for detected languages [annando]
Mailstream
Improved error handling [mexon]
Monolog
General code cleanup [nupplaphil]
NSFW
General code cleanup [MrPetovan]
pageheader
Improved styling of message box [loma-one]
s3_storage
Bumped library version [MrPetovan]
Showmore Dyn
Improved styling on mobile devices [csolisr]
Smileybutton
Improved conversation from float to int [MrPetovan]
Tumblr
Improved the import of feeds [annando]
Twitter
Improved image uploading [annando]
Removed the import of postings from Twitter [annando]
Closed Issues
8542, 10369, 12530, 12743, 12815, 13020, 13039, 13114, 13129,
13173, 13174, 13180, 13182, 13184, 13195, 13201, 13209, 13216,
13217, 13221, 13228, 13240, 13288, 13232, 13240, 13265, 13277,
13286, 13287, 13289, 13304, 13311, 13312, 13316, 13318, 13332,
13333, 13334, 13343, 13352, 13353, 13355, 13359, 13363, 13365,
13367, 13369, 13370, 13378, 13389, 13398, 13403, 13439, 13440,
13455, 13457, 13462, 13467, 13471, 13478, 13486, 13506, 13511,
13515, 13520, 13524, 13534, 13535, 13542, 13554, 13556, 13560,
13566, 13573, 13577, 13588, 13603, 13607, 13615, 13617, 13621,
13624, 13625, 13627, 13637, 13639, 13649, 13662, 13665, 13673,
13693, 13699, 13719, 13720, 13731, 13748
Version 2023.05 (2023-05-23)
Friendica Core
Updates to the translations HU, PL

View file

@ -31,7 +31,6 @@ Angristan
Anthronaut
Anton
Antron Samurai
Anubis2814
Arian - Cazare Muncitori
Asher Pen
atjn
@ -52,7 +51,6 @@ BinkaDroid
Bjoessi
bkil
bob lebonche
Boluwatife Victor
Boris Daniel Martinez Millàn
bufalo1973
ButterflyOfFire
@ -71,8 +69,8 @@ Christian Wiwie
Cohan Robinson
Colby Sollars
Copiis
Copiis Praeesse
CrystalStiletto
csolisr
Cyboulette
Cyryl Sochacki
czarnystokrotek
@ -100,7 +98,6 @@ Eelco Maljaars
effex7
Elena
emilia.krawczyk
Entropy Engineer
Eric Côté
Erich
erik
@ -111,12 +108,10 @@ F1per 3y
Fabian Dost
Fabio Comuni
Farida Khalaf
Faris
felixgilles
Filip Bugaj
Filip H.F. "FiXato" Slagter
Finn Dean
Florent C.
FlxAlbroscheit
foss-
Francesco Apruzzese
@ -126,12 +121,10 @@ Gerhard Seeber
gerhard6380
Gert Cauwenberg
Gidi Kroon
git-marijus
GLComo
greeneyedred
Gregory Smith
Grischa Brockhaus
groen
gudzpoz
GunChleoc
guzzisti
@ -141,7 +134,6 @@ Hannes Heute
Hans Meine
Hauke
Hauke Altmann
Henrik Härkönen
Herbert Thielen
hlad
hoergen
@ -164,7 +156,6 @@ Johannes Schwab
John Brazil
John Mortensen
Jonatan Nyberg
Jonathan Lamothe
Jonny Tischbein
Josef Moravek
Josh Soref
@ -177,13 +168,11 @@ Karolina
Kastal András
Keenan Pepper
Keith Fernie
keithhacks
Klaus Weidenbach
Koyu Berteon
kPherox
Kris
Kristoffer Grundström
ktlinux
KulikAlex
Lea1995polish
Leberwurscht
@ -265,7 +254,6 @@ Rain Hawk
Rainulf Pineda
Ralf Thees
ralph van der honing
Raroun
Ratten
rcmaniac
RealKinetix
@ -281,7 +269,6 @@ Roland Häder
Ruud Schilders
rwa
Ryan Voots
S. Brusch
S.Krumbholz
Sakałoŭ Alaksiej
Sam
@ -327,7 +314,6 @@ TiMESPLiNTER
Tino
Tobias Diekershoff
Tobias Hößl
Tobias Quathamer
Tom
Tom Aurlund
Tom Hu
@ -338,7 +324,6 @@ Torbjörn Andersson
TORminator
trebor
tschlotfeldt
tslmuun
Tubuntu
Tupambae.org
U-SOUND\mike
@ -360,7 +345,6 @@ Waldemar Stoczkowski
Walter Bulbazor
Wanting Chen
Wil Tur
Wladimir Palant
Wouter Broers
Xiaofei Xu
XMPPはいいぞ

View file

@ -1,30 +1,46 @@
Friendica - your open and free social network
=============================================
Friendica Social Communications Server
======================================
Welcome to the free social web. Friendica is a platform for decentralised social communication linking to other independent social and corporate services.
Welcome to the free social web.
Friendica connects you to a federated communications network of thousands of servers called the Fediverse.
Through various protocols you can interact with anyone on [Friendica]( https://friendi.ca), [Mastodon](https://joinmastodon.org), [Lemmy](https://join-lemmy.org/), [Diaspora](https://diasporafoundation.org), [Misskey](https://join.misskey.page), [Peertube](https://joinpeertube.org/), [Pixelfed](https://pixelfed.org/), [Pleroma](https://pleroma.social) and many more.
Receiving content from Tumblr, WordPress and RSS is also possible.
Friendica allows to import and mirror your content via add-ons such as ITTT and Buffer.
You can control the privacy scope of your content.
Friendica is a decentralised communications platform that integrates social communication. Our platform links to independent social projects and corporate services.
Being part of the Fediverse allows you to be free from data-harvesting corporations.
Enjoy open social communication, independent of any specific provider.
Our mission is to free friends, family and colleagues from data-harvesting corporations; we aim for social communication to be free and open, while flowing between any provider as easily as email does.
[Join Friendica](https://dir.friendica.social/servers) today or set up [your own Friendica instance](doc/Install.md).
Friendica connects you effortlessly to a federated communications network of several thousand servers, with more than half a million user registrations. You can directly connect to anyone on [Friendica]( https://friendi.ca), [Mastodon](https://joinmastodon.org/), [Diaspora](https://diasporafoundation.org/), [GnuSocial](https://gnu.io/social/), [Pleroma](https://pleroma.social/), or [Hubzilla](https://hubzilla.org/), regardless where each user profile is hosted.
### Friendica on desktop
With Friendica, you can also fully interact with anyone on Twitter and receive any content from Tumblr, Wordpress or RSS. Friendica allows you to integrate most things on the web via a range of addons such as ITTT, Buffer; you will be able to easily control your own data as you decide.
![Frio theme in desktop browser](images/screenshots/friendica-2023-12-frio-desktop.png?raw=true "Frio theme in desktop browser")
Join today and [get your Friendica profile!](https://dir.friendica.social/servers 'Join Friendica today!')
### Friendica on mobile
Have a look at the [installation documentation](doc/Install.md) for further information about installing and using Friendica.
<p float="left">
<img src="images/screenshots/friendica-2023-10-frio-mobile-timeline-dark-blue.png" width="370" alt="frio on mobile, dark color scheme">
<img src="images/screenshots/friendica-2023-10-frio-mobile-options-light-blue.png" width="370" alt="frio on mobile, light color scheme">
</p>
### Friendica Screenshots
| ![Frio theme in mobile browser](images/screenshots/friendica-frio-mobile-profile-1.png?raw=true "Frio theme in mobile browser") ![Frio theme in mobile browser](images/screenshots/friendica-frio-mobile-profile-2.png?raw=true "Frio theme in mobile browser") |
|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
| *Frio theme, mobile browser. Timeline and composer view.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-green-profile-1.png?raw=true "Frio theme in desktop browser") |
| *Frio theme, desktop browser. Timeline view, contact info popped up, control menu open.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-green-profile-2.png?raw=true "Frio theme in desktop browser") |
| *Frio theme, desktop browser. Menu open for controlling individual posts.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-red-profile-3.png?raw=true "Frio theme in desktop browser") |
| *Frio theme, desktop browser. Profile view, notification menu open.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-red-profile-2.png?raw=true "Frio theme in desktop browser") |
| *Number of new posts, in total and by circle.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-red-profile-1.png?raw=true "Frio theme in desktop browser") |
| *Calendar with popup of event.* |
| ![Frio theme default colour in standard browser on tablet](images/screenshots/friendica-frio-default-profile-1.png?raw=true "Frio theme default colour in standard browser on tablet") |
| *Notifications menu and private messages counter, standard browser on tablet.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-brown-profile-2.png?raw=true "Frio theme in desktop browser") |
| *Number of visible contacts, standard browser.* |
| ![Frio theme in desktop browser](images/screenshots/friendica-frio-brown-profile-1.png?raw=true "Frio theme in desktop browser") |
| *Network posts chronologically ordered, standard browser.* |
| ![Vier theme in desktop browser](images/screenshots/friendica-vier-profile.png?raw=true "Vier theme in desktop browser") |
| *Vier theme, desktop browser. Public timeline view.* |
| ![Vier theme in desktop browser](images/screenshots/friendica-vier-community.png?raw=true "Vier theme in desktop browser") |
| *Vier theme, desktop browser. Community post displayed.* |
## Endorsements
- Friendica is listed on [![Awesome Humane Tech](images/humane-tech-badge.svg)](https://codeberg.org/teaserbot-labs/delightful-humane-design) in the [Fediverse category](https://codeberg.org/teaserbot-labs/delightful-humane-design#fediverse).
- [![Awesome Humane Tech](images/humane-tech-badge.svg)](https://github.com/humanetech-community/awesome-humane-tech) On August 12th 2020, Friendica was added to [the curated Awesome Humane Tech directory](https://github.com/humanetech-community/awesome-humane-tech) in [the "Fediverse" category](https://github.com/humanetech-community/awesome-humane-tech#fediverse).

View file

@ -1 +1 @@
2024.03-dev
2023.09-dev

View file

@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -163,14 +163,9 @@ if (!$foreground) {
exit(1);
} elseif ($pid) {
// The parent process continues here
if (!file_put_contents($pidfile, $pid)) {
echo "Pid file wasn't written.\n";
Logger::warning('Could not store pid file');
posix_kill($pid, SIGTERM);
exit(1);
}
echo 'Child process started with pid ' . $pid . ".\n";
Logger::notice('Child process started', ['pid' => $pid]);
file_put_contents($pidfile, $pid);
exit(0);
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,7 +1,7 @@
#!/usr/bin/php
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,7 +1,7 @@
#!/usr/bin/env php
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -13,7 +13,7 @@
"issues": "https://github.com/friendica/friendica/issues"
},
"require": {
"php": ">=7.4",
"php": ">=7.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
@ -44,7 +44,7 @@
"mattwright/urlresolver": "^2.0",
"michelf/php-markdown": "^1.7",
"minishlink/web-push": "^6.0",
"mobiledetect/mobiledetectlib": "^3.74",
"mobiledetect/mobiledetectlib": "^2.8",
"nikic/fast-route": "^1.3",
"paragonie/hidden-string": "^1.0",
"patrickschur/language-detection": "^5.0.0",
@ -53,7 +53,7 @@
"pragmarx/google2fa": "^5.0",
"pragmarx/recovery": "^0.2",
"psr/clock": "^1.0",
"psr/container": "^2.0",
"psr/container": "^1.0",
"psr/log": "^1.1",
"seld/cli-prompt": "^1.0",
"smarty/smarty": "^4",
@ -102,7 +102,7 @@
},
"config": {
"platform": {
"php": "7.4"
"php": "7.3"
},
"autoloader-suffix": "Friendica",
"optimize-autoloader": true,

320
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "082b16e2c88895f1a03d5b0ffe678ba7",
"content-hash": "c208d7f5176358ea157f109c1c7d68dd",
"packages": [
{
"name": "asika/simple-console",
@ -123,16 +123,16 @@
},
{
"name": "bower-asset/base64",
"version": "1.3.0",
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/davidchambers/Base64.js.git",
"reference": "22192690552ba07bf035f95a5d2d1a0e2f0ced46"
"reference": "0048721faa641f1519c4c081725f42da2ada5e9a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/davidchambers/Base64.js/zipball/22192690552ba07bf035f95a5d2d1a0e2f0ced46",
"reference": "22192690552ba07bf035f95a5d2d1a0e2f0ced46",
"url": "https://api.github.com/repos/davidchambers/Base64.js/zipball/0048721faa641f1519c4c081725f42da2ada5e9a",
"reference": "0048721faa641f1519c4c081725f42da2ada5e9a",
"shasum": ""
},
"type": "bower-asset-library",
@ -150,7 +150,7 @@
"WTFPL"
],
"description": "Base64 encoding and decoding",
"time": "2023-09-18T21:37:26+00:00"
"time": "2023-02-22T16:04:49+00:00"
},
{
"name": "bower-asset/dompurify",
@ -299,16 +299,16 @@
},
{
"name": "composer/ca-bundle",
"version": "1.4.0",
"version": "1.3.5",
"source": {
"type": "git",
"url": "https://github.com/composer/ca-bundle.git",
"reference": "b66d11b7479109ab547f9405b97205640b17d385"
"reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/b66d11b7479109ab547f9405b97205640b17d385",
"reference": "b66d11b7479109ab547f9405b97205640b17d385",
"url": "https://api.github.com/repos/composer/ca-bundle/zipball/74780ccf8c19d6acb8d65c5f39cd72110e132bbd",
"reference": "74780ccf8c19d6acb8d65c5f39cd72110e132bbd",
"shasum": ""
},
"require": {
@ -320,7 +320,7 @@
"phpstan/phpstan": "^0.12.55",
"psr/log": "^1.0",
"symfony/phpunit-bridge": "^4.2 || ^5",
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"
"symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0"
},
"type": "library",
"extra": {
@ -366,20 +366,20 @@
"type": "tidelift"
}
],
"time": "2023-12-18T12:05:55+00:00"
"time": "2023-01-11T08:27:00+00:00"
},
{
"name": "dasprid/enum",
"version": "1.0.5",
"version": "1.0.4",
"source": {
"type": "git",
"url": "https://github.com/DASPRiD/Enum.git",
"reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016"
"reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/6faf451159fb8ba4126b925ed2d78acfce0dc016",
"reference": "6faf451159fb8ba4126b925ed2d78acfce0dc016",
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
"reference": "8e6b6ea76eabbf19ea2bf5b67b98e1860474012f",
"shasum": ""
},
"require": {
@ -412,7 +412,7 @@
"enum",
"map"
],
"time": "2023-08-25T16:18:39+00:00"
"time": "2023-03-01T18:44:03+00:00"
},
{
"name": "divineomega/do-file-cache",
@ -591,20 +591,20 @@
},
{
"name": "ezyang/htmlpurifier",
"version": "v4.17.0",
"version": "v4.16.0",
"source": {
"type": "git",
"url": "https://github.com/ezyang/htmlpurifier.git",
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c"
"reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c",
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c",
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/523407fb06eb9e5f3d59889b3978d5bfe94299c8",
"reference": "523407fb06eb9e5f3d59889b3978d5bfe94299c8",
"shasum": ""
},
"require": {
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0"
},
"require-dev": {
"cerdic/css-tidy": "^1.7 || ^2.0",
@ -644,7 +644,7 @@
"keywords": [
"html"
],
"time": "2023-11-17T15:01:25+00:00"
"time": "2022-09-18T07:06:19+00:00"
},
{
"name": "fgrosse/phpasn1",
@ -1037,16 +1037,16 @@
},
{
"name": "guzzlehttp/promises",
"version": "1.5.3",
"version": "1.5.2",
"source": {
"type": "git",
"url": "https://github.com/guzzle/promises.git",
"reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e"
"reference": "b94b2807d85443f9719887892882d0329d1e2598"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/guzzle/promises/zipball/67ab6e18aaa14d753cc148911d273f6e6cb6721e",
"reference": "67ab6e18aaa14d753cc148911d273f6e6cb6721e",
"url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
"reference": "b94b2807d85443f9719887892882d0329d1e2598",
"shasum": ""
},
"require": {
@ -1056,6 +1056,11 @@
"symfony/phpunit-bridge": "^4.4 || ^5.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
}
},
"autoload": {
"files": [
"src/functions_include.php"
@ -1108,7 +1113,7 @@
"type": "tidelift"
}
],
"time": "2023-05-21T12:31:43+00:00"
"time": "2022-08-28T14:55:35+00:00"
},
{
"name": "guzzlehttp/psr7",
@ -1317,6 +1322,24 @@
"html",
"markdown"
],
"funding": [
{
"url": "https://www.colinodell.com/sponsor",
"type": "custom"
},
{
"url": "https://www.paypal.me/colinpodell/10.00",
"type": "custom"
},
{
"url": "https://github.com/colinodell",
"type": "github"
},
{
"url": "https://www.patreon.com/colinodell",
"type": "patreon"
}
],
"time": "2020-07-01T00:34:03+00:00"
},
{
@ -1601,33 +1624,31 @@
},
{
"name": "mobiledetect/mobiledetectlib",
"version": "3.74.3",
"version": "2.8.41",
"source": {
"type": "git",
"url": "https://github.com/serbanghita/Mobile-Detect.git",
"reference": "39582ab62f86b40e4edb698159f895929a29c346"
"reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/39582ab62f86b40e4edb698159f895929a29c346",
"reference": "39582ab62f86b40e4edb698159f895929a29c346",
"url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1",
"reference": "fc9cccd4d3706d5a7537b562b59cc18f9e4c0cb1",
"shasum": ""
},
"require": {
"php": ">=7.4"
"php": ">=5.0.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.14",
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.7"
"phpunit/phpunit": "~4.8.35||~5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Detection\\": "src/"
"psr-0": {
"Detection": "namespaced/"
},
"classmap": [
"src/MobileDetect.php"
"Mobile_Detect.php"
]
},
"notification-url": "https://packagist.org/downloads/",
@ -1638,7 +1659,7 @@
{
"name": "Serban Ghita",
"email": "serbanghita@gmail.com",
"homepage": "https://mobiledetect.net",
"homepage": "http://mobiledetect.net",
"role": "Developer"
}
],
@ -1651,13 +1672,7 @@
"mobile detector",
"php mobile detect"
],
"funding": [
{
"url": "https://github.com/serbanghita",
"type": "github"
}
],
"time": "2023-10-27T16:28:04+00:00"
"time": "2022-11-08T18:31:26+00:00"
},
{
"name": "nikic/fast-route",
@ -2900,16 +2915,16 @@
},
{
"name": "paragonie/sodium_compat",
"version": "v1.20.0",
"version": "v1.19.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/sodium_compat.git",
"reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6"
"reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/e592a3e06d1fa0d43988c7c7d9948ca836f644b6",
"reference": "e592a3e06d1fa0d43988c7c7d9948ca836f644b6",
"url": "https://api.github.com/repos/paragonie/sodium_compat/zipball/cb15e403ecbe6a6cc515f855c310eb6b1872a933",
"reference": "cb15e403ecbe6a6cc515f855c310eb6b1872a933",
"shasum": ""
},
"require": {
@ -2978,26 +2993,26 @@
"secret-key cryptography",
"side-channel resistant"
],
"time": "2023-04-30T00:54:53+00:00"
"time": "2022-09-26T03:40:35+00:00"
},
{
"name": "patrickschur/language-detection",
"version": "v5.3.0",
"version": "v5.1.0",
"source": {
"type": "git",
"url": "https://github.com/patrickschur/language-detection.git",
"reference": "b8da335336c09fa6814fe0ca0d6d506c357cd7b9"
"reference": "728f1434dcf126ce3ed7118567c014d80d7fdd9c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/patrickschur/language-detection/zipball/b8da335336c09fa6814fe0ca0d6d506c357cd7b9",
"reference": "b8da335336c09fa6814fe0ca0d6d506c357cd7b9",
"url": "https://api.github.com/repos/patrickschur/language-detection/zipball/728f1434dcf126ce3ed7118567c014d80d7fdd9c",
"reference": "728f1434dcf126ce3ed7118567c014d80d7fdd9c",
"shasum": ""
},
"require": {
"ext-json": "*",
"ext-mbstring": "*",
"php": "^7.4 || ^8.0"
"php": "^7.3 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5.0"
@ -3025,7 +3040,7 @@
"detection",
"language"
],
"time": "2023-08-18T22:46:39+00:00"
"time": "2021-03-05T22:18:57+00:00"
},
{
"name": "pear/console_table",
@ -3084,16 +3099,16 @@
},
{
"name": "phpseclib/phpseclib",
"version": "3.0.34",
"version": "3.0.19",
"source": {
"type": "git",
"url": "https://github.com/phpseclib/phpseclib.git",
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a"
"reference": "cc181005cf548bfd8a4896383bb825d859259f95"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/56c79f16a6ae17e42089c06a2144467acc35348a",
"reference": "56c79f16a6ae17e42089c06a2144467acc35348a",
"url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/cc181005cf548bfd8a4896383bb825d859259f95",
"reference": "cc181005cf548bfd8a4896383bb825d859259f95",
"shasum": ""
},
"require": {
@ -3186,7 +3201,7 @@
"type": "tidelift"
}
],
"time": "2023-11-27T11:13:31+00:00"
"time": "2023-03-05T17:13:09+00:00"
},
{
"name": "pragmarx/google2fa",
@ -3463,27 +3478,22 @@
},
{
"name": "psr/container",
"version": "2.0.2",
"version": "1.1.1",
"source": {
"type": "git",
"url": "https://github.com/php-fig/container.git",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963"
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963",
"url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf",
"reference": "8622567409010282b7aeebe4bb841fe98b58dcaf",
"shasum": ""
},
"require": {
"php": ">=7.4.0"
"php": ">=7.2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Psr\\Container\\": "src/"
@ -3508,20 +3518,20 @@
"container-interop",
"psr"
],
"time": "2021-11-05T16:47:00+00:00"
"time": "2021-03-05T17:36:06+00:00"
},
{
"name": "psr/http-client",
"version": "1.0.3",
"version": "1.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-client.git",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90"
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90",
"reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90",
"url": "https://api.github.com/repos/php-fig/http-client/zipball/0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"reference": "0955afe48220520692d2d09f7ab7e0f93ffd6a31",
"shasum": ""
},
"require": {
@ -3557,7 +3567,7 @@
"psr",
"psr-18"
],
"time": "2023-09-23T14:17:50+00:00"
"time": "2023-04-10T20:12:12+00:00"
},
{
"name": "psr/http-factory",
@ -3801,16 +3811,16 @@
},
{
"name": "smarty/smarty",
"version": "v4.3.4",
"version": "v4.3.1",
"source": {
"type": "git",
"url": "https://github.com/smarty-php/smarty.git",
"reference": "3931d8f54b8f7a4ffab538582d34d4397ba8daa5"
"reference": "e28cb0915b4e3749bf57d4ebae2984e25395cfe5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/3931d8f54b8f7a4ffab538582d34d4397ba8daa5",
"reference": "3931d8f54b8f7a4ffab538582d34d4397ba8daa5",
"url": "https://api.github.com/repos/smarty-php/smarty/zipball/e28cb0915b4e3749bf57d4ebae2984e25395cfe5",
"reference": "e28cb0915b4e3749bf57d4ebae2984e25395cfe5",
"shasum": ""
},
"require": {
@ -3858,7 +3868,7 @@
"keywords": [
"templating"
],
"time": "2023-09-14T10:59:08+00:00"
"time": "2023-03-28T19:47:03+00:00"
},
{
"name": "spomky-labs/base64url",
@ -3923,16 +3933,16 @@
},
{
"name": "symfony/polyfill-intl-idn",
"version": "v1.28.0",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-idn.git",
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d"
"reference": "639084e360537a19f9ee352433b84ce831f3d2da"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/ecaafce9f77234a6a449d29e49267ba10499116d",
"reference": "ecaafce9f77234a6a449d29e49267ba10499116d",
"url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/639084e360537a19f9ee352433b84ce831f3d2da",
"reference": "639084e360537a19f9ee352433b84ce831f3d2da",
"shasum": ""
},
"require": {
@ -3946,7 +3956,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -4003,20 +4013,20 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:30:37+00:00"
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-intl-normalizer",
"version": "v1.28.0",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92"
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92",
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6",
"shasum": ""
},
"require": {
@ -4028,7 +4038,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -4084,7 +4094,7 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "symfony/polyfill-php56",
@ -4153,16 +4163,16 @@
},
{
"name": "symfony/polyfill-php72",
"version": "v1.28.0",
"version": "v1.27.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php72.git",
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179"
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/70f4aebd92afca2f865444d30a4d2151c13c3179",
"reference": "70f4aebd92afca2f865444d30a4d2151c13c3179",
"url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97",
"reference": "869329b1e9894268a8a61dabb69153029b7a8c97",
"shasum": ""
},
"require": {
@ -4171,7 +4181,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.28-dev"
"dev-main": "1.27-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -4222,7 +4232,7 @@
"type": "tidelift"
}
],
"time": "2023-01-26T09:26:14+00:00"
"time": "2022-11-03T14:55:06+00:00"
},
{
"name": "ua-parser/uap-php",
@ -4893,38 +4903,38 @@
},
{
"name": "mockery/mockery",
"version": "1.6.7",
"version": "1.5.1",
"source": {
"type": "git",
"url": "https://github.com/mockery/mockery.git",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06"
"reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06",
"url": "https://api.github.com/repos/mockery/mockery/zipball/e92dcc83d5a51851baf5f5591d32cb2b16e3684e",
"reference": "e92dcc83d5a51851baf5f5591d32cb2b16e3684e",
"shasum": ""
},
"require": {
"hamcrest/hamcrest-php": "^2.0.1",
"lib-pcre": ">=7.0",
"php": ">=7.3"
"php": "^7.3 || ^8.0"
},
"conflict": {
"phpunit/phpunit": "<8.0"
},
"require-dev": {
"phpunit/phpunit": "^8.5 || ^9.6.10",
"symplify/easy-coding-standard": "^12.0.8"
"phpunit/phpunit": "^8.5 || ^9.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.4.x-dev"
}
},
"autoload": {
"files": [
"library/helpers.php",
"library/Mockery.php"
],
"psr-4": {
"Mockery\\": "library/Mockery"
"psr-0": {
"Mockery": "library/"
}
},
"notification-url": "https://packagist.org/downloads/",
@ -4935,20 +4945,12 @@
{
"name": "Pádraic Brady",
"email": "padraic.brady@gmail.com",
"homepage": "https://github.com/padraic",
"role": "Author"
"homepage": "http://blog.astrumfutura.com"
},
{
"name": "Dave Marshall",
"email": "dave.marshall@atstsolutions.co.uk",
"homepage": "https://davedevelopment.co.uk",
"role": "Developer"
},
{
"name": "Nathanael Esayeas",
"email": "nathanael.esayeas@protonmail.com",
"homepage": "https://github.com/ghostwriter",
"role": "Lead Developer"
"homepage": "http://davedevelopment.co.uk"
}
],
"description": "Mockery is a simple yet flexible PHP mock object framework",
@ -4965,7 +4967,7 @@
"test double",
"testing"
],
"time": "2023-12-10T02:24:34+00:00"
"time": "2022-09-07T15:32:08+00:00"
},
{
"name": "myclabs/deep-copy",
@ -5024,16 +5026,16 @@
},
{
"name": "nikic/php-parser",
"version": "v4.18.0",
"version": "v4.15.4",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999"
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
"shasum": ""
},
"require": {
@ -5072,7 +5074,7 @@
"parser",
"php"
],
"time": "2023-12-10T21:03:43+00:00"
"time": "2023-03-05T19:49:14+00:00"
},
{
"name": "phar-io/manifest",
@ -5179,16 +5181,16 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.29",
"version": "9.2.26",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76"
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/6a3a87ac2bbe33b25042753df8195ba4aa534c76",
"reference": "6a3a87ac2bbe33b25042753df8195ba4aa534c76",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
"shasum": ""
},
"require": {
@ -5248,7 +5250,7 @@
"type": "github"
}
],
"time": "2023-09-19T04:57:46+00:00"
"time": "2023-03-06T12:58:08+00:00"
},
{
"name": "phpunit/php-file-iterator",
@ -5477,16 +5479,16 @@
},
{
"name": "phpunit/phpunit",
"version": "9.6.15",
"version": "9.6.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1"
"reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1",
"reference": "05017b80304e0eb3f31d90194a563fd53a6021f1",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
"reference": "c993f0d3b0489ffc42ee2fe0bd645af1538a63b2",
"shasum": ""
},
"require": {
@ -5501,7 +5503,7 @@
"phar-io/manifest": "^2.0.3",
"phar-io/version": "^3.0.2",
"php": ">=7.3",
"phpunit/php-code-coverage": "^9.2.28",
"phpunit/php-code-coverage": "^9.2.13",
"phpunit/php-file-iterator": "^3.0.5",
"phpunit/php-invoker": "^3.1.1",
"phpunit/php-text-template": "^2.0.3",
@ -5571,7 +5573,7 @@
"type": "tidelift"
}
],
"time": "2023-12-01T16:55:19+00:00"
"time": "2023-04-14T08:58:40+00:00"
},
{
"name": "sebastian/cli-parser",
@ -5853,16 +5855,16 @@
},
{
"name": "sebastian/diff",
"version": "4.0.5",
"version": "4.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
"shasum": ""
},
"require": {
@ -5911,7 +5913,7 @@
"type": "github"
}
],
"time": "2023-05-07T05:35:17+00:00"
"time": "2020-10-26T13:10:38+00:00"
},
{
"name": "sebastian/environment",
@ -6047,16 +6049,16 @@
},
{
"name": "sebastian/global-state",
"version": "5.0.6",
"version": "5.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/global-state.git",
"reference": "bde739e7565280bda77be70044ac1047bc007e34"
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34",
"reference": "bde739e7565280bda77be70044ac1047bc007e34",
"url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"reference": "0ca8db5a5fc9c8646244e629625ac486fa286bf2",
"shasum": ""
},
"require": {
@ -6103,7 +6105,7 @@
"type": "github"
}
],
"time": "2023-08-02T09:26:13+00:00"
"time": "2022-02-14T08:28:10+00:00"
},
{
"name": "sebastian/lines-of-code",
@ -6475,16 +6477,16 @@
},
{
"name": "theseer/tokenizer",
"version": "1.2.2",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/theseer/tokenizer.git",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96"
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e",
"reference": "34a41e998c2183e22995f158c581e7b5e755ab9e",
"shasum": ""
},
"require": {
@ -6517,7 +6519,7 @@
"type": "github"
}
],
"time": "2023-11-20T00:12:19+00:00"
"time": "2021-07-28T10:34:58+00:00"
}
],
"aliases": [],
@ -6528,7 +6530,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": ">=7.4",
"php": ">=7.3",
"ext-ctype": "*",
"ext-curl": "*",
"ext-dom": "*",
@ -6545,7 +6547,7 @@
},
"platform-dev": [],
"platform-overrides": {
"php": "7.4"
"php": "7.3"
},
"plugin-api-version": "1.1.0"
}

View file

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2024.03-dev (Yellow Archangel)
-- DB_UPDATE_VERSION 1545
-- Friendica 2023.09-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1534
-- ------------------------------------------
@ -73,6 +73,8 @@ CREATE TABLE IF NOT EXISTS `user` (
`blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user',
`hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unknown viewers',
`blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user',
`unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user',
`cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '',
`notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options',
`page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type',
`account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
@ -490,26 +492,6 @@ CREATE TABLE IF NOT EXISTS `cache` (
INDEX `k_expires` (`k`,`expires`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Stores temporary data';
--
-- TABLE channel
--
CREATE TABLE IF NOT EXISTS `channel` (
`id` int unsigned NOT NULL auto_increment COMMENT '',
`uid` mediumint unsigned NOT NULL COMMENT 'User id',
`label` varchar(64) NOT NULL COMMENT 'Channel label',
`description` varchar(64) COMMENT 'Channel description',
`circle` int COMMENT 'Circle or channel that this channel is based on',
`access-key` varchar(1) COMMENT 'Access key',
`include-tags` varchar(1023) COMMENT 'Comma separated list of tags that will be included in the channel',
`exclude-tags` varchar(1023) COMMENT 'Comma separated list of tags that aren\'t allowed in the channel',
`full-text-search` varchar(1023) COMMENT 'Full text search pattern, see https://mariadb.com/kb/en/full-text-index-overview/#in-boolean-mode',
`media-type` smallint unsigned COMMENT 'Filtered media types',
`languages` mediumtext COMMENT 'Desired languages',
PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User defined Channels';
--
-- TABLE config
--
@ -1235,23 +1217,6 @@ CREATE TABLE IF NOT EXISTS `post-category` (
FOREIGN KEY (`tid`) REFERENCES `tag` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='post relation to categories';
--
-- TABLE post-counts
--
CREATE TABLE IF NOT EXISTS `post-counts` (
`uri-id` int unsigned NOT NULL COMMENT 'Id of the item-uri table entry that contains the item uri',
`vid` smallint unsigned NOT NULL COMMENT 'Id of the verb table entry that contains the activity verbs',
`reaction` varchar(1) NOT NULL COMMENT 'Emoji Reaction',
`parent-uri-id` int unsigned COMMENT 'Id of the item-uri table that contains the parent uri',
`count` int unsigned DEFAULT 0 COMMENT 'Number of activities',
PRIMARY KEY(`uri-id`,`vid`,`reaction`),
INDEX `vid` (`vid`),
INDEX `parent-uri-id` (`parent-uri-id`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`vid`) REFERENCES `verb` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
FOREIGN KEY (`parent-uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Original remote activity';
--
-- TABLE post-collection
--
@ -1344,7 +1309,6 @@ CREATE TABLE IF NOT EXISTS `post-engagement` (
`contact-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Person, organisation, news, community, relay',
`media-type` tinyint NOT NULL DEFAULT 0 COMMENT 'Type of media in a bit array (1 = image, 2 = video, 4 = audio',
`language` varbinary(128) COMMENT 'Language information about this post',
`searchtext` mediumtext COMMENT 'Simplified text for the full text search',
`created` datetime COMMENT '',
`restricted` boolean NOT NULL DEFAULT '0' COMMENT 'If true, this post is either unlisted or not from a federated network',
`comments` mediumint unsigned COMMENT 'Number of comments',
@ -1352,7 +1316,6 @@ CREATE TABLE IF NOT EXISTS `post-engagement` (
PRIMARY KEY(`uri-id`),
INDEX `owner-id` (`owner-id`),
INDEX `created` (`created`),
FULLTEXT INDEX `searchtext` (`searchtext`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Engagement data per post';
@ -1550,8 +1513,7 @@ CREATE TABLE IF NOT EXISTS `post-user` (
INDEX `event-id` (`event-id`),
INDEX `psid` (`psid`),
INDEX `author-id_uid` (`author-id`,`uid`),
INDEX `author-id_created` (`author-id`,`created`),
INDEX `owner-id_created` (`owner-id`,`created`),
INDEX `author-id_received` (`author-id`,`received`),
INDEX `parent-uri-id_uid` (`parent-uri-id`,`uid`),
INDEX `uid_wall_received` (`uid`,`wall`,`received`),
INDEX `uid_contactid` (`uid`,`contact-id`),
@ -1612,17 +1574,11 @@ CREATE TABLE IF NOT EXISTS `post-thread-user` (
INDEX `post-user-id` (`post-user-id`),
INDEX `commented` (`commented`),
INDEX `received` (`received`),
INDEX `author-id_created` (`author-id`,`created`),
INDEX `owner-id_created` (`owner-id`,`created`),
INDEX `uid_received` (`uid`,`received`),
INDEX `uid_wall_received` (`uid`,`wall`,`received`),
INDEX `uid_commented` (`uid`,`commented`),
INDEX `uid_created` (`uid`,`created`),
INDEX `uid_starred` (`uid`,`starred`),
INDEX `uid_mention` (`uid`,`mention`),
INDEX `contact-id_commented` (`contact-id`,`commented`),
INDEX `contact-id_received` (`contact-id`,`received`),
INDEX `contact-id_created` (`contact-id`,`created`),
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`conversation-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`owner-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT,
@ -1890,16 +1846,6 @@ CREATE TABLE IF NOT EXISTS `subscription` (
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Push Subscription for the API';
--
-- TABLE check-full-text-search
--
CREATE TABLE IF NOT EXISTS `check-full-text-search` (
`pid` int unsigned NOT NULL COMMENT 'The ID of the process',
`searchtext` mediumtext COMMENT 'Simplified text for the full text search',
PRIMARY KEY(`pid`),
FULLTEXT INDEX `searchtext` (`searchtext`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Check for a full text search match in user defined channels before storing the message in the system';
--
-- TABLE userd
--
@ -2024,65 +1970,6 @@ CREATE VIEW `circle-member-view` AS SELECT
INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id`
INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`;
--
-- VIEW post-counts-view
--
DROP VIEW IF EXISTS `post-counts-view`;
CREATE VIEW `post-counts-view` AS SELECT
`post-counts`.`uri-id` AS `uri-id`,
`post-counts`.`vid` AS `vid`,
`verb`.`name` AS `verb`,
`post-counts`.`reaction` AS `reaction`,
`post-counts`.`parent-uri-id` AS `parent-uri-id`,
`post-counts`.`count` AS `count`
FROM `post-counts`
INNER JOIN `verb` ON `verb`.`id` = `post-counts`.`vid`;
--
-- VIEW post-timeline-view
--
DROP VIEW IF EXISTS `post-timeline-view`;
CREATE VIEW `post-timeline-view` AS SELECT
`post-user`.`uid` AS `uid`,
`post-user`.`uri-id` AS `uri-id`,
`post-user`.`gravity` AS `gravity`,
`post-user`.`created` AS `created`,
`post-user`.`edited` AS `edited`,
`post-thread-user`.`commented` AS `commented`,
`post-user`.`received` AS `received`,
`post-thread-user`.`changed` AS `changed`,
`post-user`.`private` AS `private`,
`post-user`.`visible` AS `visible`,
`post-user`.`deleted` AS `deleted`,
`post-user`.`origin` AS `origin`,
`post-user`.`global` AS `global`,
`post-user`.`network` AS `network`,
`post-user`.`protocol` AS `protocol`,
`post-user`.`vid` AS `vid`,
`post-user`.`contact-id` AS `contact-id`,
`contact`.`blocked` AS `contact-blocked`,
`contact`.`readonly` AS `contact-readonly`,
`contact`.`pending` AS `contact-pending`,
`contact`.`rel` AS `contact-rel`,
`contact`.`uid` AS `contact-uid`,
`contact`.`self` AS `self`,
`post-user`.`author-id` AS `author-id`,
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`gsid` AS `author-gsid`,
`post-user`.`owner-id` AS `owner-id`,
`owner`.`blocked` AS `owner-blocked`,
`owner`.`gsid` AS `owner-gsid`,
`post-user`.`causer-id` AS `causer-id`,
`causer`.`blocked` AS `causer-blocked`,
`causer`.`gsid` AS `causer-gsid`
FROM `post-user`
LEFT JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-user`.`contact-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-user`.`author-id`
STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = `post-user`.`owner-id`
LEFT JOIN `contact` AS `causer` ON `causer`.`id` = `post-user`.`causer-id`;
--
-- VIEW post-user-view
--
@ -2183,7 +2070,6 @@ CREATE VIEW `post-user-view` AS SELECT
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`author`.`baseurl` AS `author-baseurl`,
`post-user`.`owner-id` AS `owner-id`,
@ -2368,7 +2254,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`post-thread-user`.`owner-id` AS `owner-id`,
`owner`.`uri-id` AS `owner-uri-id`,
@ -2537,7 +2422,6 @@ CREATE VIEW `post-view` AS SELECT
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`post`.`owner-id` AS `owner-id`,
`owner`.`uri-id` AS `owner-uri-id`,
@ -2683,7 +2567,6 @@ CREATE VIEW `post-thread-view` AS SELECT
`author`.`blocked` AS `author-blocked`,
`author`.`hidden` AS `author-hidden`,
`author`.`updated` AS `author-updated`,
`author`.`contact-type` AS `author-contact-type`,
`author`.`gsid` AS `author-gsid`,
`post-thread`.`owner-id` AS `owner-id`,
`owner`.`uri-id` AS `owner-uri-id`,
@ -2966,6 +2849,8 @@ CREATE VIEW `owner-view` AS SELECT
`user`.`blockwall` AS `blockwall`,
`user`.`hidewall` AS `hidewall`,
`user`.`blocktags` AS `blocktags`,
`user`.`unkmail` AS `unkmail`,
`user`.`cntunkmail` AS `cntunkmail`,
`user`.`notify-flags` AS `notify-flags`,
`user`.`page-flags` AS `page-flags`,
`user`.`account-type` AS `account-type`,

View file

@ -23,10 +23,10 @@ If you do not have an OpenID address or do not wish to use OpenID, leave this fi
If you have an OpenID account elsewhere and wish to use it, enter the address into this field and click 'Register'.
Friendica will attempt to extract as much information as possible from your OpenID provider and return to this page with those items already filled in.
### Your Display Name
### Your Full Name
Please provide your display name **as you would like it to be displayed on this system**.
Some people use their real name for this, but you're under no obligation to do so yourself.
Please provide your full name **as you would like it to be displayed on this system**.
Most people use their real name for this, but you're under no obligation to do so yourself.
### Email Address

View file

@ -221,15 +221,6 @@ Please note: body contents are bbcode - not HTML
Called when receiving a post from another source. This may also be used to post local activity or system generated messages.
`$b` is the item array of information to be stored in the database and the item body is bbcode.
### detect_languages
Called after the language detection. This can be used for alternative language detection methods.
`$data` is an array:
- **text**: The text that is analyzed.
- **detected**: (input/output) Array of language codes detected in the related text. The array key is the language code, the array value the probability.
- **uri-id**: The Uri-Id of the item.
- **author-id**: The id of the author contact.
### addon_settings
Called when generating the HTML for the addon settings page.
`$data` is an array containing:
@ -809,7 +800,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
### src/Model/Item.php
Hook::callAll('detect_languages', $item);
Hook::callAll('post_local', $item);
Hook::callAll('post_remote', $item);
Hook::callAll('post_local_end', $posted_item);

View file

@ -1,85 +0,0 @@
Channels
=====
* [Home](help)
Channels are a way to discover new content or to display content that you might have missed otherwise.
There are several predefined channels, additionally you can create your own channels, based on some rules.
Channels only display posts from the last 24 hours (this value can be changed by the admin).
In the display settings in the section "Timelines" you can define which channels and other timelines you want to see in the "Channels" widget on the network page and which channels should appear in the menu bar at the top of the page.
Also in the display settings in the section "Channels" you can define all the languages that you want to see in your channels. Here you can select more than one language.
On the contact page you can define the channel frequency for every contact. The options are:
* Default frequency: Posts by this contact are displayed in the "for you" channel if you interact often with this contact or if a post reached some level of interaction.
* Display all posts of this contact: All posts from this contact will appear on the "for you" channel.
* Display only few posts: When a contact creates a lot of posts in a short period, this setting reduces the number of displayed posts in every channel.
* Never display posts: Posts from this contact will never be displayed in any channel.
Predefined Channels
---
* For you: Posts from contacts you interact with and who interact with you. In detail, it consists of:
* Posts from people you interact with on a more than average level.
* Posts from the accounts that you follow with a more than average number of interactions-
* Posts from accounts where you activated "notify on new posts" or where you have set the channel frequency accordingly.
* What's Hot: Posts with a more than average number of interactions.
* Language: Posts in your language.
* Followers: Posts from your followers that you don't follow.
* Sharers of sharers: Posts from accounts that are followed by accounts that you follow.
* Images: Posts with images.
* Audio: Posts with audio.
* Videos: Posts with videos.
User defined Channels
---
In the "Channels" settings you can create your own channels.
Each channel is defined by these values:
* Label: This value is mandatory and is used for the menu label.
* Description: A short description of the content. This can help to keep the overview, when you have got a lot of channels.
* Access Key: When you want to access this channel via an access key, you can define it here. Pay attention to not use an already used one.
* Circle: This defines the data source for this channel. By default it is set to the public timeline. There are some predefined values, like the accounts that you follow or the accounts that follow you. Also all of your circles can be selected.
* Include Tags: Comma separated list of tags. A post will be used when it contains any of the listed tags.
* Exclude Tags: Comma separated list of tags. If a post contain any of these tags, then it will not be part of nthis channel.
* Full Text Search: This can be used to include or exclude content, based on the content and some additional keywords. It uses the "boolean mode" operators from MariaDB: https://mariadb.com/kb/en/full-text-index-overview/#in-boolean-mode
* Images, Videos, Audio: When selected, you will see content with the selected media type. This can be combined. If none of these fields are checked, you will see any content, with or without attacked media.
Additional keywords for the full text search
---
Additionally to the search for content, there are additional keywords that can be used in the full text search:
* from - Use "from:nickname" or "from:nickname@domain.tld" to search for posts from a specific author.
* to - Use "from:nickname" or "from:nickname@domain.tld" to search for posts with the given contact as receiver.
* group - Use "from:nickname" or "from:nickname@domain.tld" to search for group post of the given group.
* server - Use "server:hostname" to search for posts from a specific server. In the case of group postings, the search text contains both the hostname of the group server and the author's hostname.
* source - The ActivityPub type of the post source. Use this for example to include or exclude group posts or posts from services (aka bots).
* source:person - The post is created by a regular user account.
* source:organization - The post is created by an organisation.
* source:group - The post is created by or distributed via a group.
* source:service - The posts originates from a service account. This source type is often used to mark bot accounts.
* source:application - The post is created by an application. This is most likely unused in the fediverse for post creation.
* tag - Use "tag:tagname" to search for a specific tag.
* network - Use this to include or exclude some networks from your channel.
* network:apub - ActivityPub (Used by the systems in the Fediverse)
* network:dfrn - Legacy Friendica protocol. Nowayday Friendica mostly uses ActivityPub.
* network:dspr - The Diaspora protocol is mainly used by Diaspora itself. Some other systems support the protocol as well like Hubzilla, Socialhome or Ganggo.
* network:feed - RSS/Atom feeds
* network:mail - Mails that had been imported via IMAP.
* network:stat - The OStatus protocol is mainly used by old GNU Social installations.
* network:dscs - Posts that are received by the Discourse connector.
* network:tmbl - Posts that are received by the Tumblr connector.
* network:bsky - Posts that are received by the Bluesky connector.
* platform - Use this to include or exclude some platforms from your channel, e.g. "+platform:friendica". In the case of group postings, the search text contains both the platform of the group server and the author's platform.
* visibility - You have the choice between different visibilities. You can only see unlisted or private posts that you have the access for.
* visibility:public
* visibility:unlisted
* visibility:private
Remember that you can combine these kerywords.
So for example you can create a channel with all posts that talk about the Fediverse - that aren't posted in the Fediverse with the search terms: "fediverse -network:apub -network:dfrn"

View file

@ -17,7 +17,6 @@ Friendica Documentation and Resources
* [Circles and Privacy](help/Circles-and-Privacy)
* [Tags and Mentions](help/Tags-and-Mentions)
* [Community Groups](help/Groups)
* [Channels](help/Channels)
* [Chats](help/Chats)
* Further information
* [Move your account](help/Move-Account)

View file

@ -28,9 +28,9 @@ Due to the large variety of operating systems and PHP platforms in existence we
### Requirements
* Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file
* PHP 7.4+
* PHP 7.3+ (PHP8 is not fully supported yet)
* PHP *command line* access with register_argc_argv set to true in the php.ini file
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar and OpenSSL extensions
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip and OpenSSL extensions
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
* Some form of email server or email gateway such that PHP mail() works.
If you cannot set up your own email server, you can use the [phpmailer](https://github.com/friendica/friendica-addons/tree/develop/phpmailer) addon and use a remote SMTP server.

View file

@ -77,11 +77,11 @@ Additionally to the setting in the admin panel, you can decide if registrations
To enable invitation based registration, you have to set the `invitation_only` setting to `true` in the `system` section of the [config/local.config.php](/help/Config) file.
If you want to use this method, the registration policy has to be set to either *open* or *requires approval*.
#### Check Display Names
#### Check Full Names
You may find a lot of spammers trying to register on your site.
During testing we discovered that since these registrations were automatic, the "Display Name" field was often set to just an account name with no space between first and last name.
If you would like to support people with only one name as their display name, you can leave this setting set to false.
During testing we discovered that since these registrations were automatic, the "Full Name" field was often set to just an account name with no space between first and last name.
If you would like to support people with only one name as their full name, you may change this setting to true.
Default is false.
#### OpenID

View file

@ -17,8 +17,6 @@ Database Tables
| [arrived-activity](help/database/db_arrived-activity) | Id of arrived activities |
| [attach](help/database/db_attach) | file attachments |
| [cache](help/database/db_cache) | Stores temporary data |
| [channel](help/database/db_channel) | User defined Channels |
| [check-full-text-search](help/database/db_check-full-text-search) | Check for a full text search match in user defined channels before storing the message in the system |
| [config](help/database/db_config) | main configuration storage |
| [contact](help/database/db_contact) | contact table |
| [contact-relation](help/database/db_contact-relation) | Contact relations |
@ -61,7 +59,6 @@ Database Tables
| [post-category](help/database/db_post-category) | post relation to categories |
| [post-collection](help/database/db_post-collection) | Collection of posts |
| [post-content](help/database/db_post-content) | Content for all posts |
| [post-counts](help/database/db_post-counts) | Original remote activity |
| [post-delivery](help/database/db_post-delivery) | Delivery data for posts for the batch processing |
| [post-delivery-data](help/database/db_post-delivery-data) | Delivery data for items |
| [post-engagement](help/database/db_post-engagement) | Engagement data per post |

View file

@ -1,38 +0,0 @@
Table channel
===========
User defined Channels
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ---------------- | ------------------------------------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | -------------- |
| id | | int unsigned | NO | PRI | NULL | auto_increment |
| uid | User id | mediumint unsigned | NO | | NULL | |
| label | Channel label | varchar(64) | NO | | NULL | |
| description | Channel description | varchar(64) | YES | | NULL | |
| circle | Circle or channel that this channel is based on | int | YES | | NULL | |
| access-key | Access key | varchar(1) | YES | | NULL | |
| include-tags | Comma separated list of tags that will be included in the channel | varchar(1023) | YES | | NULL | |
| exclude-tags | Comma separated list of tags that aren't allowed in the channel | varchar(1023) | YES | | NULL | |
| full-text-search | Full text search pattern, see https://mariadb.com/kb/en/full-text-index-overview/#in-boolean-mode | varchar(1023) | YES | | NULL | |
| media-type | Filtered media types | smallint unsigned | YES | | NULL | |
| languages | Desired languages | mediumtext | YES | | NULL | |
Indexes
------------
| Name | Fields |
| ------- | ------ |
| PRIMARY | id |
| uid | uid |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uid | [user](help/database/db_user) | uid |
Return to [database documentation](help/database)

View file

@ -1,23 +0,0 @@
Table check-full-text-search
===========
Check for a full text search match in user defined channels before storing the message in the system
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ---------- | ---------------------------------------- | ------------ | ---- | --- | ------- | ----- |
| pid | The ID of the process | int unsigned | NO | PRI | NULL | |
| searchtext | Simplified text for the full text search | mediumtext | YES | | NULL | |
Indexes
------------
| Name | Fields |
| ---------- | -------------------- |
| PRIMARY | pid |
| searchtext | FULLTEXT, searchtext |
Return to [database documentation](help/database)

View file

@ -1,35 +0,0 @@
Table post-counts
===========
Original remote activity
Fields
------
| Field | Description | Type | Null | Key | Default | Extra |
| ------------- | ----------------------------------------------------------- | ----------------- | ---- | --- | ------- | ----- |
| uri-id | Id of the item-uri table entry that contains the item uri | int unsigned | NO | PRI | NULL | |
| vid | Id of the verb table entry that contains the activity verbs | smallint unsigned | NO | PRI | NULL | |
| reaction | Emoji Reaction | varchar(1) | NO | PRI | NULL | |
| parent-uri-id | Id of the item-uri table that contains the parent uri | int unsigned | YES | | NULL | |
| count | Number of activities | int unsigned | YES | | 0 | |
Indexes
------------
| Name | Fields |
| ------------- | --------------------- |
| PRIMARY | uri-id, vid, reaction |
| vid | vid |
| parent-uri-id | parent-uri-id |
Foreign Keys
------------
| Field | Target Table | Target Field |
|-------|--------------|--------------|
| uri-id | [item-uri](help/database/db_item-uri) | id |
| vid | [verb](help/database/db_verb) | id |
| parent-uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database)

View file

@ -13,7 +13,6 @@ Fields
| contact-type | Person, organisation, news, community, relay | tinyint | NO | | 0 | |
| media-type | Type of media in a bit array (1 = image, 2 = video, 4 = audio | tinyint | NO | | 0 | |
| language | Language information about this post | varbinary(128) | YES | | NULL | |
| searchtext | Simplified text for the full text search | mediumtext | YES | | NULL | |
| created | | datetime | YES | | NULL | |
| restricted | If true, this post is either unlisted or not from a federated network | boolean | NO | | 0 | |
| comments | Number of comments | mediumint unsigned | YES | | NULL | |
@ -22,12 +21,11 @@ Fields
Indexes
------------
| Name | Fields |
| ---------- | -------------------- |
| PRIMARY | uri-id |
| owner-id | owner-id |
| created | created |
| searchtext | FULLTEXT, searchtext |
| Name | Fields |
| -------- | -------- |
| PRIMARY | uri-id |
| owner-id | owner-id |
| created | created |
Foreign Keys
------------

View file

@ -36,31 +36,25 @@ Fields
Indexes
------------
| Name | Fields |
| -------------------- | --------------------- |
| PRIMARY | uid, uri-id |
| uri-id | uri-id |
| conversation-id | conversation-id |
| owner-id | owner-id |
| author-id | author-id |
| causer-id | causer-id |
| uid | uid |
| contact-id | contact-id |
| psid | psid |
| post-user-id | post-user-id |
| commented | commented |
| received | received |
| author-id_created | author-id, created |
| owner-id_created | owner-id, created |
| uid_received | uid, received |
| uid_wall_received | uid, wall, received |
| uid_commented | uid, commented |
| uid_created | uid, created |
| uid_starred | uid, starred |
| uid_mention | uid, mention |
| contact-id_commented | contact-id, commented |
| contact-id_received | contact-id, received |
| contact-id_created | contact-id, created |
| Name | Fields |
| ----------------- | ------------------- |
| PRIMARY | uid, uri-id |
| uri-id | uri-id |
| conversation-id | conversation-id |
| owner-id | owner-id |
| author-id | author-id |
| causer-id | causer-id |
| uid | uid |
| contact-id | contact-id |
| psid | psid |
| post-user-id | post-user-id |
| commented | commented |
| received | received |
| uid_received | uid, received |
| uid_wall_received | uid, wall, received |
| uid_commented | uid, commented |
| uid_starred | uid, starred |
| uid_mention | uid, mention |
Foreign Keys
------------

View file

@ -58,8 +58,7 @@ Indexes
| event-id | event-id |
| psid | psid |
| author-id_uid | author-id, uid |
| author-id_created | author-id, created |
| owner-id_created | owner-id, created |
| author-id_received | author-id, received |
| parent-uri-id_uid | parent-uri-id, uid |
| uid_wall_received | uid, wall, received |
| uid_contactid | uid, contact-id |

View file

@ -34,6 +34,8 @@ Fields
| blockwall | Prohibit contacts to post to the profile page of the user | boolean | NO | | 0 | |
| hidewall | Hide profile details from unknown viewers | boolean | NO | | 0 | |
| blocktags | Prohibit contacts to tag the post of this user | boolean | NO | | 0 | |
| unkmail | Permit unknown people to send private mails to this user | boolean | NO | | 0 | |
| cntunkmail | | int unsigned | NO | | 10 | |
| notify-flags | email notification options | smallint unsigned | NO | | 65535 | |
| page-flags | page/profile type | tinyint unsigned | NO | | 0 | |
| account-type | | tinyint unsigned | NO | | 0 | |

View file

@ -103,15 +103,6 @@ Derzeitige Hooks
$b ist das Item-Array einer Information, die in der Datenbank und im Item gespeichert ist.
{Bitte beachte: der Seiteninhalt ist bbcode - nicht HTML)
**'detect_languages'**
Wird nach der Sprachenerkennung aufgerufen.
Dieser Hook kann dafür verwendet werden, alternative Erkennungsfunktionen einzubinden.
`$data` ist ein Array:
'text' => Der analysierte Text.
'detected' => (Eingabe/Ausgabe) Das Array mit den erkannten Sprachen. Der Sprachcode ist der Array-Schlüssel, der Array-Wert ist der dezimale Wert für die Wahrscheinlichkeit.
'uri-id' => Die Uri-Id des Beitrags
'author-id' => Die Contact-id des Autors.
**'addon_settings'** - wird aufgerufen, wenn die HTML-Ausgabe der Addon-Einstellungsseite generiert wird.
$b ist die HTML-Ausgabe (String) der Addon-Einstellungsseite vor dem finalen "</form>"-Tag.
@ -325,7 +316,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
### src/Model/Item.php
Hook::callAll('detect_languages', $item);
Hook::callAll('post_local', $item);
Hook::callAll('post_remote', $item);
Hook::callAll('post_local_end', $posted_item);

View file

@ -17,7 +17,6 @@ Friendica - Dokumentation und Ressourcen
* [Circles und Privatsphäre](help/Circles-and-Privacy)
* [Tags und Erwähnungen](help/Tags-and-Mentions)
* [Community-Gruppen](help/Groups)
* [Channels](help/Channels)
* [Chats](help/Chats)
* Weiterführende Informationen
* [Account umziehen](help/Move-Account)

View file

@ -25,9 +25,9 @@ Requirements
---
* Apache mit einer aktiverten mod-rewrite-Funktion und dem Eintrag "Options All", so dass du die lokale .htaccess-Datei nutzen kannst
* PHP 7.4+
* PHP 7.3+ (PHP 8 wird noch nicht komplett unterstützt)
* PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
* Curl, GD, GMP, PDO, mbstrings, MySQLi, hash, xml, zip, IntlChar and OpenSSL-Erweiterung
* Curl, GD, GMP, PDO, MySQLi, xml, zip und OpenSSL-Erweiterung
* Das POSIX Modul muss aktiviert sein ([CentOS, RHEL](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) haben dies z.B. deaktiviert)
* Einen E-Mail Server, so dass PHP `mail()` funktioniert.
Wenn kein eigener E-Mail Server zur Verfügung steht, kann alternativ das [phpmailer](https://github.com/friendica/friendica-addons/tree/develop/phpmailer) Addon mit einem externen SMTP Account verwendet werden.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,011 KiB

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -221,10 +221,6 @@ function item_insert(int $uid, array $request, bool $preview, string $return_pat
DI::contentItem()->postProcessPost($post, $recipients);
if (($post['private'] == Item::PRIVATE) && ($post['thr-parent-id'] != $post['uri-id'])) {
DI::contentItem()->copyPermissions($post['thr-parent-id'], $post['uri-id'], $post['parent-uri-id']);
}
Logger::debug('post_complete');
item_post_return(DI::baseUrl(), $return_path);

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -312,16 +312,16 @@ function photos_post(App $a)
Photo::update(['height' => $height, 'width' => $width], ['resource-id' => $resource_id, 'uid' => $page_owner_uid, 'scale' => 0], $image);
if ($width > \Friendica\Util\Proxy::PIXEL_MEDIUM || $height > \Friendica\Util\Proxy::PIXEL_MEDIUM) {
$image->scaleDown(\Friendica\Util\Proxy::PIXEL_MEDIUM);
if ($width > 640 || $height > 640) {
$image->scaleDown(640);
$width = $image->getWidth();
$height = $image->getHeight();
Photo::update(['height' => $height, 'width' => $width], ['resource-id' => $resource_id, 'uid' => $page_owner_uid, 'scale' => 1], $image);
}
if ($width > \Friendica\Util\Proxy::PIXEL_SMALL || $height > \Friendica\Util\Proxy::PIXEL_SMALL) {
$image->scaleDown(\Friendica\Util\Proxy::PIXEL_SMALL);
if ($width > 320 || $height > 320) {
$image->scaleDown(320);
$width = $image->getWidth();
$height = $image->getHeight();

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -31,7 +31,7 @@ use Friendica\Model\Contact;
function update_contact_content(App $a)
{
if (!empty(DI::args()->get(1)) && !empty($_GET['force'])) {
$contact = DBA::selectFirst('account-user-view', ['pid', 'deleted'], ['id' => DI::args()->get(1)]);
$contact = Contact::getById(DI::args()->get(1), ['id', 'deleted']);
if (DBA::isResult($contact) && empty($contact['deleted'])) {
DI::page()['aside'] = '';
@ -39,7 +39,7 @@ function update_contact_content(App $a)
$item = Post::selectFirst(['parent'], ['id' => $_GET['item']]);
}
$text = Contact::getThreadsFromId($contact['pid'], DI::userSession()->getLocalUserId(), true, $item['parent'] ?? 0, $_GET['last_received'] ?? '');
$text = Contact::getPostsFromId($contact['id'], true, true, $item['parent'] ?? 0);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
Contact: mailto:info@friendi.ca
Expires: 2024-10-30T23:59:59Z
Expires: 2024-04-30T23:59:59Z
Preferred-Languages: en

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -63,8 +63,8 @@ use Psr\Log\LoggerInterface;
class App
{
const PLATFORM = 'Friendica';
const CODENAME = 'Yellow Archangel';
const VERSION = '2024.03-dev';
const CODENAME = 'Giant Rhubarb';
const VERSION = '2023.09-dev';
// Allow themes to control internal parameters
// by changing App values in theme.php
@ -567,7 +567,6 @@ class App
{
$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'] ?? '']);
$request_start = microtime(true);
$this->profiler->set($start_time, 'start');
$this->profiler->set(microtime(true), 'classinit');
@ -716,10 +715,10 @@ class App
$response = $page->run($this, $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)]);
System::echoResponse($response);
$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'] ?? '']);
$page->exit($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->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'] ?? '']);
$httpException->rawContent($e);
}
$page->logRuntime($this->config, 'runFrontend');

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -36,7 +36,6 @@ use Friendica\Core\System;
use Friendica\Core\Theme;
use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Util\Images;
use Friendica\Util\Network;
use Friendica\Util\Profiler;
use Friendica\Util\Strings;
@ -283,7 +282,7 @@ class Page implements ArrayAccess
'$stylesheets' => $this->stylesheets,
// Dropzone
'$max_imagesize' => round(Images::getMaxUploadBytes() / 1000000, 0),
'$max_imagesize' => round(\Friendica\Util\Strings::getBytesFromShorthand($config->get('system', 'maximagesize')) / 1000000, 1),
]) . $this->page['htmlhead'];
}
@ -402,6 +401,36 @@ class Page implements ArrayAccess
$this->footerScripts[] = trim($url, '/');
}
/**
* Directly exit with the current response (include setting all headers)
*
* @param ResponseInterface $response
*/
public function exit(ResponseInterface $response)
{
header(sprintf("HTTP/%s %s %s",
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase())
);
foreach ($response->getHeaders() as $key => $header) {
if (is_array($header)) {
$header_str = implode(',', $header);
} else {
$header_str = $header;
}
if (empty($key)) {
header($header_str);
} else {
header("$key: $header_str");
}
}
echo $response->getBody();
}
/**
* Executes the creation of the current page and prints it to the screen
*
@ -497,9 +526,7 @@ class Page implements ArrayAccess
}
if ($_GET["mode"] == "raw") {
$response->withBody(Utils::streamFor($target->saveHTML()));
System::echoResponse($response);
System::exit();
System::httpExit(substr($target->saveHTML(), 6, -8), Response::TYPE_HTML);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -129,24 +129,6 @@ class BaseCollection extends \ArrayIterator
return new static(array_reverse($this->getArrayCopy()), $this->getTotalCount());
}
/**
* Split the collection in smaller collections no bigger than the provided length
*
* @param int $length
* @return static[]
*/
public function chunk(int $length): array
{
if ($length < 1) {
throw new \RangeException('BaseCollection->chunk(): Size parameter expected to be greater than 0');
}
return array_map(function ($array) {
return new static($array);
}, array_chunk($this->getArrayCopy(), $length));
}
/**
* @inheritDoc
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -27,7 +27,6 @@ use Friendica\Capabilities\ICanCreateResponses;
use Friendica\Core\Hook;
use Friendica\Core\L10n;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Model\User;
use Friendica\Module\Response;
use Friendica\Module\Special\HTTPException as ModuleHTTPException;
@ -107,7 +106,8 @@ abstract class BaseModule implements ICanHandleRequests
*/
protected function rawContent(array $request = [])
{
// $this->httpExit(...);
// echo '';
// exit;
}
/**
@ -189,11 +189,6 @@ abstract class BaseModule implements ICanHandleRequests
$this->response->setHeader('*', 'Access-Control-Allow-Headers');
$this->response->setHeader(Router::GET, 'Access-Control-Allow-Methods');
$this->response->setHeader('false', 'Access-Control-Allow-Credentials');
} elseif (substr($this->args->getQueryString(), 0, 9) == 'nodeinfo/') {
$this->response->setHeader('*', 'Access-Control-Allow-Origin');
$this->response->setHeader('*', 'Access-Control-Allow-Headers');
$this->response->setHeader(Router::GET, 'Access-Control-Allow-Methods');
$this->response->setHeader('false', 'Access-Control-Allow-Credentials');
} elseif (substr($this->args->getQueryString(), 0, 8) == 'profile/') {
$this->response->setHeader('*', 'Access-Control-Allow-Origin');
$this->response->setHeader('*', 'Access-Control-Allow-Headers');
@ -239,8 +234,7 @@ abstract class BaseModule implements ICanHandleRequests
$timestamp = microtime(true);
// "rawContent" is especially meant for technical endpoints.
// This endpoint doesn't need any theme initialization or
// templating and is expected to exit on its own if it is set.
// This endpoint doesn't need any theme initialization or other comparable stuff.
$this->rawContent($request);
try {
@ -462,76 +456,4 @@ abstract class BaseModule implements ICanHandleRequests
return $tabs;
}
/**
* This function adds the content and a content-type HTTP header to the output.
* After finishing the process is getting killed.
*
* @param string $content
* @param string $type
* @param string|null $content_type
* @return void
* @throws HTTPException\InternalServerErrorException
*/
public function httpExit(string $content, string $type = Response::TYPE_HTML, ?string $content_type = null)
{
$this->response->setType($type, $content_type);
$this->response->addContent($content);
System::echoResponse($this->response->generate());
System::exit();
}
/**
* Send HTTP status header and exit.
*
* @param integer $httpCode HTTP status result value
* @param string $message Error message. Optional.
* @param mixed $content Response body. Optional.
* @throws \Exception
*/
public function httpError(int $httpCode, string $message = '', $content = '')
{
if ($httpCode >= 400) {
$this->logger->debug('Exit with error', ['code' => $httpCode, 'message' => $message, 'method' => $this->args->getMethod(), 'agent' => $this->server['HTTP_USER_AGENT'] ?? '']);
}
$this->response->setStatus($httpCode, $message);
$this->httpExit($content);
}
/**
* Display the response using JSON to encode the content
*
* @param mixed $content
* @param string $content_type
* @param int $options A combination of json_encode() binary flags
* @return void
* @throws HTTPException\InternalServerErrorException
* @see json_encode()
*/
public function jsonExit($content, string $content_type = 'application/json', int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)
{
$this->httpExit(json_encode($content, $options), ICanCreateResponses::TYPE_JSON, $content_type);
}
/**
* Display a non-200 HTTP code response using JSON to encode the content and exit
*
* @param int $httpCode
* @param mixed $content
* @param string $content_type
* @return void
* @throws HTTPException\InternalServerErrorException
*/
public function jsonError(int $httpCode, $content, string $content_type = 'application/json')
{
if ($httpCode >= 400) {
$this->logger->debug('Exit with error', ['code' => $httpCode, 'content_type' => $content_type, 'method' => $this->args->getMethod(), 'agent' => $this->server['HTTP_USER_AGENT'] ?? '']);
}
$this->response->setStatus($httpCode);
$this->jsonExit($content, $content_type);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -70,7 +70,7 @@ interface ICanCreateResponses
*
* @throws InternalServerErrorException
*/
public function setType(string $type = ICanCreateResponses::TYPE_HTML, ?string $content_type = null): void;
public function setType(string $type, ?string $content_type = null): void;
/**
* Sets the status and the reason for the response

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -222,7 +222,7 @@ class ContactSelector
'pleroma' => 'pleroma', 'red' => 'hubzilla', 'redmatrix' => 'hubzilla',
'socialhome' => 'social-home', 'wordpress' => 'wordpress', 'lemmy' => 'users',
'plume' => 'plume', 'funkwhale' => 'funkwhale', 'nextcloud' => 'nextcloud', 'drupal' => 'drupal',
'firefish' => 'fire', 'calckey' => 'calculator', 'kbin' => 'check', 'threads' => 'instagram'];
'firefish' => 'fire', 'calckey' => 'calculator', 'kbin' => 'check'];
$search = array_keys($nets);
$replace = array_values($nets);

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -251,21 +251,19 @@ class Conversation
/**
* Format the activity text for an item/photo/video
*
* @param array $links array of pre-linked names of actors
* @param string $verb one of 'like, 'dislike', 'attendyes', 'attendno', 'attendmaybe'
* @param int $id item id
* @param string $activity Activity URI
* @param array $emojis Array with emoji reactions
* @param array $links = array of pre-linked names of actors
* @param string $verb = one of 'like, 'dislike', 'attendyes', 'attendno', 'attendmaybe'
* @param int $id = item id
* @return string formatted text
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
public function formatActivity(array $links, string $verb, int $id, string $activity, array $emojis): string
public function formatActivity(array $links, string $verb, int $id): string
{
$this->profiler->startRecording('rendering');
$expanded = '';
$phrase = $this->getLikerPhrase($verb, $links);
$total = max(count($links), $emojis[$activity]['total'] ?? 0);
$total = count($links);
if ($total > 1) {
$spanatts = "class=\"btn btn-link fakelink\" onclick=\"openClose('{$verb}list-$id');\"";
@ -569,7 +567,7 @@ class Conversation
$live_update_div = '<div id="live-search"></div>' . "\r\n";
}
$page_dropping = $this->session->getLocalUserId() && $this->pConfig->get($this->session->getLocalUserId(), 'system', 'show_page_drop', true) && ($this->session->getLocalUserId() == $uid && $mode != self::MODE_SEARCH);
$page_dropping = $this->session->getLocalUserId() && $this->session->getLocalUserId() == $uid && $mode != self::MODE_SEARCH;
if (!$update) {
$_SESSION['return_path'] = $this->args->getQueryString();
@ -656,6 +654,10 @@ class Conversation
* But for now, this array respects the old style, just in case
*/
foreach ($items as $item) {
if (in_array($item['author-id'], $this->getBlocklist())) {
continue;
}
// Can we put this after the visibility check?
$this->builtinActivityPuller($item, $conv_responses);
@ -690,6 +692,29 @@ class Conversation
return $threads;
}
private function getBlocklist(): array
{
if (!$this->session->getLocalUserId()) {
return [];
}
$str_blocked = str_replace(["\n", "\r"], ",", $this->pConfig->get($this->session->getLocalUserId(), 'system', 'blocked') ?? '');
if (empty($str_blocked)) {
return [];
}
$blocklist = [];
foreach (explode(',', $str_blocked) as $entry) {
$cid = Contact::getIdForURL(trim($entry), 0, false);
if (!empty($cid)) {
$blocklist[] = $cid;
}
}
return $blocklist;
}
/**
* Adds some information (Causer, post reason, direction) to the fetched post row.
*
@ -864,17 +889,14 @@ class Conversation
$condition['author-hidden'] = false;
}
$emojis = $this->getEmojis($uriids);
$quoteshares = $this->getQuoteShares($uriids);
$counts = $this->getCounts($uriids);
if (!$this->config->get('system', 'legacy_activities')) {
if ($this->config->get('system', 'emoji_activities')) {
$emojis = $this->getEmojis($uriids);
$condition = DBA::mergeConditions($condition, ["(`gravity` != ? OR `origin`)", ItemModel::GRAVITY_ACTIVITY]);
}
$condition = DBA::mergeConditions(
$condition,
["`uid` IN (0, ?) AND (NOT `verb` IN (?, ?, ?) OR `verb` IS NULL)", $uid, Activity::FOLLOW, Activity::VIEW, Activity::READ]
["`uid` IN (0, ?) AND (NOT `vid` IN (?, ?, ?) OR `vid` IS NULL)", $uid, Verb::getID(Activity::FOLLOW), Verb::getID(Activity::VIEW), Verb::getID(Activity::READ)]
);
$condition = DBA::mergeConditions($condition, ["(`uid` != ? OR `private` != ?)", 0, ItemModel::PRIVATE]);
@ -990,9 +1012,7 @@ class Conversation
}
foreach ($items as $key => $row) {
$items[$key]['emojis'] = $emojis[$key] ?? [];
$items[$key]['counts'] = $counts[$key] ?? 0;
$items[$key]['quoteshares'] = $quoteshares[$key] ?? [];
$items[$key]['emojis'] = $emojis[$key] ?? [];
$always_display = in_array($mode, [self::MODE_CONTACTS, self::MODE_CONTACT_POSTS]);
@ -1025,16 +1045,6 @@ class Conversation
*/
private function getEmojis(array $uriids): array
{
$emojis = [];
foreach (Post\Counts::get(['parent-uri-id' => $uriids]) as $count) {
$emojis[$count['uri-id']][$count['reaction']]['emoji'] = $count['reaction'];
$emojis[$count['uri-id']][$count['reaction']]['verb'] = Verb::getByID($count['vid']);
$emojis[$count['uri-id']][$count['reaction']]['total'] = $count['count'];
$emojis[$count['uri-id']][$count['reaction']]['title'] = [];
}
// @todo The following code should be removed, once that we display activity authors on demand
$activity_emoji = [
Activity::LIKE => '👍',
Activity::DISLIKE => '👎',
@ -1043,74 +1053,37 @@ class Conversation
Activity::ATTENDNO => '❌',
Activity::ANNOUNCE => '♻',
Activity::VIEW => '📺',
Activity::READ => '📖',
];
$verbs = array_merge(array_keys($activity_emoji), [Activity::EMOJIREACT, Activity::POST]);
$condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => [ItemModel::GRAVITY_ACTIVITY, ItemModel::GRAVITY_COMMENT], 'verb' => $verbs], ["NOT `deleted`"]);
$index_list = array_values($activity_emoji);
$verbs = array_merge(array_keys($activity_emoji), [Activity::EMOJIREACT]);
$condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => ItemModel::GRAVITY_ACTIVITY, 'verb' => $verbs], ["NOT `deleted`"]);
$separator = chr(255) . chr(255) . chr(255);
$sql = "SELECT `parent-uri-id`, `thr-parent-id`, `body`, `verb`, `gravity`, GROUP_CONCAT(REPLACE(`author-name`, '" . $separator . "', ' ') SEPARATOR '" . $separator . "' LIMIT 50) AS `title` FROM `post-view` WHERE " . array_shift($condition) . " GROUP BY `parent-uri-id`, `thr-parent-id`, `verb`, `body`, `gravity`";
$sql = "SELECT `thr-parent-id`, `body`, `verb`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`author-name`, '" . $separator . "', ' ') SEPARATOR '" . $separator . "' LIMIT 50) AS `title` FROM `post-view` WHERE " . array_shift($condition) . " GROUP BY `thr-parent-id`, `verb`, `body`";
$emojis = [];
$rows = DBA::p($sql, $condition);
while ($row = DBA::fetch($rows)) {
if ($row['gravity'] == ItemModel::GRAVITY_ACTIVITY) {
$emoji = $row['body'] ?: $activity_emoji[$row['verb']];
} else {
$emoji = '';
$row['verb'] = $row['body'] ? Activity::EMOJIREACT : $row['verb'];
$emoji = $row['body'] ?: $activity_emoji[$row['verb']];
if (!isset($index_list[$emoji])) {
$index_list[] = $emoji;
}
$index = array_search($emoji, $index_list);
if (isset($emojis[$row['thr-parent-id']][$emoji]['title'])) {
$emojis[$row['thr-parent-id']][$emoji]['title'] = array_unique(array_merge($emojis[$row['thr-parent-id']][$emoji]['title'] ?? [], explode($separator, $row['title'])));
}
$emojis[$row['thr-parent-id']][$index]['emoji'] = $emoji;
$emojis[$row['thr-parent-id']][$index]['verb'] = $row['verb'];
$emojis[$row['thr-parent-id']][$index]['total'] = ($emojis[$row['thr-parent-id']][$index]['total'] ?? 0) + $row['total'];
$emojis[$row['thr-parent-id']][$index]['title'] = array_unique(array_merge($emojis[$row['thr-parent-id']][$index]['title'] ?? [], explode($separator, $row['title'])));
}
DBA::close($rows);
return $emojis;
}
/**
* Fetch comment counts from the conversation
*
* @param array $uriids
* @return array
*/
private function getCounts(array $uriids): array
{
$counts = [];
foreach (Post\Counts::get(['parent-uri-id' => $uriids, 'verb' => Activity::POST]) as $count) {
$counts[$count['parent-uri-id']] = ($counts[$count['parent-uri-id']] ?? 0) + $count['count'];
}
return $counts;
}
/**
* Fetch quote shares from the conversation
*
* @param array $uriids
* @return array
*/
private function getQuoteShares(array $uriids): array
{
$condition = DBA::mergeConditions(['quote-uri-id' => $uriids], ["NOT `quote-uri-id` IS NULL"]);
$separator = chr(255) . chr(255) . chr(255);
$sql = "SELECT `quote-uri-id`, COUNT(*) AS `total`, GROUP_CONCAT(REPLACE(`name`, '" . $separator . "', ' ') SEPARATOR '" . $separator . "' LIMIT 50) AS `title` FROM `post-content` INNER JOIN `post` ON `post`.`uri-id` = `post-content`.`uri-id` INNER JOIN `contact` ON `post`.`author-id` = `contact`.`id` WHERE " . array_shift($condition) . " GROUP BY `quote-uri-id`";
$quotes = [];
$rows = DBA::p($sql, $condition);
while ($row = DBA::fetch($rows)) {
$quotes[$row['quote-uri-id']]['total'] = $row['total'];
$quotes[$row['quote-uri-id']]['title'] = array_unique(explode($separator, $row['title']));
}
DBA::close($rows);
return $quotes;
}
/**
* Plucks the children of the given parent from a given item list.
*
@ -1266,10 +1239,16 @@ class Conversation
return $parents;
}
$blocklist = $this->getBlocklist();
$item_array = [];
// Dedupes the item list on the uri to prevent infinite loops
foreach ($item_list as $item) {
if (in_array($item['author-id'], $blocklist)) {
continue;
}
$item_array[$item['uri-id']] = $item;
}
@ -1284,8 +1263,6 @@ class Conversation
usort($parents, [$this, 'sortThrFeaturedReceived']);
} elseif (stristr($order, 'pinned_commented')) {
usort($parents, [$this, 'sortThrFeaturedCommented']);
} elseif (stristr($order, 'pinned_created')) {
usort($parents, [$this, 'sortThrFeaturedCreated']);
} elseif (stristr($order, 'received')) {
usort($parents, [$this, 'sortThrReceived']);
} elseif (stristr($order, 'commented')) {
@ -1363,24 +1340,6 @@ class Conversation
return strcmp($b['commented'], $a['commented']);
}
/**
* usort() callback to sort item arrays by featured and the created key
*
* @param array $a
* @param array $b
* @return int
*/
private function sortThrFeaturedCreated(array $a, array $b): int
{
if ($b['featured'] && !$a['featured']) {
return 1;
} elseif (!$b['featured'] && $a['featured']) {
return -1;
}
return strcmp($b['created'], $a['created']);
}
/**
* usort() callback to sort item arrays by the received key
*
@ -1458,6 +1417,10 @@ class Conversation
continue;
}
if (in_array($item['author-id'], $this->getBlocklist())) {
continue;
}
// prevent private email from leaking.
if ($item['network'] === Protocol::MAIL && $this->session->getLocalUserId() != $item['uid']) {
continue;

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,26 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Collection;
class UserDefinedChannels extends Timelines
{
}

View file

@ -1,34 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Entity;
class Channel extends Timeline
{
const WHATSHOT = 'whatshot';
const FORYOU = 'foryou';
const FOLLOWERS = 'followers';
const SHARERSOFSHARERS = 'sharersofsharers';
const IMAGE = 'image';
const VIDEO = 'video';
const AUDIO = 'audio';
const LANGUAGE = 'language';
}

View file

@ -1,28 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Entity;
final class Community extends Timeline
{
const LOCAL = 'local';
const GLOBAL = 'global';
}

View file

@ -1,31 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Entity;
final class Network extends Timeline
{
const STAR = 'star';
const MENTION = 'mention';
const RECEIVED = 'received';
const COMMENTED = 'commented';
const CREATED = 'created';
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -22,21 +22,30 @@
namespace Friendica\Content\Conversation\Entity;
/**
* @property-read string $code Channel code
* @property-read string $label Channel label
* @property-read string $description Channel description
* @property-read string $accessKey Access key
* @property-read string $path Path
* @property-read int $uid User of the channel
* @property-read string $includeTags The tags to include in the channel
* @property-read string $excludeTags The tags to exclude in the channel
* @property-read string $fullTextSearch full text search pattern
* @property-read int $mediaType Media types that are included in the channel
* @property-read array $languages Channel languages
* @property-read int $circle Circle or timeline this channel is based on
* @property-read string $code Channel code
* @property-read string $label Channel label
* @property-read string $description Channel description
* @property-read string $accessKey Access key
* @property-read string $path Path
*/
class Timeline extends \Friendica\BaseEntity
final class Timeline extends \Friendica\BaseEntity
{
const WHATSHOT = 'whatshot';
const FORYOU = 'foryou';
const FOLLOWERS = 'followers';
const SHARERSOFSHARERS = 'sharersofsharers';
const IMAGE = 'image';
const VIDEO = 'video';
const AUDIO = 'audio';
const LANGUAGE = 'language';
const LOCAL = 'local';
const GLOBAL = 'global';
const STAR = 'star';
const MENTION = 'mention';
const RECEIVED = 'received';
const COMMENTED = 'commented';
const CREATED = 'created';
/** @var string */
protected $code;
/** @var string */
@ -47,34 +56,13 @@ class Timeline extends \Friendica\BaseEntity
protected $accessKey;
/** @var string */
protected $path;
/** @var int */
protected $uid;
/** @var int */
protected $circle;
/** @var string */
protected $includeTags;
/** @var string */
protected $excludeTags;
/** @var string */
protected $fullTextSearch;
/** @var int */
protected $mediaType;
/** @var array */
protected $languages;
public function __construct(string $code = null, string $label = null, string $description = null, string $accessKey = null, string $path = null, int $uid = null, string $includeTags = null, string $excludeTags = null, string $fullTextSearch = null, int $mediaType = null, int $circle = null, array $languages = null)
public function __construct(string $code, string $label, string $description, string $accessKey, string $path = null)
{
$this->code = $code;
$this->label = $label;
$this->description = $description;
$this->accessKey = $accessKey;
$this->path = $path;
$this->uid = $uid;
$this->includeTags = $includeTags;
$this->excludeTags = $excludeTags;
$this->fullTextSearch = $fullTextSearch;
$this->mediaType = $mediaType;
$this->circle = $circle;
$this->languages = $languages;
$this->code = $code;
$this->label = $label;
$this->description = $description;
$this->accessKey = $accessKey;
$this->path = $path;
}
}

View file

@ -1,26 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Entity;
class UserDefinedChannel extends Channel
{
}

View file

@ -1,59 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Factory;
use Friendica\Content\Conversation\Collection\Timelines;
use Friendica\Content\Conversation\Entity\Channel as ChannelEntity;
use Friendica\Model\User;
final class Channel extends Timeline
{
/**
* List of available channels
*
* @param integer $uid
* @return Timelines
*/
public function getTimelines(int $uid): Timelines
{
$iso639 = new \Matriphe\ISO639\ISO639;
$native = $iso639->nativeByCode1(User::getLanguageCode($uid));
$tabs = [
new ChannelEntity(ChannelEntity::FORYOU, $this->l10n->t('For you'), $this->l10n->t('Posts from contacts you interact with and who interact with you'), 'y'),
new ChannelEntity(ChannelEntity::WHATSHOT, $this->l10n->t('What\'s Hot'), $this->l10n->t('Posts with a lot of interactions'), 'h'),
new ChannelEntity(ChannelEntity::LANGUAGE, $native, $this->l10n->t('Posts in %s', $native), 'g'),
new ChannelEntity(ChannelEntity::FOLLOWERS, $this->l10n->t('Followers'), $this->l10n->t('Posts from your followers that you don\'t follow'), 'f'),
new ChannelEntity(ChannelEntity::SHARERSOFSHARERS, $this->l10n->t('Sharers of sharers'), $this->l10n->t('Posts from accounts that are followed by accounts that you follow'), 'r'),
new ChannelEntity(ChannelEntity::IMAGE, $this->l10n->t('Images'), $this->l10n->t('Posts with images'), 'i'),
new ChannelEntity(ChannelEntity::AUDIO, $this->l10n->t('Audio'), $this->l10n->t('Posts with audio'), 'd'),
new ChannelEntity(ChannelEntity::VIDEO, $this->l10n->t('Videos'), $this->l10n->t('Posts with videos'), 'v'),
];
return new Timelines($tabs);
}
public function isTimeline(string $selectedTab): bool
{
return in_array($selectedTab, [ChannelEntity::WHATSHOT, ChannelEntity::FORYOU, ChannelEntity::FOLLOWERS, ChannelEntity::SHARERSOFSHARERS, ChannelEntity::IMAGE, ChannelEntity::VIDEO, ChannelEntity::AUDIO, ChannelEntity::LANGUAGE]);
}
}

View file

@ -1,56 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Factory;
use Friendica\Content\Conversation\Collection\Timelines;
use Friendica\Content\Conversation\Entity\Community as CommunityEntity;
use Friendica\Module\Conversation\Community as CommunityModule;
final class Community extends Timeline
{
/**
* List of available communities
*
* @param boolean $authenticated
* @return Timelines
*/
public function getTimelines(bool $authenticated): Timelines
{
$page_style = $this->config->get('system', 'community_page_style');
$tabs = [];
if (($authenticated || in_array($page_style, [CommunityModule::LOCAL_AND_GLOBAL, CommunityModule::LOCAL])) && empty($this->config->get('system', 'singleuser'))) {
$tabs[] = new CommunityEntity(CommunityEntity::LOCAL, $this->l10n->t('Local Community'), $this->l10n->t('Posts from local users on this server'), 'l');
}
if ($authenticated || in_array($page_style, [CommunityModule::LOCAL_AND_GLOBAL, CommunityModule::GLOBAL])) {
$tabs[] = new CommunityEntity(CommunityEntity::GLOBAL, $this->l10n->t('Global Community'), $this->l10n->t('Posts from users of the whole federated network'), 'g');
}
return new Timelines($tabs);
}
public function isTimeline(string $selectedTab): bool
{
return in_array($selectedTab, [CommunityEntity::LOCAL, CommunityEntity::GLOBAL]);
}
}

View file

@ -1,51 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Factory;
use Friendica\Content\Conversation\Collection\Timelines;
use Friendica\Content\Conversation\Entity\Network as NetworkEntity;
final class Network extends Timeline
{
/**
* List of available network timelines
*
* @param string $command
* @return Timelines
*/
public function getTimelines(string $command): Timelines
{
$tabs = [
new NetworkEntity(NetworkEntity::COMMENTED, $this->l10n->t('Latest Activity'), $this->l10n->t('Sort by latest activity'), 'e', $command . '?' . http_build_query(['order' => 'commented'])),
new NetworkEntity(NetworkEntity::RECEIVED, $this->l10n->t('Latest Posts'), $this->l10n->t('Sort by post received date'), 't', $command . '?' . http_build_query(['order' => 'received'])),
new NetworkEntity(NetworkEntity::CREATED, $this->l10n->t('Latest Creation'), $this->l10n->t('Sort by post creation date'), 'q', $command . '?' . http_build_query(['order' => 'created'])),
new NetworkEntity(NetworkEntity::MENTION, $this->l10n->t('Personal'), $this->l10n->t('Posts that mention or involve you'), 'r', $command . '?' . http_build_query(['mention' => true])),
new NetworkEntity(NetworkEntity::STAR, $this->l10n->t('Starred'), $this->l10n->t('Favourite Posts'), 'm', $command . '?' . http_build_query(['star' => true])),
];
return new Timelines($tabs);
}
public function isTimeline(string $selectedTab): bool
{
return in_array($selectedTab, [NetworkEntity::COMMENTED, NetworkEntity::RECEIVED, NetworkEntity::CREATED, NetworkEntity::MENTION, NetworkEntity::STAR]);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -21,26 +21,100 @@
namespace Friendica\Content\Conversation\Factory;
use Friendica\Content\Conversation\Repository\UserDefinedChannel;
use Friendica\Content\Conversation\Collection\Timelines;
use Friendica\Model\User;
use Friendica\Content\Conversation\Entity\Timeline as TimelineEntity;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Module\Conversation\Community;
use Psr\Log\LoggerInterface;
class Timeline extends \Friendica\BaseFactory
final class Timeline extends \Friendica\BaseFactory
{
/** @var L10n */
protected $l10n;
/** @var IManageConfigValues The config */
protected $config;
/** @var UserDefinedChannel */
protected $channelRepository;
public function __construct(UserDefinedChannel $channel, L10n $l10n, LoggerInterface $logger, IManageConfigValues $config)
public function __construct(L10n $l10n, LoggerInterface $logger, IManageConfigValues $config)
{
parent::__construct($logger);
$this->channelRepository = $channel;
$this->l10n = $l10n;
$this->config = $config;
$this->l10n = $l10n;
$this->config = $config;
}
/**
* List of available channels
*
* @param integer $uid
* @return Timelines
*/
public function getChannelsForUser(int $uid): Timelines
{
$language = User::getLanguageCode($uid);
$languages = $this->l10n->getAvailableLanguages(true);
$tabs = [
new TimelineEntity(TimelineEntity::FORYOU, $this->l10n->t('For you'), $this->l10n->t('Posts from contacts you interact with and who interact with you'), 'y'),
new TimelineEntity(TimelineEntity::WHATSHOT, $this->l10n->t('What\'s Hot'), $this->l10n->t('Posts with a lot of interactions'), 'h'),
new TimelineEntity(TimelineEntity::LANGUAGE, $languages[$language], $this->l10n->t('Posts in %s', $languages[$language]), 'g'),
new TimelineEntity(TimelineEntity::FOLLOWERS, $this->l10n->t('Followers'), $this->l10n->t('Posts from your followers that you don\'t follow'), 'f'),
new TimelineEntity(TimelineEntity::SHARERSOFSHARERS, $this->l10n->t('Sharers of sharers'), $this->l10n->t('Posts from accounts that are followed by accounts that you follow'), 'r'),
new TimelineEntity(TimelineEntity::IMAGE, $this->l10n->t('Images'), $this->l10n->t('Posts with images'), 'i'),
new TimelineEntity(TimelineEntity::AUDIO, $this->l10n->t('Audio'), $this->l10n->t('Posts with audio'), 'd'),
new TimelineEntity(TimelineEntity::VIDEO, $this->l10n->t('Videos'), $this->l10n->t('Posts with videos'), 'v'),
];
return new Timelines($tabs);
}
/**
* List of available communities
*
* @param boolean $authenticated
* @return Timelines
*/
public function getCommunities(bool $authenticated): Timelines
{
$page_style = $this->config->get('system', 'community_page_style');
$tabs = [];
if (($authenticated || in_array($page_style, [Community::LOCAL_AND_GLOBAL, Community::LOCAL])) && empty($this->config->get('system', 'singleuser'))) {
$tabs[] = new TimelineEntity(TimelineEntity::LOCAL, $this->l10n->t('Local Community'), $this->l10n->t('Posts from local users on this server'), 'l');
}
if ($authenticated || in_array($page_style, [Community::LOCAL_AND_GLOBAL, Community::GLOBAL])) {
$tabs[] = new TimelineEntity(TimelineEntity::GLOBAL, $this->l10n->t('Global Community'), $this->l10n->t('Posts from users of the whole federated network'), 'g');
}
return new Timelines($tabs);
}
/**
* List of available network feeds
*
* @param string $command
* @return Timelines
*/
public function getNetworkFeeds(string $command): Timelines
{
$tabs = [
new TimelineEntity(TimelineEntity::COMMENTED, $this->l10n->t('Latest Activity'), $this->l10n->t('Sort by latest activity'), 'e', $command . '?' . http_build_query(['order' => 'commented'])),
new TimelineEntity(TimelineEntity::RECEIVED, $this->l10n->t('Latest Posts'), $this->l10n->t('Sort by post received date'), 't', $command . '?' . http_build_query(['order' => 'received'])),
new TimelineEntity(TimelineEntity::CREATED, $this->l10n->t('Latest Creation'), $this->l10n->t('Sort by post creation date'), 'q', $command . '?' . http_build_query(['order' => 'created'])),
new TimelineEntity(TimelineEntity::MENTION, $this->l10n->t('Personal'), $this->l10n->t('Posts that mention or involve you'), 'r', $command . '?' . http_build_query(['mention' => true])),
new TimelineEntity(TimelineEntity::STAR, $this->l10n->t('Starred'), $this->l10n->t('Favourite Posts'), 'm', $command . '?' . http_build_query(['star' => true])),
];
return new Timelines($tabs);
}
public function isCommunity(string $selectedTab): bool
{
return in_array($selectedTab, [TimelineEntity::LOCAL, TimelineEntity::GLOBAL]);
}
public function isChannel(string $selectedTab): bool
{
return in_array($selectedTab, [TimelineEntity::WHATSHOT, TimelineEntity::FORYOU, TimelineEntity::FOLLOWERS, TimelineEntity::SHARERSOFSHARERS, TimelineEntity::IMAGE, TimelineEntity::VIDEO, TimelineEntity::AUDIO, TimelineEntity::LANGUAGE]);
}
}

View file

@ -1,55 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Factory;
use Friendica\Capabilities\ICanCreateFromTableRow;
use Friendica\Content\Conversation\Entity;
final class UserDefinedChannel extends Timeline implements ICanCreateFromTableRow
{
public function isTimeline(string $selectedTab, int $uid): bool
{
return is_numeric($selectedTab) && $uid && $this->channelRepository->existsById($selectedTab, $uid);
}
public function createFromTableRow(array $row): Entity\UserDefinedChannel
{
if (is_string($row['languages'])) {
$row['languages'] = unserialize($row['languages']);
}
return new Entity\UserDefinedChannel(
$row['id'] ?? null,
$row['label'],
$row['description'] ?? null,
$row['access-key'] ?? null,
null,
$row['uid'],
$row['include-tags'] ?? null,
$row['exclude-tags'] ?? null,
$row['full-text-search'] ?? null,
$row['media-type'] ?? null,
$row['circle'] ?? null,
$row['languages'] ?? null,
);
}
}

View file

@ -1,179 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Conversation\Repository;
use Friendica\BaseCollection;
use Friendica\Content\Conversation\Collection\UserDefinedChannels;
use Friendica\Content\Conversation\Entity;
use Friendica\Content\Conversation\Factory;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Database\Database;
use Friendica\Model\Post\Engagement;
use Friendica\Model\User;
use Psr\Log\LoggerInterface;
class UserDefinedChannel extends \Friendica\BaseRepository
{
protected static $table_name = 'channel';
/** @var IManagePersonalConfigValues */
private $pConfig;
public function __construct(Database $database, LoggerInterface $logger, Factory\UserDefinedChannel $factory, IManagePersonalConfigValues $pConfig)
{
parent::__construct($database, $logger, $factory);
$this->pConfig = $pConfig;
}
/**
* @param array $condition
* @param array $params
* @return UserDefinedChannels
* @throws \Exception
*/
protected function _select(array $condition, array $params = []): BaseCollection
{
$rows = $this->db->selectToArray(static::$table_name, [], $condition, $params);
$Entities = new UserDefinedChannels();
foreach ($rows as $fields) {
$Entities[] = $this->factory->createFromTableRow($fields);
}
return $Entities;
}
/**
* Fetch a single user channel
*
* @param int $id The id of the user defined channel
* @param int $uid The user that this channel belongs to. (Not part of the primary key)
* @return Entity\UserDefinedChannel
* @throws \Friendica\Network\HTTPException\NotFoundException
*/
public function selectById(int $id, int $uid): Entity\UserDefinedChannel
{
return $this->_selectOne(['id' => $id, 'uid' => $uid]);
}
/**
* Checks if the provided channel id exists for this user
*
* @param integer $id
* @param integer $uid
* @return boolean
*/
public function existsById(int $id, int $uid): bool
{
return $this->exists(['id' => $id, 'uid' => $uid]);
}
/**
* Delete the given channel
*
* @param integer $id
* @param integer $uid
* @return boolean
*/
public function deleteById(int $id, int $uid): bool
{
return $this->db->delete(self::$table_name, ['id' => $id, 'uid' => $uid]);
}
/**
* Fetch all user channels
*
* @param integer $uid
* @return UserDefinedChannels
* @throws \Exception
*/
public function selectByUid(int $uid): UserDefinedChannels
{
return $this->_select(['uid' => $uid]);
}
public function save(Entity\UserDefinedChannel $Channel): Entity\UserDefinedChannel
{
$fields = [
'label' => $Channel->label,
'description' => $Channel->description,
'access-key' => $Channel->accessKey,
'uid' => $Channel->uid,
'circle' => $Channel->circle,
'include-tags' => $Channel->includeTags,
'exclude-tags' => $Channel->excludeTags,
'full-text-search' => $Channel->fullTextSearch,
'media-type' => $Channel->mediaType,
'languages' => serialize($Channel->languages),
];
if ($Channel->code) {
$this->db->update(self::$table_name, $fields, ['uid' => $Channel->uid, 'id' => $Channel->code]);
} else {
$this->db->insert(self::$table_name, $fields, Database::INSERT_IGNORE);
$newChannelId = $this->db->lastInsertId();
$Channel = $this->selectById($newChannelId, $Channel->uid);
}
return $Channel;
}
/**
* Checks, if one of the user defined channels matches with the given search text
* @todo To increase the performance, this functionality should be replaced with a single SQL call.
*
* @param string $searchtext
* @param string $language
* @return boolean
*/
public function match(string $searchtext, string $language): bool
{
if (!in_array($language, User::getLanguages())) {
$this->logger->debug('Unwanted language found. No matched channel found.', ['language' => $language, 'searchtext' => $searchtext]);
return false;
}
$store = false;
$this->db->insert('check-full-text-search', ['pid' => getmypid(), 'searchtext' => $searchtext], Database::INSERT_UPDATE);
$channels = $this->db->select(self::$table_name, ['full-text-search', 'uid', 'label'], ["`full-text-search` != ? AND `circle` = ?", '', 0]);
while ($channel = $this->db->fetch($channels)) {
$channelsearchtext = $channel['full-text-search'];
foreach (Engagement::KEYWORDS as $keyword) {
$channelsearchtext = preg_replace('~(' . $keyword . ':.[\w@\.-]+)~', '"$1"', $channelsearchtext);
}
if ($this->db->exists('check-full-text-search', ["`pid` = ? AND MATCH (`searchtext`) AGAINST (? IN BOOLEAN MODE)", getmypid(), $channelsearchtext])) {
if (in_array($language, $this->pConfig->get($channel['uid'], 'channel', 'languages', [User::getLanguageCode($channel['uid'])]))) {
$store = true;
$this->logger->debug('Matching channel found.', ['uid' => $channel['uid'], 'label' => $channel['label'], 'language' => $language, 'channelsearchtext' => $channelsearchtext, 'searchtext' => $searchtext]);
break;
}
}
}
$this->db->close($channels);
$this->db->delete('check-full-text-search', ['pid' => getmypid()]);
return $store;
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -106,12 +106,14 @@ class GroupManager
* Sidebar widget to show subscribed Friendica groups. If activated
* in the settings, it appears in the network page sidebar
*
* @param int $uid The ID of the User
* @param string $baseurl Base module path
* @param int $uid The ID of the User
* @param int $cid The contact id which is used to mark a group as "selected"
* @return string
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
public static function widget(int $uid)
public static function widget(string $baseurl, int $uid, int $cid = 0)
{
$o = '';
@ -128,11 +130,14 @@ class GroupManager
$entries = [];
foreach ($contacts as $contact) {
$selected = (($cid == $contact['id']) ? ' group-selected' : '');
$entry = [
'url' => 'contact/' . $contact['id'] . '/conversations',
'url' => $baseurl . '/' . $contact['id'],
'external_url' => Contact::magicLinkByContact($contact),
'name' => $contact['name'],
'cid' => $contact['id'],
'selected' => $selected,
'micro' => DI::baseUrl()->remove(Contact::getMicro($contact)),
'id' => ++$id,
];

View file

@ -1,154 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content;
use Friendica\Content\Image\Collection\MasonryImageRow;
use Friendica\Content\Image\Entity\MasonryImage;
use Friendica\Content\Post\Collection\PostMedias;
use Friendica\Core\Renderer;
class Image
{
public static function getBodyAttachHtml(PostMedias $PostMediaImages): string
{
$media = '';
if ($PostMediaImages->haveDimensions()) {
if (count($PostMediaImages) > 1) {
$media = self::getHorizontalMasonryHtml($PostMediaImages);
} elseif (count($PostMediaImages) == 1) {
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single_with_height_allocation.tpl'), [
'$image' => $PostMediaImages[0],
'$allocated_height' => $PostMediaImages[0]->getAllocatedHeight(),
'$allocated_max_width' => ($PostMediaImages[0]->previewWidth ?? $PostMediaImages[0]->width) . 'px',
]);
}
} else {
if (count($PostMediaImages) > 1) {
$media = self::getImageGridHtml($PostMediaImages);
} elseif (count($PostMediaImages) == 1) {
$media = Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/single.tpl'), [
'$image' => $PostMediaImages[0],
]);
}
}
return $media;
}
/**
* @param PostMedias $images
* @return string
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException
*/
private static function getImageGridHtml(PostMedias $images): string
{
// Image for first column (fc) and second column (sc)
$images_fc = [];
$images_sc = [];
for ($i = 0; $i < count($images); $i++) {
($i % 2 == 0) ? ($images_fc[] = $images[$i]) : ($images_sc[] = $images[$i]);
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/grid.tpl'), [
'columns' => [
'fc' => $images_fc,
'sc' => $images_sc,
],
]);
}
/**
* Creates a horizontally masoned gallery with a fixed maximum number of pictures per row.
*
* For each row, we calculate how much of the total width each picture will take depending on their aspect ratio
* and how much relative height it needs to accomodate all pictures next to each other with their height normalized.
*
* @param array $images
* @return string
* @throws \Friendica\Network\HTTPException\ServiceUnavailableException
*/
private static function getHorizontalMasonryHtml(PostMedias $images): string
{
static $column_size = 2;
$rows = array_map(
function (PostMedias $PostMediaImages) {
if ($singleImageInRow = count($PostMediaImages) == 1) {
$PostMediaImages[] = $PostMediaImages[0];
}
$widths = [];
$heights = [];
foreach ($PostMediaImages as $PostMediaImage) {
if ($PostMediaImage->width && $PostMediaImage->height) {
$widths[] = $PostMediaImage->width;
$heights[] = $PostMediaImage->height;
} else {
$widths[] = $PostMediaImage->previewWidth;
$heights[] = $PostMediaImage->previewHeight;
}
}
$maxHeight = max($heights);
// Corrected width preserving aspect ratio when all images on a row are the same height
$correctedWidths = [];
foreach ($widths as $i => $width) {
$correctedWidths[] = $width * $maxHeight / $heights[$i];
}
$totalWidth = array_sum($correctedWidths);
$row_images2 = [];
if ($singleImageInRow) {
unset($PostMediaImages[1]);
}
foreach ($PostMediaImages as $i => $PostMediaImage) {
$row_images2[] = new MasonryImage(
$PostMediaImage->uriId,
$PostMediaImage->url,
$PostMediaImage->preview,
$PostMediaImage->description ?? '',
100 * $correctedWidths[$i] / $totalWidth,
100 * $maxHeight / $correctedWidths[$i]
);
}
// This magic value will stay constant for each image of any given row and is ultimately
// used to determine the height of the row container relative to the available width.
$commonHeightRatio = 100 * $correctedWidths[0] / $totalWidth / ($widths[0] / $heights[0]);
return new MasonryImageRow($row_images2, count($row_images2), $commonHeightRatio);
},
$images->chunk($column_size)
);
return Renderer::replaceMacros(Renderer::getMarkupTemplate('content/image/horizontal_masonry.tpl'), [
'$rows' => $rows,
'$column_size' => $column_size,
]);
}
}

View file

@ -1,57 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Image\Collection;
use Friendica\Content\Image\Entity;
use Friendica\BaseCollection;
use Friendica\Content\Image\Entity\MasonryImage;
class MasonryImageRow extends BaseCollection
{
/** @var ?float */
protected $heightRatio;
/**
* @param MasonryImage[] $entities
* @param int|null $totalCount
* @param float|null $heightRatio
*/
public function __construct(array $entities = [], int $totalCount = null, float $heightRatio = null)
{
parent::__construct($entities, $totalCount);
$this->heightRatio = $heightRatio;
}
/**
* @return Entity\MasonryImage
*/
public function current(): Entity\MasonryImage
{
return parent::current();
}
public function getHeightRatio(): ?float
{
return $this->heightRatio;
}
}

View file

@ -1,60 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Image\Entity;
use Friendica\BaseEntity;
use Psr\Http\Message\UriInterface;
/**
* @property-read int $uriId
* @property-read UriInterface $url
* @property-read ?UriInterface $preview
* @property-read string $description
* @property-read float $heightRatio
* @property-read float $widthRatio
* @see \Friendica\Content\Image::getHorizontalMasonryHtml()
*/
class MasonryImage extends BaseEntity
{
/** @var int */
protected $uriId;
/** @var UriInterface */
protected $url;
/** @var ?UriInterface */
protected $preview;
/** @var string */
protected $description;
/** @var float Ratio of the width of the image relative to the total width of the images on the row */
protected $widthRatio;
/** @var float Ratio of the height of the image relative to its width for height allocation */
protected $heightRatio;
public function __construct(int $uriId, UriInterface $url, ?UriInterface $preview, string $description, float $widthRatio, float $heightRatio)
{
$this->url = $url;
$this->uriId = $uriId;
$this->preview = $preview;
$this->description = $description;
$this->widthRatio = $widthRatio;
$this->heightRatio = $heightRatio;
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -48,7 +48,6 @@ use Friendica\Model\User;
use Friendica\Network\HTTPException;
use Friendica\Object\EMail\ItemCCEMail;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\ACLFormatter;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Emailer;
@ -298,7 +297,7 @@ class Item
if ($this->activity->match($item['verb'], Activity::TAG)) {
$fields = [
'author-id', 'author-link', 'author-name', 'author-network', 'author-link', 'author-alias',
'author-id', 'author-link', 'author-name', 'author-network',
'verb', 'object-type', 'resource-id', 'body', 'plink'
];
$obj = Post::selectFirst($fields, ['uri' => $item['parent-uri']]);
@ -636,10 +635,10 @@ class Item
public function addSharedPost(array $item, string $body = ''): string
{
if (empty($body)) {
$body = $item['body'] ?? '';
$body = $item['body'];
}
if (empty($item['quote-uri-id']) || ($item['quote-uri-id'] == $item['uri-id'])) {
if (empty($item['quote-uri-id'])) {
return $body;
}
@ -730,7 +729,7 @@ class Item
*/
public function getSharedPost(array $item, array $fields = []): array
{
if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) {
if (!empty($item['quote-uri-id'])) {
$shared = Post::selectFirst($fields, ['uri-id' => $item['quote-uri-id'], 'uid' => [0, $item['uid'] ?? 0]]);
if (is_array($shared)) {
return [
@ -771,7 +770,7 @@ class Item
return $attributes;
}
if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) {
if (!empty($item['quote-uri-id'])) {
$shared = Post::selectFirst(['author-name', 'author-link', 'author-avatar', 'plink', 'created', 'guid', 'uri', 'body'], ['uri-id' => $item['quote-uri-id']]);
if (!empty($shared)) {
return [
@ -992,14 +991,12 @@ class Item
$post['deny_gid'] = $owner['deny_gid'];
}
if (!isset($post['private'])) {
if ($post['allow_gid'] || $post['allow_cid'] || $post['deny_gid'] || $post['deny_cid']) {
$post['private'] = ItemModel::PRIVATE;
} elseif ($this->pConfig->get($post['uid'], 'system', 'unlisted')) {
$post['private'] = ItemModel::UNLISTED;
} else {
$post['private'] = ItemModel::PUBLIC;
}
if ($post['allow_gid'] || $post['allow_cid'] || $post['deny_gid'] || $post['deny_cid']) {
$post['private'] = ItemModel::PRIVATE;
} elseif ($this->pConfig->get($post['uid'], 'system', 'unlisted')) {
$post['private'] = ItemModel::UNLISTED;
} else {
$post['private'] = ItemModel::PUBLIC;
}
if (empty($post['contact-id'])) {
@ -1069,40 +1066,4 @@ class Item
));
}
}
public function copyPermissions(int $fromUriId, int $toUriId, int $parentUriId)
{
$from = Post::selectFirstPost(['author-id'], ['uri-id' => $fromUriId]);
$from_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $from['author-id']]);
$to = Post::selectFirstPost(['author-id'], ['uri-id' => $toUriId]);
$to_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $to['author-id']]);
$parent = Post::selectFirstPost(['author-id'], ['uri-id' => $parentUriId]);
$parent_author = DBA::selectFirst('account-view', ['ap-followers'], ['id' => $parent['author-id']]);
$followers = '';
foreach (array_column(Tag::getByURIId($parentUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url') as $url) {
if ($url == $parent_author['ap-followers']) {
$followers = $url;
break;
}
}
$existing = array_column(Tag::getByURIId($toUriId, [Tag::TO, Tag::CC, Tag::BCC]), 'url');
foreach (Tag::getByURIId($fromUriId, [Tag::TO, Tag::CC, Tag::BCC]) as $receiver) {
if ($receiver['url'] == $from_author['ap-followers']) {
if (!empty($followers)) {
$receiver['url'] = $followers;
$receiver['name'] = trim(parse_url($receiver['url'], PHP_URL_PATH), '/');
Tag::store($toUriId, $receiver['type'], $receiver['name'], $receiver['url']);
}
$receiver['url'] = $to_author['ap-followers'];
$receiver['name'] = trim(parse_url($receiver['url'], PHP_URL_PATH), '/');
}
if (in_array($receiver['url'], $existing)) {
continue;
}
Tag::store($toUriId, $receiver['type'], $receiver['name'], $receiver['url']);
}
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,57 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Post\Collection;
use Friendica\BaseCollection;
use Friendica\Content\Post\Entity;
class PostMedias extends BaseCollection
{
/**
* @param Entity\PostMedia[] $entities
* @param int|null $totalCount
*/
public function __construct(array $entities = [], int $totalCount = null)
{
parent::__construct($entities, $totalCount);
}
/**
* @return Entity\PostMedia
*/
public function current(): Entity\PostMedia
{
return parent::current();
}
/**
* Determine whether all the collection's item have at least one set of dimensions provided
*
* @return bool
*/
public function haveDimensions(): bool
{
return array_reduce($this->getArrayCopy(), function (bool $carry, Entity\PostMedia $item) {
return $carry && $item->hasDimensions();
}, true);
}
}

View file

@ -1,300 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Post\Entity;
use Friendica\BaseEntity;
use Friendica\Network\Entity\MimeType;
use Friendica\Util\Images;
use Friendica\Util\Proxy;
use Psr\Http\Message\UriInterface;
/**
* @property-read int $id
* @property-read int $uriId
* @property-read ?int $activityUriId
* @property-read UriInterface $url
* @property-read int $type
* @property-read MimeType $mimetype
* @property-read ?int $width
* @property-read ?int $height
* @property-read ?int $size
* @property-read ?UriInterface $preview
* @property-read ?int $previewWidth
* @property-read ?int $previewHeight
* @property-read ?string $description
* @property-read ?string $name
* @property-read ?UriInterface $authorUrl
* @property-read ?string $authorName
* @property-read ?UriInterface $authorImage
* @property-read ?UriInterface $publisherUrl
* @property-read ?string $publisherName
* @property-read ?UriInterface $publisherImage
* @property-read ?string $blurhash
*/
class PostMedia extends BaseEntity
{
const TYPE_UNKNOWN = 0;
const TYPE_IMAGE = 1;
const TYPE_VIDEO = 2;
const TYPE_AUDIO = 3;
const TYPE_TEXT = 4;
const TYPE_APPLICATION = 5;
const TYPE_TORRENT = 16;
const TYPE_HTML = 17;
const TYPE_XML = 18;
const TYPE_PLAIN = 19;
const TYPE_ACTIVITY = 20;
const TYPE_ACCOUNT = 21;
const TYPE_DOCUMENT = 128;
/** @var int */
protected $id;
/** @var int */
protected $uriId;
/** @var UriInterface */
protected $url;
/** @var int One of TYPE_* */
protected $type;
/** @var MimeType */
protected $mimetype;
/** @var ?int */
protected $activityUriId;
/** @var ?int In pixels */
protected $width;
/** @var ?int In pixels */
protected $height;
/** @var ?int In bytes */
protected $size;
/** @var ?UriInterface Preview URL */
protected $preview;
/** @var ?int In pixels */
protected $previewWidth;
/** @var ?int In pixels */
protected $previewHeight;
/** @var ?string Alternative text like for images */
protected $description;
/** @var ?string */
protected $name;
/** @var ?UriInterface */
protected $authorUrl;
/** @var ?string */
protected $authorName;
/** @var ?UriInterface Image URL */
protected $authorImage;
/** @var ?UriInterface */
protected $publisherUrl;
/** @var ?string */
protected $publisherName;
/** @var ?UriInterface Image URL */
protected $publisherImage;
/** @var ?string Blurhash string representation for images
* @see https://github.com/woltapp/blurhash
* @see https://blurha.sh/
*/
protected $blurhash;
public function __construct(
int $uriId,
UriInterface $url,
int $type,
MimeType $mimetype,
?int $activityUriId,
?int $width = null,
?int $height = null,
?int $size = null,
?UriInterface $preview = null,
?int $previewWidth = null,
?int $previewHeight = null,
?string $description = null,
?string $name = null,
?UriInterface $authorUrl = null,
?string $authorName = null,
?UriInterface $authorImage = null,
?UriInterface $publisherUrl = null,
?string $publisherName = null,
?UriInterface $publisherImage = null,
?string $blurhash = null,
int $id = null
)
{
$this->uriId = $uriId;
$this->url = $url;
$this->type = $type;
$this->mimetype = $mimetype;
$this->activityUriId = $activityUriId;
$this->width = $width;
$this->height = $height;
$this->size = $size;
$this->preview = $preview;
$this->previewWidth = $previewWidth;
$this->previewHeight = $previewHeight;
$this->description = $description;
$this->name = $name;
$this->authorUrl = $authorUrl;
$this->authorName = $authorName;
$this->authorImage = $authorImage;
$this->publisherUrl = $publisherUrl;
$this->publisherName = $publisherName;
$this->publisherImage = $publisherImage;
$this->blurhash = $blurhash;
$this->id = $id;
}
/**
* Get media link for given media id
*
* @param string $size One of the Proxy::SIZE_* constants
* @return string media link
*/
public function getPhotoPath(string $size = ''): string
{
return '/photo/media/' .
(Proxy::getPixelsFromSize($size) ? Proxy::getPixelsFromSize($size) . '/' : '') .
$this->id;
}
/**
* Get preview path for given media id relative to the base URL
*
* @param string $size One of the Proxy::SIZE_* constants
* @return string preview link
*/
public function getPreviewPath(string $size = ''): string
{
return '/photo/preview/' .
(Proxy::getPixelsFromSize($size) ? Proxy::getPixelsFromSize($size) . '/' : '') .
$this->id;
}
/**
* Computes the allocated height value used in the content/image/single_with_height_allocation.tpl template
*
* Either base or preview dimensions need to be set at runtime.
*
* @return string
*/
public function getAllocatedHeight(): string
{
if (!$this->hasDimensions()) {
throw new \RangeException('Either width and height or previewWidth and previewHeight must be defined to use this method.');
}
if ($this->width && $this->height) {
$width = $this->width;
$height = $this->height;
} else {
$width = $this->previewWidth;
$height = $this->previewHeight;
}
return (100 * $height / $width) . '%';
}
/**
* Return a new PostMedia entity with a different preview URI and an optional proxy size name.
* The new entity preview's width and height are rescaled according to the provided size.
*
* @param \GuzzleHttp\Psr7\Uri $preview
* @param string $size
* @return $this
*/
public function withPreview(\GuzzleHttp\Psr7\Uri $preview, string $size = ''): self
{
if ($this->width || $this->height) {
$newWidth = $this->width;
$newHeight = $this->height;
} else {
$newWidth = $this->previewWidth;
$newHeight = $this->previewHeight;
}
if ($newWidth && $newHeight && $size) {
$dimensionts = Images::getScalingDimensions($newWidth, $newHeight, Proxy::getPixelsFromSize($size));
$newWidth = $dimensionts['width'];
$newHeight = $dimensionts['height'];
}
return new static(
$this->uriId,
$this->url,
$this->type,
$this->mimetype,
$this->activityUriId,
$this->width,
$this->height,
$this->size,
$preview,
$newWidth,
$newHeight,
$this->description,
$this->name,
$this->authorUrl,
$this->authorName,
$this->authorImage,
$this->publisherUrl,
$this->publisherName,
$this->publisherImage,
$this->blurhash,
$this->id,
);
}
public function withUrl(\GuzzleHttp\Psr7\Uri $url): self
{
return new static(
$this->uriId,
$url,
$this->type,
$this->mimetype,
$this->activityUriId,
$this->width,
$this->height,
$this->size,
$this->preview,
$this->previewWidth,
$this->previewHeight,
$this->description,
$this->name,
$this->authorUrl,
$this->authorName,
$this->authorImage,
$this->publisherUrl,
$this->publisherName,
$this->publisherImage,
$this->blurhash,
$this->id,
);
}
/**
* Checks the media has at least one full set of dimensions, needed for the height allocation feature
*
* @return bool
*/
public function hasDimensions(): bool
{
return $this->width && $this->height || $this->previewWidth && $this->previewHeight;
}
}

View file

@ -1,118 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Post\Factory;
use Friendica\BaseFactory;
use Friendica\Capabilities\ICanCreateFromTableRow;
use Friendica\Content\Post\Entity;
use Friendica\Network;
use Friendica\Util\Network as UtilNetwork;
use GuzzleHttp\Psr7\Uri;
use Psr\Log\LoggerInterface;
use stdClass;
class PostMedia extends BaseFactory implements ICanCreateFromTableRow
{
/** @var Network\Factory\MimeType */
private $mimeTypeFactory;
public function __construct(Network\Factory\MimeType $mimeTypeFactory, LoggerInterface $logger)
{
parent::__construct($logger);
$this->mimeTypeFactory = $mimeTypeFactory;
}
/**
* @inheritDoc
*/
public function createFromTableRow(array $row)
{
return new Entity\PostMedia(
$row['uri-id'],
UtilNetwork::createUriFromString($row['url']),
$row['type'],
$this->mimeTypeFactory->createFromContentType($row['mimetype']),
$row['media-uri-id'],
$row['width'],
$row['height'],
$row['size'],
UtilNetwork::createUriFromString($row['preview']),
$row['preview-width'],
$row['preview-height'],
$row['description'],
$row['name'],
UtilNetwork::createUriFromString($row['author-url']),
$row['author-name'],
UtilNetwork::createUriFromString($row['author-image']),
UtilNetwork::createUriFromString($row['publisher-url']),
$row['publisher-name'],
UtilNetwork::createUriFromString($row['publisher-image']),
$row['blurhash'],
$row['id']
);
}
public function createFromBlueskyImageEmbed(int $uriId, stdClass $image): Entity\PostMedia
{
return new Entity\PostMedia(
$uriId,
new Uri($image->fullsize),
Entity\PostMedia::TYPE_IMAGE,
new Network\Entity\MimeType('unkn', 'unkn'),
null,
null,
null,
null,
new Uri($image->thumb),
null,
null,
$image->alt,
);
}
public function createFromBlueskyExternalEmbed(int $uriId, stdClass $external): Entity\PostMedia
{
return new Entity\PostMedia(
$uriId,
new Uri($external->uri),
Entity\PostMedia::TYPE_HTML,
new Network\Entity\MimeType('text', 'html'),
null,
null,
null,
null,
null,
null,
null,
$external->description,
$external->title
);
}
public function createFromAttachment(int $uriId, array $attachment)
{
$attachment['uri-id'] = $uriId;
return $this->createFromTableRow($attachment);
}
}

View file

@ -1,208 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Post\Repository;
use Friendica\BaseCollection;
use Friendica\BaseRepository;
use Friendica\Content\Post\Collection;
use Friendica\Content\Post\Entity;
use Friendica\Content\Post\Factory;
use Friendica\Database\Database;
use Friendica\Util\Strings;
use Psr\Log\LoggerInterface;
class PostMedia extends BaseRepository
{
protected static $table_name = 'post-media';
public function __construct(Database $database, LoggerInterface $logger, Factory\PostMedia $factory)
{
parent::__construct($database, $logger, $factory);
}
protected function _select(array $condition, array $params = []): BaseCollection
{
$rows = $this->db->selectToArray(static::$table_name, [], $condition, $params);
$Entities = new Collection\PostMedias();
foreach ($rows as $fields) {
try {
$Entities[] = $this->factory->createFromTableRow($fields);
} catch (\Throwable $e) {
$this->logger->warning('Invalid media row', ['code' => $e->getCode(), 'message' => $e->getMessage(), 'fields' => $fields]);
}
}
return $Entities;
}
public function selectOneById(int $postMediaId): Entity\PostMedia
{
return $this->_selectOne(['id' => $postMediaId]);
}
public function selectByUriId(int $uriId): Collection\PostMedias
{
return $this->_select(['uri-id' => $uriId]);
}
public function save(Entity\PostMedia $PostMedia): Entity\PostMedia
{
$fields = [
'uri-id' => $PostMedia->uriId,
'url' => $PostMedia->url->__toString(),
'type' => $PostMedia->type,
'mimetype' => $PostMedia->mimetype->__toString(),
'height' => $PostMedia->height,
'width' => $PostMedia->width,
'size' => $PostMedia->size,
'preview' => $PostMedia->preview ? $PostMedia->preview->__toString() : null,
'preview-height' => $PostMedia->previewHeight,
'preview-width' => $PostMedia->previewWidth,
'description' => $PostMedia->description,
'name' => $PostMedia->name,
'author-url' => $PostMedia->authorUrl ? $PostMedia->authorUrl->__toString() : null,
'author-name' => $PostMedia->authorName,
'author-image' => $PostMedia->authorImage ? $PostMedia->authorImage->__toString() : null,
'publisher-url' => $PostMedia->publisherUrl ? $PostMedia->publisherUrl->__toString() : null,
'publisher-name' => $PostMedia->publisherName,
'publisher-image' => $PostMedia->publisherImage ? $PostMedia->publisherImage->__toString() : null,
'media-uri-id' => $PostMedia->activityUriId,
'blurhash' => $PostMedia->blurhash,
];
if ($PostMedia->id) {
$this->db->update(self::$table_name, $fields, ['id' => $PostMedia->id]);
} else {
$this->db->insert(self::$table_name, $fields, Database::INSERT_IGNORE);
$newPostMediaId = $this->db->lastInsertId();
$PostMedia = $this->selectOneById($newPostMediaId);
}
return $PostMedia;
}
/**
* Split the attachment media in the three segments "visual", "link" and "additional"
*
* @param int $uri_id URI id
* @param array $links list of links that shouldn't be added
* @param bool $has_media
* @return Collection\PostMedias[] Three collections in "visual", "link" and "additional" keys
*/
public function splitAttachments(int $uri_id, array $links = [], bool $has_media = true): array
{
$attachments = [
'visual' => new Collection\PostMedias(),
'link' => new Collection\PostMedias(),
'additional' => new Collection\PostMedias(),
];
if (!$has_media) {
return $attachments;
}
$PostMedias = $this->selectByUriId($uri_id);
if (!count($PostMedias)) {
return $attachments;
}
$heights = [];
$selected = '';
$previews = [];
foreach ($PostMedias as $PostMedia) {
foreach ($links as $link) {
if (Strings::compareLink($link, $PostMedia->url)) {
continue 2;
}
}
// Avoid adding separate media entries for previews
foreach ($previews as $preview) {
if (Strings::compareLink($preview, $PostMedia->url)) {
continue 2;
}
}
// Currently these two types are ignored here.
// Posts are added differently and contacts are not displayed as attachments.
if (in_array($PostMedia->type, [Entity\PostMedia::TYPE_ACCOUNT, Entity\PostMedia::TYPE_ACTIVITY])) {
continue;
}
if (!empty($PostMedia->preview)) {
$previews[] = $PostMedia->preview;
}
//$PostMedia->filetype = $filetype;
//$PostMedia->subtype = $subtype;
if ($PostMedia->type == Entity\PostMedia::TYPE_HTML || ($PostMedia->mimetype->type == 'text' && $PostMedia->mimetype->subtype == 'html')) {
$attachments['link'][] = $PostMedia;
continue;
}
if (
in_array($PostMedia->type, [Entity\PostMedia::TYPE_AUDIO, Entity\PostMedia::TYPE_IMAGE]) ||
in_array($PostMedia->mimetype->type, ['audio', 'image'])
) {
$attachments['visual'][] = $PostMedia;
} elseif (($PostMedia->type == Entity\PostMedia::TYPE_VIDEO) || ($PostMedia->mimetype->type == 'video')) {
if (!empty($PostMedia->height)) {
// Peertube videos are delivered in many different resolutions. We pick a moderate one.
// Since only Peertube provides a "height" parameter, this wouldn't be executed
// when someone for example on Mastodon was sharing multiple videos in a single post.
$heights[$PostMedia->height] = (string)$PostMedia->url;
$video[(string) $PostMedia->url] = $PostMedia;
} else {
$attachments['visual'][] = $PostMedia;
}
} else {
$attachments['additional'][] = $PostMedia;
}
}
if (!empty($heights)) {
ksort($heights);
foreach ($heights as $height => $url) {
if (empty($selected) || $height <= 480) {
$selected = $url;
}
}
if (!empty($selected)) {
$attachments['visual'][] = $video[$selected];
unset($video[$selected]);
foreach ($video as $element) {
$attachments['additional'][] = $element;
}
}
}
return $attachments;
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -21,7 +21,6 @@
namespace Friendica\Content;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook;
use Friendica\DI;
use Friendica\Util\Strings;
@ -68,7 +67,7 @@ class Smilies
*/
public static function getList(): array
{
$texts = [
$texts = [
'&lt;3',
'&lt;/3',
'&lt;\\3',
@ -153,125 +152,6 @@ class Smilies
return $params;
}
/**
* Finds all used smilies (denoted by quoting colons like :heart:) in the provided text and normalizes their usages.
*
* @param string $text that might contain smiley usages
* @return array with smilie codes (colon included) as the keys, their image urls as values;
* the normalized string is put under the '' (empty string) key
*/
public static function extractUsedSmilies(string $text, string &$normalized = null): array
{
$emojis = [];
$normalized = BBCode::performWithEscapedTags($text, ['code'], function ($text) use (&$emojis) {
return BBCode::performWithEscapedTags($text, ['noparse', 'nobb', 'pre'], function ($text) use (&$emojis) {
if (strpos($text, '[nosmile]') !== false || self::noSmilies()) {
return $text;
}
$smilies = self::getList();
$normalized = [];
return self::performForEachWordMatch(
array_combine($smilies['texts'], $smilies['icons']),
$text,
function (string $name, string $image) use($normalized, &$emojis) {
if (array_key_exists($name, $normalized)) {
return $normalized[$name];
}
if (preg_match('/src="(.+?)"/', $image, $match)) {
$url = $match[1];
// Image smilies, which should be normalized instead of being embedded for some protocols like ActivityPub.
// Normalize name
$norm = preg_replace('/[\s\-:#~]/', '', $name);
if (!ctype_alnum($norm)) {
if (preg_match('#/smiley-(\w+)\.gif#', $url, $match)) {
$norm = $match[1];
} else {
$norm = 'smiley' . count($normalized);
}
}
$shortcode = ':' . $norm . ':';
$normalized[$name] = $shortcode;
$emojis[$norm] = $url;
return $shortcode;
} else {
$normalized[$name] = $image;
// Probably text-substitution smilies (e.g., Unicode ones).
return $image;
}
},
);
});
});
return $emojis;
}
/**
* Similar to strtr but matches only whole words and replaces texts with $callback.
*
* @param array $words
* @param string $subject
* @param callable $callback ($offset, $value)
* @return string
*/
private static function performForEachWordMatch(array $words, string $subject, callable $callback): string
{
$ord1_bitset = 0;
$ord2_bitset = 0;
$prefixes = [];
foreach ($words as $word => $_) {
if (strlen($word) < 2) {
continue;
}
$ord1 = ord($word[0]);
$ord2 = ord($word[1]);
// A smiley shortcode must not begin or end with whitespaces.
if (ctype_space($word[0]) || ctype_space($word[strlen($word) - 1])) {
continue;
}
$ord1_bitset |= 1 << ($ord1 & 31);
$ord2_bitset |= 1 << ($ord2 & 31);
if (!array_key_exists($word[0], $prefixes)) {
$prefixes[$word[0]] = [];
}
$prefixes[$word[0]][] = $word;
}
$slength = strlen($subject);
$result = '';
// $processed is used to delay string concatenation since appending a char every loop is inefficient.
$processed = 0;
// Find possible starting points for smilies.
// For built-in smilies, the two bitsets should make attempts quite efficient.
// However, presuming custom smilies follow the format of ":shortcode" or ":shortcode:",
// if the user adds more smilies (with addons), the second bitset may eventually become useless.
for ($i = 0; $i < $slength - 1; $i++) {
$c = $subject[$i];
$d = $subject[$i + 1];
if (($ord1_bitset & (1 << (ord($c) & 31))) && ($ord2_bitset & (1 << (ord($d) & 31))) && array_key_exists($c, $prefixes)) {
foreach ($prefixes[$c] as $word) {
$wlength = strlen($word);
if (substr($subject, $i, $wlength) === $word) {
// Check for boundaries
if (($i === 0 || ctype_space($subject[$i - 1]) || ctype_punct($subject[$i - 1]))
&& ($i + $wlength >= $slength || ctype_space($subject[$i + $wlength]) || ctype_punct($subject[$i + $wlength]))) {
$result .= substr($subject, $processed, $i - $processed);
$result .= call_user_func($callback, $word, $words[$word]);
$i += $wlength;
$processed = $i;
$i--;
break;
}
}
}
}
}
if ($processed < $slength) {
$result .= substr($subject, $processed);
}
return $result;
}
/**
* Copied from http://php.net/manual/en/function.str-replace.php#88569
@ -290,13 +170,7 @@ class Smilies
*/
private static function strOrigReplace(array $search, array $replace, string $subject): string
{
return self::performForEachWordMatch(
array_combine($search, $replace),
$subject,
function (string $_, string $value) {
return $value;
}
);
return strtr($subject, array_combine($search, $replace));
}
/**
@ -325,12 +199,6 @@ class Smilies
return $s;
}
private static function noSmilies(): bool {
return (intval(DI::config()->get('system', 'no_smilies')) ||
(DI::userSession()->getLocalUserId() &&
intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_smilies'))));
}
/**
* Replaces emoji shortcodes in a string from a structured array of searches and replaces.
*
@ -344,7 +212,9 @@ class Smilies
*/
public static function replaceFromArray(string $text, array $smilies, bool $no_images = false): string
{
if (self::noSmilies()) {
if (intval(DI::config()->get('system', 'no_smilies'))
|| (DI::userSession()->getLocalUserId() && intval(DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'no_smilies')))
) {
return $text;
}
@ -363,7 +233,7 @@ class Smilies
$smilies = $cleaned;
}
$text = preg_replace_callback('/\B&lt;3+?\b/', [self::class, 'heartReplaceCallback'], $text);
$text = preg_replace_callback('/&lt;(3+)/', [self::class, 'heartReplaceCallback'], $text);
$text = self::strOrigReplace($smilies['texts'], $smilies['icons'], $text);
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', [self::class, 'decode'], $text);
@ -404,35 +274,15 @@ class Smilies
*/
private static function heartReplaceCallback(array $matches): string
{
return str_repeat('❤', strlen($matches[0]) - 4);
}
/**
* Checks if the body doesn't contain any alphanumeric characters
*
* @param string $body Possibly-HTML post body
* @return boolean
*/
public static function isEmojiPost(string $body): bool
{
// Strips all whitespace
$conv = preg_replace('#\s#u', '', html_entity_decode($body));
if (empty($conv)) {
return false;
if (strlen($matches[1]) == 1) {
return $matches[0];
}
if (!class_exists('IntlChar')) {
// Most Emojis are 4 byte Unicode characters, so this is a good workaround, when IntlChar does not exist on the system
return strlen($conv) / mb_strlen($conv) == 4;
$t = '';
for ($cnt = 0; $cnt < strlen($matches[1]); $cnt ++) {
$t .= '❤';
}
for ($i = 0; $i < mb_strlen($conv); $i++) {
$character = mb_substr($conv, $i, 1);
if (\IntlChar::isalnum($character) || \IntlChar::ispunct($character) || \IntlChar::isgraph($character) && (strlen($character) <= 2)) {
return false;
}
}
return true;
return str_replace($matches[0], $t, $matches[0]);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -230,73 +230,18 @@ class BBCode
{
DI::profiler()->startRecording('rendering');
// Remove pictures in advance to avoid unneeded proxy calls
$text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", ' ', $text);
$text = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $2 ', $text);
$text = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $text);
// Remove attachment
$text = self::replaceAttachment($text);
$naked_text = HTML::toPlaintext(self::convert($text, false, self::EXTERNAL, true), 0, !$keep_urls);
$naked_text = HTML::toPlaintext(self::convert($text, false, BBCode::EXTERNAL, true), 0, !$keep_urls);
DI::profiler()->stopRecording();
return $naked_text;
}
/**
* Converts text into a format that can be used for the channel search and the language detection.
*
* @param string $text
* @param integer $uri_id
* @return string
*/
public static function toSearchText(string $text, int $uri_id): string
{
// Removes attachments
$text = self::removeAttachment($text);
// Add images because of possible alt texts
if (!empty($uri_id)) {
$text = Post\Media::addAttachmentsToBody($uri_id, $text, [Post\Media::IMAGE]);
}
if (empty($text)) {
return '';
}
// Remove links without a link description
$text = preg_replace("~\[url\=.*\]https?:.*\[\/url\]~", ' ', $text);
// Remove pictures
$text = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", ' ', $text);
// Replace picture with the alt description
$text = preg_replace("/\[img\=.*?\](.*?)\[\/img\]/ism", ' $1 ', $text);
// Remove the other pictures
$text = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $text);
// Removes mentions, remove links from hashtags
$text = preg_replace('/[@!]\[url\=.*?\].*?\[\/url\]/ism', ' ', $text);
$text = preg_replace('/[#]\[url\=.*?\](.*?)\[\/url\]/ism', ' #$1 ', $text);
$text = preg_replace('/[@!#]?\[url.*?\[\/url\]/ism', ' ', $text);
$text = preg_replace("/\[url=[^\[\]]*\](.*)\[\/url\]/Usi", ' $1 ', $text);
// Convert it to plain text
$text = self::toPlaintext($text, false);
// Remove possibly remaining links
$text = preg_replace(Strings::autoLinkRegEx(), '', $text);
// Remove all unneeded white space
do {
$oldtext = $text;
$text = str_replace([' ', "\n", "\r", '"'], ' ', $text);
} while ($oldtext != $text);
return trim($text);
}
private static function proxyUrl(string $image, int $simplehtml = self::INTERNAL, int $uriid = 0, string $size = ''): string
{
// Only send proxied pictures to API and for internal display
@ -986,7 +931,7 @@ class BBCode
$network = $contact['network'] ?? Protocol::PHANTOM;
$tpl = Renderer::getMarkupTemplate('shared_content.tpl');
$text .= self::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [
$text .= BBCode::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [
'$profile' => $attributes['profile'],
'$avatar' => $attributes['avatar'],
'$author' => $attributes['author'],
@ -1167,7 +1112,6 @@ class BBCode
public static function removeLinks(string $bbcode): string
{
DI::profiler()->startRecording('rendering');
$bbcode = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", ' ', $bbcode);
$bbcode = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", ' $1 ', $bbcode);
$bbcode = preg_replace("/\[img.*?\[\/img\]/ism", ' ', $bbcode);
@ -1234,7 +1178,7 @@ class BBCode
}
/**
* Expand Youtube and Vimeo links to
* Expand Youtube and Vimeo links to
*
* @param string $text
* @return string
@ -1387,7 +1331,7 @@ class BBCode
"\n[hr]", "[hr]\n", " [hr]", "[hr] ",
"\n[attachment ", " [attachment ", "\n[/attachment]", "[/attachment]\n", " [/attachment]", "[/attachment] ",
"[table]\n", "[table] ", " [table]", "\n[/table]", " [/table]", "[/table] ",
" \n", "\t\n", "[/li]\n", "\n[li]", "\n[*]",
" \n", "\t\n", "[/li]\n", "\n[li]", "\n[*]",
];
$replace = [
"[th]", "[th]", "[th]", "[/th]", "[/th]", "[/th]",
@ -1480,14 +1424,14 @@ class BBCode
if ($simple_html == self::INTERNAL) {
//Ensure to always start with <h4> if possible
$heading_count = 0;
for ($level = 6; $level > 0; $level--) {
for ($level = 6; $level > 0; $level--) {
if (preg_match("(\[h$level\].*?\[\/h$level\])ism", $text)) {
$heading_count++;
}
}
if ($heading_count > 0) {
$heading = min($heading_count + 3, 6);
for ($level = 6; $level > 0; $level--) {
for ($level = 6; $level > 0; $level--) {
if (preg_match("(\[h$level\].*?\[\/h$level\])ism", $text)) {
$text = preg_replace("(\[h$level\](.*?)\[\/h$level\])ism", "</p><h$heading>$1</h$heading><p>", $text);
$heading--;
@ -1548,11 +1492,7 @@ class BBCode
$text = preg_replace("(\[style=(.*?)\](.*?)\[\/style\])ism", '<span style="$1">$2</span>', $text);
// Mastodon Emoji (internal tag, do not document for users)
if ($simple_html == self::MASTODON_API) {
$text = preg_replace("(\[emoji=(.*?)](.*?)\[/emoji])ism", '$2', $text);
} else {
$text = preg_replace("(\[emoji=(.*?)](.*?)\[/emoji])ism", '<span class="mastodon emoji"><img src="$1" alt="$2" title="$2"/></span>', $text);
}
$text = preg_replace("(\[emoji=(.*?)](.*?)\[/emoji])ism", '<span class="mastodon emoji"><img src="$1" alt="$2" title="$2"/></span>', $text);
// Check for CSS classes
// @deprecated since 2021.12, left for backward-compatibility reasons
@ -1795,8 +1735,12 @@ class BBCode
$text = preg_replace("/\[event\-id\](.*?)\[\/event\-id\]/ism", '', $text);
}
if (!$for_plaintext && DI::config()->get('system', 'big_emojis') && ($simple_html != self::DIASPORA) && Smilies::isEmojiPost($text)) {
$text = '<span style="font-size: xx-large; line-height: normal;">' . $text . '</span>';
if (!$for_plaintext && DI::config()->get('system', 'big_emojis') && ($simple_html != self::DIASPORA)) {
$conv = html_entity_decode(str_replace([' ', "\n", "\r"], '', $text));
// Emojis are always 4 byte Unicode characters
if (!empty($conv) && (strlen($conv) / mb_strlen($conv) == 4)) {
$text = '<span style="font-size: xx-large; line-height: normal;">' . $text . '</span>';
}
}
// Handle mentions and hashtag links
@ -2052,7 +1996,7 @@ class BBCode
{
DI::profiler()->startRecording('rendering');
$text = self::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) {
$text = BBCode::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) {
$text = preg_replace("/[\s|\n]*\[abstract\].*?\[\/abstract\][\s|\n]*/ism", ' ', $text);
$text = preg_replace("/[\s|\n]*\[abstract=.*?\].*?\[\/abstract][\s|\n]*/ism", ' ', $text);
return $text;
@ -2074,7 +2018,7 @@ class BBCode
DI::profiler()->startRecording('rendering');
$addon = strtolower($addon);
$abstract = self::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) use ($addon) {
$abstract = BBCode::performWithEscapedTags($text, ['code', 'noparse', 'nobb', 'pre'], function ($text) use ($addon) {
if ($addon && preg_match('#\[abstract=' . preg_quote($addon, '#') . '](.*?)\[/abstract]#ism', $text, $matches)) {
return $matches[1];
}
@ -2171,9 +2115,6 @@ class BBCode
// Maybe we should make this newline at every time before a quote.
$text = str_replace(['</a><blockquote>'], ['</a><br><blockquote>'], $text);
// The converter doesn't convert these elements
$text = str_replace(['<div>', '</div>'], ['<p>', '</p>'], $text);
// Now convert HTML to Markdown
$text = HTML::toMarkdown($text);

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -22,6 +22,7 @@
namespace Friendica\Content\Text;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Contact;
@ -111,7 +112,7 @@ class Markdown
{
// @TODO Temporary until we find the source of the null value to finally set the correct type-hint
if (is_null($s)) {
Logger::warning('Received null value');
Logger::warning('Received null value', ['callstack' => System::callstack()]);
return '';
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -324,7 +324,7 @@ class Plaintext
$post['text'] = Post\Media::removeFromBody($post['text']);
$images = Post\Media::getByURIId($item['uri-id'], [Post\Media::IMAGE]);
if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) {
if (!empty($item['quote-uri-id'])) {
$images = array_merge($images, Post\Media::getByURIId($item['quote-uri-id'], [Post\Media::IMAGE]));
}
foreach ($images as $image) {
@ -355,7 +355,7 @@ class Plaintext
// Look for audio or video links
$media = Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO]);
if (!empty($item['quote-uri-id']) && ($item['quote-uri-id'] != $item['uri-id'])) {
if (!empty($item['quote-uri-id'])) {
$media = array_merge($media, Post\Media::getByURIId($item['quote-uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO]));
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -560,30 +560,12 @@ class Widget
{
$channels = [];
$enabled = DI::pConfig()->get($uid, 'system', 'enabled_timelines', []);
foreach (DI::NetworkFactory()->getTimelines('') as $channel) {
if (empty($enabled) || in_array($channel->code, $enabled)) {
$channels[] = ['ref' => $channel->code, 'name' => $channel->label];
}
foreach (DI::TimelineFactory()->getChannelsForUser($uid) as $channel) {
$channels[] = ['ref' => $channel->code, 'name' => $channel->label];
}
foreach (DI::ChannelFactory()->getTimelines($uid) as $channel) {
if (empty($enabled) || in_array($channel->code, $enabled)) {
$channels[] = ['ref' => $channel->code, 'name' => $channel->label];
}
}
foreach (DI::userDefinedChannel()->selectByUid($uid) as $channel) {
if (empty($enabled) || in_array($channel->code, $enabled)) {
$channels[] = ['ref' => $channel->code, 'name' => $channel->label];
}
}
foreach (DI::CommunityFactory()->getTimelines(true) as $community) {
if (empty($enabled) || in_array($community->code, $enabled)) {
$channels[] = ['ref' => $community->code, 'name' => $community->label];
}
foreach (DI::TimelineFactory()->getCommunities(true) as $community) {
$channels[] = ['ref' => $community->code, 'name' => $community->label];
}
return self::filter(

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,70 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Content\Widget;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\Model\Contact;
use Friendica\Network\HTTPException;
use Friendica\Util\Strings;
class Hovercard
{
/**
* @param array $contact
* @param int $localUid Used to show user actions
* @return string
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\ServiceUnavailableException
* @throws \ImagickException
*/
public static function getHTML(array $contact, int $localUid = 0): string
{
if ($localUid) {
$actions = Contact::photoMenu($contact, $localUid);
} else {
$actions = [];
}
// Move the contact data to the profile array so we can deliver it to
$tpl = Renderer::getMarkupTemplate('hovercard.tpl');
return Renderer::replaceMacros($tpl, [
'$profile' => [
'name' => $contact['name'],
'nick' => $contact['nick'],
'addr' => $contact['addr'] ?: $contact['url'],
'thumb' => Contact::getThumb($contact),
'url' => Contact::magicLinkByContact($contact),
'nurl' => $contact['nurl'],
'location' => $contact['location'],
'about' => $contact['about'],
'network_link' => Strings::formatNetworkName($contact['network'], $contact['url']),
'tags' => $contact['keywords'],
'bd' => $contact['bd'] <= DBA::NULL_DATE ? '' : $contact['bd'],
'account_type' => Contact::getAccountType($contact['contact-type']),
'contact_type' => $contact['contact-type'],
'actions' => $actions,
'self' => $contact['self'],
],
]);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
@ -26,6 +26,7 @@ use Friendica\Content\Text\BBCode;
use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Util\Network;
@ -47,7 +48,7 @@ class VCard
public static function getHTML(array $contact): string
{
if (!isset($contact['network']) || !isset($contact['id'])) {
Logger::warning('Incomplete contact', ['contact' => $contact ?? []]);
Logger::warning('Incomplete contact', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]);
}
if (!Network::isValidHttpUrl($contact['url']) && Network::isValidHttpUrl($contact['alias'])) {
@ -67,9 +68,6 @@ class VCard
$follow_link = '';
$unfollow_link = '';
$wallmessage_link = '';
$mention_label = '';
$mention_link = '';
$showgroup_link = '';
$photo = Contact::getPhoto($contact);
@ -101,15 +99,6 @@ class VCard
if (in_array($rel, [Contact::FOLLOWER, Contact::FRIEND]) && Contact::canReceivePrivateMessages($contact)) {
$wallmessage_link = 'message/new/' . $id;
}
if ($contact['contact-type'] == Contact::TYPE_COMMUNITY) {
$mention_label = DI::l10n()->t('Post to group');
$mention_link = 'compose/0?body=!' . $contact['addr'];
$showgroup_link = 'network/group/' . $id;
} else {
$mention_label = DI::l10n()->t('Mention');
$mention_link = 'compose/0?body=@' . $contact['addr'];
}
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
@ -130,10 +119,6 @@ class VCard
'$unfollow_link' => $unfollow_link,
'$wallmessage' => DI::l10n()->t('Message'),
'$wallmessage_link' => $wallmessage_link,
'$mention' => $mention_label,
'$mention_link' => $mention_link,
'$showgroup' => DI::l10n()->t('View group'),
'$showgroup_link' => $showgroup_link,
]);
}
}

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

View file

@ -1,6 +1,6 @@
<?php
/**
* @copyright Copyright (C) 2010-2024, the Friendica project
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*

Some files were not shown because too many files have changed in this diff Show more