Compare commits
No commits in common. "develop" and "stable" have entirely different histories.
4
.gitignore
vendored
|
|
@ -19,7 +19,7 @@ robots.txt
|
|||
/doc/cache
|
||||
|
||||
#ignore reports, should be generated with every build
|
||||
/report/
|
||||
report/
|
||||
|
||||
#ignore config files from eclipse, we don't want IDE files in our repository
|
||||
.project
|
||||
|
|
@ -33,7 +33,7 @@ robots.txt
|
|||
#ignore NetBeans IDE's private files (at least)
|
||||
/nbproject/private/
|
||||
|
||||
#Ignore config files from VSCode
|
||||
Ignore config files from VSCode
|
||||
/.vscode/
|
||||
|
||||
#ignore smarty cache
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
steps:
|
||||
pipeline:
|
||||
restore_cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ labels:
|
|||
|
||||
skip_clone: true
|
||||
|
||||
steps:
|
||||
pipeline:
|
||||
clone:
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git clone $CI_REPO_CLONE_URL .
|
||||
- git clone $CI_REPO_LINK .
|
||||
- git checkout $CI_COMMIT_BRANCH
|
||||
- git fetch origin $CI_COMMIT_REF
|
||||
- git merge $CI_COMMIT_SHA
|
||||
|
|
|
|||
|
|
@ -6,11 +6,7 @@ matrix:
|
|||
branches:
|
||||
exclude: [ stable ]
|
||||
|
||||
# This forces CI executions at the "opensocial" labeled location (because of much more power...)
|
||||
labels:
|
||||
location: opensocial
|
||||
|
||||
steps:
|
||||
pipeline:
|
||||
db_version_match:
|
||||
image: friendicaci/transifex
|
||||
commands:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
steps:
|
||||
pipeline:
|
||||
check:
|
||||
image: friendicaci/php-cs
|
||||
commands:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
steps:
|
||||
pipeline:
|
||||
build_xgettext:
|
||||
image: friendicaci/transifex
|
||||
commands:
|
||||
|
|
@ -9,4 +9,4 @@ steps:
|
|||
- /check-messages.sh
|
||||
|
||||
branches:
|
||||
exclude: [ stable ]
|
||||
exclude: [ stable ]
|
||||
|
|
@ -5,22 +5,20 @@ matrix:
|
|||
- PHP_MAJOR_VERSION: 7.4
|
||||
PHP_VERSION: 7.4.33
|
||||
- PHP_MAJOR_VERSION: 8.0
|
||||
PHP_VERSION: 8.0.29
|
||||
PHP_VERSION: 8.0.25
|
||||
- PHP_MAJOR_VERSION: 8.1
|
||||
PHP_VERSION: 8.1.21
|
||||
- PHP_MAJOR_VERSION: 8.2
|
||||
PHP_VERSION: 8.2.8
|
||||
PHP_VERSION: 8.1.12
|
||||
|
||||
# This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...)
|
||||
labels:
|
||||
location: opensocial
|
||||
|
||||
steps:
|
||||
pipeline:
|
||||
php-lint:
|
||||
image: php:${PHP_MAJOR_VERSION}
|
||||
group: lint
|
||||
commands:
|
||||
- find . -name \*.php -not -path './vendor/*' -not -path './view/asset/*' -print0 | xargs -0 -n1 php -l
|
||||
- ./bin/composer.phar run lint
|
||||
restore_cache:
|
||||
image: meltwater/drone-cache:dev
|
||||
settings:
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ labels:
|
|||
|
||||
skip_clone: true
|
||||
|
||||
steps:
|
||||
pipeline:
|
||||
clone:
|
||||
image: alpine/git
|
||||
commands:
|
||||
- git clone $CI_REPO_CLONE_URL .
|
||||
- git clone $CI_REPO_LINK .
|
||||
- git checkout $CI_COMMIT_BRANCH
|
||||
- git fetch origin $CI_COMMIT_REF
|
||||
- git merge $CI_COMMIT_SHA
|
||||
|
|
|
|||
|
|
@ -1,10 +1,3 @@
|
|||
Version 2023.09 (unreleased)
|
||||
Friendica Core
|
||||
|
||||
Friendica Addons
|
||||
|
||||
Closed Issues
|
||||
|
||||
Version 2023.05 (2023-05-23)
|
||||
Friendica Core
|
||||
Updates to the translations HU, PL
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
# How to Contribute
|
||||
|
||||
If you want to contribute to the project, you don’t need to have coding experience. There are a number of tasks listed in the issue tracker with the label “[Junior Jobs](https://github.com/friendica/friendica/issues?q=is%3Aopen+is%3Aissue+label%3A%22Junior+Jobs%22)” we think are good for new contributors. But you are by no means limited to these – if you find a solution to a problem (even a new one) please make a pull request at [github](https://github.com/friendica/friendica) or let us know in the [development group](https://forum.friendi.ca/profile/developers).
|
||||
If you want to contribute to the project, you don’t need to have coding experience. There are a number of tasks listed in the issue tracker with the label “[Junior Jobs](https://github.com/friendica/friendica/issues?q=is%3Aopen+is%3Aissue+label%3A%22Junior+Jobs%22)” we think are good for new contributors. But you are by no means limited to these – if you find a solution to a problem (even a new one) please make a pull request at [github](https://github.com/friendica/friendica) or let us know in the [development forum](https://forum.friendi.ca/profile/developers).
|
||||
|
||||
Contribution to Friendica is also not limited to coding. Any contribution to the [documentation](https://github.com/friendica/friendica/tree/develop/doc), the [translation](https://app.transifex.com/Friendica/friendica/dashboard/) or advertisement materials is welcome or reporting a problem. You don’t need to deal with Git(Hub) or Transifex if you don’t like to. Just [get in touch](https://forum.friendi.ca/profile/helpers) with us and we will get the materials to the appropriate places.
|
||||
|
|
|
|||
54
README.md
|
|
@ -1,11 +1,3 @@
|
|||
# friendica_2020-09-1_sharedHosting by bitPickup
|
||||
|
||||
## trying to create a fork of 2020.09-1 by adding this text directly to the readme.md
|
||||
### the -> settings: "merge commit" -> "allow edits by mantainers by default" was enabled to do this editing inside the fork
|
||||
#### the tag was set to 2020-09-1 inside the /friendica/friendica fork
|
||||
##### setting "commit directly to the [develop] branch" was seleted for this commit
|
||||
|
||||
======================================
|
||||
Friendica Social Communications Server
|
||||
======================================
|
||||
|
||||
|
|
@ -25,29 +17,29 @@ Have a look at the [installation documentation](doc/Install.md) for further info
|
|||
|
||||
### Friendica Screenshots
|
||||
|
||||
|   |
|
||||
|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
|
||||
| *Frio theme, mobile browser. Timeline and composer view.* |
|
||||
|  |
|
||||
| *Frio theme, desktop browser. Timeline view, contact info popped up, control menu open.* |
|
||||
|  |
|
||||
| *Frio theme, desktop browser. Menu open for controlling individual posts.* |
|
||||
|  |
|
||||
| *Frio theme, desktop browser. Profile view, notification menu open.* |
|
||||
|  |
|
||||
| *Number of new posts, in total and by circle.* |
|
||||
|  |
|
||||
| *Calendar with popup of event.* |
|
||||
|  |
|
||||
| *Notifications menu and private messages counter, standard browser on tablet.* |
|
||||
|  |
|
||||
| *Number of visible contacts, standard browser.* |
|
||||
|  |
|
||||
| *Network posts chronologically ordered, standard browser.* |
|
||||
|  |
|
||||
| *Vier theme, desktop browser. Public timeline view.* |
|
||||
|  |
|
||||
| *Vier theme, desktop browser. Community post displayed.* |
|
||||
|  
|
||||
|:--:|
|
||||
|*Frio theme, mobile browser. Timeline and composer view.*|
|
||||
|
|
||||
|*Frio theme, desktop browser. Timeline view, contact info popped up, control menu open.*|
|
||||
|
|
||||
|*Frio theme, desktop browser. Menu open for controlling individual posts.*|
|
||||
|
|
||||
|*Frio theme, desktop browser. Profile view, notification menu open.*|
|
||||
|
|
||||
|*Number of new posts, in total and by group.*|
|
||||
|
|
||||
|*Calender with popup of event.*|
|
||||
|
|
||||
|*Notifications menu and private messages counter, standard browser on tablet.*|
|
||||
|
|
||||
|*Number of visible contacts, standard browser.*|
|
||||
|
|
||||
|*Network posts chronologically ordered, standard browser.*|
|
||||
|
|
||||
|*Vier theme, desktop browser. Public timeline view.*|
|
||||
|
|
||||
|*Vier theme, desktop browser. Community post displayed.*|
|
||||
|
||||
## Endorsements
|
||||
|
||||
|
|
|
|||
2
VERSION
|
|
@ -1 +1 @@
|
|||
2023.09-dev
|
||||
2023.05
|
||||
|
|
|
|||
|
|
@ -58,7 +58,6 @@ if (php_sapi_name() !== 'cli') {
|
|||
|
||||
use Dice\Dice;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Security\ExAuth;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
@ -79,10 +78,7 @@ chdir($directory);
|
|||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class,['constructParams' => [LogChannel::AUTH_JABBERED]]);
|
||||
$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabberd']]);
|
||||
|
||||
\Friendica\DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
|
|
|
|||
|
|
@ -26,17 +26,13 @@ if (php_sapi_name() !== 'cli') {
|
|||
}
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\DI;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::CONSOLE]]);
|
||||
$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['console']]);
|
||||
|
||||
/// @fixme Necessary until Hooks inside the Logger can get loaded without the DI-class
|
||||
DI::init($dice);
|
||||
|
|
|
|||
|
|
@ -60,10 +60,7 @@ if (!file_exists('index.php') && (sizeof($_SERVER['argv']) != 0)) {
|
|||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [Logger\Capability\LogChannel::DAEMON]]);
|
||||
$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['daemon']]);
|
||||
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ if (php_sapi_name() !== 'cli') {
|
|||
use Dice\Dice;
|
||||
use Friendica\App;
|
||||
use Friendica\App\Mode;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Update;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\DI;
|
||||
|
|
@ -55,10 +54,7 @@ if (!file_exists("index.php") && (sizeof($_SERVER["argv"]) != 0)) {
|
|||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/../static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(LoggerInterface::class, ['constructParams' => [LogChannel::WORKER]]);
|
||||
$dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['worker']]);
|
||||
|
||||
DI::init($dice);
|
||||
\Friendica\Core\Logger\Handler\ErrorHandler::register($dice->create(\Psr\Log\LoggerInterface::class));
|
||||
|
|
|
|||
|
|
@ -77,12 +77,6 @@
|
|||
"npm-asset/textcomplete": "^0.18.2",
|
||||
"npm-asset/typeahead.js": "^0.11.1"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "For faster image processing",
|
||||
"ext-redis": "To use Redis as a locking or caching provider",
|
||||
"ext-pdo": "To use PDO as a database driver, has priority over mysqli unless database.disable_pdo is set",
|
||||
"ext-mysqli": "To use mysqli as a databse driver"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "vcs",
|
||||
|
|
@ -140,7 +134,6 @@
|
|||
"scripts": {
|
||||
"test": "phpunit",
|
||||
"lint": "find . -name \\*.php -not -path './vendor/*' -not -path './view/asset/*' -print0 | xargs -0 -n1 php -l",
|
||||
"docker:translate": "docker run --rm -v $PWD:/data -w /data friendicaci/transifex bin/run_xgettext.sh",
|
||||
"cs:install": "@composer install --working-dir=bin/dev/php-cs-fixer",
|
||||
"cs:check": [
|
||||
"@cs:install",
|
||||
|
|
|
|||
24
composer.lock
generated
|
|
@ -720,11 +720,11 @@
|
|||
},
|
||||
{
|
||||
"name": "friendica/json-ld",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://git.friendi.ca/friendica/php-json-ld",
|
||||
"reference": "1f33c8766b5cfbecfbc2122238873debeed35fb6"
|
||||
"reference": "5f6ea87b261d346e57f03457ae906e6835f0205f"
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
|
|
@ -760,7 +760,7 @@
|
|||
"Semantic Web",
|
||||
"jsonld"
|
||||
],
|
||||
"time": "2023-07-09T14:00:15+00:00"
|
||||
"time": "2023-02-20T21:56:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fxp/composer-asset-plugin",
|
||||
|
|
@ -1322,24 +1322,6 @@
|
|||
"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"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
235
database.sql
|
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2023.09-dev (Giant Rhubarb)
|
||||
-- DB_UPDATE_VERSION 1530
|
||||
-- Friendica 2023.05 (Giant Rhubarb)
|
||||
-- DB_UPDATE_VERSION 1518
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
|
@ -101,19 +101,6 @@ CREATE TABLE IF NOT EXISTS `user` (
|
|||
FOREIGN KEY (`parent-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
|
||||
|
||||
--
|
||||
-- TABLE user-gserver
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `user-gserver` (
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`gsid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Gserver id',
|
||||
`ignored` boolean NOT NULL DEFAULT '0' COMMENT 'server accounts are ignored for the user',
|
||||
PRIMARY KEY(`uid`,`gsid`),
|
||||
INDEX `gsid` (`gsid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User settings about remote servers';
|
||||
|
||||
--
|
||||
-- TABLE item-uri
|
||||
--
|
||||
|
|
@ -173,8 +160,8 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`archive` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`unsearchable` boolean NOT NULL DEFAULT '0' COMMENT 'Contact prefers to not be searchable',
|
||||
`sensitive` boolean NOT NULL DEFAULT '0' COMMENT 'Contact posts sensitive content',
|
||||
`baseurl` varbinary(383) DEFAULT '' COMMENT 'baseurl of the contact from the gserver record, can be missing',
|
||||
`gsid` int unsigned COMMENT 'Global Server ID, can be missing',
|
||||
`baseurl` varbinary(383) DEFAULT '' COMMENT 'baseurl of the contact',
|
||||
`gsid` int unsigned COMMENT 'Global Server ID',
|
||||
`bd` date NOT NULL DEFAULT '0001-01-01' COMMENT '',
|
||||
`reason` text COMMENT '',
|
||||
`self` boolean NOT NULL DEFAULT '0' COMMENT '1 if the contact is the user him/her self',
|
||||
|
|
@ -203,7 +190,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
|
|||
`confirm` varbinary(383) COMMENT '',
|
||||
`poco` varbinary(383) COMMENT '',
|
||||
`writable` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a group. Deprecated, use \'contact-type\' = \'community\' and \'manually-approve\' = false instead',
|
||||
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum. Deprecated, use \'contact-type\' = \'community\' and \'manually-approve\' = false instead',
|
||||
`prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group. Deprecated, use \'contact-type\' = \'community\' and \'manually-approve\' = true instead',
|
||||
`bdyear` varchar(4) NOT NULL DEFAULT '' COMMENT '',
|
||||
`site-pubkey` text COMMENT 'Deprecated',
|
||||
|
|
@ -265,9 +252,9 @@ CREATE TABLE IF NOT EXISTS `permissionset` (
|
|||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner id of this permission set',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed circles',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied circles',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`uid`,`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30)),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
|
|
@ -470,9 +457,9 @@ CREATE TABLE IF NOT EXISTS `attach` (
|
|||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time',
|
||||
`edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed circles',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied circles',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
`backend-class` tinytext COMMENT 'Storage backend class',
|
||||
`backend-ref` text COMMENT 'Storage backend data reference',
|
||||
PRIMARY KEY(`id`),
|
||||
|
|
@ -513,10 +500,6 @@ CREATE TABLE IF NOT EXISTS `contact-relation` (
|
|||
`last-interaction` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last interaction',
|
||||
`follow-updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last update of the contact relationship',
|
||||
`follows` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`score` smallint unsigned COMMENT 'score for interactions of cid on relation-cid',
|
||||
`relation-score` smallint unsigned COMMENT 'score for interactions of relation-cid on cid',
|
||||
`thread-score` smallint unsigned COMMENT 'score for interactions of cid on threads of relation-cid',
|
||||
`relation-thread-score` smallint unsigned COMMENT 'score for interactions of relation-cid on threads of cid',
|
||||
PRIMARY KEY(`cid`,`relation-cid`),
|
||||
INDEX `relation-cid` (`relation-cid`),
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
|
|
@ -679,9 +662,9 @@ CREATE TABLE IF NOT EXISTS `event` (
|
|||
`nofinish` boolean NOT NULL DEFAULT '0' COMMENT 'if event does have no end this is 1',
|
||||
`ignore` boolean NOT NULL DEFAULT '0' COMMENT '0 or 1',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed circles',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied circles',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid_start` (`uid`,`start`),
|
||||
INDEX `cid` (`cid`),
|
||||
|
|
@ -733,29 +716,29 @@ CREATE TABLE IF NOT EXISTS `group` (
|
|||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
|
||||
`visible` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the member list is not private',
|
||||
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the circle has been deleted',
|
||||
`cid` int unsigned COMMENT 'Contact id of group. When this field is filled then the members are synced automatically.',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of circle',
|
||||
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted',
|
||||
`cid` int unsigned COMMENT 'Contact id of forum. When this field is filled then the members are synced automatically.',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
INDEX `cid` (`cid`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy circles, circle info';
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info';
|
||||
|
||||
--
|
||||
-- TABLE group_member
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `group_member` (
|
||||
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
|
||||
`gid` int unsigned NOT NULL DEFAULT 0 COMMENT 'group.id of the associated circle',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id of the member assigned to the associated circle',
|
||||
`gid` int unsigned NOT NULL DEFAULT 0 COMMENT 'groups.id of the associated group',
|
||||
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id of the member assigned to the associated group',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `contactid` (`contact-id`),
|
||||
UNIQUE INDEX `gid_contactid` (`gid`,`contact-id`),
|
||||
FOREIGN KEY (`gid`) REFERENCES `group` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy circles, member info';
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, member info';
|
||||
|
||||
--
|
||||
-- TABLE gserver-tag
|
||||
|
|
@ -1131,9 +1114,9 @@ CREATE TABLE IF NOT EXISTS `photo` (
|
|||
`scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
|
||||
`profile` boolean NOT NULL DEFAULT '0' COMMENT '',
|
||||
`allow_cid` mediumtext COMMENT 'Access Control - list of allowed contact.id \'<19><78>\'',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed circles',
|
||||
`allow_gid` mediumtext COMMENT 'Access Control - list of allowed groups',
|
||||
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied circles',
|
||||
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
|
||||
`accessible` boolean NOT NULL DEFAULT '0' COMMENT 'Make photo publicly accessible, ignoring permissions',
|
||||
`backend-class` tinytext COMMENT 'Storage backend class',
|
||||
`backend-ref` text COMMENT 'Storage backend data reference',
|
||||
|
|
@ -1603,7 +1586,7 @@ CREATE TABLE IF NOT EXISTS `profile` (
|
|||
`profile-name` varchar(255) COMMENT 'Deprecated',
|
||||
`is-default` boolean COMMENT 'Deprecated',
|
||||
`hide-friends` boolean NOT NULL DEFAULT '0' COMMENT 'Hide friend list from viewers of this profile',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Unused in favor of user.username',
|
||||
`name` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
`pdesc` varchar(255) COMMENT 'Deprecated',
|
||||
`dob` varchar(32) NOT NULL DEFAULT '0000-00-00' COMMENT 'Day of birth',
|
||||
`address` varchar(255) NOT NULL DEFAULT '' COMMENT '',
|
||||
|
|
@ -1712,34 +1695,19 @@ CREATE TABLE IF NOT EXISTS `report` (
|
|||
`uid` mediumint unsigned COMMENT 'Reporting user',
|
||||
`reporter-id` int unsigned COMMENT 'Reporting contact',
|
||||
`cid` int unsigned NOT NULL COMMENT 'Reported contact',
|
||||
`gsid` int unsigned COMMENT 'Reported contact server',
|
||||
`comment` text COMMENT 'Report',
|
||||
`category-id` int unsigned NOT NULL DEFAULT 1 COMMENT 'Report category, one of Entity\Report::CATEGORY_*',
|
||||
`category` varchar(20) COMMENT 'Category of the report (spam, violation, other)',
|
||||
`rules` text COMMENT 'Violated rules',
|
||||
`forward` boolean COMMENT 'Forward the report to the remote server',
|
||||
`public-remarks` text COMMENT 'Remarks shared with the reporter',
|
||||
`private-remarks` text COMMENT 'Remarks shared with the moderation team',
|
||||
`last-editor-uid` mediumint unsigned COMMENT 'Last editor user',
|
||||
`assigned-uid` mediumint unsigned COMMENT 'Assigned moderator user',
|
||||
`status` tinyint unsigned NOT NULL COMMENT 'Status of the report, one of Entity\Report::STATUS_*',
|
||||
`resolution` tinyint unsigned COMMENT 'Resolution of the report, one of Entity\Report::RESOLUTION_*',
|
||||
`created` datetime(6) NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`edited` datetime(6) COMMENT 'Last time the report has been edited',
|
||||
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
|
||||
`status` tinyint unsigned COMMENT 'Status of the report',
|
||||
PRIMARY KEY(`id`),
|
||||
INDEX `uid` (`uid`),
|
||||
INDEX `cid` (`cid`),
|
||||
INDEX `reporter-id` (`reporter-id`),
|
||||
INDEX `gsid` (`gsid`),
|
||||
INDEX `last-editor-uid` (`last-editor-uid`),
|
||||
INDEX `assigned-uid` (`assigned-uid`),
|
||||
INDEX `status-resolution` (`status`,`resolution`),
|
||||
INDEX `created` (`created`),
|
||||
INDEX `edited` (`edited`),
|
||||
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`reporter-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`last-editor-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`assigned-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
|
|
@ -1753,18 +1721,7 @@ CREATE TABLE IF NOT EXISTS `report-post` (
|
|||
INDEX `uri-id` (`uri-id`),
|
||||
FOREIGN KEY (`rid`) REFERENCES `report` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
|
||||
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Individual posts attached to a moderation report';
|
||||
|
||||
--
|
||||
-- TABLE report-rule
|
||||
--
|
||||
CREATE TABLE IF NOT EXISTS `report-rule` (
|
||||
`rid` int unsigned NOT NULL COMMENT 'Report id',
|
||||
`line-id` int unsigned NOT NULL COMMENT 'Terms of service rule line number, may become invalid after a TOS change.',
|
||||
`text` text NOT NULL COMMENT 'Terms of service rule text recorded at the time of the report',
|
||||
PRIMARY KEY(`rid`,`line-id`),
|
||||
FOREIGN KEY (`rid`) REFERENCES `report` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Terms of service rule lines relevant to a moderation report';
|
||||
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
|
||||
|
||||
--
|
||||
-- TABLE search
|
||||
|
|
@ -1851,8 +1808,8 @@ CREATE TABLE IF NOT EXISTS `user-contact` (
|
|||
`rel` tinyint unsigned COMMENT 'The kind of the relation between the user and the contact',
|
||||
`info` mediumtext COMMENT '',
|
||||
`notify_new_posts` boolean COMMENT '',
|
||||
`remote_self` tinyint unsigned COMMENT '0 => No mirroring, 1-2 => Mirror as own post, 3 => Mirror as reshare',
|
||||
`fetch_further_information` tinyint unsigned COMMENT '0 => None, 1 => Fetch information, 3 => Fetch keywords, 2 => Fetch both',
|
||||
`remote_self` boolean COMMENT '',
|
||||
`fetch_further_information` tinyint unsigned COMMENT '',
|
||||
`ffi_keyword_denylist` text COMMENT '',
|
||||
`subhub` boolean COMMENT '',
|
||||
`hub-verify` varbinary(383) COMMENT '',
|
||||
|
|
@ -1917,37 +1874,6 @@ CREATE VIEW `application-view` AS SELECT
|
|||
FROM `application-token`
|
||||
INNER JOIN `application` ON `application-token`.`application-id` = `application`.`id`;
|
||||
|
||||
--
|
||||
-- VIEW circle-member-view
|
||||
--
|
||||
DROP VIEW IF EXISTS `circle-member-view`;
|
||||
CREATE VIEW `circle-member-view` AS SELECT
|
||||
`group_member`.`id` AS `id`,
|
||||
`group`.`uid` AS `uid`,
|
||||
`group_member`.`contact-id` AS `contact-id`,
|
||||
`contact`.`uri-id` AS `contact-uri-id`,
|
||||
`contact`.`url` AS `contact-link`,
|
||||
`contact`.`addr` AS `contact-addr`,
|
||||
`contact`.`name` AS `contact-name`,
|
||||
`contact`.`nick` AS `contact-nick`,
|
||||
`contact`.`thumb` AS `contact-avatar`,
|
||||
`contact`.`network` AS `contact-network`,
|
||||
`contact`.`blocked` AS `contact-blocked`,
|
||||
`contact`.`hidden` AS `contact-hidden`,
|
||||
`contact`.`readonly` AS `contact-readonly`,
|
||||
`contact`.`archive` AS `contact-archive`,
|
||||
`contact`.`pending` AS `contact-pending`,
|
||||
`contact`.`self` AS `contact-self`,
|
||||
`contact`.`rel` AS `contact-rel`,
|
||||
`contact`.`contact-type` AS `contact-contact-type`,
|
||||
`group_member`.`gid` AS `circle-id`,
|
||||
`group`.`visible` AS `circle-visible`,
|
||||
`group`.`deleted` AS `circle-deleted`,
|
||||
`group`.`name` AS `circle-name`
|
||||
FROM `group_member`
|
||||
INNER JOIN `contact` ON `group_member`.`contact-id` = `contact`.`id`
|
||||
INNER JOIN `group` ON `group_member`.`gid` = `group`.`id`;
|
||||
|
||||
--
|
||||
-- VIEW post-user-view
|
||||
--
|
||||
|
|
@ -1956,7 +1882,7 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`post-user`.`id` AS `id`,
|
||||
`post-user`.`id` AS `post-user-id`,
|
||||
`post-user`.`uid` AS `uid`,
|
||||
`post-thread-user`.`post-user-id` AS `parent`,
|
||||
`parent-post`.`id` AS `parent`,
|
||||
`item-uri`.`uri` AS `uri`,
|
||||
`post-user`.`uri-id` AS `uri-id`,
|
||||
`parent-item-uri`.`uri` AS `parent-uri`,
|
||||
|
|
@ -2042,27 +1968,23 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`author`.`addr` AS `author-addr`,
|
||||
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
|
||||
`author`.`nick` AS `author-nick`,
|
||||
`author`.`alias` AS `author-alias`,
|
||||
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
|
||||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
`author`.`hidden` AS `author-hidden`,
|
||||
`author`.`updated` AS `author-updated`,
|
||||
`author`.`gsid` AS `author-gsid`,
|
||||
`author`.`baseurl` AS `author-baseurl`,
|
||||
`post-user`.`owner-id` AS `owner-id`,
|
||||
`owner`.`uri-id` AS `owner-uri-id`,
|
||||
`owner`.`url` AS `owner-link`,
|
||||
`owner`.`addr` AS `owner-addr`,
|
||||
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
|
||||
`owner`.`nick` AS `owner-nick`,
|
||||
`owner`.`alias` AS `owner-alias`,
|
||||
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
|
||||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`gsid` AS `owner-gsid`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-user`.`causer-id` AS `causer-id`,
|
||||
`causer`.`uri-id` AS `causer-uri-id`,
|
||||
|
|
@ -2070,12 +1992,10 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
`causer`.`addr` AS `causer-addr`,
|
||||
`causer`.`name` AS `causer-name`,
|
||||
`causer`.`nick` AS `causer-nick`,
|
||||
`causer`.`alias` AS `causer-alias`,
|
||||
`causer`.`thumb` AS `causer-avatar`,
|
||||
`causer`.`network` AS `causer-network`,
|
||||
`causer`.`blocked` AS `causer-blocked`,
|
||||
`causer`.`hidden` AS `causer-hidden`,
|
||||
`causer`.`gsid` AS `causer-gsid`,
|
||||
`causer`.`contact-type` AS `causer-contact-type`,
|
||||
`post-delivery-data`.`postopts` AS `postopts`,
|
||||
`post-delivery-data`.`inform` AS `inform`,
|
||||
|
|
@ -2105,14 +2025,14 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-user`.`uri-id`) AS `has-media`,
|
||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`post-thread-user`.`network` AS `parent-network`,
|
||||
`post-thread-user`.`author-id` AS `parent-author-id`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
`parent-post`.`author-id` AS `parent-author-id`,
|
||||
`parent-post-author`.`url` AS `parent-author-link`,
|
||||
`parent-post-author`.`name` AS `parent-author-name`,
|
||||
`parent-post-author`.`nick` AS `parent-author-nick`,
|
||||
`parent-post-author`.`network` AS `parent-author-network`
|
||||
FROM `post-user`
|
||||
INNER 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 `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`
|
||||
|
|
@ -2130,7 +2050,8 @@ CREATE VIEW `post-user-view` AS SELECT
|
|||
LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `post-user`.`uri-id` AND `post-user`.`origin`
|
||||
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-user`.`uri-id`
|
||||
LEFT JOIN `permissionset` ON `permissionset`.`id` = `post-user`.`psid`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `post-thread-user`.`author-id`;
|
||||
LEFT JOIN `post-user` AS `parent-post` ON `parent-post`.`uri-id` = `post-user`.`parent-uri-id` AND `parent-post`.`uid` = `post-user`.`uid`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `parent-post`.`author-id`;
|
||||
|
||||
--
|
||||
-- VIEW post-thread-user-view
|
||||
|
|
@ -2140,7 +2061,7 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`post-user`.`id` AS `id`,
|
||||
`post-user`.`id` AS `post-user-id`,
|
||||
`post-thread-user`.`uid` AS `uid`,
|
||||
`post-thread-user`.`post-user-id` AS `parent`,
|
||||
`parent-post`.`id` AS `parent`,
|
||||
`item-uri`.`uri` AS `uri`,
|
||||
`post-thread-user`.`uri-id` AS `uri-id`,
|
||||
`parent-item-uri`.`uri` AS `parent-uri`,
|
||||
|
|
@ -2209,7 +2130,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`contact`.`pending` AS `contact-pending`,
|
||||
`contact`.`rel` AS `contact-rel`,
|
||||
`contact`.`uid` AS `contact-uid`,
|
||||
`contact`.`gsid` AS `contact-gsid`,
|
||||
`contact`.`contact-type` AS `contact-contact-type`,
|
||||
IF (`post-user`.`network` IN ('apub', 'dfrn', 'dspr', 'stat'), true, `contact`.`writable`) AS `writable`,
|
||||
`contact`.`self` AS `self`,
|
||||
|
|
@ -2226,7 +2146,6 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`author`.`addr` AS `author-addr`,
|
||||
IF (`contact`.`url` = `author`.`url` AND `contact`.`name` != '', `contact`.`name`, `author`.`name`) AS `author-name`,
|
||||
`author`.`nick` AS `author-nick`,
|
||||
`author`.`alias` AS `author-alias`,
|
||||
IF (`contact`.`url` = `author`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `author`.`thumb`) AS `author-avatar`,
|
||||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
|
|
@ -2239,13 +2158,11 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`owner`.`addr` AS `owner-addr`,
|
||||
IF (`contact`.`url` = `owner`.`url` AND `contact`.`name` != '', `contact`.`name`, `owner`.`name`) AS `owner-name`,
|
||||
`owner`.`nick` AS `owner-nick`,
|
||||
`owner`.`alias` AS `owner-alias`,
|
||||
IF (`contact`.`url` = `owner`.`url` AND `contact`.`thumb` != '', `contact`.`thumb`, `owner`.`thumb`) AS `owner-avatar`,
|
||||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`gsid` AS `owner-gsid`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-thread-user`.`causer-id` AS `causer-id`,
|
||||
`causer`.`uri-id` AS `causer-uri-id`,
|
||||
|
|
@ -2253,12 +2170,10 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
`causer`.`addr` AS `causer-addr`,
|
||||
`causer`.`name` AS `causer-name`,
|
||||
`causer`.`nick` AS `causer-nick`,
|
||||
`causer`.`alias` AS `causer-alias`,
|
||||
`causer`.`thumb` AS `causer-avatar`,
|
||||
`causer`.`network` AS `causer-network`,
|
||||
`causer`.`blocked` AS `causer-blocked`,
|
||||
`causer`.`hidden` AS `causer-hidden`,
|
||||
`causer`.`gsid` AS `causer-gsid`,
|
||||
`causer`.`contact-type` AS `causer-contact-type`,
|
||||
`post-delivery-data`.`postopts` AS `postopts`,
|
||||
`post-delivery-data`.`inform` AS `inform`,
|
||||
|
|
@ -2288,12 +2203,11 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread-user`.`uri-id`) AS `has-media`,
|
||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`post-thread-user`.`network` AS `parent-network`,
|
||||
`post-thread-user`.`author-id` AS `parent-author-id`,
|
||||
`author`.`url` AS `parent-author-link`,
|
||||
`author`.`name` AS `parent-author-name`,
|
||||
`author`.`nick` AS `parent-author-nick`,
|
||||
`author`.`network` AS `parent-author-network`
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
`parent-post`.`author-id` AS `parent-author-id`,
|
||||
`parent-post-author`.`url` AS `parent-author-link`,
|
||||
`parent-post-author`.`name` AS `parent-author-name`,
|
||||
`parent-post-author`.`network` AS `parent-author-network`
|
||||
FROM `post-thread-user`
|
||||
INNER JOIN `post-user` ON `post-user`.`id` = `post-thread-user`.`post-user-id`
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-thread-user`.`contact-id`
|
||||
|
|
@ -2312,7 +2226,9 @@ CREATE VIEW `post-thread-user-view` AS SELECT
|
|||
LEFT JOIN `item-uri` AS `quote-item-uri` ON `quote-item-uri`.`id` = `post-content`.`quote-uri-id`
|
||||
LEFT JOIN `post-delivery-data` ON `post-delivery-data`.`uri-id` = `post-thread-user`.`uri-id` AND `post-thread-user`.`origin`
|
||||
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-thread-user`.`uri-id`
|
||||
LEFT JOIN `permissionset` ON `permissionset`.`id` = `post-thread-user`.`psid`;
|
||||
LEFT JOIN `permissionset` ON `permissionset`.`id` = `post-thread-user`.`psid`
|
||||
LEFT JOIN `post-user` AS `parent-post` ON `parent-post`.`uri-id` = `post-user`.`parent-uri-id` AND `parent-post`.`uid` = `post-thread-user`.`uid`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `parent-post`.`author-id`;
|
||||
|
||||
--
|
||||
-- VIEW post-view
|
||||
|
|
@ -2394,7 +2310,6 @@ CREATE VIEW `post-view` AS SELECT
|
|||
`author`.`addr` AS `author-addr`,
|
||||
`author`.`name` AS `author-name`,
|
||||
`author`.`nick` AS `author-nick`,
|
||||
`author`.`alias` AS `author-alias`,
|
||||
`author`.`thumb` AS `author-avatar`,
|
||||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
|
|
@ -2407,27 +2322,23 @@ CREATE VIEW `post-view` AS SELECT
|
|||
`owner`.`addr` AS `owner-addr`,
|
||||
`owner`.`name` AS `owner-name`,
|
||||
`owner`.`nick` AS `owner-nick`,
|
||||
`owner`.`alias` AS `owner-alias`,
|
||||
`owner`.`thumb` AS `owner-avatar`,
|
||||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`owner`.`gsid` AS `owner-gsid`,
|
||||
`post`.`causer-id` AS `causer-id`,
|
||||
`causer`.`uri-id` AS `causer-uri-id`,
|
||||
`causer`.`url` AS `causer-link`,
|
||||
`causer`.`addr` AS `causer-addr`,
|
||||
`causer`.`name` AS `causer-name`,
|
||||
`causer`.`nick` AS `causer-nick`,
|
||||
`causer`.`alias` AS `causer-alias`,
|
||||
`causer`.`thumb` AS `causer-avatar`,
|
||||
`causer`.`network` AS `causer-network`,
|
||||
`causer`.`blocked` AS `causer-blocked`,
|
||||
`causer`.`hidden` AS `causer-hidden`,
|
||||
`causer`.`contact-type` AS `causer-contact-type`,
|
||||
`causer`.`gsid` AS `causer-gsid`,
|
||||
`post-question`.`id` AS `question-id`,
|
||||
`post-question`.`multiple` AS `question-multiple`,
|
||||
`post-question`.`voters` AS `question-voters`,
|
||||
|
|
@ -2436,11 +2347,10 @@ CREATE VIEW `post-view` AS SELECT
|
|||
EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post`.`uri-id`) AS `has-media`,
|
||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`post-thread`.`network` AS `parent-network`,
|
||||
`post-thread`.`author-id` AS `parent-author-id`,
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
`parent-post`.`author-id` AS `parent-author-id`,
|
||||
`parent-post-author`.`url` AS `parent-author-link`,
|
||||
`parent-post-author`.`name` AS `parent-author-name`,
|
||||
`parent-post-author`.`nick` AS `parent-author-nick`,
|
||||
`parent-post-author`.`network` AS `parent-author-network`
|
||||
FROM `post`
|
||||
STRAIGHT_JOIN `post-thread` ON `post-thread`.`uri-id` = `post`.`parent-uri-id`
|
||||
|
|
@ -2457,7 +2367,8 @@ CREATE VIEW `post-view` AS SELECT
|
|||
LEFT JOIN `post-content` ON `post-content`.`uri-id` = `post`.`uri-id`
|
||||
LEFT JOIN `item-uri` AS `quote-item-uri` ON `quote-item-uri`.`id` = `post-content`.`quote-uri-id`
|
||||
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post`.`uri-id`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `post-thread`.`author-id`;
|
||||
LEFT JOIN `post` AS `parent-post` ON `parent-post`.`uri-id` = `post`.`parent-uri-id`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `parent-post`.`author-id`;
|
||||
|
||||
--
|
||||
-- VIEW post-thread-view
|
||||
|
|
@ -2539,7 +2450,6 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`author`.`addr` AS `author-addr`,
|
||||
`author`.`name` AS `author-name`,
|
||||
`author`.`nick` AS `author-nick`,
|
||||
`author`.`alias` AS `author-alias`,
|
||||
`author`.`thumb` AS `author-avatar`,
|
||||
`author`.`network` AS `author-network`,
|
||||
`author`.`blocked` AS `author-blocked`,
|
||||
|
|
@ -2552,13 +2462,11 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`owner`.`addr` AS `owner-addr`,
|
||||
`owner`.`name` AS `owner-name`,
|
||||
`owner`.`nick` AS `owner-nick`,
|
||||
`owner`.`alias` AS `owner-alias`,
|
||||
`owner`.`thumb` AS `owner-avatar`,
|
||||
`owner`.`network` AS `owner-network`,
|
||||
`owner`.`blocked` AS `owner-blocked`,
|
||||
`owner`.`hidden` AS `owner-hidden`,
|
||||
`owner`.`updated` AS `owner-updated`,
|
||||
`owner`.`gsid` AS `owner-gsid`,
|
||||
`owner`.`contact-type` AS `owner-contact-type`,
|
||||
`post-thread`.`causer-id` AS `causer-id`,
|
||||
`causer`.`uri-id` AS `causer-uri-id`,
|
||||
|
|
@ -2566,12 +2474,10 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
`causer`.`addr` AS `causer-addr`,
|
||||
`causer`.`name` AS `causer-name`,
|
||||
`causer`.`nick` AS `causer-nick`,
|
||||
`causer`.`alias` AS `causer-alias`,
|
||||
`causer`.`thumb` AS `causer-avatar`,
|
||||
`causer`.`network` AS `causer-network`,
|
||||
`causer`.`blocked` AS `causer-blocked`,
|
||||
`causer`.`hidden` AS `causer-hidden`,
|
||||
`causer`.`gsid` AS `causer-gsid`,
|
||||
`causer`.`contact-type` AS `causer-contact-type`,
|
||||
`post-question`.`id` AS `question-id`,
|
||||
`post-question`.`multiple` AS `question-multiple`,
|
||||
|
|
@ -2583,12 +2489,11 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
(SELECT COUNT(DISTINCT(`author-id`)) FROM `post` WHERE `parent-uri-id` = `post-thread`.`uri-id` AND `gravity` = 6) AS `total-actors`,
|
||||
`diaspora-interaction`.`interaction` AS `signed_text`,
|
||||
`parent-item-uri`.`guid` AS `parent-guid`,
|
||||
`post-thread`.`network` AS `parent-network`,
|
||||
`post-thread`.`author-id` AS `parent-author-id`,
|
||||
`author`.`url` AS `parent-author-link`,
|
||||
`author`.`name` AS `parent-author-name`,
|
||||
`author`.`nick` AS `parent-author-nick`,
|
||||
`author`.`network` AS `parent-author-network`
|
||||
`parent-post`.`network` AS `parent-network`,
|
||||
`parent-post`.`author-id` AS `parent-author-id`,
|
||||
`parent-post-author`.`url` AS `parent-author-link`,
|
||||
`parent-post-author`.`name` AS `parent-author-name`,
|
||||
`parent-post-author`.`network` AS `parent-author-network`
|
||||
FROM `post-thread`
|
||||
INNER JOIN `post` ON `post`.`uri-id` = `post-thread`.`uri-id`
|
||||
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `post-thread`.`author-id`
|
||||
|
|
@ -2603,7 +2508,9 @@ CREATE VIEW `post-thread-view` AS SELECT
|
|||
LEFT JOIN `diaspora-interaction` ON `diaspora-interaction`.`uri-id` = `post-thread`.`uri-id`
|
||||
LEFT JOIN `post-content` ON `post-content`.`uri-id` = `post-thread`.`uri-id`
|
||||
LEFT JOIN `item-uri` AS `quote-item-uri` ON `quote-item-uri`.`id` = `post-content`.`quote-uri-id`
|
||||
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-thread`.`uri-id`;
|
||||
LEFT JOIN `post-question` ON `post-question`.`uri-id` = `post-thread`.`uri-id`
|
||||
LEFT JOIN `post` AS `parent-post` ON `parent-post`.`uri-id` = `post`.`parent-uri-id`
|
||||
LEFT JOIN `contact` AS `parent-post-author` ON `parent-post-author`.`id` = `parent-post`.`author-id`;
|
||||
|
||||
--
|
||||
-- VIEW category-view
|
||||
|
|
@ -2680,7 +2587,7 @@ CREATE VIEW `tag-view` AS SELECT
|
|||
DROP VIEW IF EXISTS `network-item-view`;
|
||||
CREATE VIEW `network-item-view` AS SELECT
|
||||
`post-user`.`uri-id` AS `uri-id`,
|
||||
`post-thread-user`.`post-user-id` AS `parent`,
|
||||
`parent-post`.`id` AS `parent`,
|
||||
`post-user`.`received` AS `received`,
|
||||
`post-thread-user`.`commented` AS `commented`,
|
||||
`post-user`.`created` AS `created`,
|
||||
|
|
@ -2693,16 +2600,17 @@ CREATE VIEW `network-item-view` AS SELECT
|
|||
`post-user`.`contact-id` AS `contact-id`,
|
||||
`ownercontact`.`contact-type` AS `contact-type`
|
||||
FROM `post-user`
|
||||
INNER 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-thread-user`.`contact-id`
|
||||
STRAIGHT_JOIN `contact` AS `authorcontact` ON `authorcontact`.`id` = `post-thread-user`.`author-id`
|
||||
STRAIGHT_JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `post-thread-user`.`owner-id`
|
||||
STRAIGHT_JOIN `post-thread-user` ON `post-thread-user`.`uri-id` = `post-user`.`parent-uri-id` AND `post-thread-user`.`uid` = `post-user`.`uid`
|
||||
INNER JOIN `contact` ON `contact`.`id` = `post-thread-user`.`contact-id`
|
||||
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `post-thread-user`.`uid` AND `author`.`cid` = `post-thread-user`.`author-id`
|
||||
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `post-thread-user`.`uid` AND `owner`.`cid` = `post-thread-user`.`owner-id`
|
||||
INNER JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `post-thread-user`.`owner-id`
|
||||
LEFT JOIN `post-user` AS `parent-post` ON `parent-post`.`uri-id` = `post-user`.`parent-uri-id` AND `parent-post`.`uid` = `post-user`.`uid`
|
||||
WHERE `post-user`.`visible` AND NOT `post-user`.`deleted`
|
||||
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
|
||||
AND (`post-user`.`hidden` IS NULL OR NOT `post-user`.`hidden`)
|
||||
AND NOT `authorcontact`.`blocked` AND NOT `ownercontact`.`blocked`
|
||||
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`authorcontact`.`id`, `ownercontact`.`id`) AND (`blocked` OR `ignored`))
|
||||
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
|
||||
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
|
||||
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
|
||||
|
||||
--
|
||||
-- VIEW network-thread-view
|
||||
|
|
@ -2710,7 +2618,7 @@ CREATE VIEW `network-item-view` AS SELECT
|
|||
DROP VIEW IF EXISTS `network-thread-view`;
|
||||
CREATE VIEW `network-thread-view` AS SELECT
|
||||
`post-thread-user`.`uri-id` AS `uri-id`,
|
||||
`post-thread-user`.`post-user-id` AS `parent`,
|
||||
`parent-post`.`id` AS `parent`,
|
||||
`post-thread-user`.`received` AS `received`,
|
||||
`post-thread-user`.`commented` AS `commented`,
|
||||
`post-thread-user`.`created` AS `created`,
|
||||
|
|
@ -2723,14 +2631,15 @@ CREATE VIEW `network-thread-view` AS SELECT
|
|||
FROM `post-thread-user`
|
||||
INNER JOIN `post-user` ON `post-user`.`id` = `post-thread-user`.`post-user-id`
|
||||
STRAIGHT_JOIN `contact` ON `contact`.`id` = `post-thread-user`.`contact-id`
|
||||
STRAIGHT_JOIN `contact` AS `authorcontact` ON `authorcontact`.`id` = `post-thread-user`.`author-id`
|
||||
STRAIGHT_JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `post-thread-user`.`owner-id`
|
||||
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `post-thread-user`.`uid` AND `author`.`cid` = `post-thread-user`.`author-id`
|
||||
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `post-thread-user`.`uid` AND `owner`.`cid` = `post-thread-user`.`owner-id`
|
||||
LEFT JOIN `contact` AS `ownercontact` ON `ownercontact`.`id` = `post-thread-user`.`owner-id`
|
||||
LEFT JOIN `post-user` AS `parent-post` ON `parent-post`.`uri-id` = `post-user`.`parent-uri-id` AND `parent-post`.`uid` = `post-user`.`uid`
|
||||
WHERE `post-user`.`visible` AND NOT `post-user`.`deleted`
|
||||
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
|
||||
AND (`post-thread-user`.`hidden` IS NULL OR NOT `post-thread-user`.`hidden`)
|
||||
AND NOT `authorcontact`.`blocked` AND NOT `ownercontact`.`blocked`
|
||||
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = `post-thread-user`.`uid` AND `cid` IN (`authorcontact`.`id`, `ownercontact`.`id`) AND (`blocked` OR `ignored`))
|
||||
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = `post-thread-user`.`uid` AND `gsid` IN (`authorcontact`.`gsid`, `ownercontact`.`gsid`) AND `ignored`);
|
||||
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
|
||||
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
|
||||
|
||||
--
|
||||
-- VIEW owner-view
|
||||
|
|
|
|||
|
|
@ -454,7 +454,7 @@ Ex: Wed May 23 06:01:13 +0000 2007
|
|||
<tr>
|
||||
<td><code>allow_gid</code></td>
|
||||
<td>String (angle-brackets escaped integers)</td>
|
||||
<td>Optional. List of allowed circle ids</td>
|
||||
<td>Optional. List of allowed group ids</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
|
@ -466,7 +466,7 @@ Ex: Wed May 23 06:01:13 +0000 2007
|
|||
<tr>
|
||||
<td><code>deny_gid</code></td>
|
||||
<td>String (angle-brackets escaped integers)</td>
|
||||
<td>Optional. List of disallowed circle ids</td>
|
||||
<td>Optional. List of disallowed group ids</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
|
|
@ -583,7 +583,7 @@ Ex: Wed May 23 06:01:13 +0000 2007
|
|||
<tr>
|
||||
<td><code>friendica_owner</code></td>
|
||||
<td>
|
||||
|
||||
|
||||
<a href="help/API-Entities#Contact">Contact</a></td>
|
||||
<td align="center">No</td>
|
||||
</tr>
|
||||
|
|
@ -984,7 +984,7 @@ Identical to [the Twitter Media Object](https://developer.twitter.com/en/docs/tw
|
|||
<tr>
|
||||
<td><code>allow_gid</code></td>
|
||||
<td>String (ACL)</td>
|
||||
<td>List of contact circle ids wrapped in angle brackets allowed to access the photo.</td>
|
||||
<td>List of contact group ids wrapped in angle brackets allowed to access the photo.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
|
@ -996,7 +996,7 @@ Identical to [the Twitter Media Object](https://developer.twitter.com/en/docs/tw
|
|||
<tr>
|
||||
<td><code>deny_gid</code></td>
|
||||
<td>String (ACL)</td>
|
||||
<td>List of contact circle ids wrapped in angle brackets forbidden to access the photo.</td>
|
||||
<td>List of contact group ids wrapped in angle brackets forbidden to access the photo.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
|
@ -1541,4 +1541,4 @@ Unused
|
|||
|
||||
## User Mention
|
||||
|
||||
Unused
|
||||
Unused
|
||||
|
|
@ -39,8 +39,8 @@ Create a new event for the current logged in user.
|
|||
- `publish` : (optional) create message for event
|
||||
- `allow_cid` : (optional) ACL-formatted list of allowed contact ids if private event
|
||||
- `allow_gid` : (optional) ACL-formatted list of disallowed contact ids if private event
|
||||
- `deny_cid` : (optional) ACL-formatted list of allowed circle ids if private event
|
||||
- `deny_gid` : (optional) ACL-formatted list of disallowed circle ids if private event
|
||||
- `deny_cid` : (optional) ACL-formatted list of allowed group ids if private event
|
||||
- `deny_gid` : (optional) ACL-formatted list of disallowed group ids if private event
|
||||
|
||||
### POST api/friendica/event_delete
|
||||
|
||||
|
|
@ -323,37 +323,33 @@ On error:
|
|||
|
||||
---
|
||||
|
||||
### GET api/friendica/circle_show
|
||||
### GET api/friendica/group_show
|
||||
|
||||
Alternatively: GET api/friendica/group_show (Backward compatibility)
|
||||
|
||||
Return all or a specified circle of the user with the containing contacts as array.
|
||||
Return all or a specified group of the user with the containing contacts as array.
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `gid`: optional, if not given, API returns all circles of the user
|
||||
* `gid`: optional, if not given, API returns all groups of the user
|
||||
|
||||
#### Return values
|
||||
|
||||
Array of:
|
||||
|
||||
* `name`: name of the circle
|
||||
* `gid`: id of the circle
|
||||
* `name`: name of the group
|
||||
* `gid`: id of the group
|
||||
* `user`: array of [Contacts](help/API-Entities#Contact)
|
||||
|
||||
### POST api/friendica/circle_create
|
||||
### POST api/friendica/group_create
|
||||
|
||||
Alternatively: POST api/friendica/group_create
|
||||
|
||||
Create the circle with the posted array of contacts as members.
|
||||
Create the group with the posted array of contacts as members.
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `name`: name of the circle to be created
|
||||
* `name`: name of the group to be created
|
||||
|
||||
#### POST data
|
||||
|
||||
JSON data as Array like the result of [GET api/friendica/circle_show](#GET+api%2Ffriendica%2Fcircle_show):
|
||||
JSON data as Array like the result of [GET api/friendica/group_show](#GET+api%2Ffriendica%2Fgroup_show):
|
||||
|
||||
* `gid`
|
||||
* `name`
|
||||
|
|
@ -364,25 +360,23 @@ JSON data as Array like the result of [GET api/friendica/circle_show](#GET+api%2
|
|||
Array of:
|
||||
|
||||
* `success`: true if successfully created or reactivated
|
||||
* `gid`: gid of the created circle
|
||||
* `name`: name of the created circle
|
||||
* `gid`: gid of the created group
|
||||
* `name`: name of the created group
|
||||
* `status`: "missing user" | "reactivated" | "ok"
|
||||
* `wrong users`: array of users, which were not available in the contact table
|
||||
|
||||
### POST api/friendica/circle_update
|
||||
### POST api/friendica/group_update
|
||||
|
||||
Alternatively: POST api/friendica/group_update
|
||||
|
||||
Update the circle with the posted array of contacts as members (post all members of the circle to the call; function will remove members not posted).
|
||||
Update the group with the posted array of contacts as members (post all members of the group to the call; function will remove members not posted).
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `gid`: id of the circle to be changed
|
||||
* `name`: name of the circle to be changed
|
||||
* `gid`: id of the group to be changed
|
||||
* `name`: name of the group to be changed
|
||||
|
||||
#### POST data
|
||||
|
||||
JSON data as array like the result of [GET api/friendica/circle_show](#GET+api%2Ffriendica%2Fcircle_show):
|
||||
JSON data as array like the result of [GET api/friendica/group_show](#GET+api%2Ffriendica%2Fgroup_show):
|
||||
|
||||
* `gid`
|
||||
* `name`
|
||||
|
|
@ -393,29 +387,27 @@ JSON data as array like the result of [GET api/friendica/circle_show](#GET+api%2
|
|||
Array of:
|
||||
|
||||
* `success`: true if successfully updated
|
||||
* `gid`: gid of the changed circle
|
||||
* `name`: name of the changed circle
|
||||
* `gid`: gid of the changed group
|
||||
* `name`: name of the changed group
|
||||
* `status`: "missing user" | "ok"
|
||||
* `wrong users`: array of users, which were not available in the contact table
|
||||
|
||||
### POST api/friendica/circle_delete
|
||||
### POST api/friendica/group_delete
|
||||
|
||||
Alternatively: POST api/friendica/group_delete
|
||||
|
||||
Delete the specified circle of contacts; API call need to include the correct gid AND name of the circle to be deleted.
|
||||
Delete the specified group of contacts; API call need to include the correct gid AND name of the group to be deleted.
|
||||
|
||||
#### Parameters
|
||||
|
||||
* `gid`: id of the circle to be deleted
|
||||
* `name`: name of the circle to be deleted
|
||||
* `gid`: id of the group to be deleted
|
||||
* `name`: name of the group to be deleted
|
||||
|
||||
#### Return values
|
||||
|
||||
Array of:
|
||||
|
||||
* `success`: true if successfully deleted
|
||||
* `gid`: gid of the deleted circle
|
||||
* `name`: name of the deleted circle
|
||||
* `gid`: gid of the deleted group
|
||||
* `name`: name of the deleted group
|
||||
* `status`: "deleted" if successfully deleted
|
||||
* `wrong users`: empty array
|
||||
|
||||
|
|
@ -564,7 +556,7 @@ Alias of [`api/friendica/photo/update`](#POST+api%2Ffriendica%2Fphoto%2Fupdate)
|
|||
|
||||
Saves data for the scales 0-2 to database (see above for scale description).
|
||||
Call adds non-public entries to items table to enable authenticated contacts to comment/like the photo.
|
||||
Client should pay attention to the fact that updated access rights are not transferred to the contacts. i.e. public photos remain publicly visible if they have been commented/liked before setting visibility back to a limited circle.
|
||||
Client should pay attention to the fact that updated access rights are not transferred to the contacts. i.e. public photos remain publicly visible if they have been commented/liked before setting visibility back to a limited group.
|
||||
Currently it is best to inform user that updating rights is not the right way to do this, and offer a solution to add photo as a new photo with the new rights instead.
|
||||
|
||||
#### Parameters
|
||||
|
|
|
|||
|
|
@ -211,7 +211,7 @@ Example:
|
|||
- `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`.
|
||||
- [`POST /api/v1/statuses`](https://docs.joinmastodon.org/methods/statuses/#create)
|
||||
- Does not support `polls` argument as Friendica does not have polls
|
||||
- Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a circle id.
|
||||
- Additionally to the static values `public`, `unlisted` and `private`, the `visibility` parameter can contain a numeric value with a group id.
|
||||
- Additional field `quote_id` for the post that is being quote reshared
|
||||
- Additional fields `friendica` for Friendica specific parameters:
|
||||
- `title`: Explicitly sets the title for a post status, ignored if used on a comment status. For post statuses the legacy behavior is to use any "spoiler text" as the title if it is provided. If both the title and spoiler text are provided for a post status then they will each be used for their respective roles. If no title is provided then the legacy behavior will persist. If you want to create a post with no title but spoiler text then explicitly set the title but set it to an empty string `""`.
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ General
|
|||
* i - Only show ignored contacts
|
||||
* y - Only show archived contacts
|
||||
* h - Only show hidden contacts
|
||||
* e - Edit contact circles
|
||||
* e - Edit contact groups
|
||||
|
||||
../contact (single contact view)
|
||||
-------------------------------
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ This is the only bit of personal information that has to be accurate.
|
|||
### Nickname
|
||||
|
||||
A nickname is used to generate web addresses for many of your personal pages, and is also treated like an email address when establishing communications with others.
|
||||
Due to the way that the nickname is used, it has some limitations.
|
||||
Due to the way that the nickname is used, it has some limitations.
|
||||
It must contain only US-ASCII text characters and numbers, and must also start with a text character.
|
||||
It also must be unique on this system.
|
||||
It also must be unique on this system.
|
||||
This is used in many places to identify your account, and once set it cannot be changed.
|
||||
|
||||
|
||||
|
|
@ -53,28 +53,28 @@ The registration form also allows you to choose whether or not to list your acco
|
|||
This is like a "phone book" and you may choose to be unlisted.
|
||||
We recommend that you select 'Yes' so that other people (friends, family, etc.) will be able to find you.
|
||||
If you choose 'No', you will essentially be invisible and have few opportunities for interaction.
|
||||
Whichever you choose, this can be changed any time from your Settings page after you login.
|
||||
Whichever you choose, this can be changed any time from your Settings page after you login.
|
||||
|
||||
### Register
|
||||
|
||||
Once you have provided the necessary details, click the 'Register' button.
|
||||
An email will be sent to you providing your account login details.
|
||||
Please check your email (including spam folders) for your registration details and initial password.
|
||||
Please check your email (including spam folders) for your registration details and initial password.
|
||||
|
||||
Login Page
|
||||
---
|
||||
|
||||
On the 'Login' page, please enter your login information that was provided during registration.
|
||||
You may use either your nickname or email address as a Login Name.
|
||||
You may use either your nickname or email address as a Login Name.
|
||||
|
||||
If you use your account to manage other accounts and these all have the same email address, please enter the nickname for the account you wish to manage.
|
||||
|
||||
If your account has been OpenID enabled, you may use your OpenID address as a login name and leave the password blank.
|
||||
You will be redirected to your OpenID provider to complete your authorisation.
|
||||
You will be redirected to your OpenID provider to complete your authorisation.
|
||||
|
||||
Otherwise, enter your password.
|
||||
This will have been initially provided in your registration email message.
|
||||
Your password is case-sensitive, so please check your 'Caps Lock' key if you are having difficulty logging in.
|
||||
Your password is case-sensitive, so please check your 'Caps Lock' key if you are having difficulty logging in.
|
||||
|
||||
Changing Your Password
|
||||
---
|
||||
|
|
@ -99,9 +99,9 @@ See Also
|
|||
|
||||
* [Profiles](help/Profiles)
|
||||
|
||||
* [Global Directory](help/Making-Friends#The+Directories)
|
||||
* [Global Directory](help/Making-Friends#The+Directories)
|
||||
|
||||
* [Circles and Privacy](help/Circles-and-Privacy)
|
||||
* [Groups and Privacy](help/Groups-and-Privacy)
|
||||
|
||||
* [Move Account](help/Move-Account)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Bugs and Issues
|
|||
If your server has a support page, you should report any bugs/issues you encounter there first.
|
||||
Reporting to your support page before reporting to the developers makes their job easier, as they don't have to deal with bug reports that might not have anything to do with them.
|
||||
Reducing the workload in this way helps us get new features faster.
|
||||
You can also contact the [friendica support group](https://forum.friendi.ca/profile/helpers) and report your problem there.
|
||||
You can also contact the [friendica support forum](https://forum.friendi.ca/profile/helpers) and report your problem there.
|
||||
Bugs are rarely limited to one person, and the chances are somebody from another node has encountered the problem too, and will be able to help you.
|
||||
|
||||
If you're a technical user, or your site doesn't have a support page, you'll need to use the [Bug Tracker](https://github.com/friendica/friendica/issues).
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ Whether you feel like an expert or like a newbie - join us with your ideas!
|
|||
|
||||
## Contact us
|
||||
|
||||
The discussion of Friendica development takes place in the following Friendica groups:
|
||||
The discussion of Friendica development takes place in the following Friendica forums:
|
||||
|
||||
* The main [group for Friendica development](https://forum.friendi.ca/profile/developers)
|
||||
* The main [forum for Friendica development](https://forum.friendi.ca/profile/developers)
|
||||
|
||||
## Help other users
|
||||
|
||||
Remember the questions you had when you first tried Friendica?
|
||||
A good place to start can be to help new people find their way around Friendica in the [general support group](https://forum.friendi.ca/profile/helpers).
|
||||
A good place to start can be to help new people find their way around Friendica in the [general support forum](https://forum.friendi.ca/profile/helpers).
|
||||
Welcome them, answer their questions, point them to documentation or ping other helpers directly if you can't help but think you know who can.
|
||||
|
||||
## Translation
|
||||
|
|
@ -33,10 +33,10 @@ If you don't want to translate the UI, or it is already done to your satisfactio
|
|||
Are you good at designing things?
|
||||
If you have seen Friendica you probably have ideas to improve it, haven't you?
|
||||
|
||||
* If you would like to work with us on enhancing the user interface, please join the [group for Friendica development](https://forum.friendi.ca/profile/developers).
|
||||
* If you would like to work with us on enhancing the user interface, please join the [forum for Friendica development](https://forum.friendi.ca/profile/developers).
|
||||
* Make plans for a better Friendica interface design and share them with us.
|
||||
* Tell us if you are able to realize your ideas or what kind of help you need.
|
||||
We can't promise we have the right skills in the group, but we'll try.
|
||||
We can't promise we have the right skills in the group but we'll try.
|
||||
* Choose a thing to start with, e.g. work on the icon set of your favorite theme
|
||||
|
||||
## Programming
|
||||
|
|
@ -72,7 +72,7 @@ just place it into `.git/hooks/post-merge` and make it executable.
|
|||
|
||||
### Coding standards
|
||||
|
||||
For the sake of consistency between contribution and general code readability, Friendica follows the widespread [PSR-2 coding standards](http://www.php-fig.org/psr/psr-2/) excepted a few rules.
|
||||
For the sake of consistency between contribution and general code readability, Friendica follows the widespread [PSR-2 coding standards](http://www.php-fig.org/psr/psr-2/) to the exception of a few rules.
|
||||
Here's a few primers if you are new to Friendica or to the PSR-2 coding standards:
|
||||
|
||||
* Indentation is tabs, period (not PSR-2).
|
||||
|
|
@ -88,7 +88,7 @@ Here's a few primers if you are new to Friendica or to the PSR-2 coding standard
|
|||
Don't worry, you don't have to know by heart the PSR-2 coding standards to start contributing to Friendica.
|
||||
There are a few tools you can use to check or fix your files before you commit.
|
||||
|
||||
For documentation, we use the standard of *one sentence per line* for the `md` files in the `/doc` and `/doc/$lng` subdirectories.
|
||||
For documentation we use the standard of *one sentence per line* for the `md` files in the `/doc` and `/doc/$lng` subdirectories.
|
||||
|
||||
#### Check with [PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer)
|
||||
|
||||
|
|
@ -113,7 +113,7 @@ If the command-line tools `diff` and `patch` are unavailable for you, `phpcbf` c
|
|||
|
||||
### Code documentation
|
||||
|
||||
If you are interested in having the documentation of the Friendica code outside the code files, you can use [Doxygen](http://doxygen.org) to generate it.
|
||||
If you are interested in having the documentation of the Friendica code outside of the code files, you can use [Doxygen](http://doxygen.org) to generate it.
|
||||
The configuration file for Doxygen is located in the base directory of the project sources.
|
||||
Run
|
||||
|
||||
|
|
@ -126,7 +126,7 @@ If you find missing documentation, don't hesitate to contact us and write it dow
|
|||
|
||||
### Issues
|
||||
|
||||
Have a look at our [issue tracker](https://github.com/friendica/friendica) on GitHub!
|
||||
Have a look at our [issue tracker](https://github.com/friendica/friendica) on github!
|
||||
|
||||
* Try to reproduce a bug that needs more inquiries and write down what you find out.
|
||||
* If a bug looks fixed, ask the bug reporters for feedback to find out if the bug can be closed.
|
||||
|
|
@ -143,13 +143,13 @@ If you want to get involved here:
|
|||
* Look at the first steps that were made (e.g. the clean theme).
|
||||
Ask us to find out whom to talk to about their experiences.
|
||||
* Talk to design people if you know any.
|
||||
* Let us know about your plans [in the dev group](https://forum.friendi.ca/profile/developers)
|
||||
* Let us know about your plans [in the dev forum](https://forum.friendi.ca/profile/developers)
|
||||
Do not worry about cross-posting.
|
||||
|
||||
### Client software
|
||||
|
||||
As Friendica is using a [Twitter/GNU Social compatible API](help/api) any of the clients for those platforms should work with Friendica as well.
|
||||
Furthermore, there are several client projects, especially for use with Friendica.
|
||||
Furthermore there are several client projects, especially for use with Friendica.
|
||||
If you are interested in improving those clients, please contact the developers of the clients directly.
|
||||
|
||||
* Android / LinageOS: **Friendiqa** [src](https://git.friendi.ca/lubuwest/Friendiqa)/[Google Play](https://play.google.com/store/apps/details?id=org.qtproject.friendiqa) developed by [Marco R](https://freunde.ma-nic.de/profile/marco)
|
||||
|
|
|
|||
|
|
@ -52,4 +52,4 @@ You can manually execute the structure update from the CLI in the base directory
|
|||
|
||||
bin/console dbstructure update
|
||||
|
||||
if there occur any errors, please contact the [support group](https://forum.friendi.ca/profile/helpers).
|
||||
if there occur any errors, please contact the [support forum](https://forum.friendi.ca/profile/helpers).
|
||||
|
|
|
|||
42
doc/FAQ.md
|
|
@ -4,14 +4,14 @@ Frequently Asked Questions - FAQ
|
|||
* [Home](help)
|
||||
|
||||
* **[Where I can find help?](help/FAQ#help)**
|
||||
* **[Why do I get warnings about certificates?](help/FAQ#ssl)**
|
||||
* **[Why do I getting warnings about certificates?](help/FAQ#ssl)**
|
||||
* **[How can I upload images, files, links, videos and sound files to posts?](help/FAQ#upload)**
|
||||
* **[Is it possible to have different avatars per profile?](help/FAQ#avatars)**
|
||||
* **[How can I view Friendica in a certain language?](help/FAQ#language)**
|
||||
* **[How do blocked, ignored, archived and hidden contacts behave?](help/FAQ#contacts)**
|
||||
* **[What happens when an account is removed? Is it truly deleted?](help/FAQ#removed)**
|
||||
* **[Can I subscribe to a hashtag?](help/FAQ#hashtag)**
|
||||
* **[How to create an RSS feed of the stream?](help/FAQ#rss)**
|
||||
* **[How to create a RSS feed of the stream?](help/FAQ#rss)**
|
||||
* **[What friendica clients can I use?](help/FAQ#clients)**
|
||||
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ Frequently Asked Questions - FAQ
|
|||
|
||||
If this FAQ does not answer your question you can always reach out to the community via the following options:
|
||||
|
||||
* Friendica Support Group: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Community chat rooms (the IRC, Matrix and XMPP rooms are bridged) these public chats are logged [from IRC](https://gnusociarg.nsupdate.info/2021/%23friendica/) and [Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/)
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: #friendica at [libera.chat](https://web.libera.chat/?channels=#friendica)
|
||||
|
|
@ -66,7 +66,7 @@ If this doesn't work, try to add the link by typing: [url=http://example.com]*se
|
|||
You can also add video and audio files to posts.
|
||||
However, instead of a direct upload you have to use one of the following methods:
|
||||
|
||||
1. Add the video or audio link of a hoster (YouTube, Vimeo, Soundcloud and anyone else with oembed/opengraph-support). Videos will be shown with a preview image you can click on to start. SoundCloud directly inserts a player to your post.
|
||||
1. Add the video or audio link of a hoster (Youtube, Vimeo, Soundcloud and anyone else with oembed/opengraph-support). Videos will be shown with a preview image you can click on to start. SoundCloud directly inserts a player to your post.
|
||||
|
||||
2. If you have your own server, you can upload multimedia files via FTP and insert the URL.
|
||||
|
||||
|
|
@ -92,7 +92,7 @@ A question mark is required for the separation between url and parameters.
|
|||
|
||||
Example:
|
||||
|
||||
https://social.example.com/profile/example
|
||||
https://social.example.com/profile/example
|
||||
|
||||
in German:
|
||||
|
||||
|
|
@ -107,48 +107,48 @@ When a certain language is forced, the language remains until session is closed.
|
|||
|
||||
Direct communication will be blocked.
|
||||
Blocked contacts are not included in delivery, and their own posts to you are not imported.
|
||||
However, their conversations with your friends will still be visible in your stream.
|
||||
However their conversations with your friends will still be visible in your stream.
|
||||
If you remove a contact completely, they can send you another friend request.
|
||||
Blocked contacts cannot do this. They cannot communicate with you directly, only through friends.
|
||||
|
||||
##### Ignored
|
||||
|
||||
Ignored contacts are included in delivery and will receive your posts and private messages.
|
||||
However, we do not import their posts or private messages to you.
|
||||
Like blocking, you will still see this person's comments to posts made by your friends.
|
||||
However we do not import their posts or private messages to you.
|
||||
Like blocking you will still see this person's comments to posts made by your friends.
|
||||
|
||||
An addon called "blockem" can be installed to collapse/hide all posts from a particular person in your stream if you desire complete blocking of an individual, including their conversations with your other friends.
|
||||
|
||||
##### Archived
|
||||
|
||||
Communication is not possible and will not be attempted.
|
||||
However, unlike blocking, existing posts this person made before being archived will be visible in your stream.
|
||||
However unlike blocking, existing posts this person made before being archived will be visible in your stream.
|
||||
|
||||
##### Hidden
|
||||
|
||||
Contact not be displayed in your public friend list.
|
||||
However, a hidden contact will appear normally in conversations and this may expose their hidden status to anybody who can see the conversation.
|
||||
However a hidden contact will appear normally in conversations and this may expose their hidden status to anybody who can see the conversation.
|
||||
|
||||
<a name="removed"></a>
|
||||
### What happens when an account is removed?
|
||||
|
||||
If you remove your account, it will be scheduled for permanent deletion in *seven days*.
|
||||
As soon as you activate the deletion process you won't be able to log in anymore.
|
||||
If you remove your account, it will be scheduled for permanent deletion in *seven days*.
|
||||
As soon as you activate the deletion process you won't be able to login any more.
|
||||
Only the administrator of your node can halt this process prior to permanent deletion.
|
||||
|
||||
After the elapsed time of seven days, all your posts, messages, photos, and personal information stored on your node will be deleted.
|
||||
Your node will also issue removal requests to all your contacts; this will also remove your profile from the global directory if you are listed.
|
||||
After the elapsed time of seven days, all your posts, messages, photos, and personal information stored on your node will be deleted.
|
||||
Your node will also issue removal requests to all your contacts; this will also remove your profile from the global directory if you are listed.
|
||||
Your username cannot be reissued for future sign-ups for security reasons.
|
||||
|
||||
<a name="hashtag"></a>
|
||||
### Can I follow a hashtag?
|
||||
|
||||
Yes. Simply add the hashtag to your saved searches.
|
||||
Yes. Simply add the hash tag to your saved searches.
|
||||
The posts will appear on your network page.
|
||||
For technical reasons, your answers to such posts won't appear on the "personal" tab in the network page and the whole thread isn't accessible via the API.
|
||||
|
||||
<a name="rss"></a>
|
||||
### How to create an RSS feed of the stream?
|
||||
### How to create a RSS feed of the stream?
|
||||
|
||||
If you want to share your public page via rss you can use one of the following links:
|
||||
|
||||
|
|
@ -191,10 +191,10 @@ The available features are client specific and may differ.
|
|||
|
||||
#### iOS
|
||||
|
||||
* [Mastodon](https://joinmastodon.org/apps) ([App Store](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974))
|
||||
* [Stella*](https://www.stella-app.net/) ([App Store](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048))
|
||||
* [Tooot](https://github.com/tooot-app) ([App Store](https://apps.apple.com/app/id1549772269)
|
||||
* [TwidereX](https://github.com/TwidereProject/TwidereX-iOS) ([App Store](https://apps.apple.com/app/twidere-x/id1530314034))
|
||||
* [Mastodon](https://joinmastodon.org/apps) ([AppStore](https://apps.apple.com/us/app/mastodon-for-iphone/id1571998974))
|
||||
* [Stella*](https://www.stella-app.net/) ([AppStore](https://apps.apple.com/us/app/stella-for-mastodon-twitter/id921372048))
|
||||
* [Tooot](https://github.com/tooot-app) ([AppStore](https://apps.apple.com/app/id1549772269)
|
||||
* [TwidereX](https://github.com/TwidereProject/TwidereX-iOS) ([AppStore](https://apps.apple.com/app/twidere-x/id1530314034))
|
||||
|
||||
#### Linux
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ The available features are client specific and may differ.
|
|||
#### macOS
|
||||
|
||||
* [TheDesk](https://thedesk.top/en/) ([GitHub](https://github.com/cutls/TheDesk))
|
||||
* [Whalebird](https://whalebird.social/en/desktop/contents) ([App Store](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop))
|
||||
* [Whalebird](https://whalebird.social/en/desktop/contents) ([AppStore](https://apps.apple.com/de/app/whalebird/id1378283354), [GitHub](https://github.com/h3poteto/whalebird-desktop))
|
||||
|
||||
#### Windows
|
||||
|
||||
|
|
|
|||
66
doc/Forums.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
Forums
|
||||
=====
|
||||
|
||||
* [Home](help)
|
||||
|
||||
|
||||
Friendica also lets you create community forums and other types of accounts that can function as discussion forums, celebrity accounts, announcement channels, news reflectors, or organization pages, depending on how you want to interact with others. Management of these pages can be delegated to other accounts, or a parent account can be designated to easily toggle multiple identities.
|
||||
|
||||
Every page in Friendica has a nickname and these must all be unique. This applies to all forums, whether they are normal profiles or forum profiles.
|
||||
|
||||
Managing Accounts
|
||||
---
|
||||
|
||||
To create a new linked account that can be used as a forum, log in to your normal account and go to Settings > Manage Accounts.
|
||||
Here you can register additional accounts with new nicknames that will be linked to your primary account.
|
||||
|
||||
You may appoint a delegate to manage your new account (e.g. forum page).
|
||||
The Delegates section of Manage Accounts page will provide you with a list of contacts on this instance under "Potential Delegates".
|
||||
Selecting one or more persons will give them access to manage your forum.
|
||||
They will be able to edit contacts, profiles, and all content for this account/page.
|
||||
Please use this facility wisely.
|
||||
Delegated managers will not be able to alter basic account settings, such as passwords or page types, or remove the account.
|
||||
|
||||
Additionally, this page is also where you can choose to designate an account as a parent user.
|
||||
If your primary account is designated as the parent user, you will be able to easily toggle identities and manage your forums or other types of accounts.
|
||||
|
||||
Types of Accounts
|
||||
---
|
||||
|
||||
On the new account, visit the Settings > Account page.
|
||||
Towards the end of the page is a section for "Advanced account types".
|
||||
Typically you would use "Personal Page - Standard" for a normal personal account with manual approval of “friends” and “followers.”
|
||||
This is the default selection.
|
||||
On this page you can change the type of account if desired.
|
||||
|
||||
The other subtypes of a Personal Page are “Soapbox” and “Love-all.”
|
||||
A Soapbox account is an announcement channel that automatically approvals follower requests.
|
||||
Everything posted by the account will go out to the followers, but there will be no opportunity for interaction.
|
||||
This setting would typically be used for announcements or corporate communications.
|
||||
“Love-all” automatically approves contacts as friends.
|
||||
|
||||
In addition to Personal Page, there are options for Organization Page, News Page, and Community Forum.
|
||||
Organization and New Pages automatically approve contact requests as followers.
|
||||
|
||||
Community Forum provide the ability for people to become friends/fans of the forum without requiring approval.
|
||||
This creates a forum page where all members can freely interact.
|
||||
|
||||
Posting to Community forums
|
||||
---
|
||||
|
||||
If you are a member of a community forum, you may post to the forum by including an @-tag in the post mentioning the forum.
|
||||
For example @bicycle would send my post to all members of the group "bicycle" in addition to the normal recipients.
|
||||
If you mention a forum (you are a member of) in a new posting, the posting will be distributed to all members of the forum, regardless of your privacy settings for the posting.
|
||||
Also, if the forum is a public forum, your posting will be public for the all internet users.
|
||||
If your post is private you must also explicitly include the group in the post permissions (to allow the forum "contact" to see the post) **and** mention it in a tag (which redistributes the post to the forum members).
|
||||
Posting privately to a public forum, will result in your posting being displayed on the forum wall, but not on yours.
|
||||
|
||||
Additionally it is possible to address a forum with the exclamation mark.
|
||||
In the example above this means that you can address the bicycle forum via !bicycle.
|
||||
The difference to the @ is that the post will only be sent to the addressed forum.
|
||||
This also means that you shouldn't address multiple forums in a single post in that way since it will only be distributed by one the forums.
|
||||
|
||||
You may also post to a community forum by posting a "wall-to-wall" post using secure cross-site authentication.
|
||||
|
||||
Comments which are relayed to community forums will be relayed back to the original post creator.
|
||||
Mentioning the forum with an @-tag in a comment does not relay the message, as distribution is controlled entirely by the original post creator.
|
||||
|
|
@ -3,7 +3,7 @@ Friendica on GitHub
|
|||
|
||||
* [Home](help)
|
||||
|
||||
Here is how you can work on the code with us. If you have any questions please write to the Friendica developers' group.
|
||||
Here is how you can work on the code with us. If you have any questions please write to the Friendica developers' forum.
|
||||
|
||||
Introduction to the workflow with our GitHub repository
|
||||
-------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -1,76 +1,78 @@
|
|||
Circles and Privacy
|
||||
Groups and Privacy
|
||||
==================
|
||||
|
||||
* [Home](help)
|
||||
|
||||
|
||||
Circles are merely collections of friends.
|
||||
Groups are merely collections of friends.
|
||||
But Friendica uses these to unlock some very powerful features.
|
||||
|
||||
**Setting Up Circles**
|
||||
**Setting Up Groups**
|
||||
|
||||
To create a circle, visit your Friendica "Contacts" page and select "Create a new circle".
|
||||
Give the circle a name.
|
||||
To create a group, visit your Friendica "Contacts" page and select "Create a new group".
|
||||
Give the group a name.
|
||||
|
||||
This brings you to a page where you can select the circle members.
|
||||
This brings you to a page where you can select the group members.
|
||||
|
||||
You will have two boxes on this page.
|
||||
The top box is the roster of current circle members.
|
||||
Below that is another box containing all of your friends who are *not* members of the circle.
|
||||
The top box is the roster of current group members.
|
||||
Below that is another box containing all of your friends who are *not* members of the group.
|
||||
|
||||
If you click on a photo of a person who isn't in the circle, they will be put into the circle.
|
||||
If you click on a photo of a person who is in the circle, they will be removed from it.
|
||||
If you click on a photo of a person who isn't in the group, they will be put into the group.
|
||||
If you click on a photo of a person who is in the group, they will be removed from it.
|
||||
|
||||
**Access Control**
|
||||
|
||||
Once you have created a circle, you may use it in any access control list.
|
||||
Once you have created a group, you may use it in any access control list.
|
||||
This is the little lock icon beneath the status update box on your home page.
|
||||
If you click this, you can select who can see and who can *not* see the post you are about to make...
|
||||
These can be individual people or circles.
|
||||
If you click this you can select who can see and who can *not* see the post you are about to make..
|
||||
These can be individual people or groups.
|
||||
|
||||
On your "Network" page, you will find posts and conversations from everybody in your network.
|
||||
You may select an individual circle on this page to show conversations pertaining only to members of that circle.
|
||||
On your "Network" page you will find posts and conversation from everybody in your network.
|
||||
You may select an individual group on this page to show conversations pertaining only to members of that group.
|
||||
|
||||
But wait, there's more...
|
||||
|
||||
If you look carefully when visiting a circle from your Network page, the lock icon under the status update box has an exclamation mark next to it.
|
||||
If you look carefully when visiting a group from your Network page, the lock icon under the status update box has an exclamation mark next to it.
|
||||
This is meant to draw attention to that lock.
|
||||
Click the lock.
|
||||
You will see that since you are only viewing a certain circle of people, your status updates while on that screen default to only being seen by that same circle of people.
|
||||
You will see that since you are only viewing a certain group of people, your status updates while on that screen default to only being seen by that same group of people.
|
||||
This is how you keep your future employers from seeing what you write to your drinking buddies.
|
||||
You can override this setting, but this makes it easy to separate your conversations into different friend circles.
|
||||
You can over-ride this setting, but this makes it easy to separate your conversations into different friend circles.
|
||||
|
||||
**Default Post Privacy**
|
||||
|
||||
By default, Friendica assumes that you want all of your posts to be private.
|
||||
Therefore, when you sign up, Friendica creates a circle for you that it will automatically add all of your contacts to.
|
||||
All of your posts are restricted to that circle by default.
|
||||
Therefore, when you sign up, Friendica creates a group for you that it will automatically add all of your contacts to.
|
||||
All of your posts are restricted to that group by default.
|
||||
|
||||
Note that this behaviour can be overridden by your site admin, in which case your posts will be "public" (i.e., visible to the entire Internet) by default.
|
||||
Note that this behaviour can be overridden by your site admin, in which case your posts will be "public" (i.e. visible to the entire Internet) by default.
|
||||
|
||||
If you want your posts to be "public" by default, you can change your default post permissions on your Settings page.
|
||||
You also have the option to change which circles you post to by default or which circle your new contacts get placed into by default.
|
||||
You also have the option there to change which groups you post to by default, or to change which group your new contacts get placed into by default.
|
||||
|
||||
**Privacy Concerns To Be Aware Of**
|
||||
|
||||
These private conversations work best when your friends are Friendica members.
|
||||
We know who else can see the conversations - nobody, *unless* your friends cut and paste the messages and send them to others.
|
||||
We know who else can see the conversations - nobody, *unless* your friends cut and paste the messages and send them to others.
|
||||
|
||||
This is a trust issue you need to be aware of.
|
||||
No software in the world can prevent your friends from leaking your confidential and trusted communications.
|
||||
Only a wise choice of friends.
|
||||
Only a wise choice of friends.
|
||||
|
||||
But it isn't as clear-cut when dealing with GNU Social and other network providers.
|
||||
But it isn't as clear cut when dealing with GNU Social and other network providers.
|
||||
If you look at the Contact Edit page for any person, we will tell you whether or not they are members of an insecure network where you should exercise caution.
|
||||
|
||||
Once you have created a post, you can not change the permissions assigned.
|
||||
Within seconds it has been delivered to lots of people - and perhaps everybody it was addressed to.
|
||||
If you mistakenly created a message and wish to take it back, the best you can do is delete it.
|
||||
We will send out a delete notification to everybody who received the message - and this should wipe out the message with the same speed as it was initially propagated.
|
||||
In most cases, it will be completely wiped from the Internet - in under a minute.
|
||||
If you mistakenly created a message and wish you could take it back, the best you can do is to delete it.
|
||||
We will send out a delete notification to everybody who received the message - and this should wipe out the message with the same speed it was initially propagated.
|
||||
In most cases it will be completely wiped from the Internet - in under a minute.
|
||||
Again, this applies to Friendica networks.
|
||||
Once a message spreads to other networks, it may not be removed quickly, and in some cases, it may not be removed at all.
|
||||
Once a message spreads to other networks, it may not be removed quickly and in some cases it may not be removed at all.
|
||||
|
||||
In case you haven't yet figured this out, we are encouraging you to encourage your friends to use Friendica - because all these privacy features work much better within a privacy-aware network.
|
||||
Many of the other social networks Friendica can connect to have no privacy controls.
|
||||
|
||||
|
||||
Profiles, Photos, and Privacy
|
||||
|
|
@ -100,7 +102,7 @@ When Friendica sends a post to these networks which exceeds the service length l
|
|||
The original is a link back to your Friendica profile.
|
||||
As Friendica cannot prove who they are, it may not be possible for these people to view your post in full.
|
||||
|
||||
For people in this situation we would recommend providing a "Twitter-length" summary, with more detail for friends that can see the post in full.
|
||||
For people in this situation we would recommend providing a "Twitter-length" summary, with more detail for friends that can see the post in full.
|
||||
You can do so by including the BBCode tag *abstract* in your posting.
|
||||
|
||||
Blocking your profile or entire Friendica site from unknown web visitors also has serious implications for communicating with GNU Social members.
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
Groups
|
||||
=====
|
||||
|
||||
* [Home](help)
|
||||
|
||||
|
||||
Friendica also lets you create accounts that can function as discussion groups, celebrity accounts, announcement channels, news reflectors, or organization pages, depending on how you want to interact with others.
|
||||
Management of these accounts can be delegated to other accounts, or a parent account can be designated to easily toggle multiple identities.
|
||||
|
||||
Every account in Friendica has a nickname and these must all be unique.
|
||||
This applies to all accounts, whether they are individual profiles or group profiles.
|
||||
|
||||
Managing Accounts
|
||||
---
|
||||
|
||||
To create a new linked account that can be used as a group, log in to your normal account and go to Settings > Manage Accounts.
|
||||
Here you can register additional accounts with new nicknames that will be linked to your primary account.
|
||||
|
||||
You may appoint a delegate to manage your new account.
|
||||
The Delegates section of Manage Accounts page will provide you with a list of contacts on this instance under "Potential Delegates".
|
||||
Selecting one or more persons will give them access to manage your newly created account.
|
||||
They will be able to edit contacts, profiles, and all content for this account.
|
||||
Please use this facility wisely.
|
||||
Delegated managers will not be able to alter basic account settings, such as passwords or account types, or remove the account.
|
||||
|
||||
Additionally, this page is also where you can choose to designate an account as a parent user.
|
||||
If your primary account is designated as the parent user, you will be able to easily toggle identities and manage your groups or other types of accounts.
|
||||
|
||||
Types of Accounts
|
||||
---
|
||||
|
||||
On the new account, visit the Settings > Account page.
|
||||
Towards the end of the page is a section for "Advanced account types".
|
||||
Typically, you would use "Personal Page - Standard" for a normal personal account with manual approval of “friends” and “followers.”
|
||||
This is the default selection.
|
||||
On this page you can change the type of account if desired.
|
||||
|
||||
The other subtypes of a Personal Page are “Soapbox” and “Love-all.”
|
||||
A Soapbox account is an announcement channel that automatically approvals follower requests.
|
||||
Everything posted by the account will go out to the followers, but there will be no opportunity for interaction.
|
||||
This setting would typically be used for announcements or corporate communications.
|
||||
“Love-all” automatically approves contacts as friends.
|
||||
|
||||
In addition to Personal Page, there are options for Organization Page, News Page, and Community Group.
|
||||
Organization and New Pages automatically approve contact requests as followers.
|
||||
|
||||
Community Group provide the ability for people to join the group without requiring approval.
|
||||
This creates a group where all members can freely interact.
|
||||
|
||||
Posting to Community groups
|
||||
---
|
||||
|
||||
If you are a member of a community group, you may post to the group by including an @-mention in the post mentioning the group.
|
||||
For example @bicycle would send my post to all members of the group "bicycle" in addition to the normal recipients.
|
||||
If you mention a group (you are a member of) in a new posting, the posting will be distributed to all members of the group, regardless of your privacy settings for the posting.
|
||||
Also, if the group is public, your posting will be public for the all internet users.
|
||||
If your post is private you must also explicitly include the group in the post permissions (to allow the group "contact" to see the post) **and** mention it in a tag (which redistributes the post to the group members).
|
||||
Posting privately to a public group, will result in your posting being displayed on the group wall, but not on yours.
|
||||
|
||||
Additionally, it is possible to address a group with the exclamation mark.
|
||||
In the example above this means that you can address the bicycle group via !bicycle.
|
||||
The difference with the @-mention is that the post will only be sent to the addressed group.
|
||||
This also means that you shouldn't address multiple groups in a single post in that way since it will only be distributed by one the groups.
|
||||
|
||||
You may also post to a community group by posting a "wall-to-wall" post using secure cross-site authentication.
|
||||
|
||||
Comments which are relayed to community groups will be relayed back to the original post creator.
|
||||
Mentioning the group with an @-mention in a comment does not relay the message, as distribution is controlled entirely by the original post creator.
|
||||
|
|
@ -14,9 +14,9 @@ Friendica Documentation and Resources
|
|||
* You and other users
|
||||
* [Connectors](help/Connectors)
|
||||
* [Making Friends](help/Making-Friends)
|
||||
* [Circles and Privacy](help/Circles-and-Privacy)
|
||||
* [Groups and Privacy](help/Groups-and-Privacy)
|
||||
* [Tags and Mentions](help/Tags-and-Mentions)
|
||||
* [Community Groups](help/Groups)
|
||||
* [Community Forums](help/Forums)
|
||||
* [Chats](help/Chats)
|
||||
* Further information
|
||||
* [Move your account](help/Move-Account)
|
||||
|
|
@ -67,7 +67,7 @@ Friendica Documentation and Resources
|
|||
|
||||
* [Main Website](https://friendi.ca)
|
||||
* Ways to get Support
|
||||
* Friendica Support Group: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* [Mailing List Archive](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) you can subscribe to the list by sending an email to ``support-request(at)friendi.ca?subject=subscribe``
|
||||
* Community chat rooms (the IRC, Matrix and XMPP rooms are bridged) these public chats are logged [from IRC](https://gnusociarg.nsupdate.info/2021/%23friendica/) and [Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/)
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ How to improve the performance of a Friendica site
|
|||
|
||||
* [Home](help)
|
||||
|
||||
Feel free to ask in the [Friendica support group](https://forum.friendi.ca/profile/helpers) if you need some clarification about the following instructions or if you need help in any other way.
|
||||
Feel free to ask in the [Friendica support forum](https://forum.friendi.ca/profile/helpers) if you need some clarification about the following instructions or if you need help in any other way.
|
||||
|
||||
System configuration
|
||||
--------
|
||||
|
|
@ -31,7 +31,7 @@ Active the following addons:
|
|||
|
||||
### rendertime
|
||||
|
||||
This addon doesn't speed up your system.
|
||||
This addon doesn't speed up your system.
|
||||
It helps to analyze your bottlenecks.
|
||||
|
||||
When enabled you see some values at the bottom of every page.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Many will.
|
|||
But **please** review the [requirements](#Requirements) and confirm these with your hosting provider prior to installation.
|
||||
|
||||
## Support
|
||||
If you encounter installation issues, please let us know via the [helper](http://forum.friendi.ca/profile/helpers) or the [developer](https://forum.friendi.ca/profile/developers) group or [file an issue](https://github.com/friendica/friendica/issues).
|
||||
If you encounter installation issues, please let us know via the [helper](http://forum.friendi.ca/profile/helpers) or the [developer](https://forum.friendi.ca/profile/developers) forum or [file an issue](https://github.com/friendica/friendica/issues).
|
||||
|
||||
Please be as clear as you can about your operating environment and provide as much detail as possible about any error messages you may see, so that we can prevent it from happening in the future.
|
||||
Due to the large variety of operating systems and PHP platforms in existence we may have only limited ability to debug your PHP installation or acquire any missing modules - but we will do our best to solve any general code issues.
|
||||
|
|
@ -23,7 +23,7 @@ Due to the large variety of operating systems and PHP platforms in existence we
|
|||
## Prerequisites
|
||||
|
||||
* Choose a domain name or subdomain name for your server. Put some thought into this. While changing it after installation is supported, things still might break.
|
||||
* Setup HTTPS on your domain.
|
||||
* Setup HTTPS on your domain.
|
||||
|
||||
### Requirements
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ Due to the large variety of operating systems and PHP platforms in existence we
|
|||
|
||||
For alternative server configurations (such as Nginx server and MariaDB database engine), refer to the [Friendica wiki](https://github.com/friendica/friendica/wiki).
|
||||
|
||||
### Optional
|
||||
### Optional
|
||||
|
||||
* PHP ImageMagick extension (php-imagick) for animated GIF support.
|
||||
|
||||
|
|
@ -105,11 +105,11 @@ If you encounter a bug, please let us know.
|
|||
|
||||
### Create a database
|
||||
|
||||
Create an empty database and note the access details (hostname, username, password, database name).
|
||||
Create an empty database and note the access details (hostname, username, password, database name).
|
||||
Generate a strong password, then enter mysql with:
|
||||
|
||||
mysql
|
||||
|
||||
|
||||
Then use the following script using the password you just generated:
|
||||
|
||||
CREATE DATABASE friendicadb;
|
||||
|
|
@ -148,7 +148,7 @@ You have the following options to automatically install Friendica:
|
|||
- using environment variables (f.e. `MYSQL_HOST`)
|
||||
- using options (f.e. `--dbhost <host>`)
|
||||
|
||||
You can combine environment variables and options, but be aware that options are prioritized over environment variables.
|
||||
You can combine environment variables and options, but be aware that options are prioritized over environment variables.
|
||||
|
||||
For more information during the installation, you can use this command line option
|
||||
|
||||
|
|
@ -157,7 +157,7 @@ For more information during the installation, you can use this command line opti
|
|||
If you wish to include all optional checks, use `-a` like this statement:
|
||||
|
||||
bin/console autoinstall -a
|
||||
|
||||
|
||||
*If* the automatic installation fails for any reason, check the following:
|
||||
|
||||
* Does `config/local.config.php` already exist? If yes, the automatic installation won't start
|
||||
|
|
@ -171,7 +171,7 @@ You can use a prepared config file like [local-sample.config.php](/config/local-
|
|||
Navigate to the main Friendica directory and execute the following command:
|
||||
|
||||
bin/console autoinstall -f <prepared.config.php>
|
||||
|
||||
|
||||
#### B.2: Environment variables
|
||||
|
||||
There are two types of environment variables.
|
||||
|
|
@ -194,7 +194,7 @@ if you don't use the option `--savedb` during installation, the DB credentials w
|
|||
**Friendica settings**
|
||||
|
||||
This variables wont be used at normal Friendica runtime.
|
||||
Instead, they get saved into `config/local.config.php`.
|
||||
Instead, they get saved into `config/local.config.php`.
|
||||
|
||||
- `FRIENDICA_URL_PATH` The URL path of Friendica (f.e. '/friendica')
|
||||
- `FRIENDICA_PHP_PATH` The path of the PHP binary
|
||||
|
|
@ -238,7 +238,7 @@ Example:
|
|||
### Verify the "host-meta" page is working
|
||||
|
||||
Friendica should respond automatically to important addresses under the */.well-known/* rewrite path.
|
||||
One critical URL would look like, for example: https://example.com/.well-known/host-meta
|
||||
One critical URL would look like, for example: https://example.com/.well-known/host-meta
|
||||
It must be visible to the public and must respond with an XML file that is automatically customized to your site.
|
||||
|
||||
If that URL is not working, it is possible that some other software is using the /.well-known/ path.
|
||||
|
|
@ -257,7 +257,7 @@ It may be necessary to chmod the /.well-known/.htaccess file if you were not giv
|
|||
At this point visit your website again, and register your personal account with the same email as in the `config.admin_email` config value.
|
||||
Registration errors should all be recoverable automatically.
|
||||
|
||||
If you get any *critical* failure at this point, it generally indicates the database was not installed correctly.
|
||||
If you get any *critical* failure at this point, it generally indicates the database was not installed correctly.
|
||||
You might wish to delete/rename `config/local.config.php` to another name and drop all the database tables so that you can start fresh.
|
||||
|
||||
## Post Install Configuration
|
||||
|
|
@ -295,7 +295,7 @@ Once started, you can check the daemon status using the following command:
|
|||
|
||||
cd /path/to/friendica; php bin/daemon.php status
|
||||
|
||||
After a server restart or any other failure, the daemon needs to be restarted.
|
||||
After a server restart or any other failure, the daemon needs to be restarted.
|
||||
This could be achieved by a cronjob.
|
||||
|
||||
### (RECOMMENDED) Logging & Log Rotation
|
||||
|
|
@ -408,7 +408,7 @@ provided by one of our members.
|
|||
> On my server I use the php protection system Suhosin [http://www.hardened-php.net/suhosin/].
|
||||
> One of the things it does is to block certain functions like proc_open, as
|
||||
> configured in `/etc/php5/conf.d/suhosin.ini`:
|
||||
>
|
||||
>
|
||||
> suhosin.executor.func.blacklist = proc_open, ...
|
||||
>
|
||||
> For those sites like Friendica that really need these functions they can be
|
||||
|
|
@ -418,34 +418,34 @@ provided by one of our members.
|
|||
> php_admin_value suhosin.executor.func.blacklist none
|
||||
> php_admin_value suhosin.executor.eval.blacklist none
|
||||
> </Directory>
|
||||
>
|
||||
>
|
||||
> This enables every function for Friendica if accessed via browser, but not for
|
||||
> the cronjob that is called via php command line. I attempted to enable it for
|
||||
> cron by using something like:
|
||||
>
|
||||
>
|
||||
> */10 * * * * cd /var/www/friendica/friendica/ && sudo -u www-data /usr/bin/php \
|
||||
> -d suhosin.executor.func.blacklist=none \
|
||||
> -d suhosin.executor.eval.blacklist=none -f bin/worker.php
|
||||
>
|
||||
>
|
||||
> This worked well for simple test cases, but the friendica-cron still failed
|
||||
> with a fatal error:
|
||||
>
|
||||
>
|
||||
> suhosin[22962]: ALERT - function within blacklist called: proc_open()
|
||||
> (attacker 'REMOTE_ADDR not set', file '/var/www/friendica/friendica/boot.php',
|
||||
> line 1341)
|
||||
>
|
||||
>
|
||||
> After a while I noticed, that `bin/worker.php` calls further PHP script via `proc_open`.
|
||||
> These scripts themselves also use `proc_open` and fail, because they are NOT
|
||||
> called with `-d suhosin.executor.func.blacklist=none`.
|
||||
>
|
||||
>
|
||||
> So the simple solution is to put the correct parameters into `config/local.config.php`:
|
||||
>
|
||||
>
|
||||
> 'config' => [
|
||||
> //Location of PHP command line processor
|
||||
> 'php_path' => '/usr/bin/php -d suhosin.executor.func.blacklist=none \
|
||||
> -d suhosin.executor.eval.blacklist=none',
|
||||
> ],
|
||||
>
|
||||
>
|
||||
> This is obvious as soon as you notice that the friendica-cron uses `proc_open`
|
||||
> to execute PHP scripts that also use `proc_open`, but it took me quite some time to find that out.
|
||||
> I hope this saves some time for other people using suhosin with function blocklists.
|
||||
|
|
|
|||
|
|
@ -20,20 +20,20 @@ You'll also see a link to a **Global Directory**.
|
|||
There are several global directories across the globe that regularly exchange information with each other.
|
||||
The specific global directory that you see usually depends on where your server is located.
|
||||
If you click through to the global directory, you will be presented with a list of everybody who choses to be listed across all instances of Friendica.
|
||||
You will also see a "Show Community Groups" link, which will direct you to Groups.
|
||||
You connect to people and groups the same way, public groups will automatically accept your introduction, whereas private groups and some individual users will need to manually approve it.
|
||||
You will also see a "Show Community Forums" link, which will direct you to Groups, Forums and Fanpages.
|
||||
You connect to people, groups and forums in the same way, except groups and forums will automatically accept your introduction request, whereas a human will approve you manually.
|
||||
|
||||
Connect to other Friendica users
|
||||
---
|
||||
|
||||
Visit their profile.
|
||||
Just beneath their profile picture will be the word 'Connect' (we're assuming this is an English language profile).
|
||||
Click that 'Connect' button, and it will take you to a 'Connect' form.
|
||||
Click that 'Connect' button and it will take you to a 'Connect' form.
|
||||
|
||||
The form is going to ask you for your Identity Address.
|
||||
This is necessary so that this person's website can find yours.
|
||||
This is necessary so that this person's website can find yours.
|
||||
|
||||
If your Friendica site is called "demo.friendica.com" and your username/nickname on that site is "bob", you would enter "bob@demo.friendica.com" in this form.
|
||||
If your Friendica site is called "demo.friendica.com" and your username/nickname on that site is "bob", you would enter "bob@demo.friendica.com" in this form.
|
||||
|
||||
Notice this looks just like an email address.
|
||||
It's meant to be that way.
|
||||
|
|
@ -41,8 +41,8 @@ It's easy for people to remember.
|
|||
|
||||
You *could* also put in the URL of your "home" page, such as "http://demo.friendica.com/profile/bob" instead of the email-style address.
|
||||
|
||||
When you've submitted the connection page, it will take you back to your own site where you must then log in (if necessary) and verify the connection request on *your* site.
|
||||
Once you've done this, the two websites can communicate with each other to complete the process (after your new friend has approved the request).
|
||||
When you've submitted the connection page, it will take you back to your own site where you must then login (if necessary) and verify the connection request on *your* site.
|
||||
Once you've done this, the two websites can communicate with each other to complete the process (after your new friend has approved the request).
|
||||
|
||||
If you already know somebody's Identity Address, you can enter it in the "connect" box on your "Contacts" page.
|
||||
This will take you through a similar process.
|
||||
|
|
@ -55,20 +55,20 @@ You can also use your Identity Address or other people's Identity Addresses to b
|
|||
Currently, Friendica supports connections with people on diaspora*, Red, Hubzilla, GNU Social, StatusNet, Mastodon, Pleroma, socialhome, and ganggo platforms.
|
||||
|
||||
If you know (for instance) "alice" on gnusocial.net (a GNU Social site) you could put alice@gnusocial.net into your Contact page and become friends across networks.
|
||||
Likewise, you can put in the URL to Alice's gnusocial.net page, if you wish.
|
||||
Likewise you can put in the URL to Alice's gnusocial.net page, if you wish.
|
||||
Note: Some versions of GNU Social software may require the full URL to your profile and may not work with the identity address.
|
||||
|
||||
People on these networks can also initiate contact with you, if they know your contact details.
|
||||
|
||||
### Other social media
|
||||
If you server provides this functionality, you can also connect with people one
|
||||
Twitter or important feeds from Tumblr, WordPress, and many more.
|
||||
Twitter or important feeds from Tumblr, Wordpress, and many more.
|
||||
|
||||
To connect, enter their contact details in the "connect" box on your "Contacts" page.
|
||||
|
||||
### Email
|
||||
If you have supplied your mailbox connection information on your Settings page, you can enter the email address of anybody that has sent you a message recently and have their email messages show up in your social stream.
|
||||
You can also reply to them from within Friendica.
|
||||
You can also reply to them from within Friendica.
|
||||
|
||||
Create an email contact with for example Alice on Gmail, enter her email in following format "mailto:alice@gmail.no".
|
||||
In order to avoid abuse or spam, you must have an email from Alice with the correct email address in your email inbox.
|
||||
|
|
@ -78,7 +78,7 @@ To subscribe to a mailing list, enter the email in following example format "mai
|
|||
|
||||
### Syndication feeds
|
||||
You can "follow" almost anybody or any website that produces a syndication feed (RSS/Atom,etc.).
|
||||
If we can find an information stream and a name to attach to the contact, we'll try to connect with them.
|
||||
If we can find an information stream and a name to attach to the contact, we'll try to connect with them.
|
||||
|
||||
Notification
|
||||
---
|
||||
|
|
@ -88,24 +88,24 @@ You will usually need to approve this before the friendship is complete.
|
|||
Approval
|
||||
---
|
||||
Some networks allow people to send you messages without being friends and without your approval.
|
||||
Friendica does not allow this by default, as it would open a gateway for spam.
|
||||
Friendica does not allow this by default, as it would open a gateway for spam.
|
||||
|
||||
Unilateral or bilateral friendships
|
||||
---
|
||||
When you receive a friendship notification from another Friendica member, you will have the option of allowing them as a "Follower" or as a "Friend".
|
||||
If they are a follower, they can see what you have to say, including private communications that you send to them, but not vice versa.
|
||||
As a friend, you can both communicate with each other.
|
||||
As a friend, you can both communicate with each other.
|
||||
|
||||
diaspora* uses a different terminology, and you are given the option of allowing them to "share with you", or being full friends.
|
||||
diaspora* uses a different terminology, and you are given the option of allowing them to "share with you", or being full friends.
|
||||
|
||||
Ignoring, blocking and deleting contacts
|
||||
---
|
||||
Once you have become friends, if you find the person constantly sends you spam or worthless information, you can "Ignore" them - without breaking off the friendship or even alerting them to the fact that you aren't interested in anything they are saying.
|
||||
In many ways they are like a "follower" - but they don't know this.
|
||||
They think they are a friend.
|
||||
They think they are a friend.
|
||||
|
||||
You can also "block" a person.
|
||||
This completely blocks communications with that person.
|
||||
They may still be able to see your public posts, as can anybody in the world, but they cannot communicate with you directly.
|
||||
They may still be able to see your public posts, as can anybody in the world, but they cannot communicate with you directly.
|
||||
|
||||
You can also delete a friend no matter what the friendship status - which completely removes everything relating to that person from your website.
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ How to move your account between servers
|
|||
|
||||
* Go to "Settings" -> "[Export personal data](uexport)"
|
||||
* Click on "Export account" to save your account data.
|
||||
* **Save the file in a secure place!** It contains your details, your contacts, circles, and personal settings. It also contains your secret keys to authenticate yourself to your contacts.
|
||||
* **Save the file in a secure place!** It contains your details, your contacts, groups, and personal settings. It also contains your secret keys to authenticate yourself to your contacts.
|
||||
* Go to your new server, and open *http://newserver.com/user/import* (there is not a direct link to this page at the moment). Please consider that this is only possible on servers with open registration. On other systems only the administrator can add accounts with an uploaded file.
|
||||
* Do NOT create a new account prior to importing your old settings - user import should be used *instead* of register.
|
||||
* Load your saved account file and click "Import".
|
||||
|
|
@ -17,7 +17,7 @@ How to move your account between servers
|
|||
|
||||
Friendica contacts
|
||||
---
|
||||
Friendica will recreate your account on the new server, with your contacts and circles.
|
||||
Friendica will recreate your account on the new server, with your contacts and groups.
|
||||
A message is sent to Friendica contacts, to inform them about your move:
|
||||
If your contacts are running on an updated server, your details on their side will be automatically updated.
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ Here are some more things to help get you started:
|
|||
|
||||
**Groups**
|
||||
|
||||
- <a href="https://forum.friendi.ca/profile/helpers">Friendica Support</a> - problems? This is the place to ask.
|
||||
|
||||
- <a href="http://forum.friendi.ca/profile/helpers">Friendica Support</a> - problems? This is the place to ask.
|
||||
|
||||
**Documentation**
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
This is the global directory.
|
||||
If you get lost, you can <a href = "help/Quick-Start-groupsandpages">click this link</a> to bring yourself back here.
|
||||
|
||||
On this page, you'll find a collection of groups.
|
||||
On this page, you'll find a collection of groups, forums and celebrity pages.
|
||||
Groups are not real people.
|
||||
Connecting to them is similar to "liking" something on Facebook, or signing up for a new group.
|
||||
Connecting to them is similar to "liking" something on Facebook, or signing up for a new forum.
|
||||
You don't have to feel awkward about introducing yourself to a new person, because they're not people!
|
||||
|
||||
When you connect to a group, all messages to that group will start appearing in your network tab.
|
||||
|
|
@ -15,6 +15,6 @@ Remember the link at the top of this page will bring you back here.
|
|||
|
||||
Once you've added some groups, <a href="help/Quick-Start-andfinally">move on to the next section</a>.
|
||||
|
||||
<iframe src="https://dir.friendica.social/group" width="950" height="600"></iframe>
|
||||
<iframe src="https://dir.friendica.social/forum" width="950" height="600"></iframe>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,89 +0,0 @@
|
|||
Friendica strategy Hooks
|
||||
===========================================
|
||||
|
||||
* [Home](help)
|
||||
|
||||
## Strategy hooks
|
||||
|
||||
This type of hook is based on the [Strategy Design Pattern](https://refactoring.guru/design-patterns/strategy).
|
||||
|
||||
A strategy class defines a possible implementation of a given interface based on a unique name.
|
||||
Every name is possible as long as it's unique and not `null`.
|
||||
Using an empty name (`''`) is possible as well and should be used as the "default" implementation.
|
||||
To register a strategy, use the [`ICanRegisterInstance`](../src/Core/Hooks/Capability/ICanRegisterInstances.php) interface.
|
||||
|
||||
After registration, a caller can automatically create this instance with the [`ICanCreateInstances`](../src/Core/Hooks/Capability/ICanCreateInstances.php) interface and the chosen name.
|
||||
|
||||
This is useful in case there are different, possible implementations for the same purpose, like for logging, locking, caching, ...
|
||||
|
||||
Normally, a config entry is used to choose the right implementation at runtime.
|
||||
And if no config entry is set, the "default" implementation should be used.
|
||||
|
||||
### Example
|
||||
|
||||
```php
|
||||
interface ExampleInterface
|
||||
{
|
||||
public function testMethod();
|
||||
}
|
||||
|
||||
public class ConcreteClassA implements ExampleInterface
|
||||
{
|
||||
public function testMethod()
|
||||
{
|
||||
echo "concrete class A";
|
||||
}
|
||||
}
|
||||
|
||||
public class ConcreteClassB implements ExampleInterface
|
||||
{
|
||||
public function testMethod()
|
||||
{
|
||||
echo "concrete class B";
|
||||
}
|
||||
}
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capability\ICanRegisterStrategies $instanceRegister */
|
||||
$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassA::class, 'A');
|
||||
$instanceRegister->registerStrategy(ExampleInterface::class, ConcreteClassB::class, 'B');
|
||||
|
||||
/** @var \Friendica\Core\Hooks\Capability\ICanCreateInstances $instanceManager */
|
||||
/** @var ConcreteClassA $concreteClass */
|
||||
$concreteClass = $instanceManager->create(ExampleInterface::class, 'A');
|
||||
|
||||
$concreteClass->testMethod();
|
||||
// output:
|
||||
// "concrete class A";
|
||||
```
|
||||
|
||||
## hooks.config.php
|
||||
|
||||
To avoid registering all strategies manually inside the code, Friendica introduced the [`hooks.config.php`](../static/hooks.config.php) file.
|
||||
|
||||
There, you can register all kind of strategies in one file.
|
||||
|
||||
### [`HookType::STRATEGY`](../src/Core/Hooks/Capability/HookType.php)
|
||||
|
||||
For each given interface, a list of key-value pairs can be set, where the key is the concrete implementation class and the value is an array of unique names.
|
||||
|
||||
### Example
|
||||
|
||||
```php
|
||||
use Friendica\Core\Hooks\Capability\BehavioralHookType as H;
|
||||
|
||||
return [
|
||||
H::STRATEGY => [
|
||||
ExampleInterface::class => [
|
||||
ConcreteClassA::class => ['A'],
|
||||
ConcreteClassB::class => ['B'],
|
||||
],
|
||||
],
|
||||
];
|
||||
```
|
||||
|
||||
## Addons
|
||||
|
||||
The hook logic is useful for decoupling the Friendica core logic, but its primary goal is to modularize Friendica in creating addons.
|
||||
|
||||
Therefor you can either use the interfaces directly as shown above, or you can place your own `hooks.config.php` file inside a `static` directory directly under your addon core directory.
|
||||
Friendica will automatically search these config files for each **activated** addon and register the given hooks.
|
||||
|
|
@ -15,7 +15,7 @@ You can tag **persons who are in your social circle** by adding the "@"-sign in
|
|||
|
||||
* @mike - indicates a known contact in your social circle whose nickname is "mike"
|
||||
* @mike_macgirvin - indicates a known contact in your social circle whose full name is "Mike Macgirvin". Note that spaces cannot be used inside tags.
|
||||
* @mike+151 - this form is used by the drop-down tag completion tool. It indicates the contact whose nickname is mike and whose contact identifier number is 151. The drop-down tool may be used to resolve people with duplicate nicknames.
|
||||
* @mike+151 - this form is used by the drop-down tag completion tool. It indicates the contact whose nickname is mike and whose contact identifier number is 151. The drop-down tool may be used to resolve people with duplicate nicknames.
|
||||
|
||||
You can tag a person on a different network or one that is **not in your social circle** by using the following notation:
|
||||
|
||||
|
|
@ -23,27 +23,27 @@ You can tag a person on a different network or one that is **not in your social
|
|||
|
||||
Unless their system blocks unsolicited "mentions", the person tagged will likely receive a "Mention" post/activity or become a direct participant in the conversation in the case of public posts.
|
||||
Friendica blocks incoming “mentions” from people with no relationship to you.
|
||||
The exception is an ongoing conversation started from a contact of both you and the 3rd person or a conversation in a group where you are a member of.
|
||||
The exception is an ongoing conversation started from a contact of both you and the 3rd person or a conversation in a forum where you are a member of.
|
||||
This is a spam prevention measure.
|
||||
|
||||
Remote mentions are delivered using the OStatus protocol.
|
||||
This protocol is used by Friendica and GNU Social and several other systems like Mastodon, but is not currently implemented in Diaspora.
|
||||
As the OStatus protocol allows this Friendica user can be @-mentioned by users from platforms using this protocol in conversations if the "Enable OStatus support" is activated on the Friendica node.
|
||||
These @-mentions won't be blocked, even if there is no relationship between the sender and the receiver of the message.
|
||||
These @-mentions wont be blocked, even if there is no relationship between the sender and the receiver of the message.
|
||||
|
||||
Friendica makes no distinction between people and groups for the purpose of tagging.
|
||||
You can use @-mentions for groups like for other accounts to tag the group.
|
||||
If you want to post something exclusively to a group (e.g. the support group) please use the !-mention instead of the @-mention.
|
||||
So !helpers will be an exclusive posting to the support group if you are connected with the group.
|
||||
If you select a group from the ACL a !-mention will be added automatically to your posting.
|
||||
Friendica makes no distinction between people and forums for the purpose of tagging.
|
||||
You can use @-mentions for forums like for other accounts to tag the forum.
|
||||
If you want to post something exclusively to a forum (e.g. the support forum) please use the bang-notation instead of the @tag.
|
||||
So !helpers will be an exclusive posting to the support forum if you are connected with the forum.
|
||||
If you select a forum from the ACL a !-mention will be added automatically to your posting.
|
||||
|
||||
If you sort your contacts into circles, you cannot @-mention these circles.
|
||||
But you can select the circle in the access control when creating a new posting, to allow (or disallow) a certain circle of people to see the posting.
|
||||
See [Circles and Privacy](help/Circles-and-Privacy) for more details about grouping your contacts.
|
||||
If you sort your contacts into groups, you cannot @-mention these groups.
|
||||
But you can select the group in the access control when creating a new posting, to allow (or disallow) a certain group of people to see the posting.
|
||||
See [Groups and Privacy](help/Groups-and-Privacy) for more details about grouping your contacts.
|
||||
|
||||
**Topical Tags**
|
||||
|
||||
Topical tags are indicated by preceding the tag name with the # character.
|
||||
Topical tags are indicated by preceding the tag name with the # character.
|
||||
This will create a link in the post to a generalised site search for the term provided.
|
||||
For example, #cars will provide a search link for all posts mentioning 'cars' on your site.
|
||||
Topical tags are generally a minimum of three characters in length.
|
||||
|
|
|
|||
|
|
@ -88,11 +88,11 @@ Click on "show" under contact name to hide the post to everyone but selected.
|
|||
|
||||
Click on "Visible to everybody" to make the post public again.
|
||||
|
||||
If you have defined some circles, you can check "show" for circles also. All contact in that circle will see the post.
|
||||
If you want to hide the post to one contact of a circle selected for "show", click "don't show" under contact name.
|
||||
If you have defined some groups, you can check "show" for groups also. All contact in that group will see the post.
|
||||
If you want to hide the post to one contact of a group selected for "show", click "don't show" under contact name.
|
||||
|
||||
Click again on "show" or "don't show" to switch it off.
|
||||
|
||||
You can search for contacts or circles with the search box.
|
||||
You can search for contacts or groups with the search box.
|
||||
|
||||
See also [Circles and Privacy](help/Circles-and-Privacy)
|
||||
See also [Group and Privacy](help/Groups-and-Privacy)
|
||||
|
|
|
|||
|
|
@ -10,10 +10,8 @@ If you installed Friendica in the ``path/to/friendica`` folder:
|
|||
1. Unpack the new Friendica archive in ``path/to/friendica_new``.
|
||||
2. Copy the following items from ``path/to/friendica`` to ``path/to/friendica_new``:
|
||||
* ``config/local.config.php``
|
||||
* ``proxy/``
|
||||
* ``.htaccess`` if using Apache web server
|
||||
|
||||
The following items only need to be copied if they are located inside your friendica path:
|
||||
* ``proxy/``
|
||||
The following items only need to be copied if they are located inside your friendica path:
|
||||
* your storage folder as set in **Admin -> Site -> File Upload -> Storage base path**
|
||||
* your item cache as set in **Admin -> Site -> Performance -> Path to item cache**
|
||||
* your temp folder as set in **Admin -> Site -> Advanced -> Temp path**
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ xml:
|
|||
The [RSStoFriendika](https://github.com/pafcu/RSStoFriendika) code can be used as an example of how to use the API with python.
|
||||
The lines for posting are located at [line 21](https://github.com/pafcu/RSStoFriendika/blob/master/RSStoFriendika.py#L21) and following.
|
||||
|
||||
def tweet(server, message, circle_allow=None):
|
||||
def tweet(server, message, group_allow=None):
|
||||
url = server + '/api/statuses/update'
|
||||
urllib2.urlopen(url, urllib.urlencode({'status': message, 'circle_allow[]': circle_allow}, doseq=True))
|
||||
urllib2.urlopen(url, urllib.urlencode({'status': message,'group_allow[]':group_allow}, doseq=True))
|
||||
|
||||
There is also a [module for python 3](https://bitbucket.org/tobiasd/python-friendica) for using the API.
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ Database Tables
|
|||
| [fetch-entry](help/database/db_fetch-entry) | |
|
||||
| [fetched-activity](help/database/db_fetched-activity) | Id of fetched activities |
|
||||
| [fsuggest](help/database/db_fsuggest) | friend suggestion stuff |
|
||||
| [group](help/database/db_group) | privacy circles, circle info |
|
||||
| [group_member](help/database/db_group_member) | privacy circles, member info |
|
||||
| [group](help/database/db_group) | privacy groups, group info |
|
||||
| [group_member](help/database/db_group_member) | privacy groups, member info |
|
||||
| [gserver](help/database/db_gserver) | Global servers |
|
||||
| [gserver-tag](help/database/db_gserver-tag) | Tags that the server has subscribed |
|
||||
| [hook](help/database/db_hook) | addon hook registry |
|
||||
|
|
@ -77,8 +77,7 @@ Database Tables
|
|||
| [push_subscriber](help/database/db_push_subscriber) | Used for OStatus: Contains feed subscribers |
|
||||
| [register](help/database/db_register) | registrations requiring admin approval |
|
||||
| [report](help/database/db_report) | |
|
||||
| [report-post](help/database/db_report-post) | Individual posts attached to a moderation report |
|
||||
| [report-rule](help/database/db_report-rule) | Terms of service rule lines relevant to a moderation report |
|
||||
| [report-post](help/database/db_report-post) | |
|
||||
| [search](help/database/db_search) | |
|
||||
| [session](help/database/db_session) | web session storage |
|
||||
| [storage](help/database/db_storage) | Data stored by Database storage backend |
|
||||
|
|
@ -86,7 +85,6 @@ Database Tables
|
|||
| [tag](help/database/db_tag) | tags and mentions |
|
||||
| [user](help/database/db_user) | The local users |
|
||||
| [user-contact](help/database/db_user-contact) | User specific public contact data |
|
||||
| [user-gserver](help/database/db_user-gserver) | User settings about remote servers |
|
||||
| [userd](help/database/db_userd) | Deleted usernames |
|
||||
| [verb](help/database/db_verb) | Activity Verbs |
|
||||
| [worker-ipc](help/database/db_worker-ipc) | Inter process communication between the frontend and the worker |
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@ Fields
|
|||
| created | creation time | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| edited | last edit time | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| allow_cid | Access Control - list of allowed contact.id '<19><78> | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed circles | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
|
||||
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied circles | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
|
||||
| backend-class | Storage backend class | tinytext | YES | | NULL | |
|
||||
| backend-ref | Storage backend data reference | text | YES | | NULL | |
|
||||
|
||||
|
|
|
|||
|
|
@ -6,17 +6,13 @@ Contact relations
|
|||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| --------------------- | -------------------------------------------------------- | ----------------- | ---- | --- | ------------------- | ----- |
|
||||
| cid | contact the related contact had interacted with | int unsigned | NO | PRI | 0 | |
|
||||
| relation-cid | related contact who had interacted with the contact | int unsigned | NO | PRI | 0 | |
|
||||
| last-interaction | Date of the last interaction | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| follow-updated | Date of the last update of the contact relationship | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| follows | | boolean | NO | | 0 | |
|
||||
| score | score for interactions of cid on relation-cid | smallint unsigned | YES | | NULL | |
|
||||
| relation-score | score for interactions of relation-cid on cid | smallint unsigned | YES | | NULL | |
|
||||
| thread-score | score for interactions of cid on threads of relation-cid | smallint unsigned | YES | | NULL | |
|
||||
| relation-thread-score | score for interactions of relation-cid on threads of cid | smallint unsigned | YES | | NULL | |
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ---------------- | --------------------------------------------------- | ------------ | ---- | --- | ------------------- | ----- |
|
||||
| cid | contact the related contact had interacted with | int unsigned | NO | PRI | 0 | |
|
||||
| relation-cid | related contact who had interacted with the contact | int unsigned | NO | PRI | 0 | |
|
||||
| last-interaction | Date of the last interaction | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| follow-updated | Date of the last update of the contact relationship | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| follows | | boolean | NO | | 0 | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ Fields
|
|||
| archive | | boolean | NO | | 0 | |
|
||||
| unsearchable | Contact prefers to not be searchable | boolean | NO | | 0 | |
|
||||
| sensitive | Contact posts sensitive content | boolean | NO | | 0 | |
|
||||
| baseurl | baseurl of the contact from the gserver record, can be missing | varbinary(383) | YES | | | |
|
||||
| gsid | Global Server ID, can be missing | int unsigned | YES | | NULL | |
|
||||
| baseurl | baseurl of the contact | varbinary(383) | YES | | | |
|
||||
| gsid | Global Server ID | int unsigned | YES | | NULL | |
|
||||
| bd | | date | NO | | 0001-01-01 | |
|
||||
| reason | | text | YES | | NULL | |
|
||||
| self | 1 if the contact is the user him/her self | boolean | NO | | 0 | |
|
||||
|
|
@ -81,7 +81,7 @@ Fields
|
|||
| confirm | | varbinary(383) | YES | | NULL | |
|
||||
| poco | | varbinary(383) | YES | | NULL | |
|
||||
| writable | | boolean | NO | | 0 | |
|
||||
| forum | contact is a group. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = false instead | boolean | NO | | 0 | |
|
||||
| forum | contact is a forum. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = false instead | boolean | NO | | 0 | |
|
||||
| prv | contact is a private group. Deprecated, use 'contact-type' = 'community' and 'manually-approve' = true instead | boolean | NO | | 0 | |
|
||||
| bdyear | | varchar(4) | NO | | | |
|
||||
| site-pubkey | Deprecated | text | YES | | NULL | |
|
||||
|
|
|
|||
|
|
@ -25,9 +25,9 @@ Fields
|
|||
| nofinish | if event does have no end this is 1 | boolean | NO | | 0 | |
|
||||
| ignore | 0 or 1 | boolean | NO | | 0 | |
|
||||
| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed circles | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
|
||||
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied circles | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Table group
|
||||
===========
|
||||
|
||||
privacy circles, circle info
|
||||
privacy groups, group info
|
||||
|
||||
Fields
|
||||
------
|
||||
|
|
@ -11,9 +11,9 @@ Fields
|
|||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
|
||||
| visible | 1 indicates the member list is not private | boolean | NO | | 0 | |
|
||||
| deleted | 1 indicates the circle has been deleted | boolean | NO | | 0 | |
|
||||
| cid | Contact id of group. When this field is filled then the members are synced automatically. | int unsigned | YES | | NULL | |
|
||||
| name | human readable name of circle | varchar(255) | NO | | | |
|
||||
| deleted | 1 indicates the group has been deleted | boolean | NO | | 0 | |
|
||||
| cid | Contact id of forum. When this field is filled then the members are synced automatically. | int unsigned | YES | | NULL | |
|
||||
| name | human readable name of group | varchar(255) | NO | | | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
Table group_member
|
||||
===========
|
||||
|
||||
privacy circles, member info
|
||||
privacy groups, member info
|
||||
|
||||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ---------- | ---------------------------------------------------------- | ------------ | ---- | --- | ------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| gid | group.id of the associated circle | int unsigned | NO | | 0 | |
|
||||
| contact-id | contact.id of the member assigned to the associated circle | int unsigned | NO | | 0 | |
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ---------- | --------------------------------------------------------- | ------------ | ---- | --- | ------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| gid | groups.id of the associated group | int unsigned | NO | | 0 | |
|
||||
| contact-id | contact.id of the member assigned to the associated group | int unsigned | NO | | 0 | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ Fields
|
|||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Owner id of this permission set | mediumint unsigned | NO | | 0 | |
|
||||
| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed circles | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
|
||||
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied circles | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ Fields
|
|||
| scale | | tinyint unsigned | NO | | 0 | |
|
||||
| profile | | boolean | NO | | 0 | |
|
||||
| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed circles | mediumtext | YES | | NULL | |
|
||||
| allow_gid | Access Control - list of allowed groups | mediumtext | YES | | NULL | |
|
||||
| deny_cid | Access Control - list of denied contact.id | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied circles | mediumtext | YES | | NULL | |
|
||||
| deny_gid | Access Control - list of denied groups | mediumtext | YES | | NULL | |
|
||||
| accessible | Make photo publicly accessible, ignoring permissions | boolean | NO | | 0 | |
|
||||
| backend-class | Storage backend class | tinytext | YES | | NULL | |
|
||||
| backend-ref | Storage backend data reference | text | YES | | NULL | |
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Fields
|
|||
| profile-name | Deprecated | varchar(255) | YES | | NULL | |
|
||||
| is-default | Deprecated | boolean | YES | | NULL | |
|
||||
| hide-friends | Hide friend list from viewers of this profile | boolean | NO | | 0 | |
|
||||
| name | Unused in favor of user.username | varchar(255) | NO | | | |
|
||||
| name | | varchar(255) | NO | | | |
|
||||
| pdesc | Deprecated | varchar(255) | YES | | NULL | |
|
||||
| dob | Day of birth | varchar(32) | NO | | 0000-00-00 | |
|
||||
| address | | varchar(255) | NO | | | |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Table report-post
|
||||
===========
|
||||
|
||||
Individual posts attached to a moderation report
|
||||
|
||||
|
||||
Fields
|
||||
------
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
Table report-rule
|
||||
===========
|
||||
|
||||
Terms of service rule lines relevant to a moderation report
|
||||
|
||||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------- | ------------------------------------------------------------------------- | ------------ | ---- | --- | ------- | ----- |
|
||||
| rid | Report id | int unsigned | NO | PRI | NULL | |
|
||||
| line-id | Terms of service rule line number, may become invalid after a TOS change. | int unsigned | NO | PRI | NULL | |
|
||||
| text | Terms of service rule text recorded at the time of the report | text | NO | | NULL | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
||||
| Name | Fields |
|
||||
| ------- | ------------ |
|
||||
| PRIMARY | rid, line-id |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
||||
| Field | Target Table | Target Field |
|
||||
|-------|--------------|--------------|
|
||||
| rid | [report](help/database/db_report) | id |
|
||||
|
||||
Return to [database documentation](help/database)
|
||||
|
|
@ -6,40 +6,28 @@ Table report
|
|||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| --------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Reporting user | mediumint unsigned | YES | | NULL | |
|
||||
| reporter-id | Reporting contact | int unsigned | YES | | NULL | |
|
||||
| cid | Reported contact | int unsigned | NO | | NULL | |
|
||||
| gsid | Reported contact server | int unsigned | YES | | NULL | |
|
||||
| comment | Report | text | YES | | NULL | |
|
||||
| category-id | Report category, one of Entity\Report::CATEGORY_* | int unsigned | NO | | 1 | |
|
||||
| forward | Forward the report to the remote server | boolean | YES | | NULL | |
|
||||
| public-remarks | Remarks shared with the reporter | text | YES | | NULL | |
|
||||
| private-remarks | Remarks shared with the moderation team | text | YES | | NULL | |
|
||||
| last-editor-uid | Last editor user | mediumint unsigned | YES | | NULL | |
|
||||
| assigned-uid | Assigned moderator user | mediumint unsigned | YES | | NULL | |
|
||||
| status | Status of the report, one of Entity\Report::STATUS_* | tinyint unsigned | NO | | NULL | |
|
||||
| resolution | Resolution of the report, one of Entity\Report::RESOLUTION_* | tinyint unsigned | YES | | NULL | |
|
||||
| created | | datetime(6) | NO | | 0001-01-01 00:00:00 | |
|
||||
| edited | Last time the report has been edited | datetime(6) | YES | | NULL | |
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ----------- | ----------------------------------------------- | ------------------ | ---- | --- | ------------------- | -------------- |
|
||||
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
|
||||
| uid | Reporting user | mediumint unsigned | YES | | NULL | |
|
||||
| reporter-id | Reporting contact | int unsigned | YES | | NULL | |
|
||||
| cid | Reported contact | int unsigned | NO | | NULL | |
|
||||
| comment | Report | text | YES | | NULL | |
|
||||
| category | Category of the report (spam, violation, other) | varchar(20) | YES | | NULL | |
|
||||
| rules | Violated rules | text | YES | | NULL | |
|
||||
| forward | Forward the report to the remote server | boolean | YES | | NULL | |
|
||||
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
|
||||
| status | Status of the report | tinyint unsigned | YES | | NULL | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
||||
| Name | Fields |
|
||||
| ----------------- | ------------------ |
|
||||
| PRIMARY | id |
|
||||
| uid | uid |
|
||||
| cid | cid |
|
||||
| reporter-id | reporter-id |
|
||||
| gsid | gsid |
|
||||
| last-editor-uid | last-editor-uid |
|
||||
| assigned-uid | assigned-uid |
|
||||
| status-resolution | status, resolution |
|
||||
| created | created |
|
||||
| edited | edited |
|
||||
| Name | Fields |
|
||||
| ----------- | ----------- |
|
||||
| PRIMARY | id |
|
||||
| uid | uid |
|
||||
| cid | cid |
|
||||
| reporter-id | reporter-id |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
|
@ -49,8 +37,5 @@ Foreign Keys
|
|||
| uid | [user](help/database/db_user) | uid |
|
||||
| reporter-id | [contact](help/database/db_contact) | id |
|
||||
| cid | [contact](help/database/db_contact) | id |
|
||||
| gsid | [gserver](help/database/db_gserver) | id |
|
||||
| last-editor-uid | [user](help/database/db_user) | uid |
|
||||
| assigned-uid | [user](help/database/db_user) | uid |
|
||||
|
||||
Return to [database documentation](help/database)
|
||||
|
|
|
|||
|
|
@ -6,28 +6,28 @@ User specific public contact data
|
|||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------------------------- | ----------------------------------------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
|
||||
| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | |
|
||||
| uid | User id | mediumint unsigned | NO | PRI | 0 | |
|
||||
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
|
||||
| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | |
|
||||
| ignored | Posts from this contact are ignored | boolean | YES | | NULL | |
|
||||
| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | |
|
||||
| hidden | This contact is hidden from the others | boolean | YES | | NULL | |
|
||||
| is-blocked | User is blocked by this contact | boolean | YES | | NULL | |
|
||||
| pending | | boolean | YES | | NULL | |
|
||||
| rel | The kind of the relation between the user and the contact | tinyint unsigned | YES | | NULL | |
|
||||
| info | | mediumtext | YES | | NULL | |
|
||||
| notify_new_posts | | boolean | YES | | NULL | |
|
||||
| remote_self | 0 => No mirroring, 1-2 => Mirror as own post, 3 => Mirror as reshare | tinyint unsigned | YES | | NULL | |
|
||||
| fetch_further_information | 0 => None, 1 => Fetch information, 3 => Fetch keywords, 2 => Fetch both | tinyint unsigned | YES | | NULL | |
|
||||
| ffi_keyword_denylist | | text | YES | | NULL | |
|
||||
| subhub | | boolean | YES | | NULL | |
|
||||
| hub-verify | | varbinary(383) | YES | | NULL | |
|
||||
| protocol | Protocol of the contact | char(4) | YES | | NULL | |
|
||||
| rating | Automatically detected feed poll frequency | tinyint | YES | | NULL | |
|
||||
| priority | Feed poll priority | tinyint unsigned | YES | | NULL | |
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------------------------- | ------------------------------------------------------------ | ------------------ | ---- | --- | ------- | ----- |
|
||||
| cid | Contact id of the linked public contact | int unsigned | NO | PRI | 0 | |
|
||||
| uid | User id | mediumint unsigned | NO | PRI | 0 | |
|
||||
| uri-id | Id of the item-uri table entry that contains the contact url | int unsigned | YES | | NULL | |
|
||||
| blocked | Contact is completely blocked for this user | boolean | YES | | NULL | |
|
||||
| ignored | Posts from this contact are ignored | boolean | YES | | NULL | |
|
||||
| collapsed | Posts from this contact are collapsed | boolean | YES | | NULL | |
|
||||
| hidden | This contact is hidden from the others | boolean | YES | | NULL | |
|
||||
| is-blocked | User is blocked by this contact | boolean | YES | | NULL | |
|
||||
| pending | | boolean | YES | | NULL | |
|
||||
| rel | The kind of the relation between the user and the contact | tinyint unsigned | YES | | NULL | |
|
||||
| info | | mediumtext | YES | | NULL | |
|
||||
| notify_new_posts | | boolean | YES | | NULL | |
|
||||
| remote_self | | boolean | YES | | NULL | |
|
||||
| fetch_further_information | | tinyint unsigned | YES | | NULL | |
|
||||
| ffi_keyword_denylist | | text | YES | | NULL | |
|
||||
| subhub | | boolean | YES | | NULL | |
|
||||
| hub-verify | | varbinary(383) | YES | | NULL | |
|
||||
| protocol | Protocol of the contact | char(4) | YES | | NULL | |
|
||||
| rating | Automatically detected feed poll frequency | tinyint | YES | | NULL | |
|
||||
| priority | Feed poll priority | tinyint unsigned | YES | | NULL | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
Table user-gserver
|
||||
===========
|
||||
|
||||
User settings about remote servers
|
||||
|
||||
Fields
|
||||
------
|
||||
|
||||
| Field | Description | Type | Null | Key | Default | Extra |
|
||||
| ------- | ---------------------------------------- | ------------------ | ---- | --- | ------- | ----- |
|
||||
| uid | Owner User id | mediumint unsigned | NO | | 0 | |
|
||||
| gsid | Gserver id | int unsigned | NO | | 0 | |
|
||||
| ignored | server accounts are ignored for the user | boolean | NO | | 0 | |
|
||||
|
||||
Indexes
|
||||
------------
|
||||
|
||||
| Name | Fields |
|
||||
| ------- | --------- |
|
||||
| PRIMARY | uid, gsid |
|
||||
| gsid | gsid |
|
||||
|
||||
Foreign Keys
|
||||
------------
|
||||
|
||||
| Field | Target Table | Target Field |
|
||||
|-------|--------------|--------------|
|
||||
| uid | [user](help/database/db_user) | uid |
|
||||
| gsid | [gserver](help/database/db_gserver) | id |
|
||||
|
||||
Return to [database documentation](help/database)
|
||||
|
|
@ -9,14 +9,14 @@ Account - Basics
|
|||
Viele, aber nicht alle Friendica-Knoten (Server) bieten die Möglichkeit zur Registrierung an.
|
||||
Falls der Friendica-Knoten, den Du besuchst, keine Registrierung anbietet, oder Du glaubst, dass Dir ein anderer Knoten möglicherweise besser gefällt, dann findest Du hier eine [Liste von öffentlichen Friendica-Knoten](https://dir.friendica.social/servers), aus der Du Dir eine netten Knoten heraussuchen kannst.
|
||||
|
||||
Auf der Startseite des Knotens wird unter dem Login-Feld ein "Registrieren"-Link angezeigt.
|
||||
Dieser Link führt dann direkt auf das Registrierungsformular.
|
||||
Auf der Startseite des Knotens wird unter dem Login-Feld ein "Registrieren"-Link angezeigt.
|
||||
Dieser Link führt dann direkt auf das Registrierungsformular.
|
||||
|
||||
### OpenID
|
||||
|
||||
Falls du keine [OpenID-Adresse](https://de.wikipedia.org/wiki/OpenID">OpenID-Adresse) hast, kannst du diesen Punkt ignorieren.
|
||||
|
||||
Solltest du eine OpenID Adresse haben, kannst Du sie im ersten Feld eintragen und "Registrieren" klicken.
|
||||
Solltest du eine OpenID Adresse haben, kannst Du sie im ersten Feld eintragen und "Registrieren" klicken.
|
||||
Friendica wird versuchen, so viele Informationen wie möglich von Deinem OpenID-Provider zu übernehmen, um diese in Dein Profil auf dieser Seite einzutragen.
|
||||
|
||||
|
||||
|
|
@ -33,55 +33,55 @@ Bitte trage eine richtige Email-Adresse ein.
|
|||
Dies ist die einzige persönliche Information, die korrekt sein muss.
|
||||
Deine Email-Adresse wird **niemals** veröffentlicht.
|
||||
|
||||
Wir benötigen diese, um Dir Account-Informationen, das Initialpasswort und die Login-Daten zu schicken. Oder z.B. Dein Passwort zurückzusetzen.
|
||||
Wir benötigen diese, um Dir Account-Informationen, das Initialpasswort und die Login-Daten zu schicken. Oder z.B. Dein Passwort zurückzusetzen.
|
||||
|
||||
Du erhältst zudem von Zeit zu Zeit Benachrichtigungen über eingegangene Nachrichten oder Punkte, die Deine Aufmerksamkeit benötigen.
|
||||
Diese Nachrichten sind in den Einstellungen jederzeit an- oder abschaltbar.
|
||||
|
||||
### Spitzname/Nickname
|
||||
|
||||
Der Spitzname wird benötigt, um eine Webadresse (Profiladresse) für viele Deiner persönlichen Seiten zu erstellen.
|
||||
Auch wird dieser wie eine Email-Adresse genutzt, wenn eine Verbindung zu anderen Personen hergestellt werden soll.
|
||||
Durch die Art, wie der Spitzname genutzt wird, gibt es bestimmte Einschränkungen:
|
||||
Der Spitzname wird benötigt, um eine Webadresse (Profiladresse) für viele Deiner persönlichen Seiten zu erstellen.
|
||||
Auch wird dieser wie eine Email-Adresse genutzt, wenn eine Verbindung zu anderen Personen hergestellt werden soll.
|
||||
Durch die Art, wie der Spitzname genutzt wird, gibt es bestimmte Einschränkungen:
|
||||
|
||||
* **er muss mit einem Buchstaben beginnen**
|
||||
* **er darf nur US-ASCII-Textzeichen und Nummern enthalten**
|
||||
* **er muss einzigartig auf diesem Friendica-Knoten sein**
|
||||
* **er kann später nicht mehr geändert werden**
|
||||
|
||||
Dieser Spitzname wird an vielen Stellen genutzt, um Deinen Account zu identifizieren, daher ist es nicht möglich ihn später zu ändern.
|
||||
Dieser Spitzname wird an vielen Stellen genutzt, um Deinen Account zu identifizieren, daher ist es nicht möglich ihn später zu ändern.
|
||||
|
||||
|
||||
### Verzeichnis-Eintrag
|
||||
|
||||
Das Registrierungsformular erlaubt es dir, direkt auszuwählen, ob Du im [Onlineverzeichnis](https://dir.friendica.social/) (Friendica Directory) aufgelistet wirst oder nicht.
|
||||
Das ist wie ein Telefonbuch und Du entscheidest, ob du darin eingetragen werden möchtest, oder nicht.
|
||||
Das Registrierungsformular erlaubt es dir, direkt auszuwählen, ob Du im [Onlineverzeichnis](https://dir.friendica.social/) (Friendica Directory) aufgelistet wirst oder nicht.
|
||||
Das ist wie ein Telefonbuch und Du entscheidest, ob du darin eingetragen werden möchtest, oder nicht.
|
||||
|
||||
* Wir bitten dich, "Ja" zu wählen, damit Andere Dich finden können, so wie Du sie finden kannst
|
||||
* Wählst Du "Nein", bist Du für Andere *nicht einfach auffindbar*
|
||||
* Wählst Du "Nein", bist Du für Andere *nicht einfach auffindbar*
|
||||
|
||||
Was auch immer Du wählst, kann jederzeit nach dem Login in Deinen Account-Einstellungen geändert werden.
|
||||
|
||||
|
||||
### Registrierung
|
||||
|
||||
Sobald Du die nötigen Informationen eingegeben hast, klicke auf "Registrieren".
|
||||
Eine Email mit den Registrierungsdetails und Deinem Initialpasswort wird an die hinterlegte Email-Adresse geschickt.
|
||||
Sobald Du die nötigen Informationen eingegeben hast, klicke auf "Registrieren".
|
||||
Eine Email mit den Registrierungsdetails und Deinem Initialpasswort wird an die hinterlegte Email-Adresse geschickt.
|
||||
Bitte prüfe den Posteingang (inkl. dem Spam-Ordner).
|
||||
|
||||
|
||||
## Login-Seite
|
||||
|
||||
Gib auf der "Login"-Seite die Informationen ein, die Du mit der oben genannten Email erhalten hast.
|
||||
Gib auf der "Login"-Seite die Informationen ein, die Du mit der oben genannten Email erhalten hast.
|
||||
Du kannst entweder Deinen Spitznamen oder die Email-Adresse als Login-Namen nutzen.
|
||||
|
||||
Wenn Du Deinen Account nutzt, um unterschiedliche '[Seiten](help/Pages)' zu verwalten, die die gleiche Email-Adresse verwenden, dann nutze bitte den Spitznamen des Accounts, der verwaltet werden soll.
|
||||
|
||||
*Wenn* Dein Account OpenID nutzt, dann kannst Du Deine OpenID-Adresse als Login-Name nutzen und das Passwort-Feld frei lassen.
|
||||
Du wirst zu Deinem OpenID-Anbieter weitergeleitet, wo Du Deine Anmeldung abschließt.
|
||||
*Wenn* Dein Account OpenID nutzt, dann kannst Du Deine OpenID-Adresse als Login-Name nutzen und das Passwort-Feld frei lassen.
|
||||
Du wirst zu Deinem OpenID-Anbieter weitergeleitet, wo Du Deine Anmeldung abschließt.
|
||||
|
||||
Wenn Du OpenID nicht nutzt, dann gib Dein Passwort ein, das Du mit der Registrierungsmail erhalten hast.
|
||||
Das Passwort muss genau so geschrieben werden, wie es in der Email steht; Groß- und Kleinschreibung wird beachtet.
|
||||
Wenn Du OpenID nicht nutzt, dann gib Dein Passwort ein, das Du mit der Registrierungsmail erhalten hast.
|
||||
Das Passwort muss genau so geschrieben werden, wie es in der Email steht; Groß- und Kleinschreibung wird beachtet.
|
||||
Falls Du Schwierigkeiten beim Login hast, prüfe bitte, ob z. B. Deine Feststelltaste aktiv ist.
|
||||
|
||||
|
||||
|
|
@ -90,7 +90,7 @@ Falls Du Schwierigkeiten beim Login hast, prüfe bitte, ob z. B. Deine Feststell
|
|||
Besuche nach Deinem ersten Login bitte die Einstellungsseite und wechsle das Passwort in eines, dass Du Dir merken kannst.
|
||||
|
||||
|
||||
## Die ersten Schritte
|
||||
## Die ersten Schritte
|
||||
|
||||
### Persönliche Daten exportieren
|
||||
|
||||
|
|
@ -98,9 +98,9 @@ Du solltest dir als erstes Deinen neu erstellen [Account exportieren](uexport) u
|
|||
In diesem Export (JSON-Datei) sind enthalten
|
||||
|
||||
* Deine Identität, die mit kryptographischen Schlüsseln ausgestattet ist
|
||||
* Deine Kontakte
|
||||
* Deine Kontakte
|
||||
|
||||
Dies ist z.B. dann nützlich wenn du mit deinem Account auf einen anderen Friendica Knoten umziehen willst, oder musst.
|
||||
Dies ist z.B. dann nützlich wenn du mit deinem Account auf einen anderen Friendica Knoten umziehen willst, oder musst.
|
||||
|
||||
### Hilfe für Neulinge
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ Ein ['Tipp für neue Mitglieder'](newmember)-Link zeigt sich in den ersten beide
|
|||
|
||||
## Schau Dir ebenfalls folgende Seiten an
|
||||
|
||||
* [Circles und Privatssphäre](help/Circles-and-Privacy)
|
||||
* [Gruppen und Privatssphäre](help/Groups-and-Privacy)
|
||||
|
||||
* [Account löschen](help/Remove-Account)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,4 +56,4 @@ Starte dazu bitte vom Grundverzeichnis deiner Friendica Instanz folgendes Komman
|
|||
|
||||
bin/console dbstructure update
|
||||
|
||||
sollten bei der Ausführung Fehler auftreten, kontaktiere bitte die [Support Gruppe](https://forum.friendi.ca/profile/helpers).
|
||||
sollten bei der Ausführung Fehler auftreten, kontaktiere bitte das [Support Forum](https://forum.friendi.ca/profile/helpers).
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ Wenn Du Deinen Account nicht nutzen kannst, kannst Du einen Account auf einer ö
|
|||
|
||||
Wenn du dir keinen weiteren Friendica Account einrichten willst, kannst du auch gerne über einen der folgenden alternativen Kanäle Hilfe suchen:
|
||||
|
||||
* Friendica Support Gruppe: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Chats der Friendica Community (die IRC, Matrix und XMPP Räume sind mit einer Brücke verbunden) Logs dieser öffentlichen Chaträume können [hier aus dem IRC](https://gnusociarg.nsupdate.info/2021/%23friendica/) und [hier aus der Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/) gefunden werden.
|
||||
* XMPP: support(at)forum.friendi.ca
|
||||
* IRC: #friendica auf [libera.chat](https://web.libera.chat/?channels=#friendica)
|
||||
|
|
@ -108,7 +108,7 @@ Zwischen der URL und dem Parameter muss ein Fragezeichen als Trennzeichen verwen
|
|||
|
||||
Ein Beispiel:
|
||||
|
||||
https://social.example.com/profile/example
|
||||
https://social.example.com/profile/example
|
||||
|
||||
auf Deutsch:
|
||||
|
||||
|
|
|
|||
63
doc/de/Forums.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
Foren
|
||||
=====
|
||||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
|
||||
In Friendica kannst Du auch Foren und/oder Prominenten-Seiten erstellen.
|
||||
|
||||
Jede Seite in Friendica hat einen einmaligen Spitznamen.
|
||||
Das gilt für alle Seiten, unabhängig davon, ob es sich um normale Profile oder Forenseiten handelt.
|
||||
|
||||
Das Erste, was Du machen musst, um ein neues Forum zu kreieren, ist einen neuen Account zu erstellen.
|
||||
Bitte beachte, dass der Seitenadministrator die Registrierung neuer Accounts sperren oder an Bedingungen knüpfen kann.
|
||||
|
||||
Wenn Du einen zweiten Account in einem System erstellst und die gleiche Email-Adresse oder den gleichen OpenID-Account nutzt, kannst Du Dich zukünftig nur noch mit Deinem Spitznamen anmelden.
|
||||
|
||||
Gehe im neuen Account auf die "Einstellungs"-Seite und dort am Ende der Seite auf "Erweiterte Konto-/Seitentyp-Einstellungen".
|
||||
Normalerweise nutzt Du "Normales Konto" für einen normalen, persönlichen Account.
|
||||
Das ist die Standardeinstellung.
|
||||
Gruppenseiten bieten die Möglichkeit, Leute als Freund/Fan ohne Kontaktbestätigung zuzulassen.
|
||||
|
||||
Die Auswahl der Einstellung, die Du wählst, hängt davon ab, wie Du mit anderen Leuten auf Deiner Seite interagieren willst.
|
||||
Die "Marktschreier"-Einstellung (Soapbox) lässt den Seitenbesitzer die gesamte Kommunikation kontrollieren.
|
||||
Alles was Du schreibst, geht an alle Seitennutzer, aber es gibt keine Möglichkeit, zu interagieren.
|
||||
Diese Seite wird normalerweise für Ankündigungen oder die Kommunikation von Gemeinschaften genutzt.
|
||||
|
||||
Die normalste Einstellung ist das "Forum-/Promi-Konto".
|
||||
Diese erstellt eine Gruppenseite, in der alle Mitglieder frei miteinander interagieren können.
|
||||
|
||||
Der "Automatische Freunde Seite"-Account ist typischerweise für persönliche Profile, bei denen Du alle Freundschaftsanfragen automatisch bestätigen willst.
|
||||
|
||||
|
||||
**Multiple Foren verwalten**
|
||||
|
||||
Wir schlagen vor, dass Du ein Gruppenforum mit der gleichen Email-Adresse und dem gleichen Passwort wie bei Deinem normalen Account nutzt.
|
||||
Wenn Du das machst, findest Du einen neuen "Verwalten"-Link in der Menüleiste, über den Du einfach zwischen den Identitäten wechseln kannst.
|
||||
Du musst das nicht machen, die Alternative ist allerdings, Dich immer wieder aus- und wieder einzuloggen.
|
||||
Und das kann umständlich sein, wenn Du mehrere verschiedene Foren/Identitäten verwaltest.
|
||||
|
||||
Du kannst ebenso jemanden wählen, der Dein Forum verwaltet.
|
||||
Mach das, indem Du die [Delegations-Setup-Seite](/settings/delegation) besuchst.
|
||||
Dort wird Dir eine Liste an "Potentiellen Bevollmächtigen" angezeigt.
|
||||
Die Auswahl einer oder mehrerer Personen gibt diesen die Möglichkeit, Dein Forum zu verwalten.
|
||||
Sie können Kontakte, Profile und alle Inhalte Deines Accounts/deiner Seite bearbeiten.
|
||||
Bitte nutze diese Einstellung mit Vorsicht.
|
||||
Delegierte haben allerdings keine Möglichkeit, grundlegende Account-Einstellungen wie das Passwort oder den Seitentypen zu ändern bzw. den Account zu löschen.
|
||||
|
||||
|
||||
**Beiträge auf Community-Foren**
|
||||
|
||||
Wenn Du Mitglied eines Community-Forums bist, kannst Du das Forum in einem Beitrag hinzufügen/erwähnen, wenn Du den @-Tag nutzt.
|
||||
Zum Beispiel würde @Fahrrad Deinen Beitrag neben den sonst ausgewählten Nutzern an alle Nutzer schicken, die in der Gruppe "Fahrrad" sind.
|
||||
Wenn Dein Beitrag privat ist, musst Du diese Gruppe explizit in den Zugriffsrechten des Beitrags auswählen **und** sie mit dem @-Tag erwähnen (was den Beitrag auf die Gruppenmitglieder erweitert).
|
||||
|
||||
Zusätzlich ist es möglich, Foren mit dem Ausrufezeichen zu adressieren.
|
||||
Im obigen Beispiel bedeutet dies, dass Du das Fahrrad-Forum per !Fahrrad erreichen würdest.
|
||||
Der Unterschied zum @ besteht darin, dass der Beitrag auschließlich über das Forum verbreitet wird und nicht an weitere Nutzer.
|
||||
Dies bedeutet auch, dass es nicht sinnvoll ist, mehrere Foren per ! in einem Beitrag zu adressieren, da nur eines der Foren den Beitrag verbreiten wird.
|
||||
|
||||
Du kannst außerdem via "Wall zu Wall" einen Beitrag auf der Community-Seite bzw. in dem Community-Forum erstellen.
|
||||
|
||||
Kommentare, die Du an ein Community-Forum schickst, werden dem Originalbeitrag hinzugefügt.
|
||||
Ein weiteres Forum mit dem @-Tag zu erwähnen, leitet den Beitrag nicht an dieses weiter, da die Verteilung der Kommentare komplett vom Originalbeitrag bestimmt wird.
|
||||
|
|
@ -1,112 +1,112 @@
|
|||
Circles und Privatsphäre
|
||||
Gruppen und Privatsphäre
|
||||
==================
|
||||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
Circles sind nur eine Ansammlung von Freunden.
|
||||
Gruppen sind nur eine Ansammlung von Freunden.
|
||||
Aber Friendica nutzt diese, um sehr mächtige Features zur Verfügung zu stellen.
|
||||
|
||||
**Circles erstellen**
|
||||
**Gruppen erstellen**
|
||||
|
||||
Um eine Circle zu erstellen, besuche deine "Kontakte"-Seite und wähle "Neue Circle erstellen" (je nach Design nur als Pluszeichen angezeigt).
|
||||
Gib deiner Circle einen Namen.
|
||||
Um eine Gruppe zu erstellen, besuche deine "Kontakte"-Seite und wähle "Neue Gruppe erstellen" (je nach Design nur als Pluszeichen angezeigt).
|
||||
Gib deiner Gruppe einen Namen.
|
||||
|
||||
Das führt dich zu einer Seite, auf der du die Gruppenmitglieder auswählen kannst.
|
||||
Das führt dich zu einer Seite, auf der du die Gruppenmitglieder auswählen kannst.
|
||||
|
||||
Du hast zwei Boxen auf der Seite.
|
||||
Die obere Box ist die Übersicht der aktuellen Mitglieder.
|
||||
Die untere beinhaltet alle Freunde, die *nicht* Mitglied dieser Circle sind.
|
||||
Du hast zwei Boxen auf der Seite.
|
||||
Die obere Box ist die Übersicht der aktuellen Mitglieder.
|
||||
Die untere beinhaltet alle Freunde, die *nicht* Mitglied dieser Gruppe sind.
|
||||
|
||||
Wenn du auf das Foto einer Person klickst, die nicht in der Circle ist, wird diese in die Circle verschoben.
|
||||
Wenn du auf das Foto einer Person klickst, die bereits in der Circle ist, dann wird diese Person daraus entfernt.
|
||||
Wenn du auf das Foto einer Person klickst, die nicht in der Gruppe ist, wird diese in die Gruppe verschoben.
|
||||
Wenn du auf das Foto einer Person klickst, die bereits in der Gruppe ist, dann wird diese Person daraus entfernt.
|
||||
|
||||
**Zugriffskontrolle**
|
||||
|
||||
Sobald du eine Circle erstellt hast, kannst du diese auf jeder Zugriffsrechteliste nutzen.
|
||||
Damit ist das kleine Schloss neben deinem Statuseditor auf deiner Startseite gemeint.
|
||||
Wenn du darauf klickst, kannst du auswählen, wer deinen Beitrag sehen kann und wer *nicht*.
|
||||
Dabei kann es sich um eine einzelne Person oder eine ganze Circle handeln.
|
||||
Sobald du eine Gruppe erstellt hast, kannst du diese auf jeder Zugriffsrechteliste nutzen.
|
||||
Damit ist das kleine Schloss neben deinem Statuseditor auf deiner Startseite gemeint.
|
||||
Wenn du darauf klickst, kannst du auswählen, wer deinen Beitrag sehen kann und wer *nicht*.
|
||||
Dabei kann es sich um eine einzelne Person oder eine ganze Gruppe handeln.
|
||||
|
||||
Auf deiner "Netzwerk"-Seite ("Unterhaltungen deiner Kontakte") findest du Beiträge und Gespräche aller deiner Kontakte in deinem Netzwerk.
|
||||
Du kannst aber auch eine einzelne Circle auswählen und nur Beiträge dieser Gruppenmitglieder anzeigen lassen.
|
||||
Auf deiner "Netzwerk"-Seite ("Unterhaltungen deiner Kontakte") findest du Beiträge und Gespräche aller deiner Kontakte in deinem Netzwerk.
|
||||
Du kannst aber auch eine einzelne Gruppe auswählen und nur Beiträge dieser Gruppenmitglieder anzeigen lassen.
|
||||
|
||||
Aber stopp, es gibt noch mehr...
|
||||
|
||||
Wenn du auf deiner "Netzwerk"-Seite eine bestimmte Circle ausgewählt hast, dann findest du im Statuseditor neben dem Schloss ein Ausrufezeichen.
|
||||
Dies dient dazu, deine Aufmerksamkeit auf das Schloss zu richten.
|
||||
Klicke auf das Schloss.
|
||||
Dort siehst du, dass dein Status-Update in dieser Ansicht standardmäßig nur für diese Circle freigegeben ist.
|
||||
Das hilft dir, deinen zukünftigen Mitarbeitern nicht das Gleiche zu schreiben wie deinen Trinkfreunden.
|
||||
Du kannst diese Einstellung natürlich auch überschreiben.
|
||||
Wenn du auf deiner "Netzwerk"-Seite eine bestimmte Gruppe ausgewählt hast, dann findest du im Statuseditor neben dem Schloss ein Ausrufezeichen.
|
||||
Dies dient dazu, deine Aufmerksamkeit auf das Schloss zu richten.
|
||||
Klicke auf das Schloss.
|
||||
Dort siehst du, dass dein Status-Update in dieser Ansicht standardmäßig nur für diese Gruppe freigegeben ist.
|
||||
Das hilft dir, deinen zukünftigen Mitarbeitern nicht das Gleiche zu schreiben wie deinen Trinkfreunden.
|
||||
Du kannst diese Einstellung natürlich auch überschreiben.
|
||||
|
||||
**Standardmäßige Zugriffsrechte von Beiträgen**
|
||||
|
||||
Standardmäßig geht Friendica davon aus, dass alle deine Beiträge privat sein sollen.
|
||||
Aus diesem Grund erstellt Friendica nach der Anmeldung eine Circle, in die automatisch alle deine Kontakte hinzugefügt werden.
|
||||
Alle deine Beiträge sind nur auf diese Circle beschränkt.
|
||||
Standardmäßig geht Friendica davon aus, dass alle deine Beiträge privat sein sollen.
|
||||
Aus diesem Grund erstellt Friendica nach der Anmeldung eine Gruppe, in die automatisch alle deine Kontakte hinzugefügt werden.
|
||||
Alle deine Beiträge sind nur auf diese Gruppe beschränkt.
|
||||
|
||||
Beachte, dass diese Einstellung von deinem Seiten-Administrator überschrieben werden kann, was bedeutet, dass alle deine Beiträge standardmäßig "öffentlich" sind (bspw. für das gesamte Internet).
|
||||
|
||||
Wenn du deine Beiträge standardmäßig "öffentlich" haben willst, dann kannst du deine Standardzugriffsrechte auf deiner Einstellungseite ändern.
|
||||
Dort kannst du außerdem festlegen, welchen Circles standardmäßig deine Beiträge erhalten oder in welche Circle deine neuen Kontakte standardmäßig eingeordnet werden.
|
||||
Wenn du deine Beiträge standardmäßig "öffentlich" haben willst, dann kannst du deine Standardzugriffsrechte auf deiner Einstellungseite ändern.
|
||||
Dort kannst du außerdem festlegen, welchen Gruppen standardmäßig deine Beiträge erhalten oder in welche Gruppe deine neuen Kontakte standardmäßig eingeordnet werden.
|
||||
|
||||
**Fragen der Privatssphäre, die zu beachten sind**
|
||||
|
||||
Diese privaten Gespräche funktionieren am besten, wenn deine Freunde Friendica-Mitglieder sind.
|
||||
Diese privaten Gespräche funktionieren am besten, wenn deine Freunde Friendica-Mitglieder sind.
|
||||
So wissen wir, wer sonst noch deine Gespräche sehen kann - niemand, *solange* deine Freunde deine Nachrichten nicht kopieren und an andere verschicken.
|
||||
|
||||
Dies ist eine Vertrauensfrage, die du beachten musst.
|
||||
Keine Software der Welt kann deine Freunde davon abhalten, die privaten Unterhaltungen zu veröffentlichen.
|
||||
Nur eine gute Auswahl deiner Freunde.
|
||||
Dies ist eine Vertrauensfrage, die du beachten musst.
|
||||
Keine Software der Welt kann deine Freunde davon abhalten, die privaten Unterhaltungen zu veröffentlichen.
|
||||
Nur eine gute Auswahl deiner Freunde.
|
||||
|
||||
Bei GNu Social und anderen Netzwerk-Anbietern ist es nicht so gesichert.
|
||||
Du musst **sehr** vorsichtig sein, wenn du Mitglieder anderer Netzwerke in einer deiner Circles hast, da es möglich ist, dass deine privaten Nachrichten in einem öffentlichen Stream enden.
|
||||
Bei GNu Social und anderen Netzwerk-Anbietern ist es nicht so gesichert.
|
||||
Du musst **sehr** vorsichtig sein, wenn du Mitglieder anderer Netzwerke in einer deiner Gruppen hast, da es möglich ist, dass deine privaten Nachrichten in einem öffentlichen Stream enden.
|
||||
Wenn du auf die "Kontakt bearbeiten"-Seite einer Person gehst, zeigen wir dir, ob sie Mitglied eines unsicheren Netzwerks ist oder nicht.
|
||||
|
||||
Sobald du einen Post erstellt hast, kannst du die Zugriffsrechte nicht mehr ändern.
|
||||
Innerhalb von Sekunden ist dieser an viele verschiedene Personen verschickt worden - möglicherweise bereits an alle Addressierten.
|
||||
Wenn du versehentlich eine Nachricht erstellt hast und sie zurücknehmen willst, dann ist es das beste, diese zu löschen.
|
||||
Wir senden eine Löschmitteilung an jeden, der deine Nachricht erhalten hat - und das sollte die Nachricht genauso schnell löschen, wie sie zunächst erstellt wurde.
|
||||
In vielen Fällen wird sie in weniger als einer Minute aus dem Internet gelöscht.
|
||||
Nochmals: das gilt für Friendica-Netzwerke.
|
||||
Sobald du einen Post erstellt hast, kannst du die Zugriffsrechte nicht mehr ändern.
|
||||
Innerhalb von Sekunden ist dieser an viele verschiedene Personen verschickt worden - möglicherweise bereits an alle Addressierten.
|
||||
Wenn du versehentlich eine Nachricht erstellt hast und sie zurücknehmen willst, dann ist es das beste, diese zu löschen.
|
||||
Wir senden eine Löschmitteilung an jeden, der deine Nachricht erhalten hat - und das sollte die Nachricht genauso schnell löschen, wie sie zunächst erstellt wurde.
|
||||
In vielen Fällen wird sie in weniger als einer Minute aus dem Internet gelöscht.
|
||||
Nochmals: das gilt für Friendica-Netzwerke.
|
||||
Sobald eine Nachricht an ein anderes Netzwerk geschickt wurde, kann es nicht mehr so schnell gelöscht werden und in manchen Fällen auch gar nicht mehr.
|
||||
|
||||
Wenn du das bisher noch nicht wusstest, dann empfehlen wir dir, deine Freunde dazu zu ermutigen, auch Friendica zu nutzen, da alle diese Privatsphären-Einstellungen innerhalb eines privatsphärenbewussten Netzwerk viel besser funktionieren.
|
||||
Wenn du das bisher noch nicht wusstest, dann empfehlen wir dir, deine Freunde dazu zu ermutigen, auch Friendica zu nutzen, da alle diese Privatsphären-Einstellungen innerhalb eines privatsphärenbewussten Netzwerk viel besser funktionieren.
|
||||
Viele andere Netzwerke, mit denen sich Friendica verbinden kann, bieten keine Kontrolle über die Privatsphäre.
|
||||
|
||||
|
||||
Profile, Fotos und die Privatsphäre
|
||||
=============================
|
||||
|
||||
Die dezentralisierte Natur von Friendica (statt eine Webseite zu haben, die alles kontrolliert, gibt es viele Webseiten, die Information austauschen) hat in der Kommunikation mit anderen Seiten einige Konsequenzen.
|
||||
Die dezentralisierte Natur von Friendica (statt eine Webseite zu haben, die alles kontrolliert, gibt es viele Webseiten, die Information austauschen) hat in der Kommunikation mit anderen Seiten einige Konsequenzen.
|
||||
Du solltest dir über einige Dinge bewusst sein, um am besten entscheiden zu können, wie du mit deiner Privatsphäre umgehst.
|
||||
|
||||
**Fotos**
|
||||
|
||||
Fotos privat zu verteilen ist ein Problem.
|
||||
Wir können Fotos nur mit Friendica-Nutzern __privat__ austauschen.
|
||||
Um mit anderen Leuten Fotos zu teilen, müssen wir erkennen, wer sie sind.
|
||||
Wir können die Identität von Friendica-Nutzern prüfen, da es hierfür einen Mechanismus gibt.
|
||||
Deine Freunde anderer Netzwerke werden deine privaten Fotos nicht sehen können, da wir deren Identität nicht überprüfen können.
|
||||
Fotos privat zu verteilen ist ein Problem.
|
||||
Wir können Fotos nur mit Friendica-Nutzern __privat__ austauschen.
|
||||
Um mit anderen Leuten Fotos zu teilen, müssen wir erkennen, wer sie sind.
|
||||
Wir können die Identität von Friendica-Nutzern prüfen, da es hierfür einen Mechanismus gibt.
|
||||
Deine Freunde anderer Netzwerke werden deine privaten Fotos nicht sehen können, da wir deren Identität nicht überprüfen können.
|
||||
|
||||
Unsere Entwickler arbeiten an einer Lösung, um deinen Freunden den Zugriff zu ermöglichen - unabhängig, zu welchem Netzwerk sie gehören.
|
||||
Unsere Entwickler arbeiten an einer Lösung, um deinen Freunden den Zugriff zu ermöglichen - unabhängig, zu welchem Netzwerk sie gehören.
|
||||
Wir nehmen hingegen Privatsphäre ernst und agieren nicht wie andere Netzwerke, die __nur so tun__ als ob deine Fotos privat sind, sie aber trotzdem anderen ohne Identitätsprüfung zeigen.
|
||||
|
||||
**Profile**
|
||||
|
||||
Dein Profil und deine "Wall" sollen vielleicht auch von Freunden anderer Netzwerke besucht werden können.
|
||||
Wenn du diese Seiten allerdings für Webbesucher sperrst, die Friendica nicht kennt, kann das auch Freunde anderer Netzwerke blockieren.
|
||||
Dein Profil und deine "Wall" sollen vielleicht auch von Freunden anderer Netzwerke besucht werden können.
|
||||
Wenn du diese Seiten allerdings für Webbesucher sperrst, die Friendica nicht kennt, kann das auch Freunde anderer Netzwerke blockieren.
|
||||
|
||||
Das kann möglicherweise ungewollte Ergebnisse produzieren, wenn du lange Statusbeiträge z.B. für Twitter oder Facebook schreibst.
|
||||
Wenn Friendica einen Beitrag an diese Netzwerke schickt und nur eine bestimmte Nachrichtenlänge erlaubt ist, dann verkürzen wir diesen und erstellen einen Link, der zum Originalbeitrag führt.
|
||||
Der Originallink führt zurück zu deinem Friendica-Profil.
|
||||
Das kann möglicherweise ungewollte Ergebnisse produzieren, wenn du lange Statusbeiträge z.B. für Twitter oder Facebook schreibst.
|
||||
Wenn Friendica einen Beitrag an diese Netzwerke schickt und nur eine bestimmte Nachrichtenlänge erlaubt ist, dann verkürzen wir diesen und erstellen einen Link, der zum Originalbeitrag führt.
|
||||
Der Originallink führt zurück zu deinem Friendica-Profil.
|
||||
Da Friendica nicht bestätigen kann, um wen es sich handelt, kann es passieren, dass diese Leute den Beitrag nicht komplett lesen können.
|
||||
|
||||
Für Leute, die davon betroffen sind, schlagen wir vor, eine Zusammenfassung in Twitter-Länge zu erstellen mit mehr Details für Freunde, die den ganzen Beitrag sehen können.
|
||||
Für Leute, die davon betroffen sind, schlagen wir vor, eine Zusammenfassung in Twitter-Länge zu erstellen mit mehr Details für Freunde, die den ganzen Beitrag sehen können.
|
||||
|
||||
Dein Profil oder deine gesamte Friendica-Seite zu blockieren, hat außerdem ernsthafte Einflüsse auf deine Kommunikation mit GNU Social-Nutzern.
|
||||
Diese Netzwerke kommunizieren mit anderen über öffentliche Protokolle, die nicht authentifiziert werden.
|
||||
Um deine Beiträge zu sehen, müssen diese Netzwerke deine Beiträge als "unbekannte Webbesucher" ansehen.
|
||||
Wenn wir das erlauben, würde es dazu führen, das absolut jeder deine Beiträge sehen.
|
||||
Und du hast Friendica so eingestellt, das nicht zuzulassen.
|
||||
Dein Profil oder deine gesamte Friendica-Seite zu blockieren, hat außerdem ernsthafte Einflüsse auf deine Kommunikation mit GNU Social-Nutzern.
|
||||
Diese Netzwerke kommunizieren mit anderen über öffentliche Protokolle, die nicht authentifiziert werden.
|
||||
Um deine Beiträge zu sehen, müssen diese Netzwerke deine Beiträge als "unbekannte Webbesucher" ansehen.
|
||||
Wenn wir das erlauben, würde es dazu führen, das absolut jeder deine Beiträge sehen.
|
||||
Und du hast Friendica so eingestellt, das nicht zuzulassen.
|
||||
Beachte also, dass das Blockieren von unbekannten Besuchern auch dazu führen kann, dass öffentliche Netzwerke (wie GNU Social) und Newsfeed-Reader auch geblockt werden.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
Gruppen
|
||||
=====
|
||||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
|
||||
In Friendica kannst Du auch Gruppen und/oder Prominenten-Seiten erstellen.
|
||||
|
||||
Jede Seite in Friendica hat einen einmaligen Spitznamen.
|
||||
Das gilt für alle Seiten, unabhängig davon, ob es sich um normale Profile oder Forenseiten handelt.
|
||||
|
||||
Das Erste, was Du machen musst, um eine neue Gruppe zu kreieren, ist einen neuen Account zu erstellen.
|
||||
Bitte beachte, dass der Seitenadministrator die Registrierung neuer Accounts sperren oder an Bedingungen knüpfen kann.
|
||||
|
||||
Wenn Du einen zweiten Account in einem System erstellst und die gleiche Email-Adresse oder den gleichen OpenID-Account nutzt, kannst Du Dich zukünftig nur noch mit Deinem Spitznamen anmelden.
|
||||
|
||||
Gehe im neuen Account auf die "Einstellungs"-Seite und dort am Ende der Seite auf "Erweiterte Konto-/Seitentyp-Einstellungen".
|
||||
Normalerweise nutzt Du "Normales Konto" für einen normalen, persönlichen Account.
|
||||
Das ist die Standardeinstellung.
|
||||
Gruppenseiten bieten die Möglichkeit, Leute als Freund/Fan ohne Kontaktbestätigung zuzulassen.
|
||||
|
||||
Die Auswahl der Einstellung, die Du wählst, hängt davon ab, wie Du mit anderen Leuten auf Deiner Seite interagieren willst.
|
||||
Die "Marktschreier"-Einstellung (Soapbox) lässt den Seitenbesitzer die gesamte Kommunikation kontrollieren.
|
||||
Alles was Du schreibst, geht an alle Seitennutzer, aber es gibt keine Möglichkeit, zu interagieren.
|
||||
Diese Seite wird normalerweise für Ankündigungen oder die Kommunikation von Gemeinschaften genutzt.
|
||||
|
||||
Die normalste Einstellung ist das "Gruppe-/Promi-Konto".
|
||||
Diese erstellt eine Gruppenseite, in der alle Mitglieder frei miteinander interagieren können.
|
||||
|
||||
Der "Automatische Freunde Seite"-Account ist typischerweise für persönliche Profile, bei denen Du alle Freundschaftsanfragen automatisch bestätigen willst.
|
||||
|
||||
|
||||
**Multiple Gruppen verwalten**
|
||||
|
||||
Wir schlagen vor, dass Du eine Gruppe mit der gleichen Email-Adresse und dem gleichen Passwort wie bei Deinem normalen Account nutzt.
|
||||
Wenn Du das machst, findest Du einen neuen "Verwalten"-Link in der Menüleiste, über den Du einfach zwischen den Identitäten wechseln kannst.
|
||||
Du musst das nicht machen, die Alternative ist allerdings, Dich immer wieder aus- und wieder einzuloggen.
|
||||
Und das kann umständlich sein, wenn Du mehrere verschiedene Gruppen/Identitäten verwaltest.
|
||||
|
||||
Du kannst ebenso jemanden wählen, der Deine Gruppe verwaltet.
|
||||
Mach das, indem Du die [Delegations-Setup-Seite](/settings/delegation) besuchst.
|
||||
Dort wird Dir eine Liste an "Potentiellen Bevollmächtigen" angezeigt.
|
||||
Die Auswahl einer oder mehrerer Personen gibt diesen die Möglichkeit, Deine Gruppe zu verwalten.
|
||||
Sie können Kontakte, Profile und alle Inhalte Deines Accounts/deiner Seite bearbeiten.
|
||||
Bitte nutze diese Einstellung mit Vorsicht.
|
||||
Delegierte haben allerdings keine Möglichkeit, grundlegende Account-Einstellungen wie das Passwort oder den Seitentypen zu ändern bzw. den Account zu löschen.
|
||||
|
||||
|
||||
**Beiträge auf Community-Gruppen**
|
||||
|
||||
Wenn Du Mitglied einer Community-Gruppen bist, kannst Du die Gruppe in einem Beitrag hinzufügen/erwähnen, wenn Du die @-Erwähnung nutzt.
|
||||
Zum Beispiel würde @Fahrrad Deinen Beitrag neben den sonst ausgewählten Nutzern an alle Nutzer schicken, die in der Gruppe "Fahrrad" sind.
|
||||
Wenn Dein Beitrag privat ist, musst Du diese Gruppe explizit in den Zugriffsrechten des Beitrags auswählen **und** sie mit der @-Erwähnung ansprechen (was den Beitrag auf die Gruppenmitglieder erweitert).
|
||||
|
||||
Zusätzlich ist es möglich, Gruppen mit einem Ausrufezeichen zu adressieren.
|
||||
Im obigen Beispiel bedeutet dies, dass Du das Fahrrad-Gruppe per !Fahrrad erreichen würdest.
|
||||
Der Unterschied zum @ besteht darin, dass der Beitrag auschließlich über die Gruppe verbreitet wird und nicht an weitere Nutzer.
|
||||
Dies bedeutet auch, dass es nicht sinnvoll ist, mehrere Gruppen per ! in einem Beitrag zu adressieren, da nur eine der Gruppen den Beitrag verbreiten wird.
|
||||
|
||||
Du kannst außerdem via "Wall zu Wall" einen Beitrag auf der Community-Seite bzw. in der Community-Gruppe erstellen.
|
||||
|
||||
Kommentare, die Du an eine Community-Gruppe schickst, werden dem Originalbeitrag hinzugefügt.
|
||||
Eine weitere Gruppe mit dem @-Erwähnung anzusprechen, leitet den Beitrag nicht an dieses weiter, da die Verteilung der Kommentare komplett vom Originalbeitrag bestimmt wird.
|
||||
|
|
@ -14,9 +14,9 @@ Friendica - Dokumentation und Ressourcen
|
|||
* Du und andere Nutzer
|
||||
* [Konnektoren (Connectors)](help/Connectors)
|
||||
* [Freunde finden](help/Making-Friends)
|
||||
* [Circles und Privatsphäre](help/Circles-and-Privacy)
|
||||
* [Gruppen und Privatsphäre](help/Groups-and-Privacy)
|
||||
* [Tags und Erwähnungen](help/Tags-and-Mentions)
|
||||
* [Community-Gruppen](help/Groups)
|
||||
* [Community-Foren](help/Forums)
|
||||
* [Chats](help/Chats)
|
||||
* Weiterführende Informationen
|
||||
* [Account umziehen](help/Move-Account)
|
||||
|
|
@ -63,7 +63,7 @@ Friendica - Dokumentation und Ressourcen
|
|||
|
||||
* [Haupt-Webseite](https://friendi.ca)
|
||||
* Support Kanäle
|
||||
* Friendica Support Gruppe: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* Friendica Support Forum: [@helpers@forum.friendi.ca](https://forum.friendi.ca/~helpers)
|
||||
* [Mailing Listen Archiv](http://mailman.friendi.ca/mailman/listinfo/support-friendi.ca) zum Abonnieren der Liste eine E-Mail an ``support-request(at)friendi.ca?subject=subscribe`` senden
|
||||
* Chats der Friendica Community (die IRC, Matrix und XMPP Räume sind mit einer Brücke verbunden) Logs dieser öffentlichen Chaträume können [hier aus dem IRC](https://gnusociarg.nsupdate.info/2021/%23frie) und [hier aus der Matrix](https://view.matrix.org/alias/%23friendi.ca:matrix.org/) gefunden werden.
|
||||
* XMPP/Jabber MUC: support(at)forum.friendi.ca
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Diese Funktionalität benötigt ein wenig mehr als die normalen Blogs.
|
|||
Nicht jeder PHP/MySQL-Hosting-Anbieter kann Friendica unterstützen.
|
||||
Viele hingegen können es. Aber **bitte** prüfe die Voraussetzungen deines Servers vor der Installation.
|
||||
|
||||
Wenn dir Fehler während der Installation auffallen, sag uns bitte über [Helper](http://forum.friendi.ca/profile/helpers) oder die [Entwickler Gruppe](https://forum.friendi.ca/profile/developers) Bescheid oder [erstelle ein Issue](https://github.com/friendica/friendica/issues).
|
||||
Wenn dir Fehler während der Installation auffallen, sag uns bitte über [Helper](http://forum.friendi.ca/profile/helpers) oder das [Entwickler Forum](https://forum.friendi.ca/profile/developers) Bescheid oder [erstelle ein Issue](https://github.com/friendica/friendica/issues).
|
||||
Gib uns bitte so viele Infos zu deinem System, wie du kannst, und beschreibe den Fehler mit allen Details und Fehlermeldungen, so dass wir den Fehler zukünftig verhindern können.
|
||||
Aufgrund der großen Anzahl an verschiedenen Betriebssystemen und PHP-Plattformen haben wir nur geringe Kapazitäten, um deine PHP-Installation zu debuggen oder fehlende Module zu ersetzen, aber wir tun unser Bestes, um allgemeine Code-Fehler zu beheben.
|
||||
|
||||
|
|
@ -182,7 +182,7 @@ Sie werden stattdessen direkt in `config/local.config.php` gespeichert.
|
|||
Gehe im Anschluss in den Friendica-Hauptordner und führe den Kommandozeilen Befehl aus:
|
||||
|
||||
bin/console autoinstall [--savedb]
|
||||
|
||||
|
||||
#### B.3: Optionen
|
||||
|
||||
Alle Optionen werden in `config/local.config.php` gespeichert und überschreiben etwaige, zugehörige Umgebungsvariablen.
|
||||
|
|
|
|||
|
|
@ -3,75 +3,75 @@ Freunde finden
|
|||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
Freundschaft kann in Friendica viele verschiedene Bedeutungen annehmen.
|
||||
Aber lasst es uns einfach halten, du willst einfach mit jemandem befreundet sein.
|
||||
Freundschaft kann in Friendica viele verschiedene Bedeutungen annehmen.
|
||||
Aber lasst es uns einfach halten, du willst einfach mit jemandem befreundet sein.
|
||||
Wie machst du das?
|
||||
|
||||
Schau dir das Verzeichnis an.
|
||||
Das Verzeichnis ist in zwei Teile aufgeteilt.
|
||||
Wenn du auf den "Verzeichnis"-Button klickst, wirst du zunächst alle Mitglieder deines Servers sehen, die sich dazu entschlossen haben, angezeigt zu werden.
|
||||
Außerdem siehst du dort einen Link zum globalen Verzeichnis.
|
||||
Wenn du dich durch das globale Verzeichnis klickst, siehst du alle Nutzer weltweit auf allen Servern, die sich entschlossen haben, im Verzeichnis zu erscheinen.
|
||||
Du wirst außerdem den Link "Show Community Groups" sehen, welcher dich zu Gruppen und Fan-Seiten führt.
|
||||
Du verbindest dich mit Personen und Gruppen auf die gleiche Art, wobei Gruppen deine Anfrage automatisch annehmen, wohingegen ein Mensch dich erst manuell bestätigen muss.
|
||||
Schau dir das Verzeichnis an.
|
||||
Das Verzeichnis ist in zwei Teile aufgeteilt.
|
||||
Wenn du auf den "Verzeichnis"-Button klickst, wirst du zunächst alle Mitglieder deines Servers sehen, die sich dazu entschlossen haben, angezeigt zu werden.
|
||||
Außerdem siehst du dort einen Link zum globalen Verzeichnis.
|
||||
Wenn du dich durch das globale Verzeichnis klickst, siehst du alle Nutzer weltweit auf allen Servern, die sich entschlossen haben, im Verzeichnis zu erscheinen.
|
||||
Du wirst außerdem den Link "Show Community Forums" sehen, welcher dich zu Gruppen, Foren und Fan-Seiten führt.
|
||||
Du verbindest dich mit Personen, Gruppen und Foren auf die gleiche Art, wobei Gruppen und Foren deine Anfrage automatisch annehmen, wohingegen ein Mensch dich erst manuell bestätigen muss.
|
||||
|
||||
*Mit anderen Friendica-Nutzern verbinden*
|
||||
|
||||
Besuche ihr Profil.
|
||||
Direkt unter dem Profilfoto ist das Wort "Verbinden" (bzw. "Connect" in einem englischsprachigem Profil).
|
||||
Klicke drauf und du gelangst zur "Verbinden"-Seite.
|
||||
Dort wirst du nach deiner Identitätsadresse gefragt.
|
||||
Besuche ihr Profil.
|
||||
Direkt unter dem Profilfoto ist das Wort "Verbinden" (bzw. "Connect" in einem englischsprachigem Profil).
|
||||
Klicke drauf und du gelangst zur "Verbinden"-Seite.
|
||||
Dort wirst du nach deiner Identitätsadresse gefragt.
|
||||
Das ist nötig, damit die Seite dein Profil finden kann.
|
||||
|
||||
*Was kommt in die Box?*
|
||||
|
||||
Wenn deine Friendica-Seite "demo.friendica.com" heißt und dein Nutzername/Spitzname auf der Seite "bob" ist, dann wäre es "bob@demo.friendica.com".
|
||||
Wie du siehst, sieht es wie eine Email-Adresse aus.
|
||||
Das ist beabsichtigt, da sich die Leute das so leichter merken können.
|
||||
Wenn deine Friendica-Seite "demo.friendica.com" heißt und dein Nutzername/Spitzname auf der Seite "bob" ist, dann wäre es "bob@demo.friendica.com".
|
||||
Wie du siehst, sieht es wie eine Email-Adresse aus.
|
||||
Das ist beabsichtigt, da sich die Leute das so leichter merken können.
|
||||
Du *kannst* auch die URL deiner Startseite eintragen, wie z.B. "http://demo.friendica.com/profile/bob", aber der Email-Adressen-Stil ist einfacher.
|
||||
|
||||
Wenn du die "Verbinden"-Seite bestätigt hast, kommst du zurück zu deiner Seite, um dort die Anfrage zu bestätigen.
|
||||
Wenn du die "Verbinden"-Seite bestätigt hast, kommst du zurück zu deiner Seite, um dort die Anfrage zu bestätigen.
|
||||
Wenn du das gemacht hast, können beide Seiten miteinander kommunizieren, um den Prozess abzuschließen (sobald dein neuer Freund die Anfrage bestätigt hat).
|
||||
|
||||
Wenn du bereits die Identitäts-Adresse einer Person kennst, kannst du diese auch direkt in das "Verbinden"-Feld auf deiner "Kontakte"-Seite eintragen.
|
||||
Wenn du bereits die Identitäts-Adresse einer Person kennst, kannst du diese auch direkt in das "Verbinden"-Feld auf deiner "Kontakte"-Seite eintragen.
|
||||
Dies wird dich durch einen ähnlichen Prozess leiten.
|
||||
|
||||
|
||||
**Alternative Netzwerke**
|
||||
|
||||
Du kannst deine oder andere Identitäts-Adressen ebenfalls nutzen, um über verschiedene Netzwerke hinweg Freundschaften aufzubauen.
|
||||
Die Liste möglicher Netzwerke steigt immer weiter.
|
||||
Wenn du z.B. "bob" auf quitter.se (eine GNU Social-Seite) kennst, dann kannst du bob@quitter.se auf deiner "Kontakt"-Seite verbinden. (Oder du kannst die URL von Bobs quitter.se-Seite eintragen, wenn du es wünscht).
|
||||
Tatsächlich kannst du jedem und jeder Website folgen, der/die einen Syndication-Feed (RSS/Atom etc.) zur Verfügung stellt.
|
||||
Wenn wir einen Informationsstrom und einen Namen dazu finden, können wir auch versuchen, uns damit zu verbinden.
|
||||
Du kannst deine oder andere Identitäts-Adressen ebenfalls nutzen, um über verschiedene Netzwerke hinweg Freundschaften aufzubauen.
|
||||
Die Liste möglicher Netzwerke steigt immer weiter.
|
||||
Wenn du z.B. "bob" auf quitter.se (eine GNU Social-Seite) kennst, dann kannst du bob@quitter.se auf deiner "Kontakt"-Seite verbinden. (Oder du kannst die URL von Bobs quitter.se-Seite eintragen, wenn du es wünscht).
|
||||
Tatsächlich kannst du jedem und jeder Website folgen, der/die einen Syndication-Feed (RSS/Atom etc.) zur Verfügung stellt.
|
||||
Wenn wir einen Informationsstrom und einen Namen dazu finden, können wir auch versuchen, uns damit zu verbinden.
|
||||
|
||||
Wenn du deine Email-Postfachverbindung auf deiner Einstellungsseite konfiguriert hast, dann kannst du die Email-Adresse jeder Person eintragen, die dir schon eine Nachricht an dein Postfach geschickt hat und bereits in deinem sozialen Stream erscheint.
|
||||
Wenn du deine Email-Postfachverbindung auf deiner Einstellungsseite konfiguriert hast, dann kannst du die Email-Adresse jeder Person eintragen, die dir schon eine Nachricht an dein Postfach geschickt hat und bereits in deinem sozialen Stream erscheint.
|
||||
Du kannst diesen Personen außerdem von Friendica aus antworten.
|
||||
|
||||
Leute können sich ebenfalls von anderen Netzwerken aus mit dir befreunden.
|
||||
Ein Freund von dir hat einen GNU Social-Account und kann sich mit dir befreunden, indem er deine Identitäts-Adresse in seine GNU Social-Verbinden-Dialogbox einträgt.
|
||||
Ein ähnlicher Mechanismus ist für Diaspora-Nutzer vorhanden, indem deine Identitäts-Adresse in ihre Suchleiste eingegeben wird.
|
||||
Leute können sich ebenfalls von anderen Netzwerken aus mit dir befreunden.
|
||||
Ein Freund von dir hat einen GNU Social-Account und kann sich mit dir befreunden, indem er deine Identitäts-Adresse in seine GNU Social-Verbinden-Dialogbox einträgt.
|
||||
Ein ähnlicher Mechanismus ist für Diaspora-Nutzer vorhanden, indem deine Identitäts-Adresse in ihre Suchleiste eingegeben wird.
|
||||
|
||||
Beachte: Manche GNU Social-Versionen benötigen die volle URL deines Profils und funktionieren möglicherweise nicht mit der Identitäts-Adresse.
|
||||
|
||||
Wenn jemand eine Freundschaftsanfrage schickt, erhältst du eine Benachrichtigung.
|
||||
Du musst dann diese Anfrage bestätigen, um die Freundschaftsanfrage abzuschließen.
|
||||
Wenn jemand eine Freundschaftsanfrage schickt, erhältst du eine Benachrichtigung.
|
||||
Du musst dann diese Anfrage bestätigen, um die Freundschaftsanfrage abzuschließen.
|
||||
|
||||
Einige Netzwerke erlauben es, Nachrichten zu schicken, ohne befreundet zu sein oder deine Bestätigung zu benötigen.
|
||||
Einige Netzwerke erlauben es, Nachrichten zu schicken, ohne befreundet zu sein oder deine Bestätigung zu benötigen.
|
||||
Friendica erlaubt dies in der Standardeinstellung nicht, da es zu Spam führen kann.
|
||||
|
||||
Wenn du eine Freundschaftsanfrage von einem anderen Friendica-Nutzer erhältst, dann hast du die Möglichkeit, diesen als "Fan" oder "Freund" einzutragen.
|
||||
Ein Fan kann sehen, was du schreibst und auch private Kommunikation sehen, die du zu diesen sendest, aber nicht umgekehrt.
|
||||
Wenn du eine Freundschaftsanfrage von einem anderen Friendica-Nutzer erhältst, dann hast du die Möglichkeit, diesen als "Fan" oder "Freund" einzutragen.
|
||||
Ein Fan kann sehen, was du schreibst und auch private Kommunikation sehen, die du zu diesen sendest, aber nicht umgekehrt.
|
||||
Als Freund kannst du in beide Richtungen kommunizieren.
|
||||
|
||||
Diaspora nutzt eine andere Terminologie mit der Unterteilung in "mit dir teilen" und "Freund".
|
||||
|
||||
Sobald ihr Freunde geworden seid, dir die Person aber permanent Spam oder sinnlose Informationen schickt, dann kannst du diese "ignorieren" - ohne die Freundschaft komplett zu beenden oder denjenigen zu zeigen, dass du nicht daran interessiert bist, was diese Person sagt.
|
||||
In verschiedener Hinsicht sind diese Personen wie "Fans", aber sie wissen es nicht.
|
||||
Sobald ihr Freunde geworden seid, dir die Person aber permanent Spam oder sinnlose Informationen schickt, dann kannst du diese "ignorieren" - ohne die Freundschaft komplett zu beenden oder denjenigen zu zeigen, dass du nicht daran interessiert bist, was diese Person sagt.
|
||||
In verschiedener Hinsicht sind diese Personen wie "Fans", aber sie wissen es nicht.
|
||||
Sie denken, sie sind als Freunde eingetragen.
|
||||
|
||||
Du kannst auch eine Person "blocken".
|
||||
Das blockt die komplette Kommunikation mit dieser Person.
|
||||
Du kannst auch eine Person "blocken".
|
||||
Das blockt die komplette Kommunikation mit dieser Person.
|
||||
Sie können zwar weiterhin öffentliche Beiträge sehen, wie auch jeder andere in der Welt, allerdings können sie nicht direkt mit dir kommunizieren.
|
||||
|
||||
Du kannst Freunde löschen, egal wie der Freundschaftsstatus ist, was dazu führt, dass alles, was mit dieser Person verbunden ist, von deiner Webseite gelöscht wird.
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
Gruppen und Seiten
|
||||
Gruppen und Seiten
|
||||
==========
|
||||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
Hier siehst Du das globale Verzeichnis.
|
||||
Hier siehst Du das globale Verzeichnis.
|
||||
Wenn Du Dich mal verirrt hast, kannst Du diesen Link klicken und wieder hierher kommen.
|
||||
|
||||
Auf dieser Seite findest Du eine Zusammenstellung von Gruppen.
|
||||
Gruppen sind keine realen Personen.
|
||||
Auf dieser Seite findest Du eine Zusammenstellung von Gruppen, Foren und Promi-Seiten.
|
||||
Gruppen sind keine realen Personen.
|
||||
Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet.
|
||||
Du musst nicht unsicher sein, ob Du jemandem zu nahe trittst, wenn Du Dich so ohne weiteres mit einer Gruppe verbindest; es handelt sich eben nicht um reale Personen.
|
||||
|
||||
Wenn Du Dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in Deinem "Netzwerk"-Tab.
|
||||
Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eines der Gruppenmitglieder persönlich hinzuzufügen.
|
||||
Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen.
|
||||
Du findest Personen Deines Interesses, anstatt Fremde hinzuzufügen.
|
||||
Suche Dir einfach eine Gruppe und füge sie so hinzu, wie Du auch normale Freunde hinzufügst.
|
||||
Es gibt eine Menge Gruppen.
|
||||
Wenn Du Dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in Deinem "Netzwerk"-Tab.
|
||||
Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eines der Gruppenmitglieder persönlich hinzuzufügen.
|
||||
Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen.
|
||||
Du findest Personen Deines Interesses, anstatt Fremde hinzuzufügen.
|
||||
Suche Dir einfach eine Gruppe und füge sie so hinzu, wie Du auch normale Freunde hinzufügst.
|
||||
Es gibt eine Menge Gruppen.
|
||||
Solltest Du beim Stöbern durch die vielen Gruppen nicht wieder hierher zurück finden, so nutze einfach den Link oben auf dieser Seite.
|
||||
|
||||
Wenn Du einige Gruppen hinzugefügt hast, gehe <a href="help/Quick-Start-andfinally">weiter zum nächsten Schritt</a>.
|
||||
|
||||
<iframe src="https://dir.friendica.social/group" width="950" height="600"></iframe>
|
||||
<iframe src="https://dir.friendica.social/forum" width="950" height="600"></iframe>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -7,37 +7,37 @@ Wie viele andere soziale Netzwerke benutzt auch Friendica eine spezielle Schreib
|
|||
|
||||
**Erwähnungen**
|
||||
|
||||
Personen werden "getaggt", indem du das "@"-Zeichen vor den Namen schreibst.
|
||||
|
||||
Personen **in deiner Kontaktliste** werden „getaggt“, indem du das “@“-Zeichen vor den Namen schreibst.
|
||||
Personen werden "getaggt", indem du das "@"-Zeichen vor den Namen schreibst.
|
||||
|
||||
Personen **in deiner Kontaktliste** werden „getaggt“, indem du das “@“-Zeichen vor den Namen schreibst.
|
||||
|
||||
* <i>@mike</i> - deutet auf eine Person hin, die im Netzwerk den Namen "mike" nutzt
|
||||
* <i>@mike_macgirvin</i> - deutet auf eine Person hin, die sich im Netzwerk "Mike Macgirvin" nennt. Beachte, dass Leerzeichen in Tags nicht genutzt werden können.
|
||||
* <i>@mike+151</i> - diese Schreibweise deutet auf eine Person hin, die "mike" heißt und deren Kontakt-Identitäts-Nummer 151 ist. Bei der Eingabe erscheint direkt ein Auswahlmenü, sodass du diese Nummer nicht selbst kennen musst.
|
||||
|
||||
Personen, die in einem anderen Netzwerk sind oder die sich **NICHT in deiner Kontaktliste** befinden, werden wie folgt getaggt:
|
||||
* <i>@mike+151</i> - diese Schreibweise deutet auf eine Person hin, die "mike" heißt und deren Kontakt-Identitäts-Nummer 151 ist. Bei der Eingabe erscheint direkt ein Auswahlmenü, sodass du diese Nummer nicht selbst kennen musst.
|
||||
|
||||
Personen, die in einem anderen Netzwerk sind oder die sich **NICHT in deiner Kontaktliste** befinden, werden wie folgt getaggt:
|
||||
|
||||
* <i>@mike@macgirvin.com</i> - diese Schreibweise wird "Fernerwähnung" (remote mention)genannt und kann nur im Email-Stil geschrieben werden, nicht als Internetadresse/URL.
|
||||
|
||||
|
||||
Wenn das System ungewollte Erwähnungen nicht blockiert, erhält diese Person eine Mitteilung oder nimmt direkt an der Diskussion teil, wenn es sich um einen öffentlichen Beitrag handelt.
|
||||
Bitte beachte, dass Friendica eingehende "Erwähnungs"-Nachrichten von Personen blockt, die du nicht zu deinem Profil hinzugefügt hast.
|
||||
Wenn das System ungewollte Erwähnungen nicht blockiert, erhält diese Person eine Mitteilung oder nimmt direkt an der Diskussion teil, wenn es sich um einen öffentlichen Beitrag handelt.
|
||||
Bitte beachte, dass Friendica eingehende "Erwähnungs"-Nachrichten von Personen blockt, die du nicht zu deinem Profil hinzugefügt hast.
|
||||
Diese Maßnahme dient dazu, Spam zu vermeiden.
|
||||
|
||||
"Fernerwähnungen" werden durch das OStatus-Protokoll übermittelt.
|
||||
Dieses Protokoll wird von Friendica, GNU Social und anderen Systemen genutzt, ist allerdings derzeit nicht in Diaspora eingebaut.
|
||||
"Fernerwähnungen" werden durch das OStatus-Protokoll übermittelt.
|
||||
Dieses Protokoll wird von Friendica, GNU Social und anderen Systemen genutzt, ist allerdings derzeit nicht in Diaspora eingebaut.
|
||||
|
||||
Friendica unterscheidet bei Tags nicht zwischen Personen und Gruppen (einige andere Netzwerke nutzen "!circle", um solche zu markieren).
|
||||
Friendica unterscheidet bei Tags nicht zwischen Personen und Gruppen (einige andere Netzwerke nutzen "!gruppe", um solche zu markieren).
|
||||
|
||||
|
||||
**Thematische Tags**
|
||||
|
||||
Thematische Tags werden durch eine "#" gekennzeichnet.
|
||||
Dieses Zeichen erstellen einen Link zur allgemeinen Seitensuche mit dem ausgewählten Begriff.
|
||||
So wird z.B. #Autos zu einer Suche führen, die alle Beiträge deiner Seite umfasst, die dieses Wort erwähnen.
|
||||
Thematische Tags haben generell eine Mindestlänge von 3 Stellen.
|
||||
Kürzere Suchbegriffe finden meist keine Suchergebnisse, wobei dieses abhängig von der Datenbankeinstellung ist.
|
||||
Tags mit einem Leerzeichen werden, wie es auch bei Namen der Fall ist, durch einen Unterstrich gekennzeichnet.
|
||||
Es ist hingegen nicht möglich, Tags zu erstellen, deren gesuchtes Wort einen Unterstrich enthält.
|
||||
Thematische Tags werden durch eine "#" gekennzeichnet.
|
||||
Dieses Zeichen erstellen einen Link zur allgemeinen Seitensuche mit dem ausgewählten Begriff.
|
||||
So wird z.B. #Autos zu einer Suche führen, die alle Beiträge deiner Seite umfasst, die dieses Wort erwähnen.
|
||||
Thematische Tags haben generell eine Mindestlänge von 3 Stellen.
|
||||
Kürzere Suchbegriffe finden meist keine Suchergebnisse, wobei dieses abhängig von der Datenbankeinstellung ist.
|
||||
Tags mit einem Leerzeichen werden, wie es auch bei Namen der Fall ist, durch einen Unterstrich gekennzeichnet.
|
||||
Es ist hingegen nicht möglich, Tags zu erstellen, deren gesuchtes Wort einen Unterstrich enthält.
|
||||
|
||||
Thematische Tags werden auch dann nicht verlinkt, wenn sie nur aus Nummern bestehen, wie z.B. #1. Wenn du einen numerischen Tag nutzen willst, füge bitte einen Beschreibungstext hinzu wie z.B. #2012_Wahl.
|
||||
|
|
|
|||
26
doc/de/groupsandpages.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
Gruppen und Seiten
|
||||
==========
|
||||
|
||||
* [Zur Startseite der Hilfe](help)
|
||||
|
||||
Hier siehst du das globale Verzeichnis.
|
||||
Wenn du dich mal verirrt hast, kannst du <a href = "help/groupsandpages">diesen Link klicken</a> und wieder hierher kommen.
|
||||
|
||||
Auf dieser Seite findest du eine Zusammenstellung von Gruppen, Foren und bekannten Seiten.
|
||||
Gruppen sind keine realen Personen.
|
||||
Sich mit diesen zu verbinden ist, als wenn man jemanden auf Facebook "liked" ("gefällt mir") oder wenn man sich in einem Forum anmeldet.
|
||||
Habe keine Sorge, falls du dich unbehaglich fühlst, wenn du dich einer neuen Person vorstellen sollst, da es sich nicht um Personen handelt.
|
||||
|
||||
Wenn du dich mit einer Gruppe verbindest, erscheinen alle Nachrichten der Gruppe in deinem "Netzwerk"-Tab.
|
||||
Du kannst diese Beiträge kommentieren oder selbst in der Gruppe schreiben, ohne eine der Gruppenmitglieder persönlich hinzuzufügen.
|
||||
Das ist ein großartiger Weg, dynamisch neue Freunde zu gewinnen.
|
||||
Du findest Personen, die du magst, anstatt Fremde hinzuzufügen.
|
||||
Suche dir einfach eine Gruppe und füge sie so hinzu, wie du auch normale Freunde hinzufügst.
|
||||
Es gibt eine Menge Gruppen und möglicherweise findest du nicht wieder zu dieser Seite zurück.
|
||||
In diesem Fall nutze einfach den Link oben auf dieser Seite.
|
||||
|
||||
Wenn du einige Gruppen hinzugefügt hast, gehe <a href="help/andfinally">weiter zum nächsten Schritt</a>.
|
||||
|
||||
<iframe src="https://dir.friendica.social/home" width="950" height="600"></iframe>
|
||||
|
||||
|
||||
BIN
images/default-group-mm.png
Normal file
|
After Width: | Height: | Size: 598 B |
|
Before Width: | Height: | Size: 543 B After Width: | Height: | Size: 543 B |
|
Before Width: | Height: | Size: 852 B After Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
IMAGES=add.png edit.png gear.png info.png menu.png \
|
||||
notify_off.png star.png delete.png feed.png circle.png \
|
||||
notify_off.png star.png delete.png feed.png group.png \
|
||||
lock.png notice.png notify_on.png user.png link.png \
|
||||
play.png plugin.png unlock.png zip.png audio.png video.png \
|
||||
image.png text.png
|
||||
|
|
@ -10,7 +10,7 @@ DESTS=10/ 16/ 22/ 48/ \
|
|||
$(addprefix 16/, $(IMAGES)) \
|
||||
$(addprefix 22/, $(IMAGES)) \
|
||||
$(addprefix 48/, $(IMAGES))
|
||||
|
||||
|
||||
all: $(DESTS)
|
||||
|
||||
%/:
|
||||
|
|
@ -18,7 +18,7 @@ all: $(DESTS)
|
|||
|
||||
10/%.png: %.png
|
||||
convert $< -resize 10x10 $@
|
||||
|
||||
|
||||
16/%.png: %.png
|
||||
convert $< -resize 16x16 $@
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
|
|
@ -30,9 +30,6 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
|
|||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$dice = (new Dice())->addRules(include __DIR__ . '/static/dependencies.config.php');
|
||||
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
|
||||
$addonLoader = $dice->create(\Friendica\Core\Addon\Capability\ICanLoadAddons::class);
|
||||
$dice = $dice->addRules($addonLoader->getActiveAddonConfig('dependencies'));
|
||||
$dice = $dice->addRule(Friendica\App\Mode::class, ['call' => [['determineRunMode', [false, $_SERVER], Dice::CHAIN_CALL]]]);
|
||||
|
||||
\Friendica\DI::init($dice);
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ function item_edit(int $uid, array $request, bool $preview, string $return_path)
|
|||
}
|
||||
|
||||
$post['edit'] = $post;
|
||||
$post['file'] = Post\Category::getTextByURIId($post['uri-id'], $post['uid']);
|
||||
$post['file'] = Post\Category::getTextByURIId($post['uri-id'], $post['uid']);
|
||||
|
||||
Post\Media::deleteByURIId($post['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE, Post\Media::HTML]);
|
||||
$post = item_process($post, $request, $preview, $return_path);
|
||||
|
|
@ -266,7 +266,6 @@ function item_process(array $post, array $request, bool $preview, string $return
|
|||
$post['uri-id'] = -1;
|
||||
$post['author-network'] = Protocol::DFRN;
|
||||
$post['author-updated'] = '';
|
||||
$post['author-alias'] = '';
|
||||
$post['author-gsid'] = 0;
|
||||
$post['author-uri-id'] = ItemURI::getIdByURI($post['author-link']);
|
||||
$post['owner-updated'] = '';
|
||||
|
|
@ -275,7 +274,7 @@ function item_process(array $post, array $request, bool $preview, string $return
|
|||
$post['body'] = BBCode::removeSharedData(Item::setHashtags($post['body']));
|
||||
$post['writable'] = true;
|
||||
|
||||
$o = DI::conversation()->render([$post], Conversation::MODE_SEARCH, false, true);
|
||||
$o = DI::conversation()->create([$post], Conversation::MODE_SEARCH, false, true);
|
||||
|
||||
System::jsonExit(['preview' => $o]);
|
||||
}
|
||||
|
|
@ -341,7 +340,7 @@ function item_content(App $a)
|
|||
|
||||
$args = DI::args();
|
||||
|
||||
if (!$args->has(2)) {
|
||||
if (!$args->has(3)) {
|
||||
throw new HTTPException\BadRequestException();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ function notes_content(App $a, bool $update = false)
|
|||
|
||||
$count = count($notes);
|
||||
|
||||
$o .= DI::conversation()->render($notes, Conversation::MODE_NOTES, $update);
|
||||
$o .= DI::conversation()->create($notes, Conversation::MODE_NOTES, $update);
|
||||
}
|
||||
|
||||
$o .= $pager->renderMinimal($count);
|
||||
|
|
|
|||
108
mod/photos.php
|
|
@ -117,7 +117,7 @@ function photos_init(App $a)
|
|||
|
||||
$tpl = Renderer::getMarkupTemplate("photos_head.tpl");
|
||||
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[
|
||||
'$ispublic' => DI::l10n()->t('everybody')
|
||||
]);
|
||||
}
|
||||
|
|
@ -163,14 +163,14 @@ function photos_post(App $a)
|
|||
|
||||
$aclFormatter = DI::aclFormatter();
|
||||
$str_contact_allow = isset($_REQUEST['contact_allow']) ? $aclFormatter->toString($_REQUEST['contact_allow']) : $owner_record['allow_cid'] ?? '';
|
||||
$str_circle_allow = isset($_REQUEST['circle_allow']) ? $aclFormatter->toString($_REQUEST['circle_allow']) : $owner_record['allow_gid'] ?? '';
|
||||
$str_group_allow = isset($_REQUEST['group_allow']) ? $aclFormatter->toString($_REQUEST['group_allow']) : $owner_record['allow_gid'] ?? '';
|
||||
$str_contact_deny = isset($_REQUEST['contact_deny']) ? $aclFormatter->toString($_REQUEST['contact_deny']) : $owner_record['deny_cid'] ?? '';
|
||||
$str_circle_deny = isset($_REQUEST['circle_deny']) ? $aclFormatter->toString($_REQUEST['circle_deny']) : $owner_record['deny_gid'] ?? '';
|
||||
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $owner_record['deny_gid'] ?? '';
|
||||
|
||||
$visibility = $_REQUEST['visibility'] ?? '';
|
||||
if ($visibility === 'public') {
|
||||
// The ACL selector introduced in version 2019.12 sends ACL input data even when the Public visibility is selected
|
||||
$str_contact_allow = $str_circle_allow = $str_contact_deny = $str_circle_deny = '';
|
||||
$str_contact_allow = $str_group_allow = $str_contact_deny = $str_group_deny = '';
|
||||
} else if ($visibility === 'custom') {
|
||||
// Since we know from the visibility parameter the item should be private, we have to prevent the empty ACL
|
||||
// case that would make it public. So we always append the author's contact id to the allowed contacts.
|
||||
|
|
@ -214,15 +214,13 @@ function photos_post(App $a)
|
|||
|
||||
// get the list of photos we are about to delete
|
||||
if ($visitor) {
|
||||
$r = DBA::toArray(DBA::p(
|
||||
"SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `contact-id` = ? AND `uid` = ? AND `album` = ?",
|
||||
$r = DBA::toArray(DBA::p("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `contact-id` = ? AND `uid` = ? AND `album` = ?",
|
||||
$visitor,
|
||||
$page_owner_uid,
|
||||
$album
|
||||
));
|
||||
} else {
|
||||
$r = DBA::toArray(DBA::p(
|
||||
"SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `uid` = ? AND `album` = ?",
|
||||
$r = DBA::toArray(DBA::p("SELECT distinct(`resource-id`) as `rid` FROM `photo` WHERE `uid` = ? AND `album` = ?",
|
||||
DI::userSession()->getLocalUserId(),
|
||||
$album
|
||||
));
|
||||
|
|
@ -260,6 +258,7 @@ function photos_post(App $a)
|
|||
// same as above but remove single photo
|
||||
if ($visitor) {
|
||||
$condition = ['contact-id' => $visitor, 'uid' => $page_owner_uid, 'resource-id' => DI::args()->getArgv()[3]];
|
||||
|
||||
} else {
|
||||
$condition = ['uid' => DI::userSession()->getLocalUserId(), 'resource-id' => DI::args()->getArgv()[3]];
|
||||
}
|
||||
|
|
@ -339,7 +338,7 @@ function photos_post(App $a)
|
|||
$photo = $photos[0];
|
||||
$ext = $phototypes[$photo['type']];
|
||||
Photo::update(
|
||||
['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny],
|
||||
['desc' => $desc, 'album' => $albname, 'allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow, 'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny],
|
||||
['resource-id' => $resource_id, 'uid' => $page_owner_uid]
|
||||
);
|
||||
|
||||
|
|
@ -406,7 +405,7 @@ function photos_post(App $a)
|
|||
if (strpos($tag, '@') === 0) {
|
||||
$profile = '';
|
||||
$contact = null;
|
||||
$name = substr($tag, 1);
|
||||
$name = substr($tag,1);
|
||||
|
||||
if ((strpos($name, '@')) || (strpos($name, 'http://'))) {
|
||||
$newname = $name;
|
||||
|
|
@ -442,15 +441,13 @@ function photos_post(App $a)
|
|||
if ($tagcid) {
|
||||
$contact = DBA::selectFirst('contact', [], ['id' => $tagcid, 'uid' => $page_owner_uid]);
|
||||
} else {
|
||||
$newname = str_replace('_', ' ', $name);
|
||||
$newname = str_replace('_',' ',$name);
|
||||
|
||||
//select someone from this user's contacts by name
|
||||
$contact = DBA::selectFirst('contact', [], ['name' => $newname, 'uid' => $page_owner_uid]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
//select someone by attag or nick and the name passed in
|
||||
$contact = DBA::selectFirst(
|
||||
'contact',
|
||||
[],
|
||||
$contact = DBA::selectFirst('contact', [],
|
||||
['(`attag` = ? OR `nick` = ?) AND `uid` = ?', $name, $name, $page_owner_uid],
|
||||
['order' => ['attag' => true]]
|
||||
);
|
||||
|
|
@ -692,13 +689,11 @@ function photos_content(App $a)
|
|||
|
||||
$uploader = '';
|
||||
|
||||
$ret = [
|
||||
'post_url' => 'profile/' . $user['nickname'] . '/photos',
|
||||
'addon_text' => $uploader,
|
||||
'default_upload' => true
|
||||
];
|
||||
$ret = ['post_url' => 'profile/' . $user['nickname'] . '/photos',
|
||||
'addon_text' => $uploader,
|
||||
'default_upload' => true];
|
||||
|
||||
Hook::callAll('photo_upload_form', $ret);
|
||||
Hook::callAll('photo_upload_form',$ret);
|
||||
|
||||
$default_upload_box = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_box.tpl'), []);
|
||||
$default_upload_submit = Renderer::replaceMacros(Renderer::getMarkupTemplate('photos_default_uploader_submit.tpl'), [
|
||||
|
|
@ -710,7 +705,7 @@ function photos_content(App $a)
|
|||
$umf_bytes = Strings::getBytesFromShorthand(ini_get('upload_max_filesize'));
|
||||
|
||||
// Per Friendica definition a value of '0' means unlimited:
|
||||
if ($mis_bytes == 0) {
|
||||
If ($mis_bytes == 0) {
|
||||
$mis_bytes = INF;
|
||||
}
|
||||
|
||||
|
|
@ -724,7 +719,7 @@ function photos_content(App $a)
|
|||
|
||||
$aclselect_e = ($visitor ? '' : ACL::getFullSelectorHTML(DI::page(), $a->getLoggedInUserId()));
|
||||
|
||||
$o .= Renderer::replaceMacros($tpl, [
|
||||
$o .= Renderer::replaceMacros($tpl,[
|
||||
'$pagename' => DI::l10n()->t('Upload Photos'),
|
||||
'$sessid' => session_id(),
|
||||
'$usage' => $usage_message,
|
||||
|
|
@ -752,7 +747,7 @@ function photos_content(App $a)
|
|||
if ($datatype === 'album') {
|
||||
// if $datum is not a valid hex, redirect to the default page
|
||||
if (is_null($datum) || !Strings::isHex($datum)) {
|
||||
DI::baseUrl()->redirect('photos/' . $user['nickname'] . '/album');
|
||||
DI::baseUrl()->redirect('photos/' . $user['nickname']. '/album');
|
||||
}
|
||||
$album = hex2bin($datum);
|
||||
|
||||
|
|
@ -761,8 +756,7 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
$total = 0;
|
||||
$r = DBA::toArray(DBA::p(
|
||||
"SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ?
|
||||
$r = DBA::toArray(DBA::p("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `album` = ?
|
||||
AND `scale` <= 4 $sql_extra GROUP BY `resource-id`",
|
||||
$owner_uid,
|
||||
$album
|
||||
|
|
@ -781,8 +775,7 @@ function photos_content(App $a)
|
|||
$order = 'DESC';
|
||||
}
|
||||
|
||||
$r = DBA::toArray(DBA::p(
|
||||
"SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`,
|
||||
$r = DBA::toArray(DBA::p("SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`,
|
||||
ANY_VALUE(`type`) AS `type`, max(`scale`) AS `scale`, ANY_VALUE(`desc`) as `desc`,
|
||||
ANY_VALUE(`created`) as `created`
|
||||
FROM `photo` WHERE `uid` = ? AND `album` = ?
|
||||
|
|
@ -816,7 +809,7 @@ function photos_content(App $a)
|
|||
|
||||
$album_e = $album;
|
||||
|
||||
$o .= Renderer::replaceMacros($edit_tpl, [
|
||||
$o .= Renderer::replaceMacros($edit_tpl,[
|
||||
'$nametext' => DI::l10n()->t('New album name: '),
|
||||
'$nickname' => $user['nickname'],
|
||||
'$album' => $album_e,
|
||||
|
|
@ -850,16 +843,16 @@ function photos_content(App $a)
|
|||
$desc_e = $rr['desc'];
|
||||
|
||||
$photos[] = [
|
||||
'id' => $rr['id'],
|
||||
'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2, 4),
|
||||
'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id']
|
||||
'id' => $rr['id'],
|
||||
'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4),
|
||||
'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id']
|
||||
. ($order_field === 'created' ? '?order=created' : ''),
|
||||
'title' => DI::l10n()->t('View Photo'),
|
||||
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' . $ext,
|
||||
'alt' => $imgalt_e,
|
||||
'desc' => $desc_e,
|
||||
'ext' => $ext,
|
||||
'hash' => $rr['resource-id'],
|
||||
'src' => 'photo/' . $rr['resource-id'] . '-' . $rr['scale'] . '.' .$ext,
|
||||
'alt' => $imgalt_e,
|
||||
'desc'=> $desc_e,
|
||||
'ext' => $ext,
|
||||
'hash'=> $rr['resource-id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -877,6 +870,7 @@ function photos_content(App $a)
|
|||
]);
|
||||
|
||||
return $o;
|
||||
|
||||
}
|
||||
|
||||
// Display one photo
|
||||
|
|
@ -961,7 +955,7 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('photo_edit_head.tpl');
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl, [
|
||||
DI::page()['htmlhead'] .= Renderer::replaceMacros($tpl,[
|
||||
'$prevlink' => $prevlink,
|
||||
'$nextlink' => $nextlink
|
||||
]);
|
||||
|
|
@ -973,7 +967,7 @@ function photos_content(App $a)
|
|||
if ($nextlink) {
|
||||
$nextlink = [$nextlink, '<div class="icon next"></div>'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count($ph) == 1) {
|
||||
|
|
@ -1013,12 +1007,12 @@ function photos_content(App $a)
|
|||
}
|
||||
|
||||
$photo = [
|
||||
'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
|
||||
'title' => DI::l10n()->t('View Full Size'),
|
||||
'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'),
|
||||
'height' => $hires['height'],
|
||||
'width' => $hires['width'],
|
||||
'album' => $hires['album'],
|
||||
'href' => 'photo/' . $hires['resource-id'] . '-' . $hires['scale'] . '.' . $phototypes[$hires['type']],
|
||||
'title'=> DI::l10n()->t('View Full Size'),
|
||||
'src' => 'photo/' . $lores['resource-id'] . '-' . $lores['scale'] . '.' . $phototypes[$lores['type']] . '?_u=' . DateTimeFormat::utcNow('ymdhis'),
|
||||
'height' => $hires['height'],
|
||||
'width' => $hires['width'],
|
||||
'album' => $hires['album'],
|
||||
'filename' => $hires['filename'],
|
||||
];
|
||||
|
||||
|
|
@ -1085,12 +1079,12 @@ function photos_content(App $a)
|
|||
|
||||
$edit = Renderer::replaceMacros($edit_tpl, [
|
||||
'$id' => $ph[0]['id'],
|
||||
'$album' => ['albname', DI::l10n()->t('New album name'), $album_e, ''],
|
||||
'$album' => ['albname', DI::l10n()->t('New album name'), $album_e,''],
|
||||
'$caption' => ['desc', DI::l10n()->t('Caption'), $caption_e, ''],
|
||||
'$tags' => ['newtag', DI::l10n()->t('Add a Tag'), "", DI::l10n()->t('Example: @bob, @Barbara_Jensen, @jim@example.com, #California, #camping')],
|
||||
'$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'), 0, '', true],
|
||||
'$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"), 1, ''],
|
||||
'$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"), 2, ''],
|
||||
'$rotate_none' => ['rotate', DI::l10n()->t('Do not rotate'),0,'', true],
|
||||
'$rotate_cw' => ['rotate', DI::l10n()->t("Rotate CW \x28right\x29"),1,''],
|
||||
'$rotate_ccw' => ['rotate', DI::l10n()->t("Rotate CCW \x28left\x29"),2,''],
|
||||
|
||||
'$nickname' => $user['nickname'],
|
||||
'$resource_id' => $ph[0]['resource-id'],
|
||||
|
|
@ -1185,7 +1179,7 @@ function photos_content(App $a)
|
|||
$qcomment = $words ? explode("\n", $words) : [];
|
||||
}
|
||||
|
||||
$comments .= Renderer::replaceMacros($cmnt_tpl, [
|
||||
$comments .= Renderer::replaceMacros($cmnt_tpl,[
|
||||
'$return_path' => '',
|
||||
'$jsreload' => $return_path,
|
||||
'$id' => $link_item['id'],
|
||||
|
|
@ -1209,19 +1203,13 @@ function photos_content(App $a)
|
|||
$activity = DI::activity();
|
||||
|
||||
if (($activity->match($item['verb'], Activity::LIKE) ||
|
||||
$activity->match($item['verb'], Activity::DISLIKE)) &&
|
||||
($item['gravity'] != Item::GRAVITY_PARENT)
|
||||
) {
|
||||
$activity->match($item['verb'], Activity::DISLIKE)) &&
|
||||
($item['gravity'] != Item::GRAVITY_PARENT)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias']
|
||||
];
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$profile_url = Contact::magicLinkByContact($author);
|
||||
if (strpos($profile_url, 'contact/redir/') === 0) {
|
||||
$sparkle = ' sparkle';
|
||||
|
|
@ -1240,7 +1228,7 @@ function photos_content(App $a)
|
|||
$title_e = $item['title'];
|
||||
$body_e = BBCode::convertForUriId($item['uri-id'], $item['body']);
|
||||
|
||||
$comments .= Renderer::replaceMacros($template, [
|
||||
$comments .= Renderer::replaceMacros($template,[
|
||||
'$id' => $item['id'],
|
||||
'$profile_url' => $profile_url,
|
||||
'$name' => $item['author-name'],
|
||||
|
|
|
|||
14
src/App.php
|
|
@ -64,7 +64,7 @@ class App
|
|||
{
|
||||
const PLATFORM = 'Friendica';
|
||||
const CODENAME = 'Giant Rhubarb';
|
||||
const VERSION = '2023.09-dev';
|
||||
const VERSION = '2023.05';
|
||||
|
||||
// Allow themes to control internal parameters
|
||||
// by changing App values in theme.php
|
||||
|
|
@ -581,7 +581,6 @@ class App
|
|||
// Force SSL redirection
|
||||
if ($this->config->get('system', 'force_ssl') &&
|
||||
(empty($server['HTTPS']) || $server['HTTPS'] === 'off') &&
|
||||
(empty($server['HTTP_X_FORWARDED_PROTO']) || $server['HTTP_X_FORWARDED_PROTO'] === 'http') &&
|
||||
!empty($server['REQUEST_METHOD']) &&
|
||||
$server['REQUEST_METHOD'] === 'GET') {
|
||||
System::externalRedirect($this->baseURL . '/' . $this->args->getQueryString());
|
||||
|
|
@ -695,9 +694,6 @@ class App
|
|||
$module = $router->getModule();
|
||||
}
|
||||
|
||||
// Display can change depending on the requested language, so it shouldn't be cached whole
|
||||
header('Vary: Accept-Language', false);
|
||||
|
||||
// Processes data from GET requests
|
||||
$httpinput = $httpInput->process();
|
||||
$input = array_merge($httpinput['variables'], $httpinput['files'], $request ?? $_REQUEST);
|
||||
|
|
@ -706,13 +702,11 @@ class App
|
|||
$timestamp = microtime(true);
|
||||
$response = $module->run($httpException, $input);
|
||||
$this->profiler->set(microtime(true) - $timestamp, 'content');
|
||||
|
||||
// Wrapping HTML responses in the theme template
|
||||
if ($response->getHeaderLine(ICanCreateResponses::X_HEADER) === ICanCreateResponses::TYPE_HTML) {
|
||||
$response = $page->run($this, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
|
||||
$page->run($this, $this->baseURL, $this->args, $this->mode, $response, $this->l10n, $this->profiler, $this->config, $pconfig, $nav, $this->session->getLocalUserId());
|
||||
} else {
|
||||
$page->exit($response);
|
||||
}
|
||||
|
||||
$page->exit($response);
|
||||
} catch (HTTPException $e) {
|
||||
$httpException->rawContent($e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,14 +127,4 @@ class BaseURL extends Uri implements UriInterface
|
|||
$redirectTo = $this->__toString() . '/' . ltrim($toUrl, '/');
|
||||
System::externalRedirect($redirectTo);
|
||||
}
|
||||
|
||||
public function isLocalUrl(string $url): bool
|
||||
{
|
||||
return strpos(Strings::normaliseLink($url), Strings::normaliseLink((string)$this)) === 0;
|
||||
}
|
||||
|
||||
public function isLocalUri(UriInterface $uri): bool
|
||||
{
|
||||
return $this->isLocalUrl((string)$uri);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ use Friendica\Network\HTTPException;
|
|||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Strings;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
/**
|
||||
|
|
@ -245,12 +244,10 @@ class Page implements ArrayAccess
|
|||
*/
|
||||
$this->page['htmlhead'] = Renderer::replaceMacros($tpl, [
|
||||
'$l10n' => [
|
||||
'delitem' => $l10n->t('Delete this item?'),
|
||||
'blockAuthor' => $l10n->t("Block this author? They won't be able to follow you nor see your public posts, and you won't be able to see their posts and their notifications."),
|
||||
'ignoreAuthor' => $l10n->t("Ignore this author? You won't be able to see their posts and their notifications."),
|
||||
'collapseAuthor' => $l10n->t("Collapse this author's posts?"),
|
||||
'ignoreServer' => $l10n->t("Ignore this author's server?"),
|
||||
'ignoreServerDesc' => $l10n->t("You won't see any content from this server including reshares in your Network page, the community pages and individual conversations."),
|
||||
'delitem' => $l10n->t('Delete this item?'),
|
||||
'blockAuthor' => $l10n->t('Block this author? They won\'t be able to follow you nor see your public posts, and you won\'t be able to see their posts and their notifications.'),
|
||||
'ignoreAuthor' => $l10n->t('Ignore this author? You won\'t be able to see their posts and their notifications.'),
|
||||
'collapseAuthor' => $l10n->t('Collapse this author\'s posts?'),
|
||||
|
||||
'likeError' => $l10n->t('Like not successful'),
|
||||
'dislikeError' => $l10n->t('Dislike not successful'),
|
||||
|
|
@ -502,6 +499,20 @@ class Page implements ArrayAccess
|
|||
$this->page['nav'] = $nav->getHtml();
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// Build the page - now that we have all the components
|
||||
if (isset($_GET["mode"]) && (($_GET["mode"] == "raw") || ($_GET["mode"] == "minimal"))) {
|
||||
$doc = new DOMDocument();
|
||||
|
|
@ -572,10 +583,6 @@ class Page implements ArrayAccess
|
|||
// Used as is in view/php/default.php
|
||||
$lang = $l10n->getCurrentLang();
|
||||
|
||||
ob_start();
|
||||
require_once $template;
|
||||
$body = ob_get_clean();
|
||||
|
||||
return $response->withBody(Utils::streamFor($body));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -251,7 +251,6 @@ abstract class BaseModule implements ICanHandleRequests
|
|||
throw $e;
|
||||
}
|
||||
|
||||
$this->response->setStatus($e->getCode(), $e->getMessage());
|
||||
$this->response->addContent($httpException->content($e));
|
||||
} finally {
|
||||
$this->profiler->set(microtime(true) - $timestamp, 'content');
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ class Notifications extends BaseCollection
|
|||
/**
|
||||
* @return Notification
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function current()
|
||||
{
|
||||
return parent::current();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
namespace Friendica\Console;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
|
||||
use Friendica\Core\KeyValueStorage\Capabilities\IManageKeyValuePairs;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Update;
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ namespace Friendica\Console;
|
|||
|
||||
use Asika\SimpleConsole\CommandArgsException;
|
||||
use Friendica\Model\APContact;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Protocol\ActivityPub\Transmitter;
|
||||
use Friendica\Protocol\Relay as ProtocolRelay;
|
||||
|
||||
/**
|
||||
* tool to control the list of ActivityPub relay servers from the CLI
|
||||
|
|
@ -90,9 +90,13 @@ HELP;
|
|||
}
|
||||
|
||||
if ((count($this->args) == 1) && ($this->getArgument(0) == 'list')) {
|
||||
foreach (ProtocolRelay::getList(['url']) as $contact) {
|
||||
$contacts = $this->dba->select('apcontact', ['url'],
|
||||
["`type` IN (?, ?) AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` = ?)",
|
||||
'Application', 'Service', 0, Contact::FRIEND]);
|
||||
while ($contact = $this->dba->fetch($contacts)) {
|
||||
$this->out($contact['url']);
|
||||
}
|
||||
$this->dba->close($contacts);
|
||||
} elseif (count($this->args) == 0) {
|
||||
throw new CommandArgsException('too few arguments');
|
||||
} elseif (count($this->args) == 1) {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ class Avatar
|
|||
return $fields;
|
||||
}
|
||||
|
||||
$filename = self::getFilename($contact['url'], $avatar);
|
||||
$filename = self::getFilename($contact['url']);
|
||||
$timestamp = time();
|
||||
|
||||
$fields['blurhash'] = $image->getBlurHash();
|
||||
|
|
@ -120,7 +120,7 @@ class Avatar
|
|||
return $fields;
|
||||
}
|
||||
|
||||
$filename = self::getFilename($contact['url'], $contact['avatar']);
|
||||
$filename = self::getFilename($contact['url']);
|
||||
$timestamp = time();
|
||||
|
||||
$fields['photo'] = self::storeAvatarCache($image, $filename, Proxy::PIXEL_SMALL, $timestamp);
|
||||
|
|
@ -130,9 +130,9 @@ class Avatar
|
|||
return $fields;
|
||||
}
|
||||
|
||||
private static function getFilename(string $url, string $host): string
|
||||
private static function getFilename(string $url): string
|
||||
{
|
||||
$guid = Item::guidFromUri($url, $host);
|
||||
$guid = Item::guidFromUri($url);
|
||||
|
||||
return substr($guid, 0, 2) . '/' . substr($guid, 3, 2) . '/' . substr($guid, 5, 3) . '/' .
|
||||
substr($guid, 9, 2) .'/' . substr($guid, 11, 2) . '/' . substr($guid, 13, 4). '/' . substr($guid, 18) . '-';
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Contact;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
|
||||
class Header
|
||||
{
|
||||
/** @var IManageConfigValues */
|
||||
private $config;
|
||||
|
||||
public function __construct(IManageConfigValues $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Mastodon banner path relative to the Friendica folder.
|
||||
*
|
||||
* Ensures the existence of a leading slash.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getMastodonBannerPath(): string
|
||||
{
|
||||
return '/' . ltrim($this->config->get('api', 'mastodon_banner'), '/');
|
||||
}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ use Friendica\Model\Contact;
|
|||
* @property-read int $rel
|
||||
* @property-read string $info
|
||||
* @property-read bool $notifyNewPosts
|
||||
* @property-read int $remoteSelf
|
||||
* @property-read bool $isRemoteSelf
|
||||
* @property-read int $fetchFurtherInformation
|
||||
* @property-read string $ffiKeywordDenylist
|
||||
* @property-read bool $subhub
|
||||
|
|
@ -47,16 +47,6 @@ use Friendica\Model\Contact;
|
|||
*/
|
||||
class LocalRelationship extends \Friendica\BaseEntity
|
||||
{
|
||||
// Fetch Further Information options, not a binary flag
|
||||
const FFI_NONE = 0;
|
||||
const FFI_INFORMATION = 1;
|
||||
const FFI_KEYWORD = 3;
|
||||
const FFI_BOTH = 2;
|
||||
|
||||
const MIRROR_DEACTIVATED = 0;
|
||||
const MIRROR_OWN_POST = 2;
|
||||
const MIRROR_NATIVE_RESHARE = 3;
|
||||
|
||||
/** @var int */
|
||||
protected $userId;
|
||||
/** @var int */
|
||||
|
|
@ -77,9 +67,9 @@ class LocalRelationship extends \Friendica\BaseEntity
|
|||
protected $info;
|
||||
/** @var bool */
|
||||
protected $notifyNewPosts;
|
||||
/** @var int One of MIRROR_* */
|
||||
protected $remoteSelf;
|
||||
/** @var int One of FFI_* */
|
||||
/** @var bool */
|
||||
protected $isRemoteSelf;
|
||||
/** @var int */
|
||||
protected $fetchFurtherInformation;
|
||||
/** @var string */
|
||||
protected $ffiKeywordDenylist;
|
||||
|
|
@ -94,7 +84,7 @@ class LocalRelationship extends \Friendica\BaseEntity
|
|||
/** @var int */
|
||||
protected $priority;
|
||||
|
||||
public function __construct(int $userId, int $contactId, bool $blocked = false, bool $ignored = false, bool $collapsed = false, bool $hidden = false, bool $pending = false, int $rel = Contact::NOTHING, string $info = '', bool $notifyNewPosts = false, int $remoteSelf = self::MIRROR_DEACTIVATED, int $fetchFurtherInformation = self::FFI_NONE, string $ffiKeywordDenylist = '', bool $subhub = false, string $hubVerify = '', string $protocol = Protocol::PHANTOM, ?int $rating = null, ?int $priority = null)
|
||||
public function __construct(int $userId, int $contactId, bool $blocked = false, bool $ignored = false, bool $collapsed = false, bool $hidden = false, bool $pending = false, int $rel = Contact::NOTHING, string $info = '', bool $notifyNewPosts = false, bool $isRemoteSelf = false, int $fetchFurtherInformation = 0, string $ffiKeywordDenylist = '', bool $subhub = false, string $hubVerify = '', string $protocol = Protocol::PHANTOM, ?int $rating = null, ?int $priority = null)
|
||||
{
|
||||
$this->userId = $userId;
|
||||
$this->contactId = $contactId;
|
||||
|
|
@ -106,7 +96,7 @@ class LocalRelationship extends \Friendica\BaseEntity
|
|||
$this->rel = $rel;
|
||||
$this->info = $info;
|
||||
$this->notifyNewPosts = $notifyNewPosts;
|
||||
$this->remoteSelf = $remoteSelf;
|
||||
$this->isRemoteSelf = $isRemoteSelf;
|
||||
$this->fetchFurtherInformation = $fetchFurtherInformation;
|
||||
$this->ffiKeywordDenylist = $ffiKeywordDenylist;
|
||||
$this->subhub = $subhub;
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ class LocalRelationship extends BaseFactory implements ICanCreateFromTableRow
|
|||
$row['rel'] ?? Contact::NOTHING,
|
||||
$row['info'] ?? '',
|
||||
$row['notify_new_posts'] ?? false,
|
||||
$row['remote_self'] ?? Entity\LocalRelationship::MIRROR_DEACTIVATED,
|
||||
$row['fetch_further_information'] ?? Entity\LocalRelationship::FFI_NONE,
|
||||
$row['remote_self'] ?? false,
|
||||
$row['fetch_further_information'] ?? 0,
|
||||
$row['ffi_keyword_denylist'] ?? '',
|
||||
$row['subhub'] ?? false,
|
||||
$row['hub-verify'] ?? '',
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class LocalRelationship extends \Friendica\BaseRepository
|
|||
'rel' => $localRelationship->rel,
|
||||
'info' => $localRelationship->info,
|
||||
'notify_new_posts' => $localRelationship->notifyNewPosts,
|
||||
'remote_self' => $localRelationship->remoteSelf,
|
||||
'remote_self' => $localRelationship->isRemoteSelf,
|
||||
'fetch_further_information' => $localRelationship->fetchFurtherInformation,
|
||||
'ffi_keyword_denylist' => $localRelationship->ffiKeywordDenylist,
|
||||
'subhub' => $localRelationship->subhub,
|
||||
|
|
|
|||
|
|
@ -220,8 +220,7 @@ class ContactSelector
|
|||
'GNU Social' => 'gnu-social', 'gnusocial' => 'gnu-social', 'hubzilla' => 'hubzilla',
|
||||
'mastodon' => 'mastodon', 'peertube' => 'peertube', 'pixelfed' => 'pixelfed',
|
||||
'pleroma' => 'pleroma', 'red' => 'hubzilla', 'redmatrix' => 'hubzilla',
|
||||
'socialhome' => 'social-home', 'wordpress' => 'wordpress', 'lemmy' => 'users',
|
||||
'firefish' => 'fire', 'calckey' => 'calculator', 'kbin' => 'check'];
|
||||
'socialhome' => 'social-home', 'wordpress' => 'wordpress'];
|
||||
|
||||
$search = array_keys($nets);
|
||||
$replace = array_values($nets);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ use Friendica\Database\DBA;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Item as ItemModel;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\Verb;
|
||||
|
|
@ -46,8 +45,6 @@ use Friendica\Network\HTTPException\InternalServerErrorException;
|
|||
use Friendica\Object\Post as PostObject;
|
||||
use Friendica\Object\Thread;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\User\Settings\Entity\UserGServer;
|
||||
use Friendica\User\Settings\Repository;
|
||||
use Friendica\Util\Crypto;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Profiler;
|
||||
|
|
@ -93,25 +90,22 @@ class Conversation
|
|||
private $mode;
|
||||
/** @var IHandleUserSessions */
|
||||
private $session;
|
||||
/** @var Repository\UserGServer */
|
||||
private $userGServer;
|
||||
|
||||
public function __construct(Repository\UserGServer $userGServer, LoggerInterface $logger, Profiler $profiler, Activity $activity, L10n $l10n, Item $item, Arguments $args, BaseURL $baseURL, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, App\Page $page, App\Mode $mode, App $app, IHandleUserSessions $session)
|
||||
public function __construct(LoggerInterface $logger, Profiler $profiler, Activity $activity, L10n $l10n, Item $item, Arguments $args, BaseURL $baseURL, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, App\Page $page, App\Mode $mode, App $app, IHandleUserSessions $session)
|
||||
{
|
||||
$this->activity = $activity;
|
||||
$this->item = $item;
|
||||
$this->config = $config;
|
||||
$this->mode = $mode;
|
||||
$this->baseURL = $baseURL;
|
||||
$this->profiler = $profiler;
|
||||
$this->logger = $logger;
|
||||
$this->l10n = $l10n;
|
||||
$this->args = $args;
|
||||
$this->pConfig = $pConfig;
|
||||
$this->page = $page;
|
||||
$this->app = $app;
|
||||
$this->session = $session;
|
||||
$this->userGServer = $userGServer;
|
||||
$this->activity = $activity;
|
||||
$this->item = $item;
|
||||
$this->config = $config;
|
||||
$this->mode = $mode;
|
||||
$this->baseURL = $baseURL;
|
||||
$this->profiler = $profiler;
|
||||
$this->logger = $logger;
|
||||
$this->l10n = $l10n;
|
||||
$this->args = $args;
|
||||
$this->pConfig = $pConfig;
|
||||
$this->page = $page;
|
||||
$this->app = $app;
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -160,8 +154,7 @@ class Conversation
|
|||
'uid' => 0,
|
||||
'id' => $activity['author-id'],
|
||||
'network' => $activity['author-network'],
|
||||
'url' => $activity['author-link'],
|
||||
'alias' => $activity['author-alias'],
|
||||
'url' => $activity['author-link']
|
||||
];
|
||||
$url = Contact::magicLinkByContact($author);
|
||||
if (strpos($url, 'contact/redir/') === 0) {
|
||||
|
|
@ -279,7 +272,7 @@ class Conversation
|
|||
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends', '<button type="button" %2$s>%1$d people</button> attend', $total, $spanatts);
|
||||
break;
|
||||
case 'attendno':
|
||||
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t attend', '<button type="button" %2$s>%1$d people</button> don\'t attend', $total, $spanatts);
|
||||
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> doesn\'t attend','<button type="button" %2$s>%1$d people</button> don\'t attend', $total, $spanatts);
|
||||
break;
|
||||
case 'attendmaybe':
|
||||
$phrase = $this->l10n->tt('<button type="button" %2$s>%1$d person</button> attends maybe', '<button type="button" %2$s>%1$d people</button> attend maybe', $total, $spanatts);
|
||||
|
|
@ -373,7 +366,6 @@ class Conversation
|
|||
'$eduline' => $this->l10n->t('Underline'),
|
||||
'$edquote' => $this->l10n->t('Quote'),
|
||||
'$edemojis' => $this->l10n->t('Add emojis'),
|
||||
'$contentwarn' => $this->l10n->t('Content Warning'),
|
||||
'$edcode' => $this->l10n->t('Code'),
|
||||
'$edimg' => $this->l10n->t('Image'),
|
||||
'$edurl' => $this->l10n->t('Link'),
|
||||
|
|
@ -444,17 +436,17 @@ class Conversation
|
|||
* The $mode parameter decides between the various renderings and also
|
||||
* figures out how to determine page owner and other contextual items
|
||||
* that are based on unique features of the calling module.
|
||||
* @param array $items An array of Posts
|
||||
* @param string $mode One of self::MODE_*
|
||||
* @param bool $update Asynchronous update rendering
|
||||
* @param bool $preview Post preview (no actual database record)
|
||||
* @param string $order Either "received" or "commented"
|
||||
* @param array $items
|
||||
* @param string $mode
|
||||
* @param $update @TODO Which type?
|
||||
* @param bool $preview
|
||||
* @param string $order
|
||||
* @param int $uid
|
||||
* @return string
|
||||
* @throws ImagickException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function render(array $items, string $mode, bool $update = false, bool $preview = false, string $order = 'commented', int $uid = 0): string
|
||||
public function create(array $items, string $mode, $update, bool $preview = false, string $order = 'commented', int $uid = 0): string
|
||||
{
|
||||
$this->profiler->startRecording('rendering');
|
||||
|
||||
|
|
@ -465,14 +457,12 @@ class Conversation
|
|||
|
||||
$live_update_div = '';
|
||||
|
||||
$userGservers = $this->userGServer->listIgnoredByUser($this->session->getLocalUserId());
|
||||
$blocklist = $this->getBlocklist();
|
||||
|
||||
$ignoredGsids = array_map(function (UserGServer $userGServer) {
|
||||
return $userGServer->gsid;
|
||||
}, $userGservers->getArrayCopy());
|
||||
$previewing = (($preview) ? ' preview ' : '');
|
||||
|
||||
if ($mode === self::MODE_NETWORK) {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode, $ignoredGsids);
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
if (!$update) {
|
||||
/*
|
||||
* The special div is needed for liveUpdate to kick in for this page.
|
||||
|
|
@ -498,7 +488,7 @@ class Conversation
|
|||
. "'; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === self::MODE_PROFILE) {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode, $ignoredGsids);
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
|
||||
if (!$update) {
|
||||
$tab = !empty($_GET['tab']) ? trim($_GET['tab']) : 'posts';
|
||||
|
|
@ -523,7 +513,7 @@ class Conversation
|
|||
. "; var netargs = '?f='; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === self::MODE_DISPLAY) {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode, $ignoredGsids);
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-display"></div>' . "\r\n"
|
||||
|
|
@ -531,7 +521,7 @@ class Conversation
|
|||
. "</script>";
|
||||
}
|
||||
} elseif ($mode === self::MODE_COMMUNITY) {
|
||||
$items = $this->addChildren($items, true, $order, $uid, $mode, $ignoredGsids);
|
||||
$items = $this->addChildren($items, true, $order, $uid, $mode);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-community"></div>' . "\r\n"
|
||||
|
|
@ -542,12 +532,12 @@ class Conversation
|
|||
. "'; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === self::MODE_CONTACTS) {
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode, $ignoredGsids);
|
||||
$items = $this->addChildren($items, false, $order, $uid, $mode);
|
||||
|
||||
if (!$update) {
|
||||
$live_update_div = '<div id="live-contact"></div>' . "\r\n"
|
||||
. "<script> var profile_uid = -1; var netargs = '" . substr($this->args->getCommand(), 8)
|
||||
. "?f='; </script>\r\n";
|
||||
."?f='; </script>\r\n";
|
||||
}
|
||||
} elseif ($mode === self::MODE_SEARCH) {
|
||||
$live_update_div = '<div id="live-search"></div>' . "\r\n";
|
||||
|
|
@ -564,14 +554,240 @@ class Conversation
|
|||
|
||||
$items = $cb['items'];
|
||||
|
||||
$conv_responses = [
|
||||
'like' => [],
|
||||
'dislike' => [],
|
||||
'attendyes' => [],
|
||||
'attendno' => [],
|
||||
'attendmaybe' => [],
|
||||
'announce' => [],
|
||||
];
|
||||
|
||||
if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'hide_dislike')) {
|
||||
unset($conv_responses['dislike']);
|
||||
}
|
||||
|
||||
// array with html for each thread (parent+comments)
|
||||
$threads = [];
|
||||
$threadsid = -1;
|
||||
|
||||
$page_template = Renderer::getMarkupTemplate("conversation.tpl");
|
||||
$formSecurityToken = BaseModule::getFormSecurityToken('contact_action');
|
||||
|
||||
$threads = $this->getThreadList($items, $mode, $preview, $page_dropping, $formSecurityToken);
|
||||
if (!empty($items)) {
|
||||
if (in_array($mode, [self::MODE_COMMUNITY, self::MODE_CONTACTS, self::MODE_PROFILE])) {
|
||||
$writable = true;
|
||||
} else {
|
||||
$writable = $items[0]['writable'] || ($items[0]['uid'] == 0) && in_array($items[0]['network'], Protocol::FEDERATED);
|
||||
}
|
||||
|
||||
if (in_array($mode, [self::MODE_FILED, self::MODE_SEARCH, self::MODE_CONTACT_POSTS])) {
|
||||
$page_template = Renderer::getMarkupTemplate('conversation.tpl');
|
||||
} else {
|
||||
$page_template = Renderer::getMarkupTemplate('threaded_conversation.tpl');
|
||||
if (!$this->session->getLocalUserId()) {
|
||||
$writable = false;
|
||||
}
|
||||
|
||||
if (in_array($mode, [self::MODE_FILED, self::MODE_SEARCH, self::MODE_CONTACT_POSTS])) {
|
||||
|
||||
/*
|
||||
* "New Item View" on network page or search page results
|
||||
* - just loop through the items and format them minimally for display
|
||||
*/
|
||||
|
||||
$tpl = 'search_item.tpl';
|
||||
|
||||
$uriids = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (in_array($item['uri-id'], $uriids)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uriids[] = $item['uri-id'];
|
||||
|
||||
if (!$this->item->isVisibleActivity($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array($item['author-id'], $blocklist)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$threadsid++;
|
||||
|
||||
// prevent private email from leaking.
|
||||
if ($item['network'] === Protocol::MAIL && $this->session->getLocalUserId() != $item['uid']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$profile_name = $item['author-name'];
|
||||
if (!empty($item['author-link']) && empty($item['author-name'])) {
|
||||
$profile_name = $item['author-link'];
|
||||
}
|
||||
|
||||
$tags = Tag::populateFromItem($item);
|
||||
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'], 'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$profile_link = Contact::magicLinkByContact($author);
|
||||
|
||||
$sparkle = '';
|
||||
if (strpos($profile_link, 'contact/redir/') === 0) {
|
||||
$sparkle = ' sparkle';
|
||||
}
|
||||
|
||||
$locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => ''];
|
||||
Hook::callAll('render_location', $locate);
|
||||
$location_html = $locate['html'] ?: Strings::escapeHtml($locate['location'] ?: $locate['coord'] ?: '');
|
||||
|
||||
$this->item->localize($item);
|
||||
if ($mode === self::MODE_FILED) {
|
||||
$dropping = true;
|
||||
} else {
|
||||
$dropping = false;
|
||||
}
|
||||
|
||||
$drop = [
|
||||
'dropping' => $dropping,
|
||||
'pagedrop' => $page_dropping,
|
||||
'select' => $this->l10n->t('Select'),
|
||||
'delete' => $this->l10n->t('Delete'),
|
||||
];
|
||||
|
||||
$likebuttons = [
|
||||
'like' => null,
|
||||
'dislike' => null,
|
||||
'share' => null,
|
||||
'announce' => null,
|
||||
];
|
||||
|
||||
if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'hide_dislike')) {
|
||||
unset($likebuttons['dislike']);
|
||||
}
|
||||
|
||||
$body_html = ItemModel::prepareBody($item, true, $preview);
|
||||
|
||||
[$categories, $folders] = $this->item->determineCategoriesTerms($item, $this->session->getLocalUserId());
|
||||
|
||||
if (!empty($item['title'])) {
|
||||
$title = $item['title'];
|
||||
} elseif (!empty($item['content-warning']) && $this->pConfig->get($this->session->getLocalUserId(), 'system', 'disable_cw', false)) {
|
||||
$title = ucfirst($item['content-warning']);
|
||||
} else {
|
||||
$title = '';
|
||||
}
|
||||
|
||||
if (!empty($item['featured'])) {
|
||||
$pinned = $this->l10n->t('Pinned item');
|
||||
} else {
|
||||
$pinned = '';
|
||||
}
|
||||
|
||||
$tmp_item = [
|
||||
'template' => $tpl,
|
||||
'id' => ($preview ? 'P0' : $item['id']),
|
||||
'guid' => ($preview ? 'Q0' : $item['guid']),
|
||||
'commented' => $item['commented'],
|
||||
'received' => $item['received'],
|
||||
'created_date' => $item['created'],
|
||||
'uriid' => $item['uri-id'],
|
||||
'network' => $item['network'],
|
||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network'], $item['author-gsid']),
|
||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link'], $item['author-gsid']),
|
||||
'linktitle' => $this->l10n->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
||||
'profile_url' => $profile_link,
|
||||
'item_photo_menu_html' => $this->item->photoMenu($item, $formSecurityToken),
|
||||
'name' => $profile_name,
|
||||
'sparkle' => $sparkle,
|
||||
'lock' => false,
|
||||
'thumb' => $this->baseURL->remove($this->item->getAuthorAvatar($item)),
|
||||
'title' => $title,
|
||||
'body_html' => $body_html,
|
||||
'tags' => $tags['tags'],
|
||||
'hashtags' => $tags['hashtags'],
|
||||
'mentions' => $tags['mentions'],
|
||||
'implicit_mentions' => $tags['implicit_mentions'],
|
||||
'txt_cats' => $this->l10n->t('Categories:'),
|
||||
'txt_folders' => $this->l10n->t('Filed under:'),
|
||||
'has_cats' => ((count($categories)) ? 'true' : ''),
|
||||
'has_folders' => ((count($folders)) ? 'true' : ''),
|
||||
'categories' => $categories,
|
||||
'folders' => $folders,
|
||||
'text' => strip_tags($body_html),
|
||||
'localtime' => DateTimeFormat::local($item['created'], 'r'),
|
||||
'utc' => DateTimeFormat::utc($item['created'], 'c'),
|
||||
'ago' => (($item['app']) ? $this->l10n->t('%s from %s', Temporal::getRelativeDate($item['created']), $item['app']) : Temporal::getRelativeDate($item['created'])),
|
||||
'location_html' => $location_html,
|
||||
'indent' => '',
|
||||
'owner_name' => '',
|
||||
'owner_url' => '',
|
||||
'owner_photo' => $this->baseURL->remove($this->item->getOwnerAvatar($item)),
|
||||
'plink' => ItemModel::getPlink($item),
|
||||
'edpost' => false,
|
||||
'pinned' => $pinned,
|
||||
'isstarred' => 'unstarred',
|
||||
'star' => false,
|
||||
'drop' => $drop,
|
||||
'vote' => $likebuttons,
|
||||
'like_html' => '',
|
||||
'dislike_html ' => '',
|
||||
'comment_html' => '',
|
||||
'conv' => ($preview ? '' : ['href' => 'display/' . $item['guid'], 'title' => $this->l10n->t('View in context')]),
|
||||
'previewing' => $previewing,
|
||||
'wait' => $this->l10n->t('Please wait'),
|
||||
'thread_level' => 1,
|
||||
];
|
||||
|
||||
$arr = ['item' => $item, 'output' => $tmp_item];
|
||||
Hook::callAll('display_item', $arr);
|
||||
|
||||
$threads[$threadsid]['id'] = $item['id'];
|
||||
$threads[$threadsid]['network'] = $item['network'];
|
||||
$threads[$threadsid]['items'] = [$arr['output']];
|
||||
}
|
||||
} else {
|
||||
// Normal View
|
||||
$page_template = Renderer::getMarkupTemplate("threaded_conversation.tpl");
|
||||
|
||||
$conv = new Thread($mode, $preview, $writable);
|
||||
|
||||
/*
|
||||
* get all the topmost parents
|
||||
* this shouldn't be needed, as we should have only them in our array
|
||||
* But for now, this array respects the old style, just in case
|
||||
*/
|
||||
foreach ($items as $item) {
|
||||
if (in_array($item['author-id'], $blocklist)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Can we put this after the visibility check?
|
||||
$this->builtinActivityPuller($item, $conv_responses);
|
||||
|
||||
// Only add what is visible
|
||||
if ($item['network'] === Protocol::MAIL && $this->session->getLocalUserId() != $item['uid']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->item->isVisibleActivity($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/// @todo Check if this call is needed or not
|
||||
$arr = ['item' => $item];
|
||||
Hook::callAll('display_item', $arr);
|
||||
|
||||
$item['pagedrop'] = $page_dropping;
|
||||
|
||||
if ($item['gravity'] == ItemModel::GRAVITY_PARENT) {
|
||||
$item_object = new PostObject($item);
|
||||
$conv->addParent($item_object);
|
||||
}
|
||||
}
|
||||
|
||||
$threads = $conv->getTemplateData($conv_responses, $formSecurityToken);
|
||||
if (!$threads) {
|
||||
$this->logger->info('[ERROR] conversation : Failed to get template data.');
|
||||
$threads = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$o = Renderer::replaceMacros($page_template, [
|
||||
|
|
@ -589,95 +805,6 @@ class Conversation
|
|||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $items
|
||||
* @param string $mode One of self::MODE_*
|
||||
* @param bool $preview
|
||||
* @param bool $pagedrop Whether to enable the user to select the thread for deletion
|
||||
* @param string $formSecurityToken A 'contact_action' form security token
|
||||
* @return array
|
||||
* @throws InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public function getThreadList(array $items, string $mode, bool $preview, bool $pagedrop, string $formSecurityToken): array
|
||||
{
|
||||
if (!$items) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (in_array($mode, [self::MODE_FILED, self::MODE_SEARCH, self::MODE_CONTACT_POSTS])) {
|
||||
$threads = $this->getContextLessThreadList($items, $mode, $preview, $pagedrop, $formSecurityToken);
|
||||
} else {
|
||||
$conv_responses = [
|
||||
'like' => [],
|
||||
'dislike' => [],
|
||||
'attendyes' => [],
|
||||
'attendno' => [],
|
||||
'attendmaybe' => [],
|
||||
'announce' => [],
|
||||
];
|
||||
|
||||
if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'hide_dislike')) {
|
||||
unset($conv_responses['dislike']);
|
||||
}
|
||||
|
||||
if (in_array($mode, [self::MODE_COMMUNITY, self::MODE_CONTACTS, self::MODE_PROFILE])) {
|
||||
$writable = true;
|
||||
} else {
|
||||
$writable = $items[0]['writable'] || ($items[0]['uid'] == 0) && in_array($items[0]['network'], Protocol::FEDERATED);
|
||||
}
|
||||
|
||||
if (!$this->session->getLocalUserId()) {
|
||||
$writable = false;
|
||||
}
|
||||
|
||||
// Normal View
|
||||
$conv = new Thread($mode, $preview, $writable);
|
||||
|
||||
/*
|
||||
* get all the topmost parents
|
||||
* this shouldn't be needed, as we should have only them in our array
|
||||
* 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);
|
||||
|
||||
// Only add what is visible
|
||||
if ($item['network'] === Protocol::MAIL && $this->session->getLocalUserId() != $item['uid']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$this->item->isVisibleActivity($item)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/// @todo Check if this call is needed or not
|
||||
$arr = ['item' => $item];
|
||||
Hook::callAll('display_item', $arr);
|
||||
|
||||
$item['pagedrop'] = $pagedrop;
|
||||
|
||||
if ($item['gravity'] == ItemModel::GRAVITY_PARENT) {
|
||||
$item_object = new PostObject($item);
|
||||
$conv->addParent($item_object);
|
||||
}
|
||||
}
|
||||
|
||||
$threads = $conv->getTemplateData($conv_responses, $formSecurityToken);
|
||||
if (!$threads) {
|
||||
$this->logger->info('[ERROR] conversation : Failed to get template data.');
|
||||
$threads = [];
|
||||
}
|
||||
}
|
||||
|
||||
return $threads;
|
||||
}
|
||||
|
||||
private function getBlocklist(): array
|
||||
{
|
||||
if (!$this->session->getLocalUserId()) {
|
||||
|
|
@ -729,8 +856,7 @@ class Conversation
|
|||
$row['causer-avatar'] = $contact['thumb'];
|
||||
$row['causer-name'] = $contact['name'];
|
||||
} elseif (($row['gravity'] == ItemModel::GRAVITY_ACTIVITY) && ($row['verb'] == Activity::ANNOUNCE) &&
|
||||
($row['author-id'] == $activity['causer-id'])
|
||||
) {
|
||||
($row['author-id'] == $activity['causer-id'])) {
|
||||
return $row;
|
||||
}
|
||||
}
|
||||
|
|
@ -755,12 +881,7 @@ class Conversation
|
|||
$row['direction'] = ['direction' => 6, 'title' => $this->l10n->t('You are following %s.', $row['causer-name'] ?: $row['author-name'])];
|
||||
break;
|
||||
case ItemModel::PR_TAG:
|
||||
$tags = Category::getArrayByURIId($row['uri-id'], $row['uid'], Category::SUBCRIPTION);
|
||||
if (!empty($tags)) {
|
||||
$row['direction'] = ['direction' => 4, 'title' => $this->l10n->t('You subscribed to %s.', implode(', ', $tags))];
|
||||
} else {
|
||||
$row['direction'] = ['direction' => 4, 'title' => $this->l10n->t('You subscribed to one or more tags in this post.')];
|
||||
}
|
||||
$row['direction'] = ['direction' => 4, 'title' => $this->l10n->t('You subscribed to one or more tags in this post.')];
|
||||
break;
|
||||
case ItemModel::PR_ANNOUNCEMENT:
|
||||
if (!empty($row['causer-id']) && $this->pConfig->get($this->session->getLocalUserId(), 'system', 'display_resharer')) {
|
||||
|
|
@ -771,15 +892,9 @@ class Conversation
|
|||
}
|
||||
|
||||
if (in_array($row['gravity'], [ItemModel::GRAVITY_PARENT, ItemModel::GRAVITY_COMMENT]) && !empty($row['causer-id'])) {
|
||||
$causer = [
|
||||
'uid' => 0,
|
||||
'id' => $row['causer-id'],
|
||||
'network' => $row['causer-network'],
|
||||
'url' => $row['causer-link'],
|
||||
'alias' => $row['causer-alias'],
|
||||
];
|
||||
$causer = ['uid' => 0, 'id' => $row['causer-id'], 'network' => $row['causer-network'], 'url' => $row['causer-link']];
|
||||
|
||||
$row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="' . htmlentities(Contact::magicLinkByContact($causer)) . '">' . htmlentities($row['causer-name']) . '</a>');
|
||||
$row['reshared'] = $this->l10n->t('%s reshared this.', '<a href="'. htmlentities(Contact::magicLinkByContact($causer)) .'">' . htmlentities($row['causer-name']) . '</a>');
|
||||
}
|
||||
$row['direction'] = ['direction' => 3, 'title' => (empty($row['causer-id']) ? $this->l10n->t('Reshared') : $this->l10n->t('Reshared by %s <%s>', $row['causer-name'], $row['causer-link']))];
|
||||
break;
|
||||
|
|
@ -829,14 +944,13 @@ class Conversation
|
|||
*
|
||||
* @param array $parents Parent items
|
||||
* @param bool $block_authors
|
||||
* @param string $order Either "received" or "commented"
|
||||
* @param bool $order
|
||||
* @param int $uid
|
||||
* @param string $mode One of self::MODE_*
|
||||
* @param array $ignoredGsids List of ids of servers ignored by the user
|
||||
* @param string $mode
|
||||
* @return array items with parents and comments
|
||||
* @throws InternalServerErrorException
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode, array $ignoredGsids = []): array
|
||||
private function addChildren(array $parents, bool $block_authors, string $order, int $uid, string $mode): array
|
||||
{
|
||||
$this->profiler->startRecording('rendering');
|
||||
if (count($parents) > 1) {
|
||||
|
|
@ -880,21 +994,15 @@ class Conversation
|
|||
$condition = DBA::mergeConditions($condition, ["(`gravity` != ? OR `origin`)", ItemModel::GRAVITY_ACTIVITY]);
|
||||
}
|
||||
|
||||
$condition = DBA::mergeConditions(
|
||||
$condition,
|
||||
["`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` 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]);
|
||||
|
||||
$condition = DBA::mergeConditions(
|
||||
$condition,
|
||||
[
|
||||
"`visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked`
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
["`visible` AND NOT `deleted` AND NOT `author-blocked` AND NOT `owner-blocked`
|
||||
AND ((NOT `contact-pending` AND (`contact-rel` IN (?, ?))) OR `self` OR `contact-uid` = ?)",
|
||||
Contact::SHARING, Contact::FRIEND, 0
|
||||
]
|
||||
);
|
||||
Contact::SHARING, Contact::FRIEND, 0]);
|
||||
|
||||
$thread_parents = Post::select(['uri-id', 'causer-id'], $condition, ['order' => ['uri-id' => false, 'uid']]);
|
||||
|
||||
|
|
@ -918,13 +1026,6 @@ class Conversation
|
|||
continue;
|
||||
}
|
||||
|
||||
if (in_array($row['author-gsid'], $ignoredGsids)
|
||||
|| in_array($row['owner-gsid'], $ignoredGsids)
|
||||
|| in_array($row['causer-gsid'], $ignoredGsids)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (($mode != self::MODE_CONTACTS) && !$row['origin']) {
|
||||
$row['featured'] = false;
|
||||
}
|
||||
|
|
@ -1008,10 +1109,8 @@ class Conversation
|
|||
$items[$key]['user-collapsed-author'] = !$always_display && in_array($row['author-id'], $collapses);
|
||||
$items[$key]['user-collapsed-owner'] = !$always_display && in_array($row['owner-id'], $collapses);
|
||||
|
||||
if (
|
||||
in_array($mode, [self::MODE_COMMUNITY, self::MODE_NETWORK]) &&
|
||||
(in_array($row['author-id'], $blocks) || in_array($row['owner-id'], $blocks) || in_array($row['author-id'], $ignores) || in_array($row['owner-id'], $ignores))
|
||||
) {
|
||||
if (in_array($mode, [self::MODE_COMMUNITY, self::MODE_NETWORK]) &&
|
||||
(in_array($row['author-id'], $blocks) || in_array($row['owner-id'], $blocks) || in_array($row['author-id'], $ignores) || in_array($row['owner-id'], $ignores))) {
|
||||
unset($items[$key]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1046,7 +1145,7 @@ class Conversation
|
|||
$condition = DBA::mergeConditions(['parent-uri-id' => $uriids, 'gravity' => ItemModel::GRAVITY_ACTIVITY, 'verb' => $verbs], ["NOT `deleted`"]);
|
||||
$separator = chr(255) . chr(255) . chr(255);
|
||||
|
||||
$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`";
|
||||
$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 = [];
|
||||
|
||||
|
|
@ -1187,7 +1286,7 @@ class Conversation
|
|||
// Searches the post item in the children
|
||||
$j = 0;
|
||||
while ($child['children'][$j]['verb'] !== Activity::POST && $j < count($child['children'])) {
|
||||
$j++;
|
||||
$j ++;
|
||||
}
|
||||
|
||||
$moved_item = $child['children'][$j];
|
||||
|
|
@ -1261,10 +1360,8 @@ class Conversation
|
|||
* items and add them as children of their top-level post.
|
||||
*/
|
||||
foreach ($parents as $i => $parent) {
|
||||
$parents[$i]['children'] = array_merge(
|
||||
$this->getItemChildren($item_array, $parent, true),
|
||||
$this->getItemChildren($item_array, $parent, false)
|
||||
);
|
||||
$parents[$i]['children'] = array_merge($this->getItemChildren($item_array, $parent, true),
|
||||
$this->getItemChildren($item_array, $parent, false));
|
||||
}
|
||||
|
||||
foreach ($parents as $i => $parent) {
|
||||
|
|
@ -1372,179 +1469,4 @@ class Conversation
|
|||
{
|
||||
return strcmp($b['created'], $a['created']);
|
||||
}
|
||||
|
||||
/**
|
||||
* "New Item View" on network page or search page results
|
||||
* - just loop through the items and format them minimally for display
|
||||
*
|
||||
* @param array $items
|
||||
* @param string $mode One of self::MODE_*
|
||||
* @param bool $preview Whether the display is a preview
|
||||
* @param bool $pagedrop Whether the user can select the threads for deletion
|
||||
* @param string $formSecurityToken A 'contact_action' form security token
|
||||
* @return array
|
||||
* @throws InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public function getContextLessThreadList(array $items, string $mode, bool $preview, bool $pagedrop, string $formSecurityToken): array
|
||||
{
|
||||
$threads = [];
|
||||
$uriids = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (in_array($item['uri-id'], $uriids)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$uriids[] = $item['uri-id'];
|
||||
|
||||
if (!$this->item->isVisibleActivity($item)) {
|
||||
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;
|
||||
}
|
||||
|
||||
$profile_name = $item['author-name'];
|
||||
if (!empty($item['author-link']) && empty($item['author-name'])) {
|
||||
$profile_name = $item['author-link'];
|
||||
}
|
||||
|
||||
$tags = Tag::populateFromItem($item);
|
||||
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias'],
|
||||
];
|
||||
$profile_link = Contact::magicLinkByContact($author);
|
||||
|
||||
$sparkle = '';
|
||||
if (strpos($profile_link, 'contact/redir/') === 0) {
|
||||
$sparkle = ' sparkle';
|
||||
}
|
||||
|
||||
$locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => ''];
|
||||
Hook::callAll('render_location', $locate);
|
||||
$location_html = $locate['html'] ?: Strings::escapeHtml($locate['location'] ?: $locate['coord'] ?: '');
|
||||
|
||||
$this->item->localize($item);
|
||||
if ($mode === self::MODE_FILED) {
|
||||
$dropping = true;
|
||||
} else {
|
||||
$dropping = false;
|
||||
}
|
||||
|
||||
$drop = [
|
||||
'dropping' => $dropping,
|
||||
'pagedrop' => $pagedrop,
|
||||
'select' => $this->l10n->t('Select'),
|
||||
'delete' => $this->l10n->t('Delete'),
|
||||
];
|
||||
|
||||
$likebuttons = [
|
||||
'like' => null,
|
||||
'dislike' => null,
|
||||
'share' => null,
|
||||
'announce' => null,
|
||||
];
|
||||
|
||||
if ($this->pConfig->get($this->session->getLocalUserId(), 'system', 'hide_dislike')) {
|
||||
unset($likebuttons['dislike']);
|
||||
}
|
||||
|
||||
$body_html = ItemModel::prepareBody($item, true, $preview);
|
||||
|
||||
[$categories, $folders] = $this->item->determineCategoriesTerms($item, $this->session->getLocalUserId());
|
||||
|
||||
if (!empty($item['title'])) {
|
||||
$title = $item['title'];
|
||||
} elseif (!empty($item['content-warning']) && $this->pConfig->get($this->session->getLocalUserId(), 'system', 'disable_cw', false)) {
|
||||
$title = ucfirst($item['content-warning']);
|
||||
} else {
|
||||
$title = '';
|
||||
}
|
||||
|
||||
if (!empty($item['featured'])) {
|
||||
$pinned = $this->l10n->t('Pinned item');
|
||||
} else {
|
||||
$pinned = '';
|
||||
}
|
||||
|
||||
$tmp_item = [
|
||||
'template' => 'search_item.tpl',
|
||||
'id' => ($preview ? 'P0' : $item['id']),
|
||||
'guid' => ($preview ? 'Q0' : $item['guid']),
|
||||
'commented' => $item['commented'],
|
||||
'received' => $item['received'],
|
||||
'created_date' => $item['created'],
|
||||
'uriid' => $item['uri-id'],
|
||||
'author_gsid' => $item['author-gsid'],
|
||||
'network' => $item['network'],
|
||||
'network_name' => ContactSelector::networkToName($item['author-network'], $item['author-link'], $item['network'], $item['author-gsid']),
|
||||
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link'], $item['author-gsid']),
|
||||
'linktitle' => $this->l10n->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
|
||||
'profile_url' => $profile_link,
|
||||
'item_photo_menu_html' => $this->item->photoMenu($item, $formSecurityToken),
|
||||
'name' => $profile_name,
|
||||
'sparkle' => $sparkle,
|
||||
'lock' => false,
|
||||
'thumb' => $this->baseURL->remove($this->item->getAuthorAvatar($item)),
|
||||
'title' => $title,
|
||||
'body_html' => $body_html,
|
||||
'tags' => $tags['tags'],
|
||||
'hashtags' => $tags['hashtags'],
|
||||
'mentions' => $tags['mentions'],
|
||||
'implicit_mentions' => $tags['implicit_mentions'],
|
||||
'txt_cats' => $this->l10n->t('Categories:'),
|
||||
'txt_folders' => $this->l10n->t('Filed under:'),
|
||||
'has_cats' => ((count($categories)) ? 'true' : ''),
|
||||
'has_folders' => ((count($folders)) ? 'true' : ''),
|
||||
'categories' => $categories,
|
||||
'folders' => $folders,
|
||||
'text' => strip_tags($body_html),
|
||||
'localtime' => DateTimeFormat::local($item['created'], 'r'),
|
||||
'utc' => DateTimeFormat::utc($item['created'], 'c'),
|
||||
'ago' => (($item['app']) ? $this->l10n->t('%s from %s', Temporal::getRelativeDate($item['created']), $item['app']) : Temporal::getRelativeDate($item['created'])),
|
||||
'location_html' => $location_html,
|
||||
'indent' => '',
|
||||
'owner_name' => '',
|
||||
'owner_url' => '',
|
||||
'owner_photo' => $this->baseURL->remove($this->item->getOwnerAvatar($item)),
|
||||
'plink' => ItemModel::getPlink($item),
|
||||
'edpost' => false,
|
||||
'pinned' => $pinned,
|
||||
'isstarred' => 'unstarred',
|
||||
'star' => false,
|
||||
'drop' => $drop,
|
||||
'vote' => $likebuttons,
|
||||
'like_html' => '',
|
||||
'dislike_html ' => '',
|
||||
'comment_html' => '',
|
||||
'conv' => $preview ? '' : ['href' => 'display/' . $item['guid'], 'title' => $this->l10n->t('View in context')],
|
||||
'previewing' => $preview ? ' preview ' : '',
|
||||
'wait' => $this->l10n->t('Please wait'),
|
||||
'thread_level' => 1,
|
||||
];
|
||||
|
||||
$arr = ['item' => $item, 'output' => $tmp_item];
|
||||
Hook::callAll('display_item', $arr);
|
||||
|
||||
$threads[] = [
|
||||
'id' => $item['id'],
|
||||
'network' => $item['network'],
|
||||
'items' => [$arr['output']],
|
||||
];
|
||||
}
|
||||
|
||||
return $threads;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ class Feature
|
|||
// Post composition
|
||||
'composition' => [
|
||||
DI::l10n()->t('Post Composition Features'),
|
||||
['aclautomention', DI::l10n()->t('Auto-mention Groups'), DI::l10n()->t('Add/remove mention when a group page is selected/deselected in ACL window.'), false, DI::config()->get('feature_lock', 'aclautomention', false)],
|
||||
['aclautomention', DI::l10n()->t('Auto-mention Forums'), DI::l10n()->t('Add/remove mention when a forum page is selected/deselected in ACL window.'), false, DI::config()->get('feature_lock', 'aclautomention', false)],
|
||||
['explicit_mentions', DI::l10n()->t('Explicit Mentions'), DI::l10n()->t('Add explicit mentions to comment box for manual control over who gets mentioned in replies.'), false, DI::config()->get('feature_lock', 'explicit_mentions', false)],
|
||||
['add_abstract', DI::l10n()->t('Add an abstract from ActivityPub content warnings'), DI::l10n()->t('Add an abstract when commenting on ActivityPub posts with a content warning. Abstracts are displayed as content warning on systems like Mastodon or Pleroma.'), false, DI::config()->get('feature_lock', 'add_abstract', false)],
|
||||
],
|
||||
|
|
@ -116,7 +116,7 @@ class Feature
|
|||
// Advanced Profile Settings
|
||||
'advanced_profile' => [
|
||||
DI::l10n()->t('Advanced Profile Settings'),
|
||||
['forumlist_profile', DI::l10n()->t('List Groups'), DI::l10n()->t('Show visitors public groups at the Advanced Profile Page'), false, DI::config()->get('feature_lock', 'forumlist_profile', false)],
|
||||
['forumlist_profile', DI::l10n()->t('List Forums'), DI::l10n()->t('Show visitors public community forums at the Advanced Profile Page'), false, DI::config()->get('feature_lock', 'forumlist_profile', false)],
|
||||
['tagadelic', DI::l10n()->t('Tag Cloud'), DI::l10n()->t('Provide a personal tag cloud on your profile page'), false, DI::config()->get('feature_lock', 'tagadelic', false)],
|
||||
['profile_membersince', DI::l10n()->t('Display Membership Date'), DI::l10n()->t('Display membership date in profile'), false, DI::config()->get('feature_lock', 'profile_membersince', false)],
|
||||
],
|
||||
|
|
|
|||
|
|
@ -29,21 +29,21 @@ use Friendica\DI;
|
|||
use Friendica\Model\Contact;
|
||||
|
||||
/**
|
||||
* This class handles methods related to the group functionality
|
||||
* This class handles methods related to the forum functionality
|
||||
*/
|
||||
class GroupManager
|
||||
class ForumManager
|
||||
{
|
||||
/**
|
||||
* Function to list all groups a user is connected with
|
||||
* Function to list all forums a user is connected with
|
||||
*
|
||||
* @param int $uid of the profile owner
|
||||
* @param boolean $lastitem Sort by lastitem
|
||||
* @param boolean $showhidden Show groups which are not hidden
|
||||
* @param boolean $showhidden Show forums which are not hidden
|
||||
* @param boolean $showprivate Show private groups
|
||||
*
|
||||
* @return array
|
||||
* 'url' => group url
|
||||
* 'name' => group name
|
||||
* 'url' => forum url
|
||||
* 'name' => forum name
|
||||
* 'id' => number of the key from the array
|
||||
* 'micro' => contact photo in format micro
|
||||
* 'thumb' => contact photo in format thumb
|
||||
|
|
@ -76,18 +76,17 @@ class GroupManager
|
|||
$condition = DBA::mergeConditions($condition, ['hidden' => false]);
|
||||
}
|
||||
|
||||
$groupList = [];
|
||||
$forumlist = [];
|
||||
|
||||
$fields = ['id', 'url', 'alias', 'name', 'micro', 'thumb', 'avatar', 'network', 'uid'];
|
||||
$fields = ['id', 'url', 'name', 'micro', 'thumb', 'avatar', 'network', 'uid'];
|
||||
$contacts = DBA::select('account-user-view', $fields, $condition, $params);
|
||||
if (!$contacts) {
|
||||
return $groupList;
|
||||
return($forumlist);
|
||||
}
|
||||
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
$groupList[] = [
|
||||
$forumlist[] = [
|
||||
'url' => $contact['url'],
|
||||
'alias' => $contact['alias'],
|
||||
'name' => $contact['name'],
|
||||
'id' => $contact['id'],
|
||||
'micro' => $contact['micro'],
|
||||
|
|
@ -96,19 +95,19 @@ class GroupManager
|
|||
}
|
||||
DBA::close($contacts);
|
||||
|
||||
return($groupList);
|
||||
return($forumlist);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Group list widget
|
||||
* Forumlist widget
|
||||
*
|
||||
* Sidebar widget to show subscribed Friendica groups. If activated
|
||||
* in the settings, it appears in the network page sidebar
|
||||
* Sidebar widget to show subscribed friendica forums. If activated
|
||||
* in the settings, it appears at the notwork page sidebar
|
||||
*
|
||||
* @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"
|
||||
* @param int $cid The contact id which is used to mark a forum as "selected"
|
||||
* @return string
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
|
|
@ -122,7 +121,7 @@ class GroupManager
|
|||
|
||||
$contacts = self::getList($uid, $lastitem, true, true);
|
||||
$total = count($contacts);
|
||||
$visibleGroups = 10;
|
||||
$visible_forums = 10;
|
||||
|
||||
if (DBA::isResult($contacts)) {
|
||||
$id = 0;
|
||||
|
|
@ -130,7 +129,7 @@ class GroupManager
|
|||
$entries = [];
|
||||
|
||||
foreach ($contacts as $contact) {
|
||||
$selected = (($cid == $contact['id']) ? ' group-selected' : '');
|
||||
$selected = (($cid == $contact['id']) ? ' forum-selected' : '');
|
||||
|
||||
$entry = [
|
||||
'url' => $baseurl . '/' . $contact['id'],
|
||||
|
|
@ -144,20 +143,18 @@ class GroupManager
|
|||
$entries[] = $entry;
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('widget/group_list.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('widget_forumlist.tpl');
|
||||
|
||||
$o .= Renderer::replaceMacros(
|
||||
$tpl,
|
||||
[
|
||||
'$title' => DI::l10n()->t('Groups'),
|
||||
'$groups' => $entries,
|
||||
'$link_desc' => DI::l10n()->t('External link to group'),
|
||||
'$new_group_page' => 'register/',
|
||||
'$title' => DI::l10n()->t('Forums'),
|
||||
'$forums' => $entries,
|
||||
'$link_desc' => DI::l10n()->t('External link to forum'),
|
||||
'$total' => $total,
|
||||
'$visible_groups' => $visibleGroups,
|
||||
'$visible_forums' => $visible_forums,
|
||||
'$showless' => DI::l10n()->t('show less'),
|
||||
'$showmore' => DI::l10n()->t('show more'),
|
||||
'$create_new_group' => DI::l10n()->t('Create new group')]
|
||||
'$showmore' => DI::l10n()->t('show more')]
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -165,9 +162,9 @@ class GroupManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Format group list as contact block
|
||||
* Format forumlist as contact block
|
||||
*
|
||||
* This function is used to show the group list in
|
||||
* This function is used to show the forumlist in
|
||||
* the advanced profile.
|
||||
*
|
||||
* @param int $uid The ID of the User
|
||||
|
|
@ -184,7 +181,7 @@ class GroupManager
|
|||
|
||||
$o = '';
|
||||
|
||||
// placeholder in case somebody wants configurability
|
||||
// place holder in case somebody wants configurability
|
||||
$show_total = 9999;
|
||||
|
||||
//don't sort by last updated item
|
||||
|
|
@ -194,7 +191,7 @@ class GroupManager
|
|||
|
||||
$total_shown = 0;
|
||||
foreach ($contacts as $contact) {
|
||||
$o .= HTML::micropro($contact, true, 'grouplist-profile-advanced');
|
||||
$o .= HTML::micropro($contact, true, 'forumlist-profile-advanced');
|
||||
$total_shown++;
|
||||
if ($total_shown == $show_total) {
|
||||
break;
|
||||
|
|
@ -205,14 +202,14 @@ class GroupManager
|
|||
}
|
||||
|
||||
/**
|
||||
* count unread group items
|
||||
* count unread forum items
|
||||
*
|
||||
* Count unread items of connected groups and private groups
|
||||
* Count unread items of connected forums and private groups
|
||||
*
|
||||
* @return array
|
||||
* 'id' => contact id
|
||||
* 'name' => contact/group name
|
||||
* 'count' => counted unseen group items
|
||||
* 'name' => contact/forum name
|
||||
* 'count' => counted unseen forum items
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function countUnseenItems()
|
||||
|
|
@ -34,16 +34,15 @@ use Friendica\Core\Protocol;
|
|||
use Friendica\Core\Session\Capability\IHandleUserSessions;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Attach;
|
||||
use Friendica\Model\Circle;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Conversation;
|
||||
use Friendica\Model\FileTag;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item as ItemModel;
|
||||
use Friendica\Model\Photo;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Tag;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Object\EMail\ItemCCEMail;
|
||||
|
|
@ -55,7 +54,6 @@ use Friendica\Util\ParseUrl;
|
|||
use Friendica\Util\Profiler;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\XML;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
|
||||
/**
|
||||
* A content helper class for displaying items
|
||||
|
|
@ -307,20 +305,18 @@ class Item
|
|||
}
|
||||
|
||||
$author_arr = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias'],
|
||||
'url' => $item['author-link'],
|
||||
];
|
||||
$author = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $item['author-name'] . '[/url]';
|
||||
|
||||
$author_arr = [
|
||||
'uid' => 0,
|
||||
'id' => $obj['author-id'],
|
||||
'uid' => 0,
|
||||
'id' => $obj['author-id'],
|
||||
'network' => $obj['author-network'],
|
||||
'url' => $obj['author-link'],
|
||||
'alias' => $obj['author-alias'],
|
||||
'url' => $obj['author-link'],
|
||||
];
|
||||
$objauthor = '[url=' . Contact::magicLinkByContact($author_arr) . ']' . $obj['author-name'] . '[/url]';
|
||||
|
||||
|
|
@ -369,18 +365,17 @@ class Item
|
|||
{
|
||||
$this->profiler->startRecording('rendering');
|
||||
$sub_link = $contact_url = $pm_url = $status_link = '';
|
||||
$photos_link = $posts_link = $block_link = $ignore_link = $collapse_link = $ignoreserver_link = '';
|
||||
$photos_link = $posts_link = $block_link = $ignore_link = '';
|
||||
|
||||
if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $item['uid'] && $item['gravity'] == ItemModel::GRAVITY_PARENT && !$item['self'] && !$item['mention']) {
|
||||
$sub_link = 'javascript:doFollowThread(' . $item['id'] . '); return false;';
|
||||
}
|
||||
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias'],
|
||||
'url' => $item['author-link'],
|
||||
];
|
||||
$profile_link = Contact::magicLinkByContact($author, $item['author-link']);
|
||||
if (strpos($profile_link, 'contact/redir/') === 0) {
|
||||
|
|
@ -409,11 +404,6 @@ class Item
|
|||
$collapse_link = $item['self'] ? '' : $contact_url . '/collapse?t=' . $formSecurityToken;
|
||||
}
|
||||
|
||||
$authorBaseUri = new Uri($item['author-baseurl'] ?? '');
|
||||
if (!empty($item['author-gsid']) && $authorBaseUri->getHost() && !DI::baseUrl()->isLocalUrl($authorBaseUri)) {
|
||||
$ignoreserver_link = 'settings/server/' . $item['author-gsid'] . '/ignore';
|
||||
}
|
||||
|
||||
if ($cid && !$item['self']) {
|
||||
$contact_url = 'contact/' . $cid;
|
||||
$posts_link = $contact_url . '/posts';
|
||||
|
|
@ -434,8 +424,7 @@ class Item
|
|||
$this->l10n->t('Send PM') => $pm_url,
|
||||
$this->l10n->t('Block') => $block_link,
|
||||
$this->l10n->t('Ignore') => $ignore_link,
|
||||
$this->l10n->t('Collapse') => $collapse_link,
|
||||
$this->l10n->t("Ignore %s server", $authorBaseUri->getHost()) => $ignoreserver_link,
|
||||
$this->l10n->t('Collapse') => $collapse_link
|
||||
];
|
||||
|
||||
if (!empty($item['language'])) {
|
||||
|
|
@ -495,16 +484,16 @@ class Item
|
|||
{
|
||||
// Look for any tags and linkify them
|
||||
$item['inform'] = '';
|
||||
$private_group = false;
|
||||
$private_forum = false;
|
||||
$private_id = null;
|
||||
$only_to_group = false;
|
||||
$group_contact = [];
|
||||
$only_to_forum = false;
|
||||
$forum_contact = [];
|
||||
$receivers = [];
|
||||
|
||||
// Convert mentions in the body to a unified format
|
||||
$item['body'] = BBCode::setMentions($item['body'], $item['uid'], $item['network']);
|
||||
|
||||
// Search for group mentions
|
||||
// Search for forum mentions
|
||||
foreach (Tag::getFromBody($item['body'], Tag::TAG_CHARACTER[Tag::MENTION] . Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
$contact = Contact::getByURLForUser($tag[2], $item['uid']);
|
||||
if (empty($contact)) {
|
||||
|
|
@ -523,45 +512,37 @@ class Item
|
|||
}
|
||||
|
||||
if (!empty($contact['prv']) || ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION])) {
|
||||
$private_group = $contact['prv'];
|
||||
$only_to_group = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_forum = $contact['prv'];
|
||||
$only_to_forum = ($tag[1] == Tag::TAG_CHARACTER[Tag::EXCLUSIVE_MENTION]);
|
||||
$private_id = $contact['id'];
|
||||
$group_contact = $contact;
|
||||
Logger::info('Private group or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Private forum or exclusive mention', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} elseif ($item['allow_cid'] == '<' . $contact['id'] . '>') {
|
||||
$private_group = false;
|
||||
$only_to_group = true;
|
||||
$private_forum = false;
|
||||
$only_to_forum = true;
|
||||
$private_id = $contact['id'];
|
||||
$group_contact = $contact;
|
||||
Logger::info('Public group', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
$forum_contact = $contact;
|
||||
Logger::info('Public forum', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
} else {
|
||||
Logger::info('Post with group mention will not be converted to a group post', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
Logger::info('Post with forum mention will not be converted to a forum post', ['url' => $tag[2], 'mention' => $tag[1]]);
|
||||
}
|
||||
}
|
||||
Logger::info('Got inform', ['inform' => $item['inform']]);
|
||||
|
||||
if (($item['gravity'] == ItemModel::GRAVITY_PARENT) && !empty($group_contact) && ($private_group || $only_to_group)) {
|
||||
// we tagged a group in a top level post. Now we change the post
|
||||
$item['private'] = $private_group ? ItemModel::PRIVATE : ItemModel::UNLISTED;
|
||||
|
||||
if ($only_to_group) {
|
||||
$cdata = Contact::getPublicAndUserContactID($group_contact['id'], $item['uid']);
|
||||
if (!empty($cdata['user'])) {
|
||||
$item['owner-id'] = $cdata['user'];
|
||||
unset($item['owner-link']);
|
||||
unset($item['owner-name']);
|
||||
unset($item['owner-avatar']);
|
||||
}
|
||||
if (($item['gravity'] == ItemModel::GRAVITY_PARENT) && !empty($forum_contact) && ($private_forum || $only_to_forum)) {
|
||||
// we tagged a forum in a top level post. Now we change the post
|
||||
$item['private'] = $private_forum ? ItemModel::PRIVATE : ItemModel::UNLISTED;
|
||||
|
||||
if ($only_to_forum) {
|
||||
$item['postopts'] = '';
|
||||
}
|
||||
|
||||
$item['deny_cid'] = '';
|
||||
$item['deny_gid'] = '';
|
||||
|
||||
if ($private_group) {
|
||||
if ($private_forum) {
|
||||
$item['allow_cid'] = '<' . $private_id . '>';
|
||||
$item['allow_gid'] = '<' . Circle::getIdForGroup($group_contact['id']) . '>';
|
||||
$item['allow_gid'] = '<' . Group::getIdForForum($forum_contact['id']) . '>';
|
||||
} else {
|
||||
$item['allow_cid'] = '';
|
||||
$item['allow_gid'] = '';
|
||||
|
|
@ -677,15 +658,14 @@ class Item
|
|||
* Add a share block for the given item array
|
||||
*
|
||||
* @param array $item
|
||||
* @param bool $add_media true = Media is added to the body
|
||||
* @param bool $for_display true = The share block is used for display purposes, false = used for connectors, transport to other systems, ...
|
||||
* @param bool $add_media
|
||||
* @return string
|
||||
*/
|
||||
public function createSharedBlockByArray(array $item, bool $add_media = false, bool $for_display = false): string
|
||||
public function createSharedBlockByArray(array $item, bool $add_media = false): string
|
||||
{
|
||||
if ($item['network'] == Protocol::FEED) {
|
||||
return PageInfo::getFooterFromUrl($item['plink']);
|
||||
} elseif (!in_array($item['network'] ?? '', Protocol::FEDERATED) && !$for_display) {
|
||||
} elseif (!in_array($item['network'] ?? '', Protocol::FEDERATED)) {
|
||||
$item['guid'] = '';
|
||||
$item['uri'] = '';
|
||||
}
|
||||
|
|
@ -704,7 +684,7 @@ class Item
|
|||
|
||||
// If it is a reshared post then reformat it to avoid display problems with two share elements
|
||||
if (!empty($shared)) {
|
||||
if (($item['network'] != Protocol::BLUESKY) && !empty($shared['guid']) && ($encapsulated_share = $this->createSharedPostByGuid($shared['guid'], true))) {
|
||||
if (!empty($shared['guid']) && ($encapsulated_share = $this->createSharedPostByGuid($shared['guid'], true))) {
|
||||
if (!empty(BBCode::fetchShareAttributes($item['body']))) {
|
||||
$item['body'] = preg_replace("/\[share.*?\](.*)\[\/share\]/ism", $encapsulated_share, $item['body']);
|
||||
} else {
|
||||
|
|
@ -883,9 +863,9 @@ class Item
|
|||
}
|
||||
|
||||
$post['allow_cid'] = isset($request['contact_allow']) ? $this->aclFormatter->toString($request['contact_allow']) : $user['allow_cid'] ?? '';
|
||||
$post['allow_gid'] = isset($request['circle_allow']) ? $this->aclFormatter->toString($request['circle_allow']) : $user['allow_gid'] ?? '';
|
||||
$post['allow_gid'] = isset($request['group_allow']) ? $this->aclFormatter->toString($request['group_allow']) : $user['allow_gid'] ?? '';
|
||||
$post['deny_cid'] = isset($request['contact_deny']) ? $this->aclFormatter->toString($request['contact_deny']) : $user['deny_cid'] ?? '';
|
||||
$post['deny_gid'] = isset($request['circle_deny']) ? $this->aclFormatter->toString($request['circle_deny']) : $user['deny_gid'] ?? '';
|
||||
$post['deny_gid'] = isset($request['group_deny']) ? $this->aclFormatter->toString($request['group_deny']) : $user['deny_gid'] ?? '';
|
||||
|
||||
$visibility = $request['visibility'] ?? '';
|
||||
if ($visibility === 'public') {
|
||||
|
|
@ -1031,7 +1011,7 @@ class Item
|
|||
$post['body'] = $this->bbCodeVideo->transform($post['body']);
|
||||
$post = $this->setObjectType($post);
|
||||
|
||||
// Personal notes must never be altered to a group post.
|
||||
// Personal notes must never be altered to a forum post.
|
||||
if ($post['post-type'] != ItemModel::PT_PERSONAL_NOTE) {
|
||||
// Look for any tags and linkify them
|
||||
$post = $this->expandTags($post);
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ class Nav
|
|||
'$apps' => $this->getAppMenu(),
|
||||
'$home' => $this->l10n->t('Go back'),
|
||||
'$clear_notifs' => $this->l10n->t('Clear notifications'),
|
||||
'$search_hint' => $this->l10n->t('@name, !group, #tags, content')
|
||||
'$search_hint' => $this->l10n->t('@name, !forum, #tags, content')
|
||||
]);
|
||||
|
||||
Hook::callAll('page_header', $nav);
|
||||
|
|
@ -273,7 +273,7 @@ class Nav
|
|||
];
|
||||
|
||||
if ($this->config->get('system', 'poco_local_search')) {
|
||||
$nav['searchoption'][] = $this->l10n->t('Groups');
|
||||
$nav['searchoption'][] = $this->l10n->t('Forums');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -312,7 +312,8 @@ class OEmbed
|
|||
*/
|
||||
public static function BBCode2HTML(string $text): string
|
||||
{
|
||||
if (DI::config()->get('system', 'no_oembed')) {
|
||||
$stopoembed = DI::config()->get('system', 'no_oembed');
|
||||
if ($stopoembed == true) {
|
||||
return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . DI::l10n()->t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text);
|
||||
}
|
||||
return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", [self::class, 'replaceCallback'], $text);
|
||||
|
|
|
|||
|
|
@ -62,10 +62,8 @@ class BBCode
|
|||
const TWITTER = 8;
|
||||
const BACKLINK = 8;
|
||||
const ACTIVITYPUB = 9;
|
||||
const BLUESKY = 10;
|
||||
|
||||
const SHARED_ANCHOR = '<hr class="shared-anchor">';
|
||||
const TOP_ANCHOR = '<br class="top-anchor">';
|
||||
const TOP_ANCHOR = '<br class="top-anchor">';
|
||||
const BOTTOM_ANCHOR = '<br class="button-anchor">';
|
||||
|
||||
const PREVIEW_NONE = 0;
|
||||
|
|
@ -142,7 +140,7 @@ class BBCode
|
|||
break;
|
||||
|
||||
case 'title':
|
||||
$value = self::toPlaintext(html_entity_decode($value, ENT_QUOTES, 'UTF-8'));
|
||||
$value = self::convert(html_entity_decode($value, ENT_QUOTES, 'UTF-8'), false, true);
|
||||
$value = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
|
||||
$value = str_replace(['[', ']'], ['[', ']'], $value);
|
||||
$data['title'] = $value;
|
||||
|
|
@ -236,7 +234,7 @@ class BBCode
|
|||
// Remove attachment
|
||||
$text = self::replaceAttachment($text);
|
||||
|
||||
$naked_text = HTML::toPlaintext(self::convert($text, false, BBCode::EXTERNAL, true), 0, !$keep_urls);
|
||||
$naked_text = HTML::toPlaintext(self::convert($text, false, 0, true), 0, !$keep_urls);
|
||||
|
||||
DI::profiler()->stopRecording();
|
||||
return $naked_text;
|
||||
|
|
@ -510,7 +508,26 @@ class BBCode
|
|||
*/
|
||||
private static function convertUrlForActivityPub(string $url): string
|
||||
{
|
||||
return sprintf('<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', $url, Strings::getStyledURL($url));
|
||||
return sprintf('<a href="%s" target="_blank" rel="noopener noreferrer">%s</a>', $url, self::getStyledURL($url));
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an URL in a nicer format (without the scheme and possibly shortened)
|
||||
*
|
||||
* @param string $url URL that is about to be reformatted
|
||||
* @return string reformatted link
|
||||
*/
|
||||
private static function getStyledURL(string $url): string
|
||||
{
|
||||
$parts = parse_url($url);
|
||||
$scheme = $parts['scheme'] . '://';
|
||||
$styled_url = str_replace($scheme, '', $url);
|
||||
|
||||
if (strlen($styled_url) > 30) {
|
||||
$styled_url = substr($styled_url, 0, 30) . "…";
|
||||
}
|
||||
|
||||
return $styled_url;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -931,7 +948,7 @@ class BBCode
|
|||
$network = $contact['network'] ?? Protocol::PHANTOM;
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('shared_content.tpl');
|
||||
$text .= BBCode::SHARED_ANCHOR . Renderer::replaceMacros($tpl, [
|
||||
$text .= Renderer::replaceMacros($tpl, [
|
||||
'$profile' => $attributes['profile'],
|
||||
'$avatar' => $attributes['avatar'],
|
||||
'$author' => $attributes['author'],
|
||||
|
|
@ -1158,40 +1175,6 @@ class BBCode
|
|||
return $match[1] . '[url=' . $data['url'] . ']' . $data['nick'] . '[/url]';
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize links to Youtube and Vimeo to a unified format.
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
private static function normalizeVideoLinks(string $text): string
|
||||
{
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand Youtube and Vimeo links to
|
||||
*
|
||||
* @param string $text
|
||||
* @return string
|
||||
*/
|
||||
public static function expandVideoLinks(string $text): string
|
||||
{
|
||||
$text = self::normalizeVideoLinks($text);
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '[url=https://www.youtube.com/watch?v=$1]https://www.youtube.com/watch?v=$1[/url]', $text);
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '[url=https://vimeo.com/$1]https://vimeo.com/$1[/url]', $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a BBCode message for a given URI-ID to a HTML message
|
||||
*
|
||||
|
|
@ -1689,9 +1672,12 @@ class BBCode
|
|||
// Backward compatibility, [iframe] support has been removed in version 2020.12
|
||||
$text = preg_replace("/\[iframe\](.*?)\[\/iframe\]/ism", '<a href="$1">$1</a>', $text);
|
||||
|
||||
$text = self::normalizeVideoLinks($text);
|
||||
|
||||
// Youtube extensions
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/watch\?v\=(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/embed\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/www.youtube.com\/shorts\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
$text = preg_replace("/\[youtube\]https?:\/\/youtu.be\/(.*?)\[\/youtube\]/ism", '[youtube]$1[/youtube]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="' . $a->getThemeInfoValue('videowidth') . '" height="' . $a->getThemeInfoValue('videoheight') . '" src="https://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
|
|
@ -1702,7 +1688,9 @@ class BBCode
|
|||
);
|
||||
}
|
||||
|
||||
// Vimeo extensions
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
$text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism", '[vimeo]$1[/vimeo]', $text);
|
||||
|
||||
if ($try_oembed) {
|
||||
$text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="' . $a->getThemeInfoValue('videowidth') . '" height="' . $a->getThemeInfoValue('videoheight') . '" src="https://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $text);
|
||||
} else {
|
||||
|
|
@ -1802,7 +1790,7 @@ class BBCode
|
|||
$text
|
||||
);
|
||||
|
||||
if (in_array($simple_html, [self::OSTATUS, self::TWITTER, self::BLUESKY])) {
|
||||
if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) {
|
||||
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", [self::class, 'expandLinksCallback'], $text);
|
||||
//$text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $text);
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]', $text);
|
||||
|
|
@ -2094,7 +2082,7 @@ class BBCode
|
|||
|
||||
// Convert it to HTML - don't try oembed
|
||||
if ($for_diaspora) {
|
||||
$text = self::convertForUriId(0, $text, self::DIASPORA);
|
||||
$text = self::convert($text, false, self::DIASPORA);
|
||||
|
||||
// Add all tags that maybe were removed
|
||||
if (preg_match_all("/#\[url\=([$url_search_string]*)\](.*?)\[\/url\]/ism", $original_text, $tags)) {
|
||||
|
|
@ -2108,7 +2096,7 @@ class BBCode
|
|||
$text = $text . ' ' . $tagline;
|
||||
}
|
||||
} else {
|
||||
$text = self::convertForUriId(0, $text, self::CONNECTORS);
|
||||
$text = self::convert($text, false, self::CONNECTORS);
|
||||
}
|
||||
|
||||
// If a link is followed by a quote then there should be a newline before it
|
||||
|
|
|
|||
|
|
@ -422,8 +422,7 @@ class HTML
|
|||
{
|
||||
$URLSearchString = "^\[\]";
|
||||
|
||||
$matches = [
|
||||
"/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism",
|
||||
$matches = ["/\[url\=([$URLSearchString]*)\].*?\[\/url\]/ism",
|
||||
"/\[url\]([$URLSearchString]*)\[\/url\]/ism",
|
||||
"/\[img\=[0-9]*x[0-9]*\](.*?)\[\/img\]/ism",
|
||||
"/\[img\](.*?)\[\/img\]/ism",
|
||||
|
|
@ -532,10 +531,8 @@ class HTML
|
|||
$ignore = false;
|
||||
|
||||
// A list of some links that should be ignored
|
||||
$list = [
|
||||
"/user/", "/tag/", "/group/", "/circle/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/",
|
||||
"//plus.google.com/", "//twitter.com/"
|
||||
];
|
||||
$list = ["/user/", "/tag/", "/group/", "/profile/", "/search?search=", "/search?tag=", "mailto:", "/u/", "/node/",
|
||||
"//plus.google.com/", "//twitter.com/"];
|
||||
foreach ($list as $listitem) {
|
||||
if (strpos($treffer[1], $listitem) !== false) {
|
||||
$ignore = true;
|
||||
|
|
@ -864,7 +861,7 @@ class HTML
|
|||
'$id' => $id,
|
||||
'$search_label' => DI::l10n()->t('Search'),
|
||||
'$save_label' => $save_label,
|
||||
'$search_hint' => DI::l10n()->t('@name, !group, #tags, content'),
|
||||
'$search_hint' => DI::l10n()->t('@name, !forum, #tags, content'),
|
||||
'$mode' => $mode,
|
||||
'$return_url' => urlencode(Search::getSearchPath($s)),
|
||||
];
|
||||
|
|
@ -877,7 +874,7 @@ class HTML
|
|||
];
|
||||
|
||||
if (DI::config()->get('system', 'poco_local_search')) {
|
||||
$values['$searchoption']['groups'] = DI::l10n()->t('Groups');
|
||||
$values['$searchoption']['forums'] = DI::l10n()->t('Forums');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -944,8 +941,7 @@ class HTML
|
|||
$domain = '(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)*' . preg_quote(trim($domain, '/'), '%');
|
||||
});
|
||||
|
||||
$config->set(
|
||||
'URI.SafeIframeRegexp',
|
||||
$config->set('URI.SafeIframeRegexp',
|
||||
'%^https://(?:
|
||||
' . implode('|', $allowedIframeDomains) . '
|
||||
)
|
||||
|
|
@ -1054,8 +1050,7 @@ class HTML
|
|||
if (isset($mediaType->parameters['charset'])) {
|
||||
return strtolower($mediaType->parameters['charset']);
|
||||
}
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
}
|
||||
} catch(\InvalidArgumentException $e) {}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class NPF
|
|||
{
|
||||
$bbcode = self::prepareBody($bbcode);
|
||||
|
||||
$html = BBCode::convertForUriId($uri_id, $bbcode, BBCode::NPF);
|
||||
$html = BBCode::convert($bbcode, false, BBCode::NPF);
|
||||
if (empty($html)) {
|
||||
return [];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,10 +137,6 @@ class Plaintext
|
|||
$abstract = BBCode::getAbstract($item['body'], Protocol::STATUSNET);
|
||||
break;
|
||||
|
||||
case BBCode::BLUESKY:
|
||||
$abstract = BBCode::getAbstract($item['body'], Protocol::BLUESKY);
|
||||
break;
|
||||
|
||||
default: // We don't know the exact target.
|
||||
// We fetch an abstract since there is a posting limit.
|
||||
if ($limit > 0) {
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use Friendica\Core\Search;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Circle;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Model\Profile;
|
||||
|
|
@ -102,7 +102,7 @@ class Widget
|
|||
public static function unavailableNetworks(): array
|
||||
{
|
||||
// Always hide content from these networks
|
||||
$networks = [Protocol::PHANTOM, Protocol::FACEBOOK, Protocol::APPNET, Protocol::TWITTER, Protocol::ZOT];
|
||||
$networks = [Protocol::PHANTOM, Protocol::FACEBOOK, Protocol::APPNET, Protocol::ZOT];
|
||||
|
||||
if (!Addon::isEnabled("discourse")) {
|
||||
$networks[] = Protocol::DISCOURSE;
|
||||
|
|
@ -116,6 +116,10 @@ class Widget
|
|||
$networks[] = Protocol::PUMPIO;
|
||||
}
|
||||
|
||||
if (!Addon::isEnabled("twitter")) {
|
||||
$networks[] = Protocol::TWITTER;
|
||||
}
|
||||
|
||||
if (!Addon::isEnabled("tumblr")) {
|
||||
$networks[] = Protocol::TUMBLR;
|
||||
}
|
||||
|
|
@ -190,29 +194,29 @@ class Widget
|
|||
}
|
||||
|
||||
/**
|
||||
* Return circle membership widget
|
||||
* Return group membership widget
|
||||
*
|
||||
* @param string $baseurl
|
||||
* @param string $selected
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function circles(string $baseurl, string $selected = ''): string
|
||||
public static function groups(string $baseurl, string $selected = ''): string
|
||||
{
|
||||
if (!DI::userSession()->getLocalUserId()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$options = array_map(function ($circle) {
|
||||
$options = array_map(function ($group) {
|
||||
return [
|
||||
'ref' => $circle['id'],
|
||||
'name' => $circle['name']
|
||||
'ref' => $group['id'],
|
||||
'name' => $group['name']
|
||||
];
|
||||
}, Circle::getByUserId(DI::userSession()->getLocalUserId()));
|
||||
}, Group::getByUserId(DI::userSession()->getLocalUserId()));
|
||||
|
||||
return self::filter(
|
||||
'circle',
|
||||
DI::l10n()->t('Circles'),
|
||||
'group',
|
||||
DI::l10n()->t('Groups'),
|
||||
'',
|
||||
DI::l10n()->t('Everyone'),
|
||||
$baseurl,
|
||||
|
|
@ -239,7 +243,6 @@ class Widget
|
|||
['ref' => 'followers', 'name' => DI::l10n()->t('Followers')],
|
||||
['ref' => 'following', 'name' => DI::l10n()->t('Following')],
|
||||
['ref' => 'mutuals', 'name' => DI::l10n()->t('Mutual friends')],
|
||||
['ref' => 'nothing', 'name' => DI::l10n()->t('No relationship')],
|
||||
];
|
||||
|
||||
return self::filter(
|
||||
|
|
@ -466,10 +469,6 @@ class Widget
|
|||
|
||||
if ($dthen) {
|
||||
// Set the start and end date to the beginning of the month
|
||||
$cutoffday = $dthen;
|
||||
$thisday = substr($dnow, 4);
|
||||
$nextday = date('Y-m-d', strtotime($dnow . ' + 1 day'));
|
||||
$nextday = substr($nextday, 4);
|
||||
$dnow = substr($dnow, 0, 8) . '01';
|
||||
$dthen = substr($dthen, 0, 8) . '01';
|
||||
|
||||
|
|
@ -502,7 +501,7 @@ class Widget
|
|||
$cutoff_year = intval(DateTimeFormat::localNow('Y')) - $visible_years;
|
||||
$cutoff = array_key_exists($cutoff_year, $ret);
|
||||
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/posted_date.tpl'), [
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/posted_date.tpl'),[
|
||||
'$title' => DI::l10n()->t('Archives'),
|
||||
'$size' => $visible_years,
|
||||
'$cutoff_year' => $cutoff_year,
|
||||
|
|
@ -510,11 +509,7 @@ class Widget
|
|||
'$url' => $url,
|
||||
'$dates' => $ret,
|
||||
'$showless' => DI::l10n()->t('show less'),
|
||||
'$showmore' => DI::l10n()->t('show more'),
|
||||
'$onthisdate' => DI::l10n()->t('On this date'),
|
||||
'$thisday' => $thisday,
|
||||
'$nextday' => $nextday,
|
||||
'$cutoffday' => $cutoffday
|
||||
'$showmore' => DI::l10n()->t('show more')
|
||||
]);
|
||||
|
||||
return $o;
|
||||
|
|
@ -534,17 +529,10 @@ class Widget
|
|||
['ref' => 'person', 'name' => DI::l10n()->t('Persons')],
|
||||
['ref' => 'organisation', 'name' => DI::l10n()->t('Organisations')],
|
||||
['ref' => 'news', 'name' => DI::l10n()->t('News')],
|
||||
['ref' => 'community', 'name' => DI::l10n()->t('Groups')],
|
||||
['ref' => 'community', 'name' => DI::l10n()->t('Forums')],
|
||||
];
|
||||
|
||||
return self::filter(
|
||||
'accounttype',
|
||||
DI::l10n()->t('Account Types'),
|
||||
'',
|
||||
DI::l10n()->t('All'),
|
||||
$base,
|
||||
$accounts,
|
||||
$accounttype
|
||||
);
|
||||
return self::filter('accounttype', DI::l10n()->t('Account Types'), '',
|
||||
DI::l10n()->t('All'), $base, $accounts, $accounttype);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class ContactBlock
|
|||
$contact_uriids = array_column($personal_contacts, 'uri-id');
|
||||
|
||||
if (!empty($contact_uriids)) {
|
||||
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'alias', 'name', 'thumb', 'avatar', 'network'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]);
|
||||
$contacts_stmt = DBA::select('contact', ['id', 'uid', 'addr', 'url', 'name', 'thumb', 'avatar', 'network'], ['uri-id' => $contact_uriids, 'uid' => $contact_uid]);
|
||||
|
||||
if (DBA::isResult($contacts_stmt)) {
|
||||
$contacts_title = DI::l10n()->tt('%d Contact', '%d Contacts', $total);
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ use Friendica\Core\Renderer;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
/**
|
||||
|
|
@ -51,15 +50,9 @@ class VCard
|
|||
Logger::warning('Incomplete contact', ['contact' => $contact ?? [], 'callstack' => System::callstack(20)]);
|
||||
}
|
||||
|
||||
if (!Network::isValidHttpUrl($contact['url']) && Network::isValidHttpUrl($contact['alias'])) {
|
||||
$contact_url = $contact['alias'];
|
||||
} else {
|
||||
$contact_url = $contact['url'];
|
||||
}
|
||||
|
||||
if ($contact['network'] != '') {
|
||||
$network_link = Strings::formatNetworkName($contact['network'], $contact_url);
|
||||
$network_avatar = ContactSelector::networkToIcon($contact['network'], $contact_url);
|
||||
$network_link = Strings::formatNetworkName($contact['network'], $contact['url']);
|
||||
$network_avatar = ContactSelector::networkToIcon($contact['network'], $contact['url']);
|
||||
} else {
|
||||
$network_link = '';
|
||||
$network_avatar = '';
|
||||
|
|
@ -90,9 +83,9 @@ class VCard
|
|||
|
||||
if (empty($contact['self']) && Protocol::supportsFollow($contact['network'])) {
|
||||
if (in_array($rel, [Contact::SHARING, Contact::FRIEND])) {
|
||||
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact_url) . '&auto=1';
|
||||
$unfollow_link = 'contact/unfollow?url=' . urlencode($contact['url']) . '&auto=1';
|
||||
} elseif (!$pending) {
|
||||
$follow_link = 'contact/follow?url=' . urlencode($contact_url) . '&auto=1';
|
||||
$follow_link = 'contact/follow?url=' . urlencode($contact['url']) . '&auto=1';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +97,7 @@ class VCard
|
|||
return Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
|
||||
'$contact' => $contact,
|
||||
'$photo' => $photo,
|
||||
'$url' => Contact::magicLinkByContact($contact, $contact_url),
|
||||
'$url' => Contact::magicLinkByContact($contact, $contact['url']),
|
||||
'$about' => BBCode::convertForUriId($contact['uri-id'] ?? 0, $contact['about'] ?? ''),
|
||||
'$xmpp' => DI::l10n()->t('XMPP:'),
|
||||
'$matrix' => DI::l10n()->t('Matrix:'),
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ use Friendica\App\Page;
|
|||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Circle;
|
||||
use Friendica\Model\Group;
|
||||
use Friendica\Model\User;
|
||||
|
||||
/**
|
||||
|
|
@ -167,12 +167,12 @@ class ACL
|
|||
|
||||
$acl_contacts[] = $acl_yourself;
|
||||
|
||||
$acl_groups = Contact::selectToArray($fields,
|
||||
$acl_forums = Contact::selectToArray($fields,
|
||||
['uid' => $user_id, 'self' => false, 'blocked' => false, 'archive' => false, 'deleted' => false,
|
||||
'network' => Protocol::FEDERATED, 'pending' => false, 'contact-type' => Contact::TYPE_COMMUNITY], $params
|
||||
);
|
||||
|
||||
$acl_contacts = array_merge($acl_groups, $acl_contacts);
|
||||
$acl_contacts = array_merge($acl_forums, $acl_contacts);
|
||||
|
||||
array_walk($acl_contacts, function (&$value) {
|
||||
$value['type'] = 'contact';
|
||||
|
|
@ -182,40 +182,40 @@ class ACL
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ACL list of circles (including meta-circles) for a given user id
|
||||
* Returns the ACL list of groups (including meta-groups) for a given user id
|
||||
*
|
||||
* @param int $user_id
|
||||
* @return array
|
||||
*/
|
||||
public static function getCircleListByUserId(int $user_id)
|
||||
public static function getGroupListByUserId(int $user_id)
|
||||
{
|
||||
$acl_circles = [
|
||||
$acl_groups = [
|
||||
[
|
||||
'id' => Circle::FOLLOWERS,
|
||||
'id' => Group::FOLLOWERS,
|
||||
'name' => DI::l10n()->t('Followers'),
|
||||
'addr' => '',
|
||||
'micro' => 'images/twopeople.png',
|
||||
'type' => 'circle',
|
||||
'type' => 'group',
|
||||
],
|
||||
[
|
||||
'id' => Circle::MUTUALS,
|
||||
'id' => Group::MUTUALS,
|
||||
'name' => DI::l10n()->t('Mutuals'),
|
||||
'addr' => '',
|
||||
'micro' => 'images/twopeople.png',
|
||||
'type' => 'circle',
|
||||
'type' => 'group',
|
||||
]
|
||||
];
|
||||
foreach (Circle::getByUserId($user_id) as $circle) {
|
||||
$acl_circles[] = [
|
||||
'id' => $circle['id'],
|
||||
'name' => $circle['name'],
|
||||
foreach (Group::getByUserId($user_id) as $group) {
|
||||
$acl_groups[] = [
|
||||
'id' => $group['id'],
|
||||
'name' => $group['name'],
|
||||
'addr' => '',
|
||||
'micro' => 'images/twopeople.png',
|
||||
'type' => 'circle',
|
||||
'type' => 'group',
|
||||
];
|
||||
}
|
||||
|
||||
return $acl_circles;
|
||||
return $acl_groups;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -279,7 +279,7 @@ class ACL
|
|||
} else {
|
||||
$visibility = 'public';
|
||||
// Default permission display for custom panel
|
||||
$default_permissions['allow_gid'] = [Circle::FOLLOWERS];
|
||||
$default_permissions['allow_gid'] = [Group::FOLLOWERS];
|
||||
}
|
||||
|
||||
$jotnets_fields = [];
|
||||
|
|
@ -303,15 +303,15 @@ class ACL
|
|||
|
||||
$acl_contacts = self::getContactListByUserId($user['uid'], $condition);
|
||||
|
||||
$acl_circles = self::getCircleListByUserId($user['uid']);
|
||||
$acl_groups = self::getGroupListByUserId($user['uid']);
|
||||
|
||||
$acl_list = array_merge($acl_circles, $acl_contacts);
|
||||
$acl_list = array_merge($acl_groups, $acl_contacts);
|
||||
|
||||
$input_names = [
|
||||
'visibility' => $form_prefix ? $form_prefix . '[visibility]' : 'visibility',
|
||||
'circle_allow' => $form_prefix ? $form_prefix . '[circle_allow]' : 'circle_allow',
|
||||
'group_allow' => $form_prefix ? $form_prefix . '[group_allow]' : 'group_allow',
|
||||
'contact_allow' => $form_prefix ? $form_prefix . '[contact_allow]' : 'contact_allow',
|
||||
'circle_deny' => $form_prefix ? $form_prefix . '[circle_deny]' : 'circle_deny',
|
||||
'group_deny' => $form_prefix ? $form_prefix . '[group_deny]' : 'group_deny',
|
||||
'contact_deny' => $form_prefix ? $form_prefix . '[contact_deny]' : 'contact_deny',
|
||||
'emailcc' => $form_prefix ? $form_prefix . '[emailcc]' : 'emailcc',
|
||||
];
|
||||
|
|
@ -321,7 +321,7 @@ class ACL
|
|||
'$public_title' => DI::l10n()->t('Public'),
|
||||
'$public_desc' => DI::l10n()->t('This content will be shown to all your followers and can be seen in the community pages and by anyone with its link.'),
|
||||
'$custom_title' => DI::l10n()->t('Limited/Private'),
|
||||
'$custom_desc' => DI::l10n()->t('This content will be shown only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.') . DI::l10n()->t('Start typing the name of a contact or a circle to show a filtered list. You can also mention the special circles "Followers" and "Mutuals".'),
|
||||
'$custom_desc' => DI::l10n()->t('This content will be shown only to the people in the first box, to the exception of the people mentioned in the second box. It won\'t appear anywhere public.') . DI::l10n()->t('Start typing the name of a contact or a group to show a filtered list. You can also mention the special groups "Followers" and "Mutuals".'),
|
||||
'$allow_label' => DI::l10n()->t('Show to:'),
|
||||
'$deny_label' => DI::l10n()->t('Except to:'),
|
||||
'$emailcc' => DI::l10n()->t('CC: email addresses'),
|
||||
|
|
@ -329,12 +329,12 @@ class ACL
|
|||
'$jotnets_summary' => DI::l10n()->t('Connectors'),
|
||||
'$visibility' => $visibility,
|
||||
'$acl_contacts' => json_encode($acl_contacts),
|
||||
'$acl_circles' => json_encode($acl_circles),
|
||||
'$acl_groups' => json_encode($acl_groups),
|
||||
'$acl_list' => json_encode($acl_list),
|
||||
'$contact_allow' => implode(',', $default_permissions['allow_cid']),
|
||||
'$circle_allow' => implode(',', $default_permissions['allow_gid']),
|
||||
'$group_allow' => implode(',', $default_permissions['allow_gid']),
|
||||
'$contact_deny' => implode(',', $default_permissions['deny_cid']),
|
||||
'$circle_deny' => implode(',', $default_permissions['deny_gid']),
|
||||
'$group_deny' => implode(',', $default_permissions['deny_gid']),
|
||||
'$for_federation' => $for_federation,
|
||||
'$jotnets_fields' => $jotnets_fields,
|
||||
'$input_names' => $input_names,
|
||||
|
|
@ -381,7 +381,7 @@ class ACL
|
|||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isValidCircle($acl_string, $uid)
|
||||
public static function isValidGroup($acl_string, $uid)
|
||||
{
|
||||
if (empty($acl_string)) {
|
||||
return true;
|
||||
|
|
@ -394,7 +394,7 @@ class ACL
|
|||
$gid_array = $array[0];
|
||||
foreach ($gid_array as $gid) {
|
||||
$gid = str_replace(['<', '>'], ['', ''], $gid);
|
||||
if (!DBA::exists('circle', ['id' => $gid, 'uid' => $uid, 'deleted' => false])) {
|
||||
if (!DBA::exists('group', ['id' => $gid, 'uid' => $uid, 'deleted' => false])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Addon\Capability;
|
||||
|
||||
/**
|
||||
* Interface for loading Addons specific content
|
||||
*/
|
||||
interface ICanLoadAddons
|
||||
{
|
||||
/**
|
||||
* Returns a merged config array of all active addons for a given config-name
|
||||
*
|
||||
* @param string $configName The config-name (config-file at the static directory, like 'hooks' => '{addon}/static/hooks.config.php)
|
||||
*
|
||||
* @return array the merged array
|
||||
*/
|
||||
public function getActiveAddonConfig(string $configName): array;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Addon\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case one or more config files of the addons are invalid
|
||||
*/
|
||||
class AddonInvalidConfigFileException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = '', $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Addon\Model;
|
||||
|
||||
use Friendica\Core\Addon\Capability\ICanLoadAddons;
|
||||
use Friendica\Core\Addon\Exception\AddonInvalidConfigFileException;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
class AddonLoader implements ICanLoadAddons
|
||||
{
|
||||
const STATIC_PATH = 'static';
|
||||
/** @var string */
|
||||
protected $basePath;
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $basePath, IManageConfigValues $config)
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function getActiveAddonConfig(string $configName): array
|
||||
{
|
||||
$addons = array_keys(array_filter($this->config->get('addons') ?? []));
|
||||
$returnConfig = [];
|
||||
|
||||
foreach ($addons as $addon) {
|
||||
$addonName = Strings::sanitizeFilePathItem(trim($addon));
|
||||
|
||||
$configFile = $this->basePath . '/addon/' . $addonName . '/' . static::STATIC_PATH . '/' . $configName . '.config.php';
|
||||
|
||||
if (!file_exists($configFile)) {
|
||||
// Addon unmodified, skipping
|
||||
continue;
|
||||
}
|
||||
|
||||
$config = include $configFile;
|
||||
|
||||
if (!is_array($config)) {
|
||||
throw new AddonInvalidConfigFileException('Error loading config file ' . $configFile);
|
||||
}
|
||||
|
||||
$returnConfig = array_merge_recursive($returnConfig, $config);
|
||||
}
|
||||
|
||||
return $returnConfig;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,15 +19,17 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Moderation\Collection\Report;
|
||||
namespace Friendica\Core\Cache\Enum;
|
||||
|
||||
class Posts extends \Friendica\BaseCollection
|
||||
/**
|
||||
* Enumeration for cache types
|
||||
*/
|
||||
abstract class Type
|
||||
{
|
||||
/**
|
||||
* @return \Friendica\Moderation\Entity\Report\Post
|
||||
*/
|
||||
public function current(): \Friendica\Moderation\Entity\Report\Post
|
||||
{
|
||||
return parent::current();
|
||||
}
|
||||
const APCU = 'apcu';
|
||||
const REDIS = 'redis';
|
||||
const ARRAY = 'array';
|
||||
const MEMCACHE = 'memcache';
|
||||
const DATABASE = 'database';
|
||||
const MEMCACHED = 'memcached';
|
||||
}
|
||||
|
|
@ -21,13 +21,16 @@
|
|||
|
||||
namespace Friendica\Core\Cache\Factory;
|
||||
|
||||
use Friendica\App\BaseURL;
|
||||
use Friendica\Core\Cache\Enum;
|
||||
use Friendica\Core\Cache\Capability\ICanCache;
|
||||
use Friendica\Core\Cache\Exception\CachePersistenceException;
|
||||
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
||||
use Friendica\Core\Cache\Type;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capability\ICanCreateInstances;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* Class CacheFactory
|
||||
|
|
@ -41,32 +44,59 @@ class Cache
|
|||
/**
|
||||
* @var string The default cache if nothing set
|
||||
*/
|
||||
const DEFAULT_TYPE = Type\DatabaseCache::NAME;
|
||||
/** @var ICanCreateInstances */
|
||||
protected $instanceCreator;
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
/** @var Profiler */
|
||||
protected $profiler;
|
||||
const DEFAULT_TYPE = Enum\Type::DATABASE;
|
||||
|
||||
public function __construct(ICanCreateInstances $instanceCreator, IManageConfigValues $config, Profiler $profiler)
|
||||
/**
|
||||
* @var IManageConfigValues The IConfiguration to read parameters out of the config
|
||||
*/
|
||||
private $config;
|
||||
|
||||
/**
|
||||
* @var Database The database connection in case that the cache is used the dba connection
|
||||
*/
|
||||
private $dba;
|
||||
|
||||
/**
|
||||
* @var string The hostname, used as Prefix for Caching
|
||||
*/
|
||||
private $hostname;
|
||||
|
||||
/**
|
||||
* @var Profiler The optional profiler if the cached should be profiled
|
||||
*/
|
||||
private $profiler;
|
||||
|
||||
/**
|
||||
* @var LoggerInterface The Friendica Logger
|
||||
*/
|
||||
private $logger;
|
||||
|
||||
public function __construct(BaseURL $baseURL, IManageConfigValues $config, Database $dba, Profiler $profiler, LoggerInterface $logger)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->instanceCreator = $instanceCreator;
|
||||
$this->profiler = $profiler;
|
||||
$this->hostname = $baseURL->getHost();
|
||||
$this->config = $config;
|
||||
$this->dba = $dba;
|
||||
$this->profiler = $profiler;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method creates a CacheDriver for distributed caching
|
||||
* This method creates a CacheDriver for distributed caching with the given cache driver name
|
||||
*
|
||||
* @param string|null $type The cache type to create (default is per config)
|
||||
*
|
||||
* @return ICanCache The instance of the CacheDriver
|
||||
*
|
||||
* @throws InvalidCacheDriverException In case the underlying cache driver isn't valid or not configured properly
|
||||
* @throws CachePersistenceException In case the underlying cache has errors during persistence
|
||||
*/
|
||||
public function createDistributed(): ICanCache
|
||||
public function createDistributed(string $type = null): ICanCache
|
||||
{
|
||||
return $this->create($this->config->get('system', 'distributed_cache_driver', self::DEFAULT_TYPE));
|
||||
if ($type === Enum\Type::APCU) {
|
||||
throw new InvalidCacheDriverException('apcu doesn\'t support distributed caching.');
|
||||
}
|
||||
|
||||
return $this->create($type ?? $this->config->get('system', 'distributed_cache_driver', self::DEFAULT_TYPE));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -87,17 +117,31 @@ class Cache
|
|||
/**
|
||||
* Creates a new Cache instance
|
||||
*
|
||||
* @param string $strategy The strategy, which cache instance should be used
|
||||
* @param string $type The type of cache
|
||||
*
|
||||
* @return ICanCache
|
||||
*
|
||||
* @throws InvalidCacheDriverException In case the underlying cache driver isn't valid or not configured properly
|
||||
* @throws CachePersistenceException In case the underlying cache has errors during persistence
|
||||
*/
|
||||
protected function create(string $strategy): ICanCache
|
||||
protected function create(string $type): ICanCache
|
||||
{
|
||||
/** @var ICanCache $cache */
|
||||
$cache = $this->instanceCreator->create(ICanCache::class, $strategy);
|
||||
switch ($type) {
|
||||
case Enum\Type::MEMCACHE:
|
||||
$cache = new Type\MemcacheCache($this->hostname, $this->config);
|
||||
break;
|
||||
case Enum\Type::MEMCACHED:
|
||||
$cache = new Type\MemcachedCache($this->hostname, $this->config, $this->logger);
|
||||
break;
|
||||
case Enum\Type::REDIS:
|
||||
$cache = new Type\RedisCache($this->hostname, $this->config);
|
||||
break;
|
||||
case Enum\Type::APCU:
|
||||
$cache = new Type\APCuCache($this->hostname);
|
||||
break;
|
||||
default:
|
||||
$cache = new Type\DatabaseCache($this->hostname, $this->dba);
|
||||
}
|
||||
|
||||
$profiling = $this->config->get('system', 'profiling', false);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Core\Cache\Type;
|
|||
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Cache\Enum\Type;
|
||||
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
||||
|
||||
/**
|
||||
|
|
@ -30,12 +31,12 @@ use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
|||
*/
|
||||
class APCuCache extends AbstractCache implements ICanCacheInMemory
|
||||
{
|
||||
const NAME = 'apcu';
|
||||
|
||||
use CompareSetTrait;
|
||||
use CompareDeleteTrait;
|
||||
|
||||
/**
|
||||
* @param string $hostname
|
||||
*
|
||||
* @throws InvalidCacheDriverException
|
||||
*/
|
||||
public function __construct(string $hostname)
|
||||
|
|
@ -172,4 +173,12 @@ class APCuCache extends AbstractCache implements ICanCacheInMemory
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Type::APCU;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ use Friendica\Core\Cache\Capability\ICanCache;
|
|||
*/
|
||||
abstract class AbstractCache implements ICanCache
|
||||
{
|
||||
const NAME = '';
|
||||
|
||||
/**
|
||||
* @var string The hostname
|
||||
*/
|
||||
|
|
@ -107,10 +105,4 @@ abstract class AbstractCache implements ICanCache
|
|||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function getName(): string
|
||||
{
|
||||
return static::NAME;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ use Friendica\Core\Cache\Enum;
|
|||
*/
|
||||
class ArrayCache extends AbstractCache implements ICanCacheInMemory
|
||||
{
|
||||
const NAME = 'array';
|
||||
|
||||
use CompareDeleteTrait;
|
||||
|
||||
/** @var array Array with the cached data */
|
||||
|
|
@ -110,4 +108,12 @@ class ArrayCache extends AbstractCache implements ICanCacheInMemory
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Enum\Type::ARRAY;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ use Friendica\Util\DateTimeFormat;
|
|||
*/
|
||||
class DatabaseCache extends AbstractCache implements ICanCache
|
||||
{
|
||||
const NAME = 'database';
|
||||
|
||||
/**
|
||||
* @var Database
|
||||
*/
|
||||
|
|
@ -156,4 +154,12 @@ class DatabaseCache extends AbstractCache implements ICanCache
|
|||
throw new CachePersistenceException('Cannot clear cache', $exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Enum\Type::DATABASE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Core\Cache\Type;
|
|||
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Cache\Enum\Type;
|
||||
use Friendica\Core\Cache\Exception\CachePersistenceException;
|
||||
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
|
|
@ -33,8 +34,6 @@ use Memcache;
|
|||
*/
|
||||
class MemcacheCache extends AbstractCache implements ICanCacheInMemory
|
||||
{
|
||||
const NAME = 'memcache';
|
||||
|
||||
use CompareSetTrait;
|
||||
use CompareDeleteTrait;
|
||||
use MemcacheCommandTrait;
|
||||
|
|
@ -170,4 +169,12 @@ class MemcacheCache extends AbstractCache implements ICanCacheInMemory
|
|||
$cacheKey = $this->getCacheKey($key);
|
||||
return $this->memcache->add($cacheKey, serialize($value), MEMCACHE_COMPRESSED, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Type::MEMCACHE;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ namespace Friendica\Core\Cache\Type;
|
|||
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Cache\Enum\Type;
|
||||
use Friendica\Core\Cache\Exception\CachePersistenceException;
|
||||
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
|
|
@ -34,8 +35,6 @@ use Psr\Log\LoggerInterface;
|
|||
*/
|
||||
class MemcachedCache extends AbstractCache implements ICanCacheInMemory
|
||||
{
|
||||
const NAME = 'memcached';
|
||||
|
||||
use CompareSetTrait;
|
||||
use CompareDeleteTrait;
|
||||
use MemcacheCommandTrait;
|
||||
|
|
@ -186,4 +185,12 @@ class MemcachedCache extends AbstractCache implements ICanCacheInMemory
|
|||
$cacheKey = $this->getCacheKey($key);
|
||||
return $this->memcached->add($cacheKey, $value, $ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Type::MEMCACHED;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,8 +21,10 @@
|
|||
|
||||
namespace Friendica\Core\Cache\Type;
|
||||
|
||||
use Exception;
|
||||
use Friendica\Core\Cache\Enum\Duration;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Cache\Enum\Type;
|
||||
use Friendica\Core\Cache\Exception\CachePersistenceException;
|
||||
use Friendica\Core\Cache\Exception\InvalidCacheDriverException;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
|
|
@ -33,8 +35,6 @@ use Redis;
|
|||
*/
|
||||
class RedisCache extends AbstractCache implements ICanCacheInMemory
|
||||
{
|
||||
const NAME = 'redis';
|
||||
|
||||
/**
|
||||
* @var Redis
|
||||
*/
|
||||
|
|
@ -57,30 +57,20 @@ class RedisCache extends AbstractCache implements ICanCacheInMemory
|
|||
$redis_host = $config->get('system', 'redis_host');
|
||||
$redis_port = $config->get('system', 'redis_port');
|
||||
$redis_pw = $config->get('system', 'redis_password');
|
||||
$redis_db = (int)$config->get('system', 'redis_db', 0);
|
||||
$redis_db = $config->get('system', 'redis_db', 0);
|
||||
|
||||
try {
|
||||
if (is_numeric($redis_port) && $redis_port > -1) {
|
||||
$connection_string = $redis_host . ':' . $redis_port;
|
||||
if (!@$this->redis->connect($redis_host, $redis_port)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $connection_string . " isn't available");
|
||||
}
|
||||
} else {
|
||||
$connection_string = $redis_host;
|
||||
if (!@$this->redis->connect($redis_host)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $connection_string . ' isn\'t available');
|
||||
}
|
||||
}
|
||||
if (!empty($redis_port) && !@$this->redis->connect($redis_host, $redis_port)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ':' . $redis_port . ' isn\'t available');
|
||||
} elseif (!@$this->redis->connect($redis_host)) {
|
||||
throw new CachePersistenceException('Expected Redis server at ' . $redis_host . ' isn\'t available');
|
||||
}
|
||||
|
||||
if (!empty($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
throw new CachePersistenceException('Cannot authenticate redis server at ' . $connection_string);
|
||||
}
|
||||
if (!empty($redis_pw) && !$this->redis->auth($redis_pw)) {
|
||||
throw new CachePersistenceException('Cannot authenticate redis server at ' . $redis_host . ':' . $redis_port);
|
||||
}
|
||||
|
||||
if ($redis_db !== 0 && !$this->redis->select($redis_db)) {
|
||||
throw new CachePersistenceException('Cannot switch to redis db ' . $redis_db . ' at ' . $connection_string);
|
||||
}
|
||||
} catch (\RedisException $exception) {
|
||||
throw new CachePersistenceException('Redis connection fails unexpectedly', $exception);
|
||||
if ($redis_db !== 0 && !$this->redis->select($redis_db)) {
|
||||
throw new CachePersistenceException('Cannot switch to redis db ' . $redis_db . ' at ' . $redis_host . ':' . $redis_port);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -221,4 +211,12 @@ class RedisCache extends AbstractCache implements ICanCacheInMemory
|
|||
$this->redis->unwatch();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return Type::REDIS;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,12 +19,11 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\User\Settings\Collection;
|
||||
namespace Friendica\Core\Hooks\Capabilities;
|
||||
|
||||
class UserGServers extends \Friendica\BaseCollection
|
||||
/**
|
||||
* All classes, implementing this interface are valid Strategies for Hook calls
|
||||
*/
|
||||
interface IAmAStrategy
|
||||
{
|
||||
public function current(): \Friendica\User\Settings\Entity\UserGServer
|
||||
{
|
||||
return parent::current();
|
||||
}
|
||||
}
|
||||
81
src/Core/Hooks/Capabilities/ICanManageInstances.php
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Capabilities;
|
||||
|
||||
use Friendica\Core\Hooks\Exceptions\HookInstanceException;
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
|
||||
/**
|
||||
* Managing special instance and decorator treatments for classes
|
||||
*/
|
||||
interface ICanManageInstances
|
||||
{
|
||||
/**
|
||||
* Register a class(strategy) for a given interface with a unique name.
|
||||
*
|
||||
* @see https://refactoring.guru/design-patterns/strategy
|
||||
*
|
||||
* @param string $interface The interface, which the given class implements
|
||||
* @param string $name An arbitrary identifier for the given class, which will be used for factories, dependency injections etc.
|
||||
* @param string $class The fully-qualified given class name
|
||||
* @param ?array $arguments Additional arguments, which can be passed to the constructor
|
||||
*
|
||||
* @return $this This interface for chain-calls
|
||||
*
|
||||
* @throws HookRegisterArgumentException in case the given class for the interface isn't valid or already set
|
||||
*/
|
||||
public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): self;
|
||||
|
||||
/**
|
||||
* Register a new decorator for a given class or interface
|
||||
* @see https://refactoring.guru/design-patterns/decorator
|
||||
*
|
||||
* @note Decorator attach new behaviors to classes without changing them or without letting them know about it.
|
||||
*
|
||||
* @param string $class The fully-qualified class or interface name, which gets decorated by a class
|
||||
* @param string $decoratorClass The fully-qualified name of the class which mimics the given class or interface and adds new functionality
|
||||
* @param array $arguments Additional arguments, which can be passed to the constructor of "decoratorClass"
|
||||
*
|
||||
* @return $this This interface for chain-calls
|
||||
*
|
||||
* @throws HookRegisterArgumentException in case the given class for the class or interface isn't valid
|
||||
*/
|
||||
public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): self;
|
||||
|
||||
/**
|
||||
* Returns a new instance of a given class for the corresponding name
|
||||
*
|
||||
* The instance will be build based on the registered strategy and the (unique) name
|
||||
*
|
||||
* In case, there are registered decorators for this class as well, all decorators of the list will be wrapped
|
||||
* around the instance before returning it
|
||||
*
|
||||
* @param string $class The fully-qualified name of the given class or interface which will get returned
|
||||
* @param string $name An arbitrary identifier to find a concrete instance strategy.
|
||||
* @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime
|
||||
*
|
||||
* @return object The concrete instance of the type "$class"
|
||||
*
|
||||
* @throws HookInstanceException In case the class cannot get created
|
||||
*/
|
||||
public function getInstance(string $class, string $name, array $arguments = []): object;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Capability;
|
||||
|
||||
/**
|
||||
* An enum of hook types, based on behavioral design patterns
|
||||
* @see https://refactoring.guru/design-patterns/behavioral-patterns
|
||||
*/
|
||||
interface BehavioralHookType
|
||||
{
|
||||
/**
|
||||
* Defines the key for the list of strategy-hooks.
|
||||
*
|
||||
* @see https://refactoring.guru/design-patterns/strategy
|
||||
*/
|
||||
const STRATEGY = 'strategy';
|
||||
const EVENT = 'event';
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Capability;
|
||||
|
||||
/**
|
||||
* creates special instances for given classes
|
||||
*/
|
||||
interface ICanCreateInstances
|
||||
{
|
||||
/**
|
||||
* Returns a new instance of a given class for the corresponding name
|
||||
*
|
||||
* The instance will be build based on the registered strategy and the (unique) name
|
||||
*
|
||||
* @param string $class The fully-qualified name of the given class or interface which will get returned
|
||||
* @param string $strategy An arbitrary identifier to find a concrete instance strategy.
|
||||
* @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime
|
||||
*
|
||||
* @return object The concrete instance of the type "$class"
|
||||
*/
|
||||
public function create(string $class, string $strategy, array $arguments = []): object;
|
||||
}
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Capability;
|
||||
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
|
||||
/**
|
||||
* Register strategies for given classes
|
||||
*/
|
||||
interface ICanRegisterStrategies
|
||||
{
|
||||
/**
|
||||
* Register a class(strategy) for a given interface with a unique name.
|
||||
*
|
||||
* @see https://refactoring.guru/design-patterns/strategy
|
||||
*
|
||||
* @param string $interface The interface, which the given class implements
|
||||
* @param string $class The fully-qualified given class name
|
||||
* A placeholder for dependencies is possible as well
|
||||
* @param ?string $name An arbitrary identifier for the given strategy, which will be used for factories, dependency injections etc.
|
||||
*
|
||||
* @return $this This interface for chain-calls
|
||||
*
|
||||
* @throws HookRegisterArgumentException in case the given class for the interface isn't valid or already set
|
||||
*/
|
||||
public function registerStrategy(string $interface, string $class, ?string $name = null): self;
|
||||
}
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Model;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Hooks\Capability\ICanCreateInstances;
|
||||
use Friendica\Core\Hooks\Capability\ICanRegisterStrategies;
|
||||
use Friendica\Core\Hooks\Exceptions\HookInstanceException;
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
use Friendica\Core\Hooks\Util\StrategiesFileManager;
|
||||
|
||||
/**
|
||||
* This class represents an instance register, which uses Dice for creation
|
||||
*
|
||||
* @see Dice
|
||||
*/
|
||||
class DiceInstanceManager implements ICanCreateInstances, ICanRegisterStrategies
|
||||
{
|
||||
protected $instance = [];
|
||||
|
||||
/** @var Dice */
|
||||
protected $dice;
|
||||
|
||||
public function __construct(Dice $dice, StrategiesFileManager $strategiesFileManager)
|
||||
{
|
||||
$this->dice = $dice;
|
||||
$strategiesFileManager->setupStrategies($this);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function registerStrategy(string $interface, string $class, ?string $name = null): ICanRegisterStrategies
|
||||
{
|
||||
if (!empty($this->instance[$interface][strtolower($name)])) {
|
||||
throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface));
|
||||
}
|
||||
|
||||
$this->instance[$interface][strtolower($name)] = $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function create(string $class, string $strategy, array $arguments = []): object
|
||||
{
|
||||
if (empty($this->instance[$class][strtolower($strategy)])) {
|
||||
throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $strategy, $class));
|
||||
}
|
||||
|
||||
return $this->dice->create($this->instance[$class][strtolower($strategy)], $arguments);
|
||||
}
|
||||
}
|
||||
104
src/Core/Hooks/Model/InstanceManager.php
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Model;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Hooks\Exceptions\HookInstanceException;
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
class InstanceManager implements ICanManageInstances
|
||||
{
|
||||
protected $instance = [];
|
||||
protected $instanceArguments = [];
|
||||
protected $decorator = [];
|
||||
|
||||
/** @var Dice */
|
||||
protected $dice;
|
||||
|
||||
public function __construct(Dice $dice)
|
||||
{
|
||||
$this->dice = $dice;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): ICanManageInstances
|
||||
{
|
||||
if (!is_a($class, $interface, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s is not a valid class for the interface %s', $class, $interface));
|
||||
}
|
||||
|
||||
if (!is_a($class, IAmAStrategy::class, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s does not inherit from the marker interface %s', $class, IAmAStrategy::class));
|
||||
}
|
||||
|
||||
if (!empty($this->instance[$interface][$name])) {
|
||||
throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface));
|
||||
}
|
||||
|
||||
$this->instance[$interface][$name] = $class;
|
||||
$this->instanceArguments[$interface][$name] = $arguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): ICanManageInstances
|
||||
{
|
||||
if (!is_a($decoratorClass, $class, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s is not a valid substitution for the given class or interface %s', $decoratorClass, $class));
|
||||
}
|
||||
|
||||
$this->decorator[$class][] = [
|
||||
'class' => $decoratorClass,
|
||||
'arguments' => $arguments,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function getInstance(string $class, string $name, array $arguments = []): object
|
||||
{
|
||||
if (empty($this->instance[$class][$name])) {
|
||||
throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class));
|
||||
}
|
||||
|
||||
$instance = $this->dice->create($this->instance[$class][$name], array_merge($this->instanceArguments[$class][$name] ?? [], $arguments));
|
||||
|
||||
foreach ($this->decorator[$class] ?? [] as $decorator) {
|
||||
$this->dice = $this->dice->addRule($class, [
|
||||
'instanceOf' => $decorator['class'],
|
||||
'constructParams' => empty($decorator['arguments']) ? null : $decorator['arguments'],
|
||||
/// @todo maybe support call structures for hooks as well in a later stage - could make factory calls easier
|
||||
'call' => null,
|
||||
'substitutions' => [$class => $instance],
|
||||
]);
|
||||
|
||||
$instance = $this->dice->create($class);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Hooks\Util;
|
||||
|
||||
use Friendica\Core\Addon\Capability\ICanLoadAddons;
|
||||
use Friendica\Core\Hooks\Capability\ICanRegisterStrategies;
|
||||
use Friendica\Core\Hooks\Exceptions\HookConfigException;
|
||||
|
||||
/**
|
||||
* Manage all strategies.config.php files
|
||||
*/
|
||||
class StrategiesFileManager
|
||||
{
|
||||
/**
|
||||
* The default hook-file-key of strategies
|
||||
* -> it's an empty string to cover empty/missing config values
|
||||
*/
|
||||
const STRATEGY_DEFAULT_KEY = '';
|
||||
const STATIC_DIR = 'static';
|
||||
const CONFIG_NAME = 'strategies';
|
||||
|
||||
/** @var ICanLoadAddons */
|
||||
protected $addonLoader;
|
||||
/** @var array */
|
||||
protected $config = [];
|
||||
/** @var string */
|
||||
protected $basePath;
|
||||
|
||||
public function __construct(string $basePath, ICanLoadAddons $addonLoader)
|
||||
{
|
||||
$this->basePath = $basePath;
|
||||
$this->addonLoader = $addonLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all kinds of hooks and registers the corresponding instances
|
||||
*
|
||||
* @param ICanRegisterStrategies $instanceRegister The instance register
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setupStrategies(ICanRegisterStrategies $instanceRegister)
|
||||
{
|
||||
foreach ($this->config as $interface => $strategy) {
|
||||
foreach ($strategy as $dependencyName => $names) {
|
||||
if (is_array($names)) {
|
||||
foreach ($names as $name) {
|
||||
$instanceRegister->registerStrategy($interface, $dependencyName, $name);
|
||||
}
|
||||
} else {
|
||||
$instanceRegister->registerStrategy($interface, $dependencyName, $names);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads all hook config files into the config cache for later usage
|
||||
*
|
||||
* Merges all hook configs from every addon - if present - as well
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function loadConfig()
|
||||
{
|
||||
// load core hook config
|
||||
$configFile = $this->basePath . '/' . static::STATIC_DIR . '/' . static::CONFIG_NAME . '.config.php';
|
||||
|
||||
if (!file_exists($configFile)) {
|
||||
throw new HookConfigException(sprintf('config file %s does not exist.', $configFile));
|
||||
}
|
||||
|
||||
$config = include $configFile;
|
||||
|
||||
if (!is_array($config)) {
|
||||
throw new HookConfigException(sprintf('Error loading config file %s.', $configFile));
|
||||
}
|
||||
|
||||
$this->config = array_merge_recursive($config, $this->addonLoader->getActiveAddonConfig(static::CONFIG_NAME));
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\KeyValueStorage\Capability;
|
||||
namespace Friendica\Core\KeyValueStorage\Capabilities;
|
||||
|
||||
use Friendica\Core\KeyValueStorage\Exceptions\KeyValueStoragePersistenceException;
|
||||
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\KeyValueStorage\Factory;
|
||||
|
||||
use Friendica\Core\Hooks\Capability\ICanCreateInstances;
|
||||
use Friendica\Core\Hooks\Util\StrategiesFileManager;
|
||||
use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
|
||||
|
||||
class KeyValueStorage
|
||||
{
|
||||
public function create(ICanCreateInstances $instanceCreator): IManageKeyValuePairs
|
||||
{
|
||||
/** @var IManageKeyValuePairs */
|
||||
return $instanceCreator->create(IManageKeyValuePairs::class, StrategiesFileManager::STRATEGY_DEFAULT_KEY);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,15 +21,13 @@
|
|||
|
||||
namespace Friendica\Core\KeyValueStorage\Type;
|
||||
|
||||
use Friendica\Core\KeyValueStorage\Capability\IManageKeyValuePairs;
|
||||
use Friendica\Core\KeyValueStorage\Capabilities\IManageKeyValuePairs;
|
||||
|
||||
/**
|
||||
* An abstract helper class for Key-Value storage classes
|
||||
*/
|
||||
abstract class AbstractKeyValueStorage implements IManageKeyValuePairs
|
||||
{
|
||||
const NAME = '';
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function get(string $key)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ use Friendica\Database\Database;
|
|||
*/
|
||||
class DBKeyValueStorage extends AbstractKeyValueStorage
|
||||
{
|
||||
const NAME = 'database';
|
||||
const DB_KEY_VALUE_TABLE = 'key-value';
|
||||
|
||||
/** @var Database */
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
namespace Friendica\Core\Lock\Enum;
|
||||
|
||||
use Friendica\Core\Cache\Type\DatabaseCache;
|
||||
use Friendica\Core\Cache\Enum\Type as CacheType;
|
||||
|
||||
/**
|
||||
* Enumeration for lock types
|
||||
|
|
@ -30,6 +30,6 @@ use Friendica\Core\Cache\Type\DatabaseCache;
|
|||
*/
|
||||
abstract class Type
|
||||
{
|
||||
const DATABASE = DatabaseCache::NAME;
|
||||
const DATABASE = CacheType::DATABASE;
|
||||
const SEMAPHORE = 'semaphore';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ namespace Friendica\Core\Lock\Factory;
|
|||
|
||||
use Friendica\Core\Cache\Factory\Cache;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Cache\Type as CacheType;
|
||||
use Friendica\Core\Cache\Enum;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Lock\Capability\ICanLock;
|
||||
use Friendica\Core\Lock\Type as LockType;
|
||||
use Friendica\Core\Lock\Type;
|
||||
use Friendica\Database\Database;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
|
@ -78,20 +78,20 @@ class Lock
|
|||
|
||||
try {
|
||||
switch ($lock_type) {
|
||||
case CacheType\MemcacheCache::NAME:
|
||||
case CacheType\MemcachedCache::NAME:
|
||||
case CacheType\RedisCache::NAME:
|
||||
case CacheType\APCuCache::NAME:
|
||||
case Enum\Type::MEMCACHE:
|
||||
case Enum\Type::MEMCACHED:
|
||||
case Enum\Type::REDIS:
|
||||
case Enum\Type::APCU:
|
||||
$cache = $this->cacheFactory->createLocal($lock_type);
|
||||
if ($cache instanceof ICanCacheInMemory) {
|
||||
return new LockType\CacheLock($cache);
|
||||
return new Type\CacheLock($cache);
|
||||
} else {
|
||||
throw new \Exception(sprintf('Incompatible cache driver \'%s\' for lock used', $lock_type));
|
||||
}
|
||||
case 'database':
|
||||
return new LockType\DatabaseLock($this->dba);
|
||||
return new Type\DatabaseLock($this->dba);
|
||||
case 'semaphore':
|
||||
return new LockType\SemaphoreLock();
|
||||
return new Type\SemaphoreLock();
|
||||
default:
|
||||
return self::useAutoDriver();
|
||||
}
|
||||
|
|
@ -116,7 +116,7 @@ class Lock
|
|||
// 1. Try to use Semaphores for - local - locking
|
||||
if (function_exists('sem_get')) {
|
||||
try {
|
||||
return new LockType\SemaphoreLock();
|
||||
return new Type\SemaphoreLock();
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->warning('Using Semaphore driver for locking failed.', ['exception' => $exception]);
|
||||
}
|
||||
|
|
@ -124,11 +124,11 @@ class Lock
|
|||
|
||||
// 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!)
|
||||
$cache_type = $this->config->get('system', 'cache_driver', 'database');
|
||||
if ($cache_type != CacheType\DatabaseCache::NAME) {
|
||||
if ($cache_type != Enum\Type::DATABASE) {
|
||||
try {
|
||||
$cache = $this->cacheFactory->createLocal($cache_type);
|
||||
if ($cache instanceof ICanCacheInMemory) {
|
||||
return new LockType\CacheLock($cache);
|
||||
return new Type\CacheLock($cache);
|
||||
}
|
||||
} catch (\Exception $exception) {
|
||||
$this->logger->warning('Using Cache driver for locking failed.', ['exception' => $exception]);
|
||||
|
|
@ -136,6 +136,6 @@ class Lock
|
|||
}
|
||||
|
||||
// 3. Use Database Locking as a Fallback
|
||||
return new LockType\DatabaseLock($this->dba);
|
||||
return new Type\DatabaseLock($this->dba);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ abstract class AbstractLock implements ICanLock
|
|||
/**
|
||||
* Check if we've locally acquired a lock
|
||||
*
|
||||
* @param string $key The Name of the lock
|
||||
* @param string key The Name of the lock
|
||||
*
|
||||
* @return bool Returns true if the lock is set
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Capability;
|
||||
namespace Friendica\Core\Logger\Capabilities;
|
||||
|
||||
interface IHaveCallIntrospections
|
||||
{
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Capability;
|
||||
|
||||
/**
|
||||
* Whenever a logging specific check is necessary, use this interface to encapsulate and centralize this logic
|
||||
*/
|
||||
interface ICheckLoggerSettings
|
||||
{
|
||||
/**
|
||||
* Checks if the logfile is set and usable
|
||||
*
|
||||
* @return string|null null in case everything is ok, otherwise returns the error
|
||||
*/
|
||||
public function checkLogfile(): ?string;
|
||||
|
||||
/**
|
||||
* Checks if the debugging logfile is usable in case it is set!
|
||||
*
|
||||
* @return string|null null in case everything is ok, otherwise returns the error
|
||||
*/
|
||||
public function checkDebugLogfile(): ?string;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Capability;
|
||||
|
||||
/**
|
||||
* An enum class for the Log channels
|
||||
*/
|
||||
interface LogChannel
|
||||
{
|
||||
/** @var string channel for the auth_ejabbered script */
|
||||
public const AUTH_JABBERED = 'auth_ejabberd';
|
||||
/** @var string Default channel in case it isn't set explicit */
|
||||
public const DEFAULT = self::APP;
|
||||
/** @var string channel for console execution */
|
||||
public const CONSOLE = 'console';
|
||||
/** @var string channel for developer focused logging */
|
||||
public const DEV = 'dev';
|
||||
/** @var string channel for daemon executions */
|
||||
public const DAEMON = 'daemon';
|
||||
/** @var string channel for worker execution */
|
||||
public const WORKER = 'worker';
|
||||
/** @var string channel for frontend app executions */
|
||||
public const APP = 'app';
|
||||
}
|
||||
|
|
@ -23,12 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case the loglevel isn't set or isn't valid
|
||||
*/
|
||||
class LogLevelException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct($message = '', Throwable $previous = null)
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case an argument of a logger class isn't valid
|
||||
*/
|
||||
class LoggerArgumentException extends \InvalidArgumentException
|
||||
{
|
||||
public function __construct($message = '', Throwable $previous = null)
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,9 @@ namespace Friendica\Core\Logger\Exception;
|
|||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* A generic exception of the logging namespace
|
||||
*/
|
||||
class LoggerException extends \Exception
|
||||
{
|
||||
public function __construct($message = '', Throwable $previous = null)
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@
|
|||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Exceptions;
|
||||
namespace Friendica\Core\Logger\Exception;
|
||||
|
||||
class HookConfigException extends \RuntimeException
|
||||
use Throwable;
|
||||
|
||||
class LoggerInvalidException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = '', \Throwable $previous = null)
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Exception in case the used logging instance is unusable because of some circumstances
|
||||
*/
|
||||
class LoggerUnusableException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = '', Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Logger\Capability\IHaveCallIntrospections;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* Abstract class for creating logger types, which includes common necessary logic/content
|
||||
*/
|
||||
abstract class AbstractLoggerTypeFactory
|
||||
{
|
||||
/** @var string */
|
||||
protected $channel;
|
||||
/** @var IHaveCallIntrospections */
|
||||
protected $introspection;
|
||||
|
||||
/**
|
||||
* @param string $channel The channel for the logger
|
||||
*/
|
||||
public function __construct(IHaveCallIntrospections $introspection, string $channel)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->introspection = $introspection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping a legacy level to the PSR-3 compliant levels
|
||||
*
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
|
||||
*
|
||||
* @param string $level the level to be mapped
|
||||
*
|
||||
* @return string the PSR-3 compliant level
|
||||
*/
|
||||
protected static function mapLegacyConfigDebugLevel(string $level): string
|
||||
{
|
||||
switch ($level) {
|
||||
// legacy WARNING
|
||||
case "0":
|
||||
return LogLevel::ERROR;
|
||||
// legacy INFO
|
||||
case "1":
|
||||
return LogLevel::WARNING;
|
||||
// legacy TRACE
|
||||
case "2":
|
||||
return LogLevel::NOTICE;
|
||||
// legacy DEBUG
|
||||
case "3":
|
||||
return LogLevel::INFO;
|
||||
// legacy DATA
|
||||
case "4":
|
||||
// legacy ALL
|
||||
case "5":
|
||||
return LogLevel::DEBUG;
|
||||
// default if nothing set
|
||||
default:
|
||||
return $level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,46 +22,134 @@
|
|||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capability\ICanCreateInstances;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Logger\Type\ProfilerLogger as ProfilerLoggerClass;
|
||||
use Friendica\Util\Profiler;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Core\Logger\Type\ProfilerLogger;
|
||||
use Friendica\Core\Logger\Type\StreamLogger;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\NullLogger;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* The logger factory for the core logging instances
|
||||
* A logger factory
|
||||
*/
|
||||
class Logger
|
||||
{
|
||||
/** @var string The channel */
|
||||
protected $channel;
|
||||
const DEV_CHANNEL = 'dev';
|
||||
|
||||
public function __construct(string $channel = LogChannel::DEFAULT)
|
||||
/** @var string The log-channel (app, worker, ...) */
|
||||
protected $channel;
|
||||
/** @var ICanManageInstances */
|
||||
protected $instanceManager;
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $channel, ICanManageInstances $instanceManager, IManageConfigValues $config, string $logfile = null)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->channel = $channel;
|
||||
$this->instanceManager = $instanceManager;
|
||||
$this->config = $config;
|
||||
|
||||
$this->instanceManager
|
||||
->registerStrategy(LoggerInterface::class, 'syslog', SyslogLogger::class)
|
||||
->registerStrategy(LoggerInterface::class, 'stream', StreamLogger::class, isset($logfile) ? [$logfile] : null);
|
||||
|
||||
if ($this->config->get('system', 'profiling') ?? false) {
|
||||
$this->instanceManager->registerDecorator(LoggerInterface::class, ProfilerLogger::class);
|
||||
}
|
||||
}
|
||||
|
||||
public function create(ICanCreateInstances $instanceCreator, IManageConfigValues $config, Profiler $profiler): LoggerInterface
|
||||
/**
|
||||
* Creates a new PSR-3 compliant logger instances
|
||||
*
|
||||
* @param string|null $loglevel (optional) A given loglevel in case the loglevel in the config isn't applicable
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*/
|
||||
public function create(string $loglevel = null): LoggerInterface
|
||||
{
|
||||
if (empty($config->get('system', 'debugging') ?? false)) {
|
||||
if (empty($this->config->get('system', 'debugging') ?? false)) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$name = $config->get('system', 'logger_config') ?? '';
|
||||
$loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($this->config->get('system', 'loglevel'));
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
|
||||
try {
|
||||
/** @var LoggerInterface $logger */
|
||||
$logger = $instanceCreator->create(LoggerInterface::class, $name, [$this->channel]);
|
||||
if ($config->get('system', 'profiling') ?? false) {
|
||||
return new ProfilerLoggerClass($logger, $profiler);
|
||||
} else {
|
||||
return $logger;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [$this->channel, $loglevel]);
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create(LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
return $logger;
|
||||
} catch (\Throwable $e) {
|
||||
// No logger ...
|
||||
return new NullLogger();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant develop logger
|
||||
*
|
||||
* If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
|
||||
* you'll use this logger instance for the duration of your work.
|
||||
*
|
||||
* It should never get filled during normal usage of Friendica
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createDev()
|
||||
{
|
||||
$debugging = $this->config->get('system', 'debugging');
|
||||
$stream = $this->config->get('system', 'dlogfile');
|
||||
$developerIp = $this->config->get('system', 'dlogip');
|
||||
|
||||
if ((!isset($developerIp) || !$debugging) &&
|
||||
(!is_file($stream) || is_writable($stream))) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [self::DEV_CHANNEL, LogLevel::DEBUG, $stream]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping a legacy level to the PSR-3 compliant levels
|
||||
*
|
||||
* @see https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md#5-psrlogloglevel
|
||||
*
|
||||
* @param string $level the level to be mapped
|
||||
*
|
||||
* @return string the PSR-3 compliant level
|
||||
*/
|
||||
private static function mapLegacyConfigDebugLevel(string $level): string
|
||||
{
|
||||
switch ($level) {
|
||||
// legacy WARNING
|
||||
case "0":
|
||||
return LogLevel::ERROR;
|
||||
// legacy INFO
|
||||
case "1":
|
||||
return LogLevel::WARNING;
|
||||
// legacy TRACE
|
||||
case "2":
|
||||
return LogLevel::NOTICE;
|
||||
// legacy DEBUG
|
||||
case "3":
|
||||
return LogLevel::INFO;
|
||||
// legacy DATA
|
||||
case "4":
|
||||
// legacy ALL
|
||||
case "5":
|
||||
return LogLevel::DEBUG;
|
||||
// default if nothing set
|
||||
default:
|
||||
return $level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,100 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Capability\LogChannel;
|
||||
use Friendica\Core\Logger\Exception\LoggerArgumentException;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Core\Logger\Type\StreamLogger as StreamLoggerClass;
|
||||
use Friendica\Core\Logger\Util\FileSystem;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* The logger factory for the StreamLogger instance
|
||||
*
|
||||
* @see StreamLoggerClass
|
||||
*/
|
||||
class StreamLogger extends AbstractLoggerTypeFactory
|
||||
{
|
||||
/**
|
||||
* Creates a new PSR-3 compliant stream logger instance
|
||||
*
|
||||
* @param IManageConfigValues $config The system configuration
|
||||
* @param string|null $logfile (optional) A given logfile which should be used as stream (e.g. in case of
|
||||
* developer logging)
|
||||
* @param string|null $channel (optional) A given channel in case it is different from the default
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException in case the logger cannot get created
|
||||
*/
|
||||
public function create(IManageConfigValues $config, string $logfile = null, string $channel = null): LoggerInterface
|
||||
{
|
||||
$fileSystem = new FileSystem();
|
||||
|
||||
$logfile = $logfile ?? $config->get('system', 'logfile');
|
||||
if (!@file_exists($logfile) || !@is_writable($logfile)) {
|
||||
throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $logfile));
|
||||
}
|
||||
|
||||
$loglevel = static::mapLegacyConfigDebugLevel($config->get('system', 'loglevel'));
|
||||
|
||||
if (array_key_exists($loglevel, StreamLoggerClass::levelToInt)) {
|
||||
$loglevel = StreamLoggerClass::levelToInt[$loglevel];
|
||||
} else {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $loglevel));
|
||||
}
|
||||
|
||||
$stream = $fileSystem->createStream($logfile);
|
||||
|
||||
return new StreamLoggerClass($channel ?? $this->channel, $this->introspection, $stream, $loglevel, getmypid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant develop logger
|
||||
*
|
||||
* If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
|
||||
* you'll use this logger instance for the duration of your work.
|
||||
*
|
||||
* It should never get filled during normal usage of Friendica
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function createDev(IManageConfigValues $config)
|
||||
{
|
||||
$debugging = $config->get('system', 'debugging');
|
||||
$logfile = $config->get('system', 'dlogfile');
|
||||
$developerIp = $config->get('system', 'dlogip');
|
||||
|
||||
if ((!isset($developerIp) || !$debugging) &&
|
||||
(!is_file($logfile) || is_writable($logfile))) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
return $this->create($config, $logfile, LogChannel::DEV);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger as SyslogLoggerClass;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
/**
|
||||
* The logger factory for the SyslogLogger instance
|
||||
*
|
||||
* @see SyslogLoggerClass
|
||||
*/
|
||||
class SyslogLogger extends AbstractLoggerTypeFactory
|
||||
{
|
||||
/**
|
||||
* Creates a new PSR-3 compliant syslog logger instance
|
||||
*
|
||||
* @param IManageConfigValues $config The system configuration
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*
|
||||
* @throws LoggerException in case the logger cannot get created
|
||||
*/
|
||||
public function create(IManageConfigValues $config): LoggerInterface
|
||||
{
|
||||
$logOpts = $config->get('system', 'syslog_flags') ?? SyslogLoggerClass::DEFAULT_FLAGS;
|
||||
$logFacility = $config->get('system', 'syslog_facility') ?? SyslogLoggerClass::DEFAULT_FACILITY;
|
||||
$loglevel = SyslogLogger::mapLegacyConfigDebugLevel($config->get('system', 'loglevel'));
|
||||
|
||||
if (array_key_exists($loglevel, SyslogLoggerClass::logLevels)) {
|
||||
$loglevel = SyslogLoggerClass::logLevels[$loglevel];
|
||||
} else {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $loglevel));
|
||||
}
|
||||
|
||||
return new SyslogLoggerClass($this->channel, $this->introspection, $loglevel, $logOpts, $logFacility);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Logger\Capability\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
|
@ -38,8 +38,6 @@ use Psr\Log\LogLevel;
|
|||
*/
|
||||
abstract class AbstractLogger implements LoggerInterface
|
||||
{
|
||||
const NAME = '';
|
||||
|
||||
/**
|
||||
* The output channel of this logger
|
||||
* @var string
|
||||
|
|
|
|||
26
src/Core/Logger/Type/README.md
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
## Friendica\Util\Logger
|
||||
|
||||
This namespace contains the different implementations of a Logger.
|
||||
|
||||
### Configuration guideline
|
||||
|
||||
The following settings are possible for `logger_config`:
|
||||
- [`stream`](StreamLogger.php): A small logger for files or streams
|
||||
- [`syslog`](SyslogLogger.php): Prints the logging output into the syslog
|
||||
|
||||
[`VoidLogger`](VoidLogger.php) is a fallback logger without any function if no debugging is enabled.
|
||||
|
||||
[`ProfilerLogger`](ProfilerLogger.php) is a wrapper around an existing logger in case profiling is enabled for Friendica.
|
||||
Every log call will be saved to the `Profiler` with a timestamp.
|
||||
|
||||
### Implementation guideline
|
||||
|
||||
Each logging implementation should pe capable of printing at least the following information:
|
||||
- An unique ID for each Request/Call
|
||||
- The process ID (PID)
|
||||
- A timestamp of the logging entry
|
||||
- The critically of the log entry
|
||||
- A log message
|
||||
- A context of the log message (f.e which user)
|
||||
|
||||
If possible, a Logger should extend [`AbstractLogger`](AbstractLogger.php), because it contains additional, Friendica specific business logic for each logging call.
|
||||
|
|
@ -21,25 +21,33 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Logger\Capability\IHaveCallIntrospections;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerArgumentException;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\FileSystem;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* A Logger instance for logging into a stream (file, stdout, stderr)
|
||||
*/
|
||||
class StreamLogger extends AbstractLogger
|
||||
class StreamLogger extends AbstractLogger implements IAmAStrategy
|
||||
{
|
||||
const NAME = 'stream';
|
||||
|
||||
/**
|
||||
* The minimum loglevel at which this logger will be triggered
|
||||
* @var string
|
||||
*/
|
||||
private $logLevel;
|
||||
|
||||
/**
|
||||
* The file URL of the stream (if needed)
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* The stream, where the current logger is writing into
|
||||
* @var resource
|
||||
|
|
@ -52,11 +60,16 @@ class StreamLogger extends AbstractLogger
|
|||
*/
|
||||
private $pid;
|
||||
|
||||
/**
|
||||
* @var FileSystem
|
||||
*/
|
||||
private $fileSystem;
|
||||
|
||||
/**
|
||||
* Translates LogLevel log levels to integer values
|
||||
* @var array
|
||||
*/
|
||||
public const levelToInt = [
|
||||
private $levelToInt = [
|
||||
LogLevel::EMERGENCY => 0,
|
||||
LogLevel::ALERT => 1,
|
||||
LogLevel::CRITICAL => 2,
|
||||
|
|
@ -71,20 +84,41 @@ class StreamLogger extends AbstractLogger
|
|||
* {@inheritdoc}
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
*
|
||||
* @throws LoggerException
|
||||
* @throws LoggerArgumentException
|
||||
* @throws LogLevelException
|
||||
*/
|
||||
public function __construct(string $channel, IHaveCallIntrospections $introspection, $stream, int $logLevel, int $pid)
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
|
||||
{
|
||||
$this->fileSystem = $fileSystem;
|
||||
|
||||
$stream = $this->logfile ?? $config->get('system', 'logfile');
|
||||
if ((@file_exists($stream) && !@is_writable($stream)) && !@is_writable(basename($stream))) {
|
||||
throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $stream));
|
||||
}
|
||||
|
||||
parent::__construct($channel, $introspection);
|
||||
|
||||
$this->stream = $stream;
|
||||
$this->pid = $pid;
|
||||
$this->logLevel = $logLevel;
|
||||
if (is_resource($stream)) {
|
||||
$this->stream = $stream;
|
||||
} elseif (is_string($stream)) {
|
||||
$this->url = $stream;
|
||||
} else {
|
||||
throw new LoggerArgumentException('A stream must either be a resource or a string.');
|
||||
}
|
||||
|
||||
$this->pid = getmypid();
|
||||
if (array_key_exists($level, $this->levelToInt)) {
|
||||
$this->logLevel = $this->levelToInt[$level];
|
||||
} else {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
$this->checkStream();
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
if (is_resource($this->stream)) {
|
||||
if ($this->url && is_resource($this->stream)) {
|
||||
fclose($this->stream);
|
||||
}
|
||||
|
||||
|
|
@ -105,16 +139,18 @@ class StreamLogger extends AbstractLogger
|
|||
*/
|
||||
protected function addEntry($level, string $message, array $context = [])
|
||||
{
|
||||
if (!array_key_exists($level, static::levelToInt)) {
|
||||
if (!array_key_exists($level, $this->levelToInt)) {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
$logLevel = static::levelToInt[$level];
|
||||
$logLevel = $this->levelToInt[$level];
|
||||
|
||||
if ($logLevel > $this->logLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->checkStream();
|
||||
|
||||
$formattedLog = $this->formatLog($level, $message, $context);
|
||||
fwrite($this->stream, $formattedLog);
|
||||
}
|
||||
|
|
@ -149,4 +185,27 @@ class StreamLogger extends AbstractLogger
|
|||
|
||||
return $logMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current stream
|
||||
*
|
||||
* @throws LoggerException
|
||||
* @throws LoggerArgumentException
|
||||
*/
|
||||
private function checkStream()
|
||||
{
|
||||
if (is_resource($this->stream)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (empty($this->url)) {
|
||||
throw new LoggerArgumentException('Missing stream URL.');
|
||||
}
|
||||
|
||||
try {
|
||||
$this->stream = $this->fileSystem->createStream($this->url);
|
||||
} catch (\UnexpectedValueException $exception) {
|
||||
throw new LoggerException('Cannot create stream.', $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Logger\Capability\IHaveCallIntrospections;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Psr\Log\LogLevel;
|
||||
|
|
@ -30,10 +32,8 @@ use Psr\Log\LogLevel;
|
|||
* A Logger instance for syslogging (fast, but simple)
|
||||
* @see http://php.net/manual/en/function.syslog.php
|
||||
*/
|
||||
class SyslogLogger extends AbstractLogger
|
||||
class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
||||
{
|
||||
const NAME = 'syslog';
|
||||
|
||||
const IDENT = 'Friendica';
|
||||
|
||||
/** @var int The default syslog flags */
|
||||
|
|
@ -45,7 +45,7 @@ class SyslogLogger extends AbstractLogger
|
|||
* Translates LogLevel log levels to syslog log priorities.
|
||||
* @var array
|
||||
*/
|
||||
public const logLevels = [
|
||||
private $logLevels = [
|
||||
LogLevel::DEBUG => LOG_DEBUG,
|
||||
LogLevel::INFO => LOG_INFO,
|
||||
LogLevel::NOTICE => LOG_NOTICE,
|
||||
|
|
@ -60,7 +60,7 @@ class SyslogLogger extends AbstractLogger
|
|||
* Translates log priorities to string outputs
|
||||
* @var array
|
||||
*/
|
||||
protected const logToString = [
|
||||
private $logToString = [
|
||||
LOG_DEBUG => 'DEBUG',
|
||||
LOG_INFO => 'INFO',
|
||||
LOG_NOTICE => 'NOTICE',
|
||||
|
|
@ -101,18 +101,19 @@ class SyslogLogger extends AbstractLogger
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
*
|
||||
* @param string $logLevel The minimum loglevel at which this logger will be triggered
|
||||
* @param string $logOptions
|
||||
* @param string $logFacility
|
||||
* @throws LogLevelException
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function __construct(string $channel, IHaveCallIntrospections $introspection, string $logLevel, string $logOptions, string $logFacility)
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, string $level = LogLevel::NOTICE)
|
||||
{
|
||||
parent::__construct($channel, $introspection);
|
||||
|
||||
$this->logOpts = $logOptions;
|
||||
$this->logFacility = $logFacility;
|
||||
$this->logLevel = $logLevel;
|
||||
$this->logOpts = $config->get('system', 'syslog_flags') ?? static::DEFAULT_FLAGS;
|
||||
$this->logFacility = $config->get('system', 'syslog_facility') ?? static::DEFAULT_FACILITY;
|
||||
$this->logLevel = $this->mapLevelToPriority($level);
|
||||
$this->introspection->addClasses([self::class]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -148,11 +149,11 @@ class SyslogLogger extends AbstractLogger
|
|||
*/
|
||||
public function mapLevelToPriority(string $level): int
|
||||
{
|
||||
if (!array_key_exists($level, static::logLevels)) {
|
||||
if (!array_key_exists($level, $this->logLevels)) {
|
||||
throw new LogLevelException(sprintf('The level "%s" is not valid.', $level));
|
||||
}
|
||||
|
||||
return static::logLevels[$level];
|
||||
return $this->logLevels[$level];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,7 +202,7 @@ class SyslogLogger extends AbstractLogger
|
|||
$record = array_merge($record, ['uid' => $this->logUid]);
|
||||
|
||||
$logMessage = $this->channel . ' ';
|
||||
$logMessage .= '[' . static::logToString[$level] . ']: ';
|
||||
$logMessage .= '[' . $this->logToString[$level] . ']: ';
|
||||
$logMessage .= $this->psrInterpolate($message, $context) . ' ';
|
||||
$logMessage .= $this->jsonEncodeArray($context) . ' - ';
|
||||
$logMessage .= $this->jsonEncodeArray($record);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
namespace Friendica\Core\Logger\Util;
|
||||
|
||||
use Friendica\App\Request;
|
||||
use Friendica\Core\Logger\Capability\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
|
||||
/**
|
||||
* Get Introspection information about the current call
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Core\Logger\Util;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Logger\Capability\ICheckLoggerSettings;
|
||||
use Friendica\Core\Logger\Exception\LoggerUnusableException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
class LoggerSettingsCheck implements ICheckLoggerSettings
|
||||
{
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
/** @var $fileSystem */
|
||||
protected $fileSystem;
|
||||
/** @var L10n */
|
||||
protected $l10n;
|
||||
|
||||
public function __construct(IManageConfigValues $config, FileSystem $fileSystem, L10n $l10n)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->fileSystem = $fileSystem;
|
||||
$this->l10n = $l10n;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function checkLogfile(): ?string
|
||||
{
|
||||
// Check logfile permission
|
||||
if ($this->config->get('system', 'debugging')) {
|
||||
$file = $this->config->get('system', 'logfile');
|
||||
|
||||
try {
|
||||
$stream = $this->fileSystem->createStream($file);
|
||||
|
||||
if (!isset($stream)) {
|
||||
throw new LoggerUnusableException('Stream is null.');
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
return $this->l10n->t('The logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function checkDebugLogfile(): ?string
|
||||
{
|
||||
// Check logfile permission
|
||||
if ($this->config->get('system', 'debugging')) {
|
||||
$file = $this->config->get('system', 'dlogfile');
|
||||
|
||||
if (empty($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$stream = $this->fileSystem->createStream($file);
|
||||
|
||||
if (!isset($stream)) {
|
||||
throw new LoggerUnusableException('Stream is null.');
|
||||
}
|
||||
} catch (\Throwable $exception) {
|
||||
return $this->l10n->t('The debug logfile \'%s\' is not usable. No logging possible (error: \'%s\')', $file, $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,16 +22,28 @@
|
|||
namespace Friendica\Core\PConfig\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capability\ICanCreateInstances;
|
||||
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
||||
use Friendica\Core\PConfig\Repository;
|
||||
use Friendica\Core\PConfig\Type;
|
||||
use Friendica\Core\PConfig\ValueObject;
|
||||
|
||||
class PConfig
|
||||
{
|
||||
public function create(ICanCreateInstances $instanceCreator, IManageConfigValues $config): IManagePersonalConfigValues
|
||||
/**
|
||||
* @param IManageConfigValues $config The config
|
||||
* @param ValueObject\Cache $pConfigCache The personal config cache
|
||||
* @param Repository\PConfig $configRepo The configuration model
|
||||
*
|
||||
* @return IManagePersonalConfigValues
|
||||
*/
|
||||
public function create(IManageConfigValues $config, ValueObject\Cache $pConfigCache, Repository\PConfig $configRepo): IManagePersonalConfigValues
|
||||
{
|
||||
$strategy = $config->get('system', 'config_adapter');
|
||||
if ($config->get('system', 'config_adapter') === 'preload') {
|
||||
$configuration = new Type\PreloadPConfig($pConfigCache, $configRepo);
|
||||
} else {
|
||||
$configuration = new Type\JitPConfig($pConfigCache, $configRepo);
|
||||
}
|
||||
|
||||
/** @var IManagePersonalConfigValues */
|
||||
return $instanceCreator->create(IManagePersonalConfigValues::class, $strategy);
|
||||
return $configuration;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,8 +34,6 @@ use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
|
|||
*/
|
||||
abstract class AbstractPConfigValues implements IManagePersonalConfigValues
|
||||
{
|
||||
const NAME = '';
|
||||
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@ use Friendica\Core\PConfig\ValueObject;
|
|||
*/
|
||||
class JitPConfig extends AbstractPConfigValues
|
||||
{
|
||||
const NAME = 'jit';
|
||||
|
||||
/**
|
||||
* @var array Array of already loaded db values (even if there was no value)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -32,8 +32,6 @@ use Friendica\Core\PConfig\ValueObject;
|
|||
*/
|
||||
class PreloadPConfig extends AbstractPConfigValues
|
||||
{
|
||||
const NAME = 'preload';
|
||||
|
||||
/** @var array */
|
||||
private $config_loaded;
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Search
|
|||
const DEFAULT_DIRECTORY = 'https://dir.friendica.social';
|
||||
|
||||
const TYPE_PEOPLE = 0;
|
||||
const TYPE_GROUP = 1;
|
||||
const TYPE_FORUM = 1;
|
||||
const TYPE_ALL = 2;
|
||||
|
||||
/**
|
||||
|
|
@ -55,7 +55,7 @@ class Search
|
|||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function getContactsFromProbe(string $user, $only_group = false): ResultList
|
||||
public static function getContactsFromProbe(string $user, $only_forum = false): ResultList
|
||||
{
|
||||
$emptyResultList = new ResultList();
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ class Search
|
|||
return $emptyResultList;
|
||||
}
|
||||
|
||||
if ($only_group && ($user_data['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
if ($only_forum && ($user_data['contact-type'] != Contact::TYPE_COMMUNITY)) {
|
||||
return $emptyResultList;
|
||||
}
|
||||
|
||||
|
|
@ -112,8 +112,8 @@ class Search
|
|||
$searchUrl = $server . '/search';
|
||||
|
||||
switch ($type) {
|
||||
case self::TYPE_GROUP:
|
||||
$searchUrl .= '/group';
|
||||
case self::TYPE_FORUM:
|
||||
$searchUrl .= '/forum';
|
||||
break;
|
||||
case self::TYPE_PEOPLE:
|
||||
$searchUrl .= '/people';
|
||||
|
|
@ -174,7 +174,7 @@ class Search
|
|||
{
|
||||
Logger::info('Searching', ['search' => $search, 'type' => $type, 'start' => $start, 'itempage' => $itemPage]);
|
||||
|
||||
$contacts = Contact::searchByName($search, $type == self::TYPE_GROUP ? 'community' : '', true);
|
||||
$contacts = Contact::searchByName($search, $type == self::TYPE_FORUM ? 'community' : '', true);
|
||||
|
||||
$resultList = new ResultList($start, count($contacts), $itemPage);
|
||||
|
||||
|
|
@ -240,9 +240,7 @@ class Search
|
|||
$return = array_map(function ($result) {
|
||||
static $contactType = [
|
||||
'People' => Contact::TYPE_PERSON,
|
||||
// Kept for backward compatibility
|
||||
'Forum' => Contact::TYPE_COMMUNITY,
|
||||
'Group' => Contact::TYPE_COMMUNITY,
|
||||
'Organization' => Contact::TYPE_ORGANISATION,
|
||||
'News' => Contact::TYPE_NEWS,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -93,13 +93,6 @@ interface IHandleUserSessions extends IHandleSessions
|
|||
*/
|
||||
public function isSiteAdmin(): bool;
|
||||
|
||||
/**
|
||||
* Check if current user is a moderator.
|
||||
*
|
||||
* @return bool true if user is a moderator
|
||||
*/
|
||||
public function isModerator(): bool;
|
||||
|
||||
/**
|
||||
* Returns User ID of the managed user in case it's a different identity
|
||||
*
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@
|
|||
namespace Friendica\Core\Session\Factory;
|
||||
|
||||
use Friendica\App;
|
||||
use Friendica\Core\Cache\Enum;
|
||||
use Friendica\Core\Cache\Factory\Cache;
|
||||
use Friendica\Core\Cache\Type\DatabaseCache;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Session\Capability\IHandleSessions;
|
||||
use Friendica\Core\Session\Type;
|
||||
|
|
@ -74,7 +74,7 @@ class Session
|
|||
$cache = $cacheFactory->createDistributed();
|
||||
|
||||
// In case we're using the db as cache driver, use the native db session, not the cache
|
||||
if ($config->get('system', 'cache_driver') === DatabaseCache::NAME) {
|
||||
if ($config->get('system', 'cache_driver') === Enum\Type::DATABASE) {
|
||||
$handler = new Handler\Database($dba, $logger, $server);
|
||||
} else {
|
||||
$handler = new Handler\Cache($cache, $logger);
|
||||
|
|
|
|||
|
|
@ -139,12 +139,6 @@ class UserSession implements IHandleUserSessions
|
|||
return User::isSiteAdmin($this->getLocalUserId());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function isModerator(): bool
|
||||
{
|
||||
return User::isModerator($this->getLocalUserId());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function setVisitorsContacts(string $my_url)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ use Friendica\Content\Text\BBCode;
|
|||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Module\Response;
|
||||
use Friendica\Network\HTTPException\FoundException;
|
||||
use Friendica\Network\HTTPException\MovedPermanentlyException;
|
||||
|
|
@ -227,10 +226,9 @@ class System
|
|||
* @param integer $depth How many calls to include in the stacks after filtering
|
||||
* @param int $offset How many calls to shave off the top of the stack, for example if
|
||||
* this is called from a centralized method that isn't relevant to the callstack
|
||||
* @param bool $full If enabled, the callstack is not compacted
|
||||
* @return string
|
||||
*/
|
||||
public static function callstack(int $depth = 4, int $offset = 0, bool $full = false): string
|
||||
public static function callstack(int $depth = 4, int $offset = 0): string
|
||||
{
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
|
||||
|
||||
|
|
@ -245,7 +243,7 @@ class System
|
|||
|
||||
while ($func = array_pop($trace)) {
|
||||
if (!empty($func['class'])) {
|
||||
if (!$full && in_array($previous['function'], ['insert', 'fetch', 'toArray', 'exists', 'count', 'selectFirst', 'selectToArray',
|
||||
if (in_array($previous['function'], ['insert', 'fetch', 'toArray', 'exists', 'count', 'selectFirst', 'selectToArray',
|
||||
'select', 'update', 'delete', 'selectFirstForUser', 'selectForUser'])
|
||||
&& (substr($previous['class'], 0, 15) === 'Friendica\Model')) {
|
||||
continue;
|
||||
|
|
@ -253,7 +251,7 @@ class System
|
|||
|
||||
// Don't show multiple calls from the Database classes to show the essential parts of the callstack
|
||||
$func['database'] = in_array($func['class'], ['Friendica\Database\DBA', 'Friendica\Database\Database']);
|
||||
if ($full || !$previous['database'] || !$func['database']) {
|
||||
if (!$previous['database'] || !$func['database']) {
|
||||
$classparts = explode("\\", $func['class']);
|
||||
$callstack[] = array_pop($classparts).'::'.$func['function'] . (isset($func['line']) ? ' (' . $func['line'] . ')' : '');
|
||||
$previous = $func;
|
||||
|
|
@ -671,7 +669,9 @@ class System
|
|||
|
||||
if (DI::config()->get('system', 'tosdisplay')) {
|
||||
$rulelist = DI::config()->get('system', 'tosrules') ?: DI::config()->get('system', 'tostext');
|
||||
$msg = BBCode::toPlaintext($rulelist, false);
|
||||
$html = BBCode::convert($rulelist, false, BBCode::EXTERNAL);
|
||||
|
||||
$msg = HTML::toPlaintext($html, 0, true);
|
||||
foreach (explode("\n", trim($msg)) as $line) {
|
||||
$line = trim($line);
|
||||
if ($line) {
|
||||
|
|
|
|||
|
|
@ -1365,17 +1365,6 @@ class Worker
|
|||
return $new_retrial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of retrials for the current worker task
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getRetrial(): int
|
||||
{
|
||||
$queue = DI::app()->getQueue();
|
||||
return $queue['retrial'] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defers the current worker entry
|
||||
*
|
||||
|
|
|
|||
28
src/DI.php
|
|
@ -22,8 +22,6 @@
|
|||
namespace Friendica;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Logger\Capability\ICheckLoggerSettings;
|
||||
use Friendica\Core\Logger\Util\LoggerSettingsCheck;
|
||||
use Friendica\Core\Session\Capability\IHandleSessions;
|
||||
use Friendica\Core\Session\Capability\IHandleUserSessions;
|
||||
use Friendica\Navigation\SystemMessages;
|
||||
|
|
@ -210,9 +208,9 @@ abstract class DI
|
|||
return self::$dice->create(Core\Config\Util\ConfigFileManager::class);
|
||||
}
|
||||
|
||||
public static function keyValue(): Core\KeyValueStorage\Capability\IManageKeyValuePairs
|
||||
public static function keyValue(): Core\KeyValueStorage\Capabilities\IManageKeyValuePairs
|
||||
{
|
||||
return self::$dice->create(Core\KeyValueStorage\Capability\IManageKeyValuePairs::class);
|
||||
return self::$dice->create(Core\KeyValueStorage\Capabilities\IManageKeyValuePairs::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -297,11 +295,6 @@ abstract class DI
|
|||
static::init($flushDice);
|
||||
}
|
||||
|
||||
public static function logCheck(): ICheckLoggerSettings
|
||||
{
|
||||
return self::$dice->create(LoggerSettingsCheck::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoggerInterface
|
||||
*/
|
||||
|
|
@ -671,15 +664,6 @@ abstract class DI
|
|||
return self::$dice->create(Security\Authentication::class);
|
||||
}
|
||||
|
||||
//
|
||||
// "User" namespace instances
|
||||
//
|
||||
|
||||
public static function userGServer(): User\Settings\Repository\UserGServer
|
||||
{
|
||||
return self::$dice->create(User\Settings\Repository\UserGServer::class);
|
||||
}
|
||||
|
||||
//
|
||||
// "Util" namespace instances
|
||||
//
|
||||
|
|
@ -708,6 +692,14 @@ abstract class DI
|
|||
return self::$dice->create(Util\DateTimeFormat::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Util\FileSystem
|
||||
*/
|
||||
public static function fs()
|
||||
{
|
||||
return self::$dice->create(Util\FileSystem::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Util\Profiler
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -752,8 +752,8 @@ class Database
|
|||
@file_put_contents(
|
||||
$this->config->get('system', 'db_log'),
|
||||
DateTimeFormat::utcNow() . "\t" . $duration . "\t" .
|
||||
basename($backtrace[1]['file']) . "\t" .
|
||||
$backtrace[1]['line'] . "\t" . $backtrace[2]['function'] . "\t" .
|
||||
basename($backtrace[1]["file"]) . "\t" .
|
||||
$backtrace[1]["line"] . "\t" . $backtrace[2]["function"] . "\t" .
|
||||
substr($this->replaceParameters($sql, $args), 0, 4000) . "\n",
|
||||
FILE_APPEND
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ use Friendica\Network\HTTPException;
|
|||
use Psr\Log\LoggerInterface;
|
||||
use Friendica\Factory\Api\Twitter\User as TwitterUser;
|
||||
|
||||
class Circle extends BaseFactory
|
||||
class Group extends BaseFactory
|
||||
{
|
||||
/** @var twitterUser entity */
|
||||
private $twitterUser;
|
||||
|
|
@ -43,19 +43,19 @@ class Circle extends BaseFactory
|
|||
}
|
||||
|
||||
/**
|
||||
* @param int $id id of the circle
|
||||
* @param int $id id of the group
|
||||
* @return array
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public function createFromId(int $id): array
|
||||
{
|
||||
$circle = $this->dba->selectFirst('group', [], ['id' => $id, 'deleted' => false]);
|
||||
if (empty($circle)) {
|
||||
$group = $this->dba->selectFirst('group', [], ['id' => $id, 'deleted' => false]);
|
||||
if (empty($group)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$user = $this->twitterUser->createFromUserId($circle['uid'])->toArray();
|
||||
$object = new \Friendica\Object\Api\Friendica\Circle($circle, $user);
|
||||
$user = $this->twitterUser->createFromUserId($group['uid'])->toArray();
|
||||
$object = new \Friendica\Object\Api\Friendica\Group($group, $user);
|
||||
|
||||
return $object->toArray();
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ class Field extends BaseFactory
|
|||
*/
|
||||
public function createFromProfileField(ProfileField $profileField): \Friendica\Object\Api\Mastodon\Field
|
||||
{
|
||||
return new \Friendica\Object\Api\Mastodon\Field($profileField->label, BBCode::convertForUriId($profileField->uriId, $profileField->value, BBCode::ACTIVITYPUB));
|
||||
return new \Friendica\Object\Api\Mastodon\Field($profileField->label, BBCode::convert($profileField->value, false, BBCode::ACTIVITYPUB));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -40,9 +40,9 @@ class ListEntity extends BaseFactory
|
|||
/**
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public function createFromCircleId(int $id): \Friendica\Object\Api\Mastodon\ListEntity
|
||||
public function createFromGroupId(int $id): \Friendica\Object\Api\Mastodon\ListEntity
|
||||
{
|
||||
$circle = $this->dba->selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]);
|
||||
return new \Friendica\Object\Api\Mastodon\ListEntity($id, $circle['name'] ?? '', 'list');
|
||||
$group = $this->dba->selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]);
|
||||
return new \Friendica\Object\Api\Mastodon\ListEntity($id, $group['name'] ?? '', 'list');
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Federation\Entity;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use Psr\Http\Message\UriInterface;
|
||||
|
||||
/**
|
||||
* @property-read int $id
|
||||
* @property-read string $url
|
||||
* @property-read string $nurl
|
||||
* @property-read string $version
|
||||
* @property-read string $siteName
|
||||
* @property-read string $info
|
||||
* @property-read int $registerPolicy
|
||||
* @property-read int $registeredUsers
|
||||
* @property-read string $poco
|
||||
* @property-read string $noscrape
|
||||
* @property-read string $network
|
||||
* @property-read string $platform
|
||||
* @property-read int $relaySubscribe
|
||||
* @property-read string $relayScope
|
||||
* @property-read DateTimeImmutable $created
|
||||
* @property-read ?DateTimeImmutable $lastPocoQuery
|
||||
* @property-read ?DateTimeImmutable $lastContact
|
||||
* @property-read ?DateTimeImmutable $lastFailure
|
||||
* @property-read int $directoryType
|
||||
* @property-read int $detectionMethod
|
||||
* @property-read bool $failed
|
||||
* @property-read DateTimeImmutable $nextContact
|
||||
* @property-read int $protocol
|
||||
* @property-read int $activeWeekUsers
|
||||
* @property-read int $activeMonthUsers
|
||||
* @property-read int $activeHalfyearUsers
|
||||
* @property-read int $localPosts
|
||||
* @property-read int $localComments
|
||||
* @property-read bool $blocked
|
||||
*/
|
||||
class GServer extends \Friendica\BaseEntity
|
||||
{
|
||||
/** @var ?int */
|
||||
protected $id;
|
||||
/** @var UriInterface */
|
||||
protected $url;
|
||||
/** @var UriInterface */
|
||||
protected $nurl;
|
||||
/** @var string */
|
||||
protected $version;
|
||||
/** @var string */
|
||||
protected $siteName;
|
||||
/** @var string */
|
||||
protected $info;
|
||||
/** @var int One of Module\Register::* constant values */
|
||||
protected $registerPolicy;
|
||||
/** @var int */
|
||||
protected $registeredUsers;
|
||||
/** @var ?UriInterface */
|
||||
protected $poco;
|
||||
/** @var ?UriInterface */
|
||||
protected $noscrape;
|
||||
/** @var string One of the Protocol::* constant values */
|
||||
protected $network;
|
||||
/** @var string */
|
||||
protected $platform;
|
||||
/** @var bool */
|
||||
protected $relaySubscribe;
|
||||
/** @var string */
|
||||
protected $relayScope;
|
||||
/** @var DateTimeImmutable */
|
||||
protected $created;
|
||||
/** @var DateTimeImmutable */
|
||||
protected $lastPocoQuery;
|
||||
/** @var DateTimeImmutable */
|
||||
protected $lastContact;
|
||||
/** @var DateTimeImmutable */
|
||||
protected $lastFailure;
|
||||
/** @var int One of Model\Gserver::DT_* constant values */
|
||||
protected $directoryType;
|
||||
/** @var ?int One of Model\Gserver::DETECT_* constant values */
|
||||
protected $detectionMethod;
|
||||
/** @var bool */
|
||||
protected $failed;
|
||||
/** @var DateTimeImmutable */
|
||||
protected $nextContact;
|
||||
/** @var ?int One of Model\Post\DeliveryData::* constant values */
|
||||
protected $protocol;
|
||||
/** @var ?int */
|
||||
protected $activeWeekUsers;
|
||||
/** @var ?int */
|
||||
protected $activeMonthUsers;
|
||||
/** @var ?int */
|
||||
protected $activeHalfyearUsers;
|
||||
/** @var ?int */
|
||||
protected $localPosts;
|
||||
/** @var ?int */
|
||||
protected $localComments;
|
||||
/** @var bool */
|
||||
protected $blocked;
|
||||
|
||||
public function __construct(UriInterface $url, UriInterface $nurl, string $version, string $siteName, string $info, int $registerPolicy, int $registeredUsers, ?UriInterface $poco, ?UriInterface $noscrape, string $network, string $platform, bool $relaySubscribe, string $relayScope, DateTimeImmutable $created, ?DateTimeImmutable $lastPocoQuery, ?DateTimeImmutable $lastContact, ?DateTimeImmutable $lastFailure, int $directoryType, ?int $detectionMethod, bool $failed, ?DateTimeImmutable $nextContact, ?int $protocol, ?int $activeWeekUsers, ?int $activeMonthUsers, ?int $activeHalfyearUsers, ?int $localPosts, ?int $localComments, bool $blocked, ?int $id = null)
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->nurl = $nurl;
|
||||
$this->version = $version;
|
||||
$this->siteName = $siteName;
|
||||
$this->info = $info;
|
||||
$this->registerPolicy = $registerPolicy;
|
||||
$this->registeredUsers = $registeredUsers;
|
||||
$this->poco = $poco;
|
||||
$this->noscrape = $noscrape;
|
||||
$this->network = $network;
|
||||
$this->platform = $platform;
|
||||
$this->relaySubscribe = $relaySubscribe;
|
||||
$this->relayScope = $relayScope;
|
||||
$this->created = $created;
|
||||
$this->lastPocoQuery = $lastPocoQuery;
|
||||
$this->lastContact = $lastContact;
|
||||
$this->lastFailure = $lastFailure;
|
||||
$this->directoryType = $directoryType;
|
||||
$this->detectionMethod = $detectionMethod;
|
||||
$this->failed = $failed;
|
||||
$this->nextContact = $nextContact;
|
||||
$this->protocol = $protocol;
|
||||
$this->activeWeekUsers = $activeWeekUsers;
|
||||
$this->activeMonthUsers = $activeMonthUsers;
|
||||
$this->activeHalfyearUsers = $activeHalfyearUsers;
|
||||
$this->localPosts = $localPosts;
|
||||
$this->localComments = $localComments;
|
||||
$this->blocked = $blocked;
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Federation\Factory;
|
||||
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Federation\Entity;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
|
||||
class GServer extends \Friendica\BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function createFromTableRow(array $row): Entity\GServer
|
||||
{
|
||||
return new Entity\GServer(
|
||||
new Uri($row['url']),
|
||||
new Uri($row['nurl']),
|
||||
$row['version'],
|
||||
$row['site_name'],
|
||||
$row['info'] ?? '',
|
||||
$row['register_policy'],
|
||||
$row['registered-users'],
|
||||
$row['poco'] ? new Uri($row['poco']) : null,
|
||||
$row['noscrape'] ? new Uri($row['noscrape']) : null,
|
||||
$row['network'],
|
||||
$row['platform'],
|
||||
$row['relay-subscribe'],
|
||||
$row['relay-scope'],
|
||||
new \DateTimeImmutable($row['created']),
|
||||
$row['last_poco_query'] !== DBA::NULL_DATETIME ? new \DateTimeImmutable($row['last_poco_query']) : null,
|
||||
$row['last_contact'] !== DBA::NULL_DATETIME ? new \DateTimeImmutable($row['last_contact']) : null,
|
||||
$row['last_failure'] !== DBA::NULL_DATETIME ? new \DateTimeImmutable($row['last_failure']) : null,
|
||||
$row['directory-type'],
|
||||
$row['detection-method'],
|
||||
$row['failed'],
|
||||
$row['next_contact'] !== DBA::NULL_DATETIME ? new \DateTimeImmutable($row['next_contact']) : null,
|
||||
$row['protocol'],
|
||||
$row['active-week-users'],
|
||||
$row['active-month-users'],
|
||||
$row['active-halfyear-users'],
|
||||
$row['local-posts'],
|
||||
$row['local-comments'],
|
||||
$row['blocked'],
|
||||
$row['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Federation\Repository;
|
||||
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Federation\Factory;
|
||||
use Friendica\Federation\Entity;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class GServer extends \Friendica\BaseRepository
|
||||
{
|
||||
protected static $table_name = 'gserver';
|
||||
|
||||
public function __construct(Database $database, LoggerInterface $logger, Factory\GServer $factory)
|
||||
{
|
||||
parent::__construct($database, $logger, $factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $gsid
|
||||
* @return Entity\GServer
|
||||
* @throws \Friendica\Network\HTTPException\NotFoundException
|
||||
*/
|
||||
public function selectOneById(int $gsid): Entity\GServer
|
||||
{
|
||||
return $this->_selectOne(['id' => $gsid]);
|
||||
}
|
||||
}
|
||||
|
|
@ -119,11 +119,6 @@ class APContact
|
|||
return [];
|
||||
}
|
||||
|
||||
if (!Network::isValidHttpUrl($url) && !filter_var($url, FILTER_VALIDATE_EMAIL)) {
|
||||
Logger::info('Invalid URL', ['url' => $url]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$fetched_contact = [];
|
||||
|
||||
if (empty($update)) {
|
||||
|
|
@ -584,14 +579,6 @@ class APContact
|
|||
*/
|
||||
public static function isRelay(array $apcontact): bool
|
||||
{
|
||||
if (in_array($apcontact['type'], ['Person', 'Organization'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (($apcontact['type'] == 'Service') && empty($apcontact['outbox']) && empty($apcontact['sharedinbox']) && empty($apcontact['following']) && empty($apcontact['followers']) && empty($apcontact['statuses_count'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (empty($apcontact['nick']) || $apcontact['nick'] != 'relay') {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,9 +190,9 @@ class Attach
|
|||
* @param string $filetype Mimetype. optional, default = ''
|
||||
* @param integer $filesize File size in bytes. optional, default = null
|
||||
* @param string $allow_cid Permissions, allowed contacts. optional, default = ''
|
||||
* @param string $allow_gid Permissions, allowed circles. optional, default = ''
|
||||
* @param string $deny_cid Permissions, denied contacts. optional, default = ''
|
||||
* @param string $deny_gid Permissions, denied circle. optional, default = ''
|
||||
* @param string $allow_gid Permissions, allowed groups. optional, default = ''
|
||||
* @param string $deny_cid Permissions, denied contacts.optional, default = ''
|
||||
* @param string $deny_gid Permissions, denied group.optional, default = ''
|
||||
*
|
||||
* @return boolean|integer Row id on success, False on errors
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
|
|
|
|||
|
|
@ -22,10 +22,8 @@
|
|||
namespace Friendica\Model;
|
||||
|
||||
use Friendica\Contact\Avatar;
|
||||
use Friendica\Contact\Header;
|
||||
use Friendica\Contact\Introduction\Exception\IntroductionNotFoundException;
|
||||
use Friendica\Contact\LocalRelationship\Entity\LocalRelationship;
|
||||
use Friendica\Content\Conversation as ConversationContent;
|
||||
use Friendica\Content\Conversation As ConversationContent;
|
||||
use Friendica\Content\Pager;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Hook;
|
||||
|
|
@ -81,7 +79,7 @@ class Contact
|
|||
* TYPE_NEWS - the account is a news reflector
|
||||
* Associated page type: PAGE_SOAPBOX
|
||||
*
|
||||
* TYPE_COMMUNITY - the account is community group
|
||||
* TYPE_COMMUNITY - the account is community forum
|
||||
* Associated page types: PAGE_COMMUNITY, PAGE_PRVGROUP
|
||||
*
|
||||
* TYPE_RELAY - the account is a relay
|
||||
|
|
@ -113,16 +111,12 @@ class Contact
|
|||
* @}
|
||||
*/
|
||||
|
||||
/** @deprecated Use Entity\LocalRelationship::MIRROR_DEACTIVATED instead */
|
||||
const MIRROR_DEACTIVATED = LocalRelationship::MIRROR_DEACTIVATED;
|
||||
/** @deprecated Now does the same as MIRROR_OWN_POST */
|
||||
const MIRROR_FORWARDED = 1;
|
||||
/** @deprecated Use Entity\LocalRelationship::MIRROR_OWN_POST instead */
|
||||
const MIRROR_OWN_POST = LocalRelationship::MIRROR_OWN_POST;
|
||||
/** @deprecated Use Entity\LocalRelationship::MIRROR_NATIVE_RESHARE instead */
|
||||
const MIRROR_NATIVE_RESHARE = LocalRelationship::MIRROR_NATIVE_RESHARE;
|
||||
const MIRROR_DEACTIVATED = 0;
|
||||
const MIRROR_FORWARDED = 1; // Deprecated, now does the same like MIRROR_OWN_POST
|
||||
const MIRROR_OWN_POST = 2;
|
||||
const MIRROR_NATIVE_RESHARE = 3;
|
||||
|
||||
/**
|
||||
/**
|
||||
* @param array $fields Array of selected fields, empty for all
|
||||
* @param array $condition Array of fields for condition
|
||||
* @param array $params Array of several parameters
|
||||
|
|
@ -222,11 +216,6 @@ class Contact
|
|||
|
||||
Contact\User::insertForContactArray($contact);
|
||||
|
||||
if ((empty($contact['baseurl']) || empty($contact['gsid'])) && Probe::isProbable($contact['network'])) {
|
||||
Logger::debug('Update missing baseurl', ['id' => $contact['id'], 'url' => $contact['url'], 'callstack' => System::callstack(4, 0, true)]);
|
||||
UpdateContact::add(['priority' => Worker::PRIORITY_MEDIUM, 'dont_fork' => true], $contact['id']);
|
||||
}
|
||||
|
||||
return $contact['id'];
|
||||
}
|
||||
|
||||
|
|
@ -736,11 +725,8 @@ class Contact
|
|||
*/
|
||||
public static function createSelfFromUserId(int $uid): bool
|
||||
{
|
||||
$user = DBA::selectFirst(
|
||||
'user',
|
||||
['uid', 'username', 'nickname', 'pubkey', 'prvkey'],
|
||||
['uid' => $uid, 'account_expired' => false]
|
||||
);
|
||||
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'pubkey', 'prvkey'],
|
||||
['uid' => $uid, 'account_expired' => false]);
|
||||
if (!DBA::isResult($user)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -793,33 +779,29 @@ class Contact
|
|||
/**
|
||||
* Updates the self-contact for the provided user id
|
||||
*
|
||||
* @param int $uid
|
||||
* @param bool $update_avatar Force the avatar update
|
||||
* @param int $uid
|
||||
* @param bool $update_avatar Force the avatar update
|
||||
* @return bool "true" if updated
|
||||
* @throws \Exception
|
||||
* @throws HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function updateSelfFromUserID(int $uid, bool $update_avatar = false): bool
|
||||
{
|
||||
$fields = [
|
||||
'id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
$fields = ['id', 'uri-id', 'name', 'nick', 'location', 'about', 'keywords', 'avatar', 'prvkey', 'pubkey', 'manually-approve',
|
||||
'xmpp', 'matrix', 'contact-type', 'forum', 'prv', 'avatar-date', 'url', 'nurl', 'unsearchable',
|
||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'
|
||||
];
|
||||
'photo', 'thumb', 'micro', 'header', 'addr', 'request', 'notify', 'poll', 'confirm', 'poco', 'network'];
|
||||
$self = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]);
|
||||
if (!DBA::isResult($self)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = ['uid', 'username', 'nickname', 'page-flags', 'account-type', 'prvkey', 'pubkey'];
|
||||
$fields = ['uid', 'nickname', 'page-flags', 'account-type', 'prvkey', 'pubkey'];
|
||||
$user = DBA::selectFirst('user', $fields, ['uid' => $uid, 'account_expired' => false]);
|
||||
if (!DBA::isResult($user)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$fields = [
|
||||
'name', 'photo', 'thumb', 'about', 'address', 'locality', 'region',
|
||||
'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish'
|
||||
];
|
||||
$fields = ['name', 'photo', 'thumb', 'about', 'address', 'locality', 'region',
|
||||
'country-name', 'pub_keywords', 'xmpp', 'matrix', 'net-publish'];
|
||||
$profile = DBA::selectFirst('profile', $fields, ['uid' => $uid]);
|
||||
if (!DBA::isResult($profile)) {
|
||||
return false;
|
||||
|
|
@ -829,7 +811,7 @@ class Contact
|
|||
$url = DI::baseUrl() . '/profile/' . $user['nickname'];
|
||||
|
||||
$fields = [
|
||||
'name' => $user['username'],
|
||||
'name' => $profile['name'],
|
||||
'nick' => $user['nickname'],
|
||||
'avatar-date' => $self['avatar-date'],
|
||||
'location' => Profile::formatLocation($profile),
|
||||
|
|
@ -848,10 +830,11 @@ class Contact
|
|||
'addr' => $user['nickname'] . '@' . substr(DI::baseUrl(), strpos(DI::baseUrl(), '://') + 3),
|
||||
'request' => DI::baseUrl() . '/dfrn_request/' . $user['nickname'],
|
||||
'notify' => DI::baseUrl() . '/dfrn_notify/' . $user['nickname'],
|
||||
'poll' => DI::baseUrl() . '/dfrn_poll/' . $user['nickname'],
|
||||
'poll' => DI::baseUrl() . '/dfrn_poll/'. $user['nickname'],
|
||||
'confirm' => DI::baseUrl() . '/dfrn_confirm/' . $user['nickname'],
|
||||
];
|
||||
|
||||
|
||||
$avatar = Photo::selectFirst(['resource-id', 'type'], ['uid' => $uid, 'profile' => true]);
|
||||
if (DBA::isResult($avatar)) {
|
||||
if ($update_avatar) {
|
||||
|
|
@ -867,7 +850,7 @@ class Contact
|
|||
// We are adding a timestamp value so that other systems won't use cached content
|
||||
$timestamp = strtotime($fields['avatar-date']);
|
||||
|
||||
$prefix = DI::baseUrl() . '/photo/' . $avatar['resource-id'] . '-';
|
||||
$prefix = DI::baseUrl() . '/photo/' .$avatar['resource-id'] . '-';
|
||||
$suffix = '.' . $file_suffix . '?ts=' . $timestamp;
|
||||
|
||||
$fields['photo'] = $prefix . '4' . $suffix;
|
||||
|
|
@ -1205,22 +1188,22 @@ class Contact
|
|||
*/
|
||||
if (empty($contact['uid'])) {
|
||||
$menu = [
|
||||
'profile' => [DI::l10n()->t('View Profile'), $profile_link, true],
|
||||
'network' => [DI::l10n()->t('Network Posts'), $posts_link, false],
|
||||
'edit' => [DI::l10n()->t('View Contact'), $contact_url, false],
|
||||
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true],
|
||||
'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true],
|
||||
'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ],
|
||||
'network' => [DI::l10n()->t('Network Posts') , $posts_link , false],
|
||||
'edit' => [DI::l10n()->t('View Contact') , $contact_url , false],
|
||||
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ],
|
||||
'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ],
|
||||
];
|
||||
} else {
|
||||
$menu = [
|
||||
'status' => [DI::l10n()->t('View Status'), $status_link, true],
|
||||
'profile' => [DI::l10n()->t('View Profile'), $profile_link, true],
|
||||
'photos' => [DI::l10n()->t('View Photos'), $photos_link, true],
|
||||
'network' => [DI::l10n()->t('Network Posts'), $posts_link, false],
|
||||
'edit' => [DI::l10n()->t('View Contact'), $contact_url, false],
|
||||
'pm' => [DI::l10n()->t('Send PM'), $pm_url, false],
|
||||
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link, true],
|
||||
'unfollow' => [DI::l10n()->t('Unfollow'), $unfollow_link, true],
|
||||
'status' => [DI::l10n()->t('View Status') , $status_link , true ],
|
||||
'profile' => [DI::l10n()->t('View Profile') , $profile_link , true ],
|
||||
'photos' => [DI::l10n()->t('View Photos') , $photos_link , true ],
|
||||
'network' => [DI::l10n()->t('Network Posts') , $posts_link , false],
|
||||
'edit' => [DI::l10n()->t('View Contact') , $contact_url , false],
|
||||
'pm' => [DI::l10n()->t('Send PM') , $pm_url , false],
|
||||
'follow' => [DI::l10n()->t('Connect/Follow'), $follow_link , true ],
|
||||
'unfollow' => [DI::l10n()->t('Unfollow') , $unfollow_link, true ],
|
||||
];
|
||||
|
||||
if (!empty($contact['pending'])) {
|
||||
|
|
@ -1327,11 +1310,9 @@ class Contact
|
|||
if (($uid == 0) && (empty($data['network']) || ($data['network'] == Protocol::PHANTOM))) {
|
||||
// Fetch data for the public contact via the first found personal contact
|
||||
/// @todo Check if this case can happen at all (possibly with mail accounts?)
|
||||
$fields = [
|
||||
'name', 'nick', 'url', 'addr', 'alias', 'avatar', 'header', 'contact-type',
|
||||
$fields = ['name', 'nick', 'url', 'addr', 'alias', 'avatar', 'header', 'contact-type',
|
||||
'keywords', 'location', 'about', 'unsearchable', 'batch', 'notify', 'poll',
|
||||
'request', 'confirm', 'poco', 'subscribe', 'network', 'baseurl', 'gsid'
|
||||
];
|
||||
'request', 'confirm', 'poco', 'subscribe', 'network', 'baseurl', 'gsid'];
|
||||
|
||||
$personal_contact = DBA::selectFirst('contact', $fields, ["`addr` = ? AND `uid` != 0", $url]);
|
||||
if (!DBA::isResult($personal_contact)) {
|
||||
|
|
@ -1377,7 +1358,6 @@ class Contact
|
|||
$fields = [
|
||||
'uid' => $uid,
|
||||
'url' => $data['url'],
|
||||
'baseurl' => $data['baseurl'] ?? '',
|
||||
'nurl' => Strings::normaliseLink($data['url']),
|
||||
'network' => $data['network'],
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
|
|
@ -1478,7 +1458,7 @@ class Contact
|
|||
if (!empty($contact['batch'])) {
|
||||
$condition = ['archive' => true, 'uid' => 0, 'network' => Protocol::FEDERATED, 'batch' => $contact['batch'], 'contact-type' => self::TYPE_RELAY];
|
||||
return DBA::exists('contact', $condition);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1571,15 +1551,11 @@ class Contact
|
|||
$contact_field = ((($contact["contact-type"] == self::TYPE_COMMUNITY) || ($contact['network'] == Protocol::MAIL)) ? 'owner-id' : 'author-id');
|
||||
|
||||
if ($thread_mode) {
|
||||
$condition = [
|
||||
"((`$contact_field` = ? AND `gravity` = ?) OR (`author-id` = ? AND `gravity` = ? AND `vid` = ? AND `protocol` != ? AND `thr-parent-id` = `parent-uri-id`)) AND " . $sql,
|
||||
$cid, Item::GRAVITY_PARENT, $cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA, DI::userSession()->getLocalUserId()
|
||||
];
|
||||
$condition = ["((`$contact_field` = ? AND `gravity` = ?) OR (`author-id` = ? AND `gravity` = ? AND `vid` = ? AND `protocol` != ? AND `thr-parent-id` = `parent-uri-id`)) AND " . $sql,
|
||||
$cid, Item::GRAVITY_PARENT, $cid, Item::GRAVITY_ACTIVITY, Verb::getID(Activity::ANNOUNCE), Conversation::PARCEL_DIASPORA, DI::userSession()->getLocalUserId()];
|
||||
} else {
|
||||
$condition = [
|
||||
"`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql,
|
||||
$cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId()
|
||||
];
|
||||
$condition = ["`$contact_field` = ? AND `gravity` IN (?, ?) AND " . $sql,
|
||||
$cid, Item::GRAVITY_PARENT, Item::GRAVITY_COMMENT, DI::userSession()->getLocalUserId()];
|
||||
}
|
||||
|
||||
if (!empty($parent)) {
|
||||
|
|
@ -1592,26 +1568,16 @@ class Contact
|
|||
}
|
||||
|
||||
if ($only_media) {
|
||||
$condition = DBA::mergeConditions($condition, [
|
||||
"`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
|
||||
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO
|
||||
]);
|
||||
$condition = DBA::mergeConditions($condition, ["`uri-id` IN (SELECT `uri-id` FROM `post-media` WHERE `type` IN (?, ?, ?))",
|
||||
Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]);
|
||||
}
|
||||
|
||||
if (DI::mode()->isMobile()) {
|
||||
$itemsPerPage = DI::pConfig()->get(
|
||||
DI::userSession()->getLocalUserId(),
|
||||
'system',
|
||||
'itemspage_mobile_network',
|
||||
DI::config()->get('system', 'itemspage_network_mobile')
|
||||
);
|
||||
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_mobile_network',
|
||||
DI::config()->get('system', 'itemspage_network_mobile'));
|
||||
} else {
|
||||
$itemsPerPage = DI::pConfig()->get(
|
||||
DI::userSession()->getLocalUserId(),
|
||||
'system',
|
||||
'itemspage_network',
|
||||
DI::config()->get('system', 'itemspage_network')
|
||||
);
|
||||
$itemsPerPage = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'itemspage_network',
|
||||
DI::config()->get('system', 'itemspage_network'));
|
||||
}
|
||||
|
||||
$pager = new Pager(DI::l10n(), DI::args()->getQueryString(), $itemsPerPage);
|
||||
|
|
@ -1637,7 +1603,7 @@ class Contact
|
|||
}
|
||||
}
|
||||
|
||||
$o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACTS, $update, false, 'pinned_commented', DI::userSession()->getLocalUserId());
|
||||
$o .= DI::conversation()->create($items, ConversationContent::MODE_CONTACTS, $update, false, 'pinned_commented', DI::userSession()->getLocalUserId());
|
||||
} else {
|
||||
$fields = array_merge(Item::DISPLAY_FIELDLIST, ['featured']);
|
||||
$items = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), $fields, $condition, $params));
|
||||
|
|
@ -1645,16 +1611,14 @@ class Contact
|
|||
if ($pager->getStart() == 0) {
|
||||
$cdata = self::getPublicAndUserContactID($cid, DI::userSession()->getLocalUserId());
|
||||
if (!empty($cdata['public'])) {
|
||||
$condition = [
|
||||
"`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
||||
$cdata['public'], Post\Collection::FEATURED
|
||||
];
|
||||
$condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)",
|
||||
$cdata['public'], Post\Collection::FEATURED];
|
||||
$pinned = Post::toArray(Post::selectForUser(DI::userSession()->getLocalUserId(), $fields, $condition, $params));
|
||||
$items = array_merge($pinned, $items);
|
||||
}
|
||||
}
|
||||
|
||||
$o .= DI::conversation()->render($items, ConversationContent::MODE_CONTACT_POSTS, $update);
|
||||
$o .= DI::conversation()->create($items, ConversationContent::MODE_CONTACT_POSTS, $update);
|
||||
}
|
||||
|
||||
if (!$update) {
|
||||
|
|
@ -1688,7 +1652,7 @@ class Contact
|
|||
break;
|
||||
|
||||
case self::TYPE_COMMUNITY:
|
||||
$account_type = DI::l10n()->t("Group");
|
||||
$account_type = DI::l10n()->t("Forum");
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -1755,7 +1719,7 @@ class Contact
|
|||
} elseif (DI::config()->get('system', 'avatar_cache') && (empty($contact['photo']) || empty($contact['thumb']) || empty($contact['micro']))) {
|
||||
Logger::info('Adding avatar cache file', ['id' => $cid, 'contact' => $contact]);
|
||||
self::updateAvatar($cid, $contact['avatar'], true);
|
||||
return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1906,7 +1870,13 @@ class Contact
|
|||
switch ($platform) {
|
||||
case 'friendica':
|
||||
case 'friendika':
|
||||
$header = DI::baseUrl() . (new Header(DI::config()))->getMastodonBannerPath();
|
||||
/**
|
||||
* Picture credits
|
||||
* @author Lostinlight <https://mastodon.xyz/@lightone>
|
||||
* @license CC0 https://creativecommons.org/share-your-work/public-domain/cc0/
|
||||
* @link https://gitlab.com/lostinlight/per_aspera_ad_astra/-/blob/master/friendica-404/friendica-promo-bubbles.jpg
|
||||
*/
|
||||
$header = DI::baseUrl() . '/images/friendica-banner.jpg';
|
||||
break;
|
||||
case 'diaspora':
|
||||
/**
|
||||
|
|
@ -2151,10 +2121,8 @@ class Contact
|
|||
*/
|
||||
public static function getAvatarUrlForUrl(string $url, int $uid, string $size = ''): string
|
||||
{
|
||||
$condition = [
|
||||
"`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
|
||||
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0
|
||||
];
|
||||
$condition = ["`nurl` = ? AND ((`uid` = ? AND `network` IN (?, ?)) OR `uid` = ?)",
|
||||
Strings::normaliseLink($url), $uid, Protocol::FEED, Protocol::MAIL, 0];
|
||||
$contact = self::selectFirst(['id', 'updated'], $condition, ['order' => ['uid' => true]]);
|
||||
return self::getAvatarUrlForId($contact['id'] ?? 0, $size, $contact['updated'] ?? '');
|
||||
}
|
||||
|
|
@ -2225,11 +2193,8 @@ class Contact
|
|||
*/
|
||||
public static function updateAvatar(int $cid, string $avatar, bool $force = false, bool $create_cache = false)
|
||||
{
|
||||
$contact = DBA::selectFirst(
|
||||
'contact',
|
||||
['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'],
|
||||
['id' => $cid, 'self' => false]
|
||||
);
|
||||
$contact = DBA::selectFirst('contact', ['uid', 'avatar', 'photo', 'thumb', 'micro', 'blurhash', 'xmpp', 'addr', 'nurl', 'url', 'network', 'uri-id'],
|
||||
['id' => $cid, 'self' => false]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -2303,12 +2268,10 @@ class Contact
|
|||
}
|
||||
|
||||
if ($default_avatar && Proxy::isLocalImage($avatar)) {
|
||||
$fields = [
|
||||
'avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
||||
$fields = ['avatar' => $avatar, 'avatar-date' => DateTimeFormat::utcNow(),
|
||||
'photo' => $avatar,
|
||||
'thumb' => self::getDefaultAvatar($contact, Proxy::SIZE_THUMB),
|
||||
'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO)
|
||||
];
|
||||
'micro' => self::getDefaultAvatar($contact, Proxy::SIZE_MICRO)];
|
||||
Logger::debug('Use default avatar', ['id' => $cid, 'uid' => $uid]);
|
||||
}
|
||||
|
||||
|
|
@ -2367,11 +2330,8 @@ class Contact
|
|||
$uids = [];
|
||||
if (($uid == 0) && !in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
// Collect all user contacts of the given public contact
|
||||
$personal_contacts = DBA::select(
|
||||
'contact',
|
||||
['id', 'uid'],
|
||||
["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid]
|
||||
);
|
||||
$personal_contacts = DBA::select('contact', ['id', 'uid'],
|
||||
["`nurl` = ? AND `id` != ? AND NOT `self`", $contact['nurl'], $cid]);
|
||||
while ($personal_contact = DBA::fetch($personal_contacts)) {
|
||||
$cids[] = $personal_contact['id'];
|
||||
$uids[] = $personal_contact['uid'];
|
||||
|
|
@ -2600,7 +2560,7 @@ class Contact
|
|||
* @throws HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function updateFromProbe(int $id, string $network = ''): bool
|
||||
public static function updateFromProbe(int $id, string $network = '')
|
||||
{
|
||||
$contact = DBA::selectFirst('contact', ['uid', 'url'], ['id' => $id]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
|
|
@ -2690,12 +2650,10 @@ class Contact
|
|||
// These fields aren't updated by this routine:
|
||||
// 'sensitive'
|
||||
|
||||
$fields = [
|
||||
'uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
|
||||
$fields = ['uid', 'uri-id', 'avatar', 'header', 'name', 'nick', 'location', 'keywords', 'about', 'subscribe',
|
||||
'manually-approve', 'unsearchable', 'url', 'addr', 'batch', 'notify', 'poll', 'request', 'confirm', 'poco',
|
||||
'network', 'alias', 'baseurl', 'gsid', 'forum', 'prv', 'contact-type', 'pubkey', 'last-item', 'xmpp', 'matrix',
|
||||
'created', 'last-update'
|
||||
];
|
||||
'created', 'last-update'];
|
||||
$contact = DBA::selectFirst('contact', $fields, ['id' => $id]);
|
||||
if (!DBA::isResult($contact)) {
|
||||
return false;
|
||||
|
|
@ -2719,7 +2677,7 @@ class Contact
|
|||
return true;
|
||||
}
|
||||
|
||||
$has_local_data = self::hasLocalData($id, $contact);
|
||||
$has_local_data = self::hasLocalData($id, $contact);
|
||||
|
||||
$uid = $contact['uid'];
|
||||
unset($contact['uid']);
|
||||
|
|
@ -2766,8 +2724,7 @@ class Contact
|
|||
// We must not try to update relay contacts via probe. They are no real contacts.
|
||||
// We check after the probing to be able to correct falsely detected contact types.
|
||||
if (($contact['contact-type'] == self::TYPE_RELAY) &&
|
||||
(!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))
|
||||
) {
|
||||
(!Strings::compareLink($ret['url'], $contact['url']) || in_array($ret['network'], [Protocol::FEED, Protocol::PHANTOM]))) {
|
||||
self::updateContact($id, $uid, $uriid, $contact['url'], ['failed' => false, 'local-data' => $has_local_data, 'last-update' => $updated, 'next-update' => $success_next_update, 'success_update' => $updated]);
|
||||
Logger::info('Not updating relais', ['id' => $id, 'url' => $contact['url']]);
|
||||
return true;
|
||||
|
|
@ -2816,7 +2773,7 @@ class Contact
|
|||
}
|
||||
|
||||
$update = false;
|
||||
$guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url'], $ret['baseurl'] ?? $ret['alias'] ?? '');
|
||||
$guid = ($ret['guid'] ?? '') ?: Item::guidFromUri($ret['url']);
|
||||
|
||||
// make sure to not overwrite existing values with blank entries except some technical fields
|
||||
$keep = ['batch', 'notify', 'poll', 'request', 'confirm', 'poco', 'baseurl'];
|
||||
|
|
@ -3005,7 +2962,7 @@ class Contact
|
|||
return $result;
|
||||
}
|
||||
|
||||
$arr = ['url' => $url, 'uid' => $uid, 'contact' => []];
|
||||
$arr = ['url' => $url, 'contact' => []];
|
||||
|
||||
Hook::callAll('follow', $arr);
|
||||
|
||||
|
|
@ -3103,32 +3060,31 @@ class Contact
|
|||
|
||||
// create contact record
|
||||
self::insert([
|
||||
'uid' => $uid,
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
'url' => $ret['url'],
|
||||
'nurl' => Strings::normaliseLink($ret['url']),
|
||||
'addr' => $ret['addr'],
|
||||
'alias' => $ret['alias'],
|
||||
'batch' => $ret['batch'],
|
||||
'notify' => $ret['notify'],
|
||||
'poll' => $ret['poll'],
|
||||
'poco' => $ret['poco'],
|
||||
'name' => $ret['name'],
|
||||
'nick' => $ret['nick'],
|
||||
'network' => $ret['network'],
|
||||
'baseurl' => $ret['baseurl'],
|
||||
'gsid' => $ret['gsid'] ?? null,
|
||||
'contact-type' => $ret['account-type'] ?? self::TYPE_PERSON,
|
||||
'protocol' => $protocol,
|
||||
'pubkey' => $ret['pubkey'],
|
||||
'rel' => $new_relation,
|
||||
'priority' => $ret['priority'],
|
||||
'writable' => $writeable,
|
||||
'hidden' => $hidden,
|
||||
'blocked' => 0,
|
||||
'readonly' => 0,
|
||||
'pending' => $pending,
|
||||
'subhub' => $subhub
|
||||
'uid' => $uid,
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
'url' => $ret['url'],
|
||||
'nurl' => Strings::normaliseLink($ret['url']),
|
||||
'addr' => $ret['addr'],
|
||||
'alias' => $ret['alias'],
|
||||
'batch' => $ret['batch'],
|
||||
'notify' => $ret['notify'],
|
||||
'poll' => $ret['poll'],
|
||||
'poco' => $ret['poco'],
|
||||
'name' => $ret['name'],
|
||||
'nick' => $ret['nick'],
|
||||
'network' => $ret['network'],
|
||||
'baseurl' => $ret['baseurl'],
|
||||
'gsid' => $ret['gsid'] ?? null,
|
||||
'protocol' => $protocol,
|
||||
'pubkey' => $ret['pubkey'],
|
||||
'rel' => $new_relation,
|
||||
'priority'=> $ret['priority'],
|
||||
'writable'=> $writeable,
|
||||
'hidden' => $hidden,
|
||||
'blocked' => 0,
|
||||
'readonly'=> 0,
|
||||
'pending' => $pending,
|
||||
'subhub' => $subhub
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
@ -3141,11 +3097,7 @@ class Contact
|
|||
$contact_id = $contact['id'];
|
||||
$result['cid'] = $contact_id;
|
||||
|
||||
if ($contact['contact-type'] == self::TYPE_COMMUNITY) {
|
||||
Circle::addMember(User::getDefaultGroupCircle($uid), $contact_id);
|
||||
} else {
|
||||
Circle::addMember(User::getDefaultCircle($uid), $contact_id);
|
||||
}
|
||||
Group::addMember(User::getDefaultGroup($uid), $contact_id);
|
||||
|
||||
// Update the avatar
|
||||
self::updateAvatar($contact_id, $ret['photo']);
|
||||
|
|
@ -3187,7 +3139,7 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
$fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked', 'baseurl'];
|
||||
$fields = ['id', 'url', 'name', 'nick', 'avatar', 'photo', 'network', 'blocked'];
|
||||
$pub_contact = DBA::selectFirst('contact', $fields, ['id' => $datarray['author-id']]);
|
||||
if (!DBA::isResult($pub_contact)) {
|
||||
// Should never happen
|
||||
|
|
@ -3220,10 +3172,8 @@ class Contact
|
|||
}
|
||||
|
||||
// Contact is blocked at user-level
|
||||
if (
|
||||
!empty($contact['id']) && !empty($importer['id']) &&
|
||||
Contact\User::isBlocked($contact['id'], $importer['id'])
|
||||
) {
|
||||
if (!empty($contact['id']) && !empty($importer['id']) &&
|
||||
Contact\User::isBlocked($contact['id'], $importer['id'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -3231,12 +3181,9 @@ class Contact
|
|||
self::unmarkForArchival($contact);
|
||||
|
||||
if (($contact['rel'] == self::SHARING)
|
||||
|| ($sharing && $contact['rel'] == self::FOLLOWER)
|
||||
) {
|
||||
self::update(
|
||||
['rel' => self::FRIEND, 'writable' => true, 'pending' => false],
|
||||
['id' => $contact['id'], 'uid' => $importer['uid']]
|
||||
);
|
||||
|| ($sharing && $contact['rel'] == self::FOLLOWER)) {
|
||||
self::update(['rel' => self::FRIEND, 'writable' => true, 'pending' => false],
|
||||
['id' => $contact['id'], 'uid' => $importer['uid']]);
|
||||
}
|
||||
|
||||
// Ensure to always have the correct network type, independent from the connection request method
|
||||
|
|
@ -3258,7 +3205,6 @@ class Contact
|
|||
'created' => DateTimeFormat::utcNow(),
|
||||
'url' => $url,
|
||||
'nurl' => Strings::normaliseLink($url),
|
||||
'baseurl' => $pub_contact['baseurl'] ?? '',
|
||||
'name' => $name,
|
||||
'nick' => $nick,
|
||||
'network' => $network,
|
||||
|
|
@ -3276,7 +3222,7 @@ class Contact
|
|||
|
||||
Post\UserNotification::insertNotification($pub_contact['id'], Activity::FOLLOW, $importer['uid']);
|
||||
|
||||
$contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo', 'contact-type'], ['id' => $contact_id]);
|
||||
$contact_record = DBA::selectFirst('contact', ['id', 'network', 'name', 'url', 'photo'], ['id' => $contact_id]);
|
||||
|
||||
/// @TODO Encapsulate this into a function/method
|
||||
$fields = ['uid', 'username', 'email', 'page-flags', 'notify-flags', 'language'];
|
||||
|
|
@ -3292,11 +3238,7 @@ class Contact
|
|||
DI::intro()->save($intro);
|
||||
}
|
||||
|
||||
if ($contact_record['contact-type'] == self::TYPE_COMMUNITY) {
|
||||
Circle::addMember(User::getDefaultGroupCircle($importer['uid']), $contact_record['id']);
|
||||
} else {
|
||||
Circle::addMember(User::getDefaultCircle($importer['uid']), $contact_record['id']);
|
||||
}
|
||||
Group::addMember(User::getDefaultGroup($importer['uid']), $contact_record['id']);
|
||||
|
||||
if (($user['notify-flags'] & Notification\Type::INTRO) && $user['page-flags'] == User::PAGE_FLAGS_NORMAL) {
|
||||
DI::notify()->createFromArray([
|
||||
|
|
@ -3480,7 +3422,7 @@ class Contact
|
|||
*/
|
||||
public static function magicLinkById(int $cid, string $url = ''): string
|
||||
{
|
||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'alias', 'uid'], ['id' => $cid]);
|
||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'uid'], ['id' => $cid]);
|
||||
|
||||
return self::magicLinkByContact($contact, $url);
|
||||
}
|
||||
|
|
@ -3497,7 +3439,7 @@ class Contact
|
|||
*/
|
||||
public static function magicLinkByContact(array $contact, string $url = ''): string
|
||||
{
|
||||
$destination = $url ?: (!Network::isValidHttpUrl($contact['url']) && !empty($contact['alias']) && Network::isValidHttpUrl($contact['alias']) ? $contact['alias'] : $contact['url']);
|
||||
$destination = $url ?: $contact['url'];
|
||||
|
||||
if (!DI::userSession()->isAuthenticated()) {
|
||||
return $destination;
|
||||
|
|
@ -3530,13 +3472,13 @@ class Contact
|
|||
}
|
||||
|
||||
/**
|
||||
* Is the contact a group?
|
||||
* Is the contact a forum?
|
||||
*
|
||||
* @param integer $contactid ID of the contact
|
||||
*
|
||||
* @return boolean "true" if it is a group
|
||||
* @return boolean "true" if it is a forum
|
||||
*/
|
||||
public static function isGroup(int $contactid): bool
|
||||
public static function isForum(int $contactid): bool
|
||||
{
|
||||
$fields = ['contact-type'];
|
||||
$condition = ['id' => $contactid];
|
||||
|
|
@ -3545,7 +3487,7 @@ class Contact
|
|||
return false;
|
||||
}
|
||||
|
||||
// Is it a group?
|
||||
// Is it a forum?
|
||||
return ($contact['contact-type'] == self::TYPE_COMMUNITY);
|
||||
}
|
||||
|
||||
|
|
@ -3626,10 +3568,8 @@ class Contact
|
|||
$params['limit'] = $limit;
|
||||
}
|
||||
|
||||
$condition = DBA::mergeConditions(
|
||||
$condition,
|
||||
["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]
|
||||
);
|
||||
$condition = DBA::mergeConditions($condition,
|
||||
["(`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]);
|
||||
|
||||
return DBA::selectToArray('account-user-view', [], $condition, $params);
|
||||
}
|
||||
|
|
@ -3679,7 +3619,7 @@ class Contact
|
|||
*/
|
||||
public static function getRandomContact(): array
|
||||
{
|
||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'alias', 'uid'], [
|
||||
$contact = DBA::selectFirst('contact', ['id', 'network', 'url', 'uid'], [
|
||||
"`uid` = ? AND `network` = ? AND NOT `failed` AND `last-item` > ?",
|
||||
0, Protocol::DFRN, DateTimeFormat::utc('now - 1 month'),
|
||||
], ['order' => ['RAND()']]);
|
||||
|
|
|
|||
|
|
@ -21,18 +21,17 @@
|
|||
|
||||
namespace Friendica\Model\Contact;
|
||||
|
||||
use Friendica\Content\Widget;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
|
||||
/**
|
||||
* This class provides information about contact circles based on the "group_member" table.
|
||||
* This class provides information about contact groups based on the "group_member" table.
|
||||
*/
|
||||
class Circle
|
||||
class Group
|
||||
{
|
||||
/**
|
||||
* Returns a list of contacts belonging in a circle
|
||||
* Returns a list of contacts belonging in a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @return array
|
||||
|
|
@ -43,22 +42,19 @@ class Circle
|
|||
$return = [];
|
||||
|
||||
if (intval($gid)) {
|
||||
$networks = Widget::unavailableNetworks();
|
||||
$sql_values = array_merge([$gid, DI::userSession()->getLocalUserId()], $networks);
|
||||
|
||||
$stmt = DBA::p('SELECT `circle_member`.`contact-id`, `contact`.*
|
||||
$stmt = DBA::p('SELECT `group_member`.`contact-id`, `contact`.*
|
||||
FROM `contact`
|
||||
INNER JOIN `group_member` AS `circle_member`
|
||||
ON `contact`.`id` = `circle_member`.`contact-id`
|
||||
INNER JOIN `group_member`
|
||||
ON `contact`.`id` = `group_member`.`contact-id`
|
||||
WHERE `gid` = ?
|
||||
AND `contact`.`uid` = ?
|
||||
AND NOT `contact`.`self`
|
||||
AND NOT `contact`.`deleted`
|
||||
AND NOT `contact`.`blocked`
|
||||
AND NOT `contact`.`pending`
|
||||
AND NOT `contact`.`network` IN (' . substr(str_repeat('?, ', count($networks)), 0, -2) . ')
|
||||
ORDER BY `contact`.`name` ASC',
|
||||
$sql_values
|
||||
$gid,
|
||||
DI::userSession()->getLocalUserId()
|
||||
);
|
||||
|
||||
if (DBA::isResult($stmt)) {
|
||||
|
|
@ -70,29 +66,24 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns uncircled contact count or list for user
|
||||
* Returns ungrouped contact count or list for user
|
||||
*
|
||||
* Returns either the total number of uncircled contacts for the given user
|
||||
* id or a paginated list of uncircled contacts.
|
||||
* Returns either the total number of ungrouped contacts for the given user
|
||||
* id or a paginated list of ungrouped contacts.
|
||||
*
|
||||
* @param int $uid uid
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function listUncircled(int $uid)
|
||||
public static function listUngrouped(int $uid)
|
||||
{
|
||||
$networks = Widget::unavailableNetworks();
|
||||
$query = "`uid` = ? AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `failed`
|
||||
AND NOT `network` IN (" . substr(str_repeat('?, ', count($networks)), 0, -2) . ")
|
||||
AND `id` NOT IN (SELECT DISTINCT(`contact-id`) FROM `group_member` AS `circle_member` INNER JOIN `group` AS `circle` ON `circle`.`id` = `circle_member`.`gid`
|
||||
WHERE `circle`.`uid` = ? AND `contact-id` = `contact`.`id`)";
|
||||
$condition = array_merge([$query], [$uid], $networks, [$uid]);
|
||||
|
||||
return Contact::selectToArray([], $condition);
|
||||
return Contact::selectToArray([], ["`uid` = ? AND NOT `self` AND NOT `deleted` AND NOT `blocked` AND NOT `pending` AND NOT `failed`
|
||||
AND `id` NOT IN (SELECT DISTINCT(`contact-id`) FROM `group_member` INNER JOIN `group` ON `group`.`id` = `group_member`.`gid`
|
||||
WHERE `group`.`uid` = ? AND `contact-id` = `contact`.`id`)", $uid, $uid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a contact from all circles
|
||||
* Remove a contact from all groups
|
||||
*
|
||||
* @param integer $contact_id
|
||||
*
|
||||
|
|
@ -31,8 +31,6 @@ use Friendica\Model\APContact;
|
|||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\Profile;
|
||||
use Friendica\Model\User;
|
||||
use Friendica\Model\Verb;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Strings;
|
||||
|
|
@ -772,77 +770,4 @@ class Relation
|
|||
['limit' => [$offset, $count], 'order' => [$shuffle ? 'RAND()' : 'name']]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the interaction scores for the given user
|
||||
*
|
||||
* @param integer $uid
|
||||
* @return void
|
||||
*/
|
||||
public static function calculateInteractionScore(int $uid)
|
||||
{
|
||||
$days = DI::config()->get('system', 'interaction_score_days');
|
||||
$contact_id = Contact::getPublicIdByUserId($uid);
|
||||
|
||||
Logger::debug('Calculation - start', ['uid' => $uid, 'cid' => $contact_id, 'days' => $days]);
|
||||
|
||||
$follow = Verb::getID(Activity::FOLLOW);
|
||||
$view = Verb::getID(Activity::VIEW);
|
||||
$read = Verb::getID(Activity::READ);
|
||||
|
||||
DBA::update('contact-relation', ['score' => 0, 'relation-score' => 0, 'thread-score' => 0, 'relation-thread-score' => 0], ['cid' => $contact_id]);
|
||||
|
||||
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
|
||||
Logger::debug('Calculate score', ['uid' => $uid, 'total' => $total['activity']]);
|
||||
|
||||
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
while ($interaction = DBA::fetch($interactions)) {
|
||||
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
|
||||
DBA::update('contact-relation', ['score' => $score], ['cid' => $contact_id, 'relation-cid' => $interaction['author-id']]);
|
||||
}
|
||||
DBA::close($interactions);
|
||||
|
||||
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
|
||||
Logger::debug('Calculate thread-score', ['uid' => $uid, 'total' => $total['activity']]);
|
||||
|
||||
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post`.`uri-id` = `post-user`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
while ($interaction = DBA::fetch($interactions)) {
|
||||
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
|
||||
DBA::update('contact-relation', ['thread-score' => $score], ['cid' => $contact_id, 'relation-cid' => $interaction['author-id']]);
|
||||
}
|
||||
DBA::close($interactions);
|
||||
|
||||
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
|
||||
Logger::debug('Calculate relation-score', ['uid' => $uid, 'total' => $total['activity']]);
|
||||
|
||||
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`thr-parent-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
while ($interaction = DBA::fetch($interactions)) {
|
||||
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
|
||||
DBA::update('contact-relation', ['relation-score' => $score], ['cid' => $contact_id, 'relation-cid' => $interaction['author-id']]);
|
||||
}
|
||||
DBA::close($interactions);
|
||||
|
||||
$total = DBA::fetchFirst("SELECT count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?)",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
|
||||
Logger::debug('Calculate relation-thread-score', ['uid' => $uid, 'total' => $total['activity']]);
|
||||
|
||||
$interactions = DBA::p("SELECT `post`.`author-id`, count(*) AS `activity` FROM `post-user` INNER JOIN `post` ON `post-user`.`uri-id` = `post`.`parent-uri-id` WHERE `post-user`.`author-id` = ? AND `post-user`.`received` >= ? AND `post-user`.`uid` = ? AND `post`.`author-id` != ? AND NOT `post`.`vid` IN (?, ?, ?) GROUP BY `post`.`author-id`",
|
||||
$contact_id, DateTimeFormat::utc('now - ' . $days . ' day'), $uid, $contact_id, $follow, $view, $read);
|
||||
while ($interaction = DBA::fetch($interactions)) {
|
||||
$score = min((int)(($interaction['activity'] / $total['activity']) * 65535), 65535);
|
||||
DBA::update('contact-relation', ['relation-thread-score' => $score], ['cid' => $contact_id, 'relation-cid' => $interaction['author-id']]);
|
||||
}
|
||||
DBA::close($interactions);
|
||||
Logger::debug('Calculation - end', ['uid' => $uid]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ use Friendica\Core\Protocol;
|
|||
use Friendica\Core\System;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\Database\DBStructure;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Model\ItemURI;
|
||||
|
|
@ -94,14 +95,13 @@ class User
|
|||
|
||||
$update_fields = self::preparedFields($fields);
|
||||
if (!empty($update_fields)) {
|
||||
$contacts = DBA::select('account-user-view', ['pid', 'uri-id', 'uid'], $condition);
|
||||
$contacts = DBA::select('contact', ['uri-id', 'uid'], $condition);
|
||||
while ($contact = DBA::fetch($contacts)) {
|
||||
if (empty($contact['uri-id']) || empty($contact['uid'])) {
|
||||
continue;
|
||||
}
|
||||
$update_fields['cid'] = $contact['pid'];
|
||||
$ret = DBA::update('user-contact', $update_fields, ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid']], true);
|
||||
Logger::info('Updated user contact', ['uid' => $contact['uid'], 'id' => $contact['pid'], 'uri-id' => $contact['uri-id'], 'ret' => $ret]);
|
||||
$ret = DBA::update('user-contact', $update_fields, ['uri-id' => $contact['uri-id'], 'uid' => $contact['uid']]);
|
||||
Logger::info('Updated user contact', ['uid' => $contact['uid'], 'uri-id' => $contact['uri-id'], 'ret' => $ret]);
|
||||
}
|
||||
|
||||
DBA::close($contacts);
|
||||
|
|
|
|||
|
|
@ -344,6 +344,7 @@ class Event
|
|||
$event['id'] = $event_id;
|
||||
|
||||
$item['uid'] = $event['uid'];
|
||||
$item['contact-id'] = $event['cid'];
|
||||
$item['uri'] = $event['uri'];
|
||||
$item['uri-id'] = ItemURI::getIdByURI($event['uri']);
|
||||
$item['guid'] = $event['guid'];
|
||||
|
|
@ -362,10 +363,11 @@ class Event
|
|||
$item['allow_gid'] = $event['allow_gid'];
|
||||
$item['deny_cid'] = $event['deny_cid'];
|
||||
$item['deny_gid'] = $event['deny_gid'];
|
||||
$item['private'] = $event['allow_cid'] && $event['allow_gid'] && $event['deny_cid'] && $event['deny_gid'] ? 0 : 1;
|
||||
$item['private'] = intval($event['private'] ?? 0);
|
||||
$item['visible'] = 1;
|
||||
$item['verb'] = Activity::POST;
|
||||
$item['object-type'] = Activity\ObjectType::EVENT;
|
||||
$item['post-type'] = Item::PT_EVENT;
|
||||
$item['origin'] = $event['cid'] === 0 ? 1 : 0;
|
||||
$item['body'] = self::getBBCode($event);
|
||||
$item['event-id'] = $event['id'];
|
||||
|
|
@ -545,14 +547,13 @@ class Event
|
|||
// Query for the event by event id
|
||||
$events = DBA::toArray(DBA::p(
|
||||
"SELECT `event`.*, `post-user`.`id` AS `itemid` FROM `event`
|
||||
LEFT JOIN `post-user`
|
||||
ON `post-user`.`event-id` = `event`.`id`
|
||||
LEFT JOIN `post-user`
|
||||
ON `post-user`.`event-id` = `event`.`id`
|
||||
AND `post-user`.`uid` = `event`.`uid`
|
||||
WHERE `event`.`id` = ?
|
||||
AND `event`.`uid` = ?
|
||||
$sql_perms",
|
||||
$event_id,
|
||||
$owner_uid
|
||||
$event_id, $owner_uid
|
||||
));
|
||||
if (empty($events)) {
|
||||
throw new HTTPException\NotFoundException(DI::l10n()->t('Event not found.'));
|
||||
|
|
@ -615,8 +616,7 @@ class Event
|
|||
AND `start` <= ?
|
||||
$sql_perms",
|
||||
$owner_uid,
|
||||
$start,
|
||||
$start,
|
||||
$start, $start,
|
||||
$finish
|
||||
));
|
||||
|
||||
|
|
@ -661,9 +661,9 @@ class Event
|
|||
$copy = null;
|
||||
$drop = null;
|
||||
if (DI::userSession()->getLocalUserId() && DI::userSession()->getLocalUserId() == $event['uid'] && $event['type'] == 'event') {
|
||||
$edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event'), '', ''] : null;
|
||||
$copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'], DI::l10n()->t('Duplicate event'), '', ''] : null;
|
||||
$drop = ['calendar/api/delete/' . $event['id'], DI::l10n()->t('Delete event'), '', ''];
|
||||
$edit = !$event['cid'] ? ['calendar/event/edit/' . $event['id'], DI::l10n()->t('Edit event') , '', ''] : null;
|
||||
$copy = !$event['cid'] ? ['calendar/event/copy/' . $event['id'] , DI::l10n()->t('Duplicate event'), '', ''] : null;
|
||||
$drop = ['calendar/api/delete/' . $event['id'] , DI::l10n()->t('Delete event') , '', ''];
|
||||
}
|
||||
|
||||
$title = BBCode::convertForUriId($event['uri-id'], Strings::escapeHtml($event['summary']));
|
||||
|
|
@ -708,7 +708,7 @@ class Event
|
|||
}
|
||||
|
||||
switch ($format) {
|
||||
// Format the exported data as a CSV file.
|
||||
// Format the exported data as a CSV file.
|
||||
case "csv":
|
||||
$o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL;
|
||||
|
||||
|
|
@ -728,7 +728,7 @@ class Event
|
|||
}
|
||||
break;
|
||||
|
||||
// Format the exported data as a ics file.
|
||||
// Format the exported data as a ics file.
|
||||
case "ical":
|
||||
$o = 'BEGIN:VCALENDAR' . PHP_EOL
|
||||
. 'VERSION:2.0' . PHP_EOL
|
||||
|
|
@ -929,13 +929,8 @@ class Event
|
|||
$location = self::locationToArray($item['event-location']);
|
||||
|
||||
// Construct the profile link (magic-auth).
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias']
|
||||
];
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$profile_link = Contact::magicLinkByContact($author);
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
|
||||
|
|
@ -1010,7 +1005,7 @@ class Event
|
|||
}
|
||||
}
|
||||
|
||||
$location['name'] = BBCode::toPlaintext($location['name'], false);
|
||||
$location['name'] = BBCode::convert($location['name']);
|
||||
|
||||
// Construct the map HTML.
|
||||
if (isset($location['address'])) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
namespace Friendica\Model;
|
||||
|
||||
use Friendica\BaseModule;
|
||||
use Friendica\Content\Widget;
|
||||
use Friendica\Core\Logger;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
|
|
@ -35,16 +34,16 @@ use Friendica\Protocol\ActivityPub;
|
|||
/**
|
||||
* functions for interacting with the group database table
|
||||
*/
|
||||
class Circle
|
||||
class Group
|
||||
{
|
||||
const FOLLOWERS = '~';
|
||||
const MUTUALS = '&';
|
||||
|
||||
/**
|
||||
* Fetches circle record by user id and maybe includes deleted circles as well
|
||||
* Fetches group record by user id and maybe includes deleted groups as well
|
||||
*
|
||||
* @param int $uid User id to fetch circle(s) for
|
||||
* @param bool $includesDeleted Whether deleted circles should be included
|
||||
* @param int $uid User id to fetch group(s) for
|
||||
* @param bool $includesDeleted Whether deleted groups should be included
|
||||
* @return array|bool Array on success, bool on error
|
||||
*/
|
||||
public static function getByUserId(int $uid, bool $includesDeleted = false)
|
||||
|
|
@ -59,16 +58,16 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks whether given circle id is found in database
|
||||
* Checks whether given group id is found in database
|
||||
*
|
||||
* @param int $circle_id Circle id
|
||||
* @param int $group_id Group id
|
||||
* @param int $uid Optional user id
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function exists(int $circle_id, int $uid = null): bool
|
||||
public static function exists(int $group_id, int $uid = null): bool
|
||||
{
|
||||
$condition = ['id' => $circle_id, 'deleted' => false];
|
||||
$condition = ['id' => $group_id, 'deleted' => false];
|
||||
|
||||
if (!is_null($uid)) {
|
||||
$condition = [
|
||||
|
|
@ -80,13 +79,13 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new contact circle
|
||||
* Create a new contact group
|
||||
*
|
||||
* Note: If we found a deleted circle with the same name, we restore it
|
||||
* Note: If we found a deleted group with the same name, we restore it
|
||||
*
|
||||
* @param int $uid User id to create circle for
|
||||
* @param string $name Name of circle
|
||||
* @return int|boolean Id of newly created circle or false on error
|
||||
* @param int $uid User id to create group for
|
||||
* @param string $name Name of group
|
||||
* @return int|boolean Id of newly created group or false on error
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function create(int $uid, string $name)
|
||||
|
|
@ -96,14 +95,14 @@ class Circle
|
|||
$gid = self::getIdByName($uid, $name); // check for dupes
|
||||
if ($gid !== false) {
|
||||
// This could be a problem.
|
||||
// Let's assume we've just created a circle which we once deleted
|
||||
// all the old members are gone, but the circle remains, so we don't break any security
|
||||
// access lists. What we're doing here is reviving the dead circle, but old content which
|
||||
// was restricted to this circle may now be seen by the new circle members.
|
||||
$circle = DBA::selectFirst('group', ['deleted'], ['id' => $gid]);
|
||||
if (DBA::isResult($circle) && $circle['deleted']) {
|
||||
// Let's assume we've just created a group which we once deleted
|
||||
// all the old members are gone, but the group remains so we don't break any security
|
||||
// access lists. What we're doing here is reviving the dead group, but old content which
|
||||
// was restricted to this group may now be seen by the new group members.
|
||||
$group = DBA::selectFirst('group', ['deleted'], ['id' => $gid]);
|
||||
if (DBA::isResult($group) && $group['deleted']) {
|
||||
DBA::update('group', ['deleted' => 0], ['id' => $gid]);
|
||||
DI::sysmsg()->addNotice(DI::l10n()->t('A deleted circle with this name was revived. Existing item permissions <strong>may</strong> apply to this circle and any future members. If this is not what you intended, please create another circle with a different name.'));
|
||||
DI::sysmsg()->addNotice(DI::l10n()->t('A deleted group with this name was revived. Existing item permissions <strong>may</strong> apply to this group and any future members. If this is not what you intended, please create another group with a different name.'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -117,10 +116,10 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Update circle information.
|
||||
* Update group information.
|
||||
*
|
||||
* @param int $id Circle ID
|
||||
* @param string $name Circle name
|
||||
* @param int $id Group ID
|
||||
* @param string $name Group name
|
||||
*
|
||||
* @return bool Was the update successful?
|
||||
* @throws \Exception
|
||||
|
|
@ -131,10 +130,10 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a list of circle ids a contact belongs to
|
||||
* Get a list of group ids a contact belongs to
|
||||
*
|
||||
* @param int $cid Contact id
|
||||
* @return array Circle ids
|
||||
* @return array Group ids
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getIdsByContactId(int $cid): array
|
||||
|
|
@ -144,50 +143,50 @@ class Circle
|
|||
return [];
|
||||
}
|
||||
|
||||
$circleIds = [];
|
||||
$groupIds = [];
|
||||
|
||||
$stmt = DBA::select('group_member', ['gid'], ['contact-id' => $cid]);
|
||||
while ($circle = DBA::fetch($stmt)) {
|
||||
$circleIds[] = $circle['gid'];
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$groupIds[] = $group['gid'];
|
||||
}
|
||||
DBA::close($stmt);
|
||||
|
||||
// Meta-circles
|
||||
// Meta-groups
|
||||
if ($contact['rel'] == Contact::FOLLOWER || $contact['rel'] == Contact::FRIEND) {
|
||||
$circleIds[] = self::FOLLOWERS;
|
||||
$groupIds[] = self::FOLLOWERS;
|
||||
}
|
||||
|
||||
if ($contact['rel'] == Contact::FRIEND) {
|
||||
$circleIds[] = self::MUTUALS;
|
||||
$groupIds[] = self::MUTUALS;
|
||||
}
|
||||
|
||||
return $circleIds;
|
||||
return $groupIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* count unread circle items
|
||||
* count unread group items
|
||||
*
|
||||
* Count unread items of each circle of the local user
|
||||
* Count unread items of each groups of the local user
|
||||
*
|
||||
* @return array
|
||||
* 'id' => circle id
|
||||
* 'name' => circle name
|
||||
* 'count' => counted unseen circle items
|
||||
* 'id' => group id
|
||||
* 'name' => group name
|
||||
* 'count' => counted unseen group items
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function countUnseen()
|
||||
{
|
||||
$stmt = DBA::p("SELECT `circle`.`id`, `circle`.`name`,
|
||||
(SELECT COUNT(*) FROM `post-user-view`
|
||||
$stmt = DBA::p("SELECT `group`.`id`, `group`.`name`,
|
||||
(SELECT COUNT(*) FROM `post-user`
|
||||
WHERE `uid` = ?
|
||||
AND `unseen`
|
||||
AND `contact-id` IN
|
||||
(SELECT `contact-id`
|
||||
FROM `group_member` AS `circle_member`
|
||||
WHERE `circle_member`.`gid` = `circle`.`id`)
|
||||
FROM `group_member`
|
||||
WHERE `group_member`.`gid` = `group`.`id`)
|
||||
) AS `count`
|
||||
FROM `group` AS `circle`
|
||||
WHERE `circle`.`uid` = ?;",
|
||||
FROM `group`
|
||||
WHERE `group`.`uid` = ?;",
|
||||
DI::userSession()->getLocalUserId(),
|
||||
DI::userSession()->getLocalUserId()
|
||||
);
|
||||
|
|
@ -196,13 +195,13 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the circle id for a user/name couple
|
||||
* Get the group id for a user/name couple
|
||||
*
|
||||
* Returns false if no circle has been found.
|
||||
* Returns false if no group has been found.
|
||||
*
|
||||
* @param int $uid User id
|
||||
* @param string $name Circle name
|
||||
* @return int|boolean Circle's id number or false on error
|
||||
* @param string $name Group name
|
||||
* @return int|boolean Groups' id number or false on error
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getIdByName(int $uid, string $name)
|
||||
|
|
@ -211,16 +210,16 @@ class Circle
|
|||
return false;
|
||||
}
|
||||
|
||||
$circle = DBA::selectFirst('group', ['id'], ['uid' => $uid, 'name' => $name]);
|
||||
if (DBA::isResult($circle)) {
|
||||
return $circle['id'];
|
||||
$group = DBA::selectFirst('group', ['id'], ['uid' => $uid, 'name' => $name]);
|
||||
if (DBA::isResult($group)) {
|
||||
return $group['id'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a circle as deleted
|
||||
* Mark a group as deleted
|
||||
*
|
||||
* @param int $gid
|
||||
* @return boolean
|
||||
|
|
@ -232,13 +231,13 @@ class Circle
|
|||
return false;
|
||||
}
|
||||
|
||||
$circle = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (!DBA::isResult($circle)) {
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (!DBA::isResult($group)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// remove circle from default posting lists
|
||||
$user = DBA::selectFirst('user', ['def_gid', 'allow_gid', 'deny_gid'], ['uid' => $circle['uid']]);
|
||||
// remove group from default posting lists
|
||||
$user = DBA::selectFirst('user', ['def_gid', 'allow_gid', 'deny_gid'], ['uid' => $group['uid']]);
|
||||
if (DBA::isResult($user)) {
|
||||
$change = false;
|
||||
|
||||
|
|
@ -256,21 +255,21 @@ class Circle
|
|||
}
|
||||
|
||||
if ($change) {
|
||||
DBA::update('user', $user, ['uid' => $circle['uid']]);
|
||||
DBA::update('user', $user, ['uid' => $group['uid']]);
|
||||
}
|
||||
}
|
||||
|
||||
// remove all members
|
||||
DBA::delete('group_member', ['gid' => $gid]);
|
||||
|
||||
// remove circle
|
||||
// remove group
|
||||
$return = DBA::update('group', ['deleted' => 1], ['id' => $gid]);
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a contact to a circle
|
||||
* Adds a contact to a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param int $cid
|
||||
|
|
@ -284,12 +283,12 @@ class Circle
|
|||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$circle = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($circle)) {
|
||||
throw new HTTPException\NotFoundException('Circle not found.');
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
|
@ -298,7 +297,7 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes a contact from a circle
|
||||
* Removes a contact from a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param int $cid
|
||||
|
|
@ -312,12 +311,12 @@ class Circle
|
|||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$circle = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($circle)) {
|
||||
throw new HTTPException\NotFoundException('Circle not found.');
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
|
@ -326,7 +325,7 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Adds contacts to a circle
|
||||
* Adds contacts to a group
|
||||
*
|
||||
* @param int $gid
|
||||
* @param array $contacts Array with contact ids
|
||||
|
|
@ -340,13 +339,13 @@ class Circle
|
|||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$circle = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($circle)) {
|
||||
throw new HTTPException\NotFoundException('Circle not found.');
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
|
@ -356,9 +355,9 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Removes contacts from a circle
|
||||
* Removes contacts from a group
|
||||
*
|
||||
* @param int $gid Circle id
|
||||
* @param int $gid Group id
|
||||
* @param array $contacts Contact ids
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
|
|
@ -370,15 +369,15 @@ class Circle
|
|||
}
|
||||
|
||||
// @TODO Backward compatibility with user contacts, remove by version 2022.03
|
||||
$circle = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($circle)) {
|
||||
throw new HTTPException\NotFoundException('Circle not found.');
|
||||
$group = DBA::selectFirst('group', ['uid'], ['id' => $gid]);
|
||||
if (empty($group)) {
|
||||
throw new HTTPException\NotFoundException('Group not found.');
|
||||
}
|
||||
|
||||
$contactIds = [];
|
||||
|
||||
foreach ($contacts as $cid) {
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $circle['uid']);
|
||||
$cdata = Contact::getPublicAndUserContactID($cid, $group['uid']);
|
||||
if (empty($cdata['user'])) {
|
||||
throw new HTTPException\NotFoundException('Invalid contact.');
|
||||
}
|
||||
|
|
@ -391,18 +390,18 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the combined list of contact ids from a circle id list
|
||||
* Returns the combined list of contact ids from a group id list
|
||||
*
|
||||
* @param int $uid User id
|
||||
* @param array $circle_ids Circles ids
|
||||
* @param array $group_ids Groups ids
|
||||
* @param boolean $check_dead Whether check "dead" records (?)
|
||||
* @param boolean $expand_followers Expand the list of followers
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function expand(int $uid, array $circle_ids, bool $check_dead = false, bool $expand_followers = true): array
|
||||
public static function expand(int $uid, array $group_ids, bool $check_dead = false, bool $expand_followers = true): array
|
||||
{
|
||||
if (!is_array($circle_ids) || !count($circle_ids)) {
|
||||
if (!is_array($group_ids) || !count($group_ids)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -420,7 +419,7 @@ class Circle
|
|||
$networks = array_diff($networks, [Protocol::MAIL]);
|
||||
}
|
||||
|
||||
$key = array_search(self::FOLLOWERS, $circle_ids);
|
||||
$key = array_search(self::FOLLOWERS, $group_ids);
|
||||
if ($key !== false) {
|
||||
if ($expand_followers) {
|
||||
$followers = Contact::selectToArray(['id'], [
|
||||
|
|
@ -439,10 +438,10 @@ class Circle
|
|||
} else {
|
||||
$followers_collection = true;
|
||||
}
|
||||
unset($circle_ids[$key]);
|
||||
unset($group_ids[$key]);
|
||||
}
|
||||
|
||||
$key = array_search(self::MUTUALS, $circle_ids);
|
||||
$key = array_search(self::MUTUALS, $group_ids);
|
||||
if ($key !== false) {
|
||||
$mutuals = Contact::selectToArray(['id'], [
|
||||
'uid' => $uid,
|
||||
|
|
@ -458,12 +457,12 @@ class Circle
|
|||
$return[] = $mutual['id'];
|
||||
}
|
||||
|
||||
unset($circle_ids[$key]);
|
||||
unset($group_ids[$key]);
|
||||
}
|
||||
|
||||
$stmt = DBA::select('group_member', ['contact-id'], ['gid' => $circle_ids]);
|
||||
while ($circle_member = DBA::fetch($stmt)) {
|
||||
$return[] = $circle_member['contact-id'];
|
||||
$stmt = DBA::select('group_member', ['contact-id'], ['gid' => $group_ids]);
|
||||
while ($group_member = DBA::fetch($stmt)) {
|
||||
$return[] = $group_member['contact-id'];
|
||||
}
|
||||
DBA::close($stmt);
|
||||
|
||||
|
|
@ -479,18 +478,17 @@ class Circle
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a templated circle selection list
|
||||
* Returns a templated group selection list
|
||||
*
|
||||
* @param int $uid User id
|
||||
* @param int $gid A pre-selected circle
|
||||
* @param string $id The id of the option group
|
||||
* @param string $label The label of the option group
|
||||
* @param int $gid An optional pre-selected group
|
||||
* @param string $label An optional label of the list
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function getSelectorHTML(int $uid, int $gid, string $id, string $label): string
|
||||
public static function displayGroupSelection(int $uid, int $gid = 0, string $label = ''): string
|
||||
{
|
||||
$display_circles = [
|
||||
$display_groups = [
|
||||
[
|
||||
'name' => '',
|
||||
'id' => '0',
|
||||
|
|
@ -499,50 +497,53 @@ class Circle
|
|||
];
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => $uid, 'cid' => null], ['order' => ['name']]);
|
||||
while ($circle = DBA::fetch($stmt)) {
|
||||
$display_circles[] = [
|
||||
'name' => $circle['name'],
|
||||
'id' => $circle['id'],
|
||||
'selected' => $gid == $circle['id'] ? 'true' : ''
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$display_groups[] = [
|
||||
'name' => $group['name'],
|
||||
'id' => $group['id'],
|
||||
'selected' => $gid == $group['id'] ? 'true' : ''
|
||||
];
|
||||
}
|
||||
DBA::close($stmt);
|
||||
|
||||
Logger::info('Got circles', $display_circles);
|
||||
Logger::info('Got groups', $display_groups);
|
||||
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('circle_selection.tpl'), [
|
||||
'$id' => $id,
|
||||
if ($label == '') {
|
||||
$label = DI::l10n()->t('Default privacy group for new contacts');
|
||||
}
|
||||
|
||||
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('group_selection.tpl'), [
|
||||
'$label' => $label,
|
||||
'$circles' => $display_circles
|
||||
'$groups' => $display_groups
|
||||
]);
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create circle sidebar widget
|
||||
* Create group sidebar widget
|
||||
*
|
||||
* @param string $every
|
||||
* @param string $each
|
||||
* @param string $editmode
|
||||
* 'standard' => include link 'Edit circles'
|
||||
* 'extended' => include link 'Create new circle'
|
||||
* 'full' => include link 'Create new circle' and provide for each circle a link to edit this circle
|
||||
* @param string|int $circle_id Distinct circle id or 'everyone'
|
||||
* 'standard' => include link 'Edit groups'
|
||||
* 'extended' => include link 'Create new group'
|
||||
* 'full' => include link 'Create new group' and provide for each group a link to edit this group
|
||||
* @param string|int $group_id Distinct group id or 'everyone'
|
||||
* @param int $cid Contact id
|
||||
* @return string Sidebar widget HTML code
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function sidebarWidget(string $every = 'contact', string $each = 'circle', string $editmode = 'standard', $circle_id = '', int $cid = 0)
|
||||
public static function sidebarWidget(string $every = 'contact', string $each = 'group', string $editmode = 'standard', $group_id = '', int $cid = 0)
|
||||
{
|
||||
if (!DI::userSession()->getLocalUserId()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$display_circles = [
|
||||
$display_groups = [
|
||||
[
|
||||
'text' => DI::l10n()->t('Everybody'),
|
||||
'id' => 0,
|
||||
'selected' => (($circle_id === 'everyone') ? 'circle-selected' : ''),
|
||||
'selected' => (($group_id === 'everyone') ? 'group-selected' : ''),
|
||||
'href' => $every,
|
||||
]
|
||||
];
|
||||
|
|
@ -553,82 +554,77 @@ class Circle
|
|||
}
|
||||
|
||||
$stmt = DBA::select('group', [], ['deleted' => false, 'uid' => DI::userSession()->getLocalUserId(), 'cid' => null], ['order' => ['name']]);
|
||||
while ($circle = DBA::fetch($stmt)) {
|
||||
$selected = (($circle_id == $circle['id']) ? ' circle-selected' : '');
|
||||
while ($group = DBA::fetch($stmt)) {
|
||||
$selected = (($group_id == $group['id']) ? ' group-selected' : '');
|
||||
|
||||
if ($editmode == 'full') {
|
||||
$circleedit = [
|
||||
'href' => 'circle/' . $circle['id'],
|
||||
$groupedit = [
|
||||
'href' => 'group/' . $group['id'],
|
||||
'title' => DI::l10n()->t('edit'),
|
||||
];
|
||||
} else {
|
||||
$circleedit = null;
|
||||
$groupedit = null;
|
||||
}
|
||||
|
||||
if ($each == 'circle') {
|
||||
$networks = Widget::unavailableNetworks();
|
||||
$sql_values = array_merge([$circle['id']], $networks);
|
||||
$condition = ["`circle-id` = ? AND NOT `contact-network` IN (" . substr(str_repeat("?, ", count($networks)), 0, -2) . ")"];
|
||||
$condition = array_merge($condition, $sql_values);
|
||||
|
||||
$count = DBA::count('circle-member-view', $condition);
|
||||
$circle_name = sprintf('%s (%d)', $circle['name'], $count);
|
||||
if ($each == 'group') {
|
||||
$count = DBA::count('group_member', ['gid' => $group['id']]);
|
||||
$group_name = sprintf('%s (%d)', $group['name'], $count);
|
||||
} else {
|
||||
$circle_name = $circle['name'];
|
||||
$group_name = $group['name'];
|
||||
}
|
||||
|
||||
$display_circles[] = [
|
||||
'id' => $circle['id'],
|
||||
$display_groups[] = [
|
||||
'id' => $group['id'],
|
||||
'cid' => $cid,
|
||||
'text' => $circle_name,
|
||||
'href' => $each . '/' . $circle['id'],
|
||||
'edit' => $circleedit,
|
||||
'text' => $group_name,
|
||||
'href' => $each . '/' . $group['id'],
|
||||
'edit' => $groupedit,
|
||||
'selected' => $selected,
|
||||
'ismember' => in_array($circle['id'], $member_of),
|
||||
'ismember' => in_array($group['id'], $member_of),
|
||||
];
|
||||
}
|
||||
DBA::close($stmt);
|
||||
|
||||
// Don't show the circles on the network page when there is only one
|
||||
if ((count($display_circles) <= 2) && ($each == 'network')) {
|
||||
// Don't show the groups on the network page when there is only one
|
||||
if ((count($display_groups) <= 2) && ($each == 'network')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('circle_side.tpl');
|
||||
$tpl = Renderer::getMarkupTemplate('group_side.tpl');
|
||||
$o = Renderer::replaceMacros($tpl, [
|
||||
'$add' => DI::l10n()->t('add'),
|
||||
'$title' => DI::l10n()->t('Circles'),
|
||||
'$circles' => $display_circles,
|
||||
'$new_circle' => $editmode == 'extended' || $editmode == 'full' ? 1 : '',
|
||||
'$circle_page' => 'circle/',
|
||||
'$edittext' => DI::l10n()->t('Edit circle'),
|
||||
'$uncircled' => $every === 'contact' ? DI::l10n()->t('Contacts not in any circle') : '',
|
||||
'$uncircled_selected' => (($circle_id === 'none') ? 'circle-selected' : ''),
|
||||
'$createtext' => DI::l10n()->t('Create a new circle'),
|
||||
'$create_circle' => DI::l10n()->t('Circle Name: '),
|
||||
'$edit_circles_text' => DI::l10n()->t('Edit circles'),
|
||||
'$form_security_token' => BaseModule::getFormSecurityToken('circle_edit'),
|
||||
'$title' => DI::l10n()->t('Groups'),
|
||||
'$groups' => $display_groups,
|
||||
'newgroup' => $editmode == 'extended' || $editmode == 'full' ? 1 : '',
|
||||
'grouppage' => 'group/',
|
||||
'$edittext' => DI::l10n()->t('Edit group'),
|
||||
'$ungrouped' => $every === 'contact' ? DI::l10n()->t('Contacts not in any group') : '',
|
||||
'$ungrouped_selected' => (($group_id === 'none') ? 'group-selected' : ''),
|
||||
'$createtext' => DI::l10n()->t('Create a new group'),
|
||||
'$creategroup' => DI::l10n()->t('Group Name: '),
|
||||
'$editgroupstext' => DI::l10n()->t('Edit groups'),
|
||||
'$form_security_token' => BaseModule::getFormSecurityToken('group_edit'),
|
||||
]);
|
||||
|
||||
return $o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the circle id for the given contact id
|
||||
* Fetch the group id for the given contact id
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
* @return integer Circle ID
|
||||
* @return integer Group IO
|
||||
*/
|
||||
public static function getIdForGroup(int $id): int
|
||||
public static function getIdForForum(int $id): int
|
||||
{
|
||||
Logger::info('Get id for group id', ['id' => $id]);
|
||||
Logger::info('Get id for forum id', ['id' => $id]);
|
||||
$contact = Contact::getById($id, ['uid', 'name', 'contact-type', 'manually-approve']);
|
||||
if (empty($contact) || ($contact['contact-type'] != Contact::TYPE_COMMUNITY) || !$contact['manually-approve']) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$circle = DBA::selectFirst('group', ['id'], ['uid' => $contact['uid'], 'cid' => $id]);
|
||||
if (empty($circle)) {
|
||||
$group = DBA::selectFirst('group', ['id'], ['uid' => $contact['uid'], 'cid' => $id]);
|
||||
if (empty($group)) {
|
||||
$fields = [
|
||||
'uid' => $contact['uid'],
|
||||
'name' => $contact['name'],
|
||||
|
|
@ -637,21 +633,21 @@ class Circle
|
|||
DBA::insert('group', $fields);
|
||||
$gid = DBA::lastInsertId();
|
||||
} else {
|
||||
$gid = $circle['id'];
|
||||
$gid = $group['id'];
|
||||
}
|
||||
|
||||
return $gid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the followers of a given contact id and store them as circle members
|
||||
* Fetch the followers of a given contact id and store them as group members
|
||||
*
|
||||
* @param integer $id Contact ID
|
||||
* @return void
|
||||
*/
|
||||
public static function updateMembersForGroup(int $id)
|
||||
public static function updateMembersForForum(int $id)
|
||||
{
|
||||
Logger::info('Update group members', ['id' => $id]);
|
||||
Logger::info('Update forum members', ['id' => $id]);
|
||||
|
||||
$contact = Contact::getById($id, ['uid', 'url']);
|
||||
if (empty($contact)) {
|
||||
|
|
@ -663,14 +659,14 @@ class Circle
|
|||
return;
|
||||
}
|
||||
|
||||
$gid = self::getIdForGroup($id);
|
||||
$gid = self::getIdForForum($id);
|
||||
if (empty($gid)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$circle_members = DBA::selectToArray('group_member', ['contact-id'], ['gid' => $gid]);
|
||||
if (!empty($circle_members)) {
|
||||
$current = array_unique(array_column($circle_members, 'contact-id'));
|
||||
$group_members = DBA::selectToArray('group_member', ['contact-id'], ['gid' => $gid]);
|
||||
if (!empty($group_members)) {
|
||||
$current = array_unique(array_column($group_members, 'contact-id'));
|
||||
} else {
|
||||
$current = [];
|
||||
}
|
||||
|
|
@ -686,6 +682,6 @@ class Circle
|
|||
}
|
||||
|
||||
DBA::delete('group_member', ['gid' => $gid, 'contact-id' => $current]);
|
||||
Logger::info('Updated group members', ['id' => $id, 'count' => DBA::count('group_member', ['gid' => $gid])]);
|
||||
Logger::info('Updated forum members', ['id' => $id, 'count' => DBA::count('group_member', ['gid' => $gid])]);
|
||||
}
|
||||
}
|
||||
|
|
@ -21,7 +21,6 @@
|
|||
|
||||
namespace Friendica\Model;
|
||||
|
||||
use Friendica\Contact\LocalRelationship\Entity\LocalRelationship;
|
||||
use Friendica\Content\Text\BBCode;
|
||||
use Friendica\Content\Text\HTML;
|
||||
use Friendica\Core\Hook;
|
||||
|
|
@ -32,7 +31,6 @@ use Friendica\Core\System;
|
|||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Post\Category;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\ActivityPub;
|
||||
|
|
@ -96,9 +94,9 @@ class Item
|
|||
'wall', 'private', 'starred', 'origin', 'parent-origin', 'title', 'body', 'language',
|
||||
'content-warning', 'location', 'coord', 'app', 'rendered-hash', 'rendered-html', 'object',
|
||||
'quote-uri', 'quote-uri-id', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'mention', 'global',
|
||||
'author-id', 'author-link', 'author-alias', 'author-name', 'author-avatar', 'author-network', 'author-updated', 'author-gsid', 'author-baseurl', 'author-addr', 'author-uri-id',
|
||||
'owner-id', 'owner-link', 'owner-alias', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated', 'owner-gsid',
|
||||
'causer-id', 'causer-link', 'causer-alias', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network', 'causer-gsid',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network', 'author-updated', 'author-gsid', 'author-addr', 'author-uri-id',
|
||||
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'owner-network', 'owner-contact-type', 'owner-updated',
|
||||
'causer-id', 'causer-link', 'causer-name', 'causer-avatar', 'causer-contact-type', 'causer-network',
|
||||
'contact-id', 'contact-uid', 'contact-link', 'contact-name', 'contact-avatar',
|
||||
'writable', 'self', 'cid', 'alias',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
|
|
@ -110,35 +108,31 @@ class Item
|
|||
];
|
||||
|
||||
// Field list that is used to deliver items via the protocols
|
||||
const DELIVER_FIELDLIST = [
|
||||
'uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid',
|
||||
'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target',
|
||||
'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app',
|
||||
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin',
|
||||
'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||
'event-nofinish', 'event-ignore', 'event-id'
|
||||
];
|
||||
const DELIVER_FIELDLIST = ['uid', 'id', 'parent', 'uri-id', 'uri', 'thr-parent', 'parent-uri', 'guid',
|
||||
'parent-guid', 'conversation', 'received', 'created', 'edited', 'verb', 'object-type', 'object', 'target',
|
||||
'private', 'title', 'body', 'raw-body', 'location', 'coord', 'app',
|
||||
'inform', 'deleted', 'extid', 'post-type', 'post-reason', 'gravity',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'author-id', 'author-addr', 'author-link', 'author-name', 'author-avatar', 'owner-id', 'owner-link', 'contact-uid',
|
||||
'signed_text', 'network', 'wall', 'contact-id', 'plink', 'origin',
|
||||
'thr-parent-id', 'parent-uri-id', 'quote-uri', 'quote-uri-id', 'postopts', 'pubmail',
|
||||
'event-created', 'event-edited', 'event-start', 'event-finish',
|
||||
'event-summary', 'event-desc', 'event-location', 'event-type',
|
||||
'event-nofinish', 'event-ignore', 'event-id'];
|
||||
|
||||
// All fields in the item table
|
||||
const ITEM_FIELDLIST = [
|
||||
'id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent',
|
||||
'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid',
|
||||
'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid',
|
||||
'created', 'edited', 'commented', 'received', 'changed', 'verb',
|
||||
'postopts', 'plink', 'resource-id', 'event-id', 'inform',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason',
|
||||
'private', 'pubmail', 'visible', 'starred',
|
||||
'unseen', 'deleted', 'origin', 'mention', 'global', 'network',
|
||||
'title', 'content-warning', 'body', 'location', 'coord', 'app',
|
||||
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
||||
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id'
|
||||
];
|
||||
const ITEM_FIELDLIST = ['id', 'uid', 'parent', 'uri', 'parent-uri', 'thr-parent',
|
||||
'guid', 'uri-id', 'parent-uri-id', 'thr-parent-id', 'conversation', 'vid',
|
||||
'quote-uri', 'quote-uri-id', 'contact-id', 'wall', 'gravity', 'extid', 'psid',
|
||||
'created', 'edited', 'commented', 'received', 'changed', 'verb',
|
||||
'postopts', 'plink', 'resource-id', 'event-id', 'inform',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid', 'post-type', 'post-reason',
|
||||
'private', 'pubmail', 'visible', 'starred',
|
||||
'unseen', 'deleted', 'origin', 'mention', 'global', 'network',
|
||||
'title', 'content-warning', 'body', 'location', 'coord', 'app',
|
||||
'rendered-hash', 'rendered-html', 'object-type', 'object', 'target-type', 'target',
|
||||
'author-id', 'author-link', 'author-name', 'author-avatar', 'author-network',
|
||||
'owner-id', 'owner-link', 'owner-name', 'owner-avatar', 'causer-id'];
|
||||
|
||||
// List of all verbs that don't need additional content data.
|
||||
// Never reorder or remove entries from this list. Just add new ones at the end, if needed.
|
||||
|
|
@ -146,8 +140,7 @@ class Item
|
|||
Activity::LIKE, Activity::DISLIKE,
|
||||
Activity::ATTEND, Activity::ATTENDNO, Activity::ATTENDMAYBE,
|
||||
Activity::FOLLOW,
|
||||
Activity::ANNOUNCE
|
||||
];
|
||||
Activity::ANNOUNCE];
|
||||
|
||||
// Privacy levels
|
||||
const PUBLIC = 0;
|
||||
|
|
@ -198,10 +191,8 @@ class Item
|
|||
}
|
||||
|
||||
// We only need to call the line by line update for specific fields
|
||||
if (
|
||||
empty($fields['body']) && empty($fields['file']) &&
|
||||
empty($fields['attach']) && empty($fields['edited'])
|
||||
) {
|
||||
if (empty($fields['body']) && empty($fields['file']) &&
|
||||
empty($fields['attach']) && empty($fields['edited'])) {
|
||||
return $rows;
|
||||
}
|
||||
|
||||
|
|
@ -328,11 +319,9 @@ class Item
|
|||
{
|
||||
Logger::info('Mark item for deletion by id', ['id' => $item_id, 'callstack' => System::callstack()]);
|
||||
// locate item to be deleted
|
||||
$fields = [
|
||||
'id', 'uri', 'uri-id', 'uid', 'parent', 'parent-uri-id', 'origin',
|
||||
$fields = ['id', 'uri', 'uri-id', 'uid', 'parent', 'parent-uri-id', 'origin',
|
||||
'deleted', 'resource-id', 'event-id',
|
||||
'verb', 'object-type', 'object', 'target', 'contact-id', 'psid', 'gravity'
|
||||
];
|
||||
'verb', 'object-type', 'object', 'target', 'contact-id', 'psid', 'gravity'];
|
||||
$item = Post::selectFirst($fields, ['id' => $item_id]);
|
||||
if (!DBA::isResult($item)) {
|
||||
Logger::info('Item not found.', ['id' => $item_id]);
|
||||
|
|
@ -370,7 +359,7 @@ class Item
|
|||
|
||||
// If item has attachments, drop them
|
||||
$attachments = Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT]);
|
||||
foreach ($attachments as $attachment) {
|
||||
foreach($attachments as $attachment) {
|
||||
if (preg_match('|attach/(\d+)|', $attachment['url'], $matches)) {
|
||||
Attach::delete(['id' => $matches[1], 'uid' => $item['uid']]);
|
||||
}
|
||||
|
|
@ -484,7 +473,7 @@ class Item
|
|||
private static function contactId(array $item): int
|
||||
{
|
||||
if ($item['uid'] == 0) {
|
||||
return $item['owner-id'];
|
||||
return $item['author-id'];
|
||||
}
|
||||
|
||||
if ($item['origin']) {
|
||||
|
|
@ -492,23 +481,6 @@ class Item
|
|||
return $owner['id'];
|
||||
}
|
||||
|
||||
$contact_id = 0;
|
||||
$user_contact_id = 0;
|
||||
foreach (['group-link', 'causer-link', 'owner-link', 'author-link'] as $field) {
|
||||
if (empty($item[$field])) {
|
||||
continue;
|
||||
}
|
||||
if (!$user_contact_id && Contact::isSharingByURL($item[$field], $item['uid'], true)) {
|
||||
$user_contact_id = Contact::getIdForURL($item[$field], $item['uid']);
|
||||
} elseif (!$contact_id) {
|
||||
$contact_id = Contact::getIdForURL($item[$field]);
|
||||
}
|
||||
}
|
||||
|
||||
if ($user_contact_id) {
|
||||
return $user_contact_id;
|
||||
}
|
||||
|
||||
if (!empty($item['causer-id']) && Contact::isSharing($item['causer-id'], $item['uid'], true)) {
|
||||
$cdata = Contact::getPublicAndUserContactID($item['causer-id'], $item['uid']);
|
||||
if (!empty($cdata['user'])) {
|
||||
|
|
@ -516,7 +488,24 @@ class Item
|
|||
}
|
||||
}
|
||||
|
||||
if ($contact_id) {
|
||||
if ($item['gravity'] == self::GRAVITY_PARENT) {
|
||||
if (Contact::isSharingByURL($item['owner-link'], $item['uid'], true)) {
|
||||
$contact_id = Contact::getIdForURL($item['owner-link'], $item['uid']);
|
||||
} else {
|
||||
$contact_id = Contact::getIdForURL($item['owner-link']);
|
||||
}
|
||||
if (!empty($contact_id)) {
|
||||
return $contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
if (Contact::isSharingByURL($item['author-link'], $item['uid'], true)) {
|
||||
$contact_id = Contact::getIdForURL($item['author-link'], $item['uid']);
|
||||
} else {
|
||||
$contact_id = Contact::getIdForURL($item['author-link']);
|
||||
}
|
||||
|
||||
if (!empty($contact_id)) {
|
||||
return $contact_id;
|
||||
}
|
||||
|
||||
|
|
@ -562,10 +551,8 @@ class Item
|
|||
return true;
|
||||
}
|
||||
|
||||
$condition = [
|
||||
'uri-id' => $item['uri-id'], 'uid' => $item['uid'],
|
||||
'network' => [$item['network'], Protocol::DFRN]
|
||||
];
|
||||
$condition = ['uri-id' => $item['uri-id'], 'uid' => $item['uid'],
|
||||
'network' => [$item['network'], Protocol::DFRN]];
|
||||
if (Post::exists($condition)) {
|
||||
Logger::notice('duplicated item with the same uri found.', $condition);
|
||||
return true;
|
||||
|
|
@ -580,10 +567,8 @@ class Item
|
|||
}
|
||||
} elseif ($item['network'] == Protocol::OSTATUS) {
|
||||
// Check for an existing post with the same content. There seems to be a problem with OStatus.
|
||||
$condition = [
|
||||
"`body` = ? AND `network` = ? AND `created` = ? AND `contact-id` = ? AND `uid` = ?",
|
||||
$item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']
|
||||
];
|
||||
$condition = ["`body` = ? AND `network` = ? AND `created` = ? AND `contact-id` = ? AND `uid` = ?",
|
||||
$item['body'], $item['network'], $item['created'], $item['contact-id'], $item['uid']];
|
||||
if (Post::exists($condition)) {
|
||||
Logger::notice('duplicated item with the same body found.', $item);
|
||||
return true;
|
||||
|
|
@ -657,10 +642,8 @@ class Item
|
|||
return false;
|
||||
}
|
||||
|
||||
$condition = [
|
||||
'verb' => Activity::FOLLOW, 'uid' => $item['uid'],
|
||||
'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']
|
||||
];
|
||||
$condition = ['verb' => Activity::FOLLOW, 'uid' => $item['uid'],
|
||||
'parent-uri' => $item['parent-uri'], 'author-id' => $item['author-id']];
|
||||
if (Post::exists($condition)) {
|
||||
// It happens that we receive multiple follow requests by the same author - we only store one.
|
||||
Logger::info('Follow: Found existing follow request from author', ['author-id' => $item['author-id'], 'parent-uri' => $item['parent-uri']]);
|
||||
|
|
@ -712,8 +695,7 @@ class Item
|
|||
private static function getDuplicateID(array $item): int
|
||||
{
|
||||
if (empty($item['network']) || in_array($item['network'], Protocol::FEDERATED)) {
|
||||
$condition = [
|
||||
'`uri-id` = ? AND `uid` = ? AND `network` IN (?, ?, ?, ?)',
|
||||
$condition = ['`uri-id` = ? AND `uid` = ? AND `network` IN (?, ?, ?, ?)',
|
||||
$item['uri-id'],
|
||||
$item['uid'],
|
||||
Protocol::ACTIVITYPUB,
|
||||
|
|
@ -769,12 +751,10 @@ class Item
|
|||
*/
|
||||
private static function getTopLevelParent(array $item): array
|
||||
{
|
||||
$fields = [
|
||||
'uid', 'uri', 'parent-uri', 'id', 'deleted',
|
||||
$fields = ['uid', 'uri', 'parent-uri', 'id', 'deleted',
|
||||
'uri-id', 'parent-uri-id',
|
||||
'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid',
|
||||
'wall', 'private', 'origin', 'author-id'
|
||||
];
|
||||
'wall', 'private', 'origin', 'author-id'];
|
||||
$condition = ['uri-id' => [$item['thr-parent-id'], $item['parent-uri-id']], 'uid' => $item['uid']];
|
||||
$params = ['order' => ['id' => false]];
|
||||
$parent = Post::selectFirst($fields, $condition, $params);
|
||||
|
|
@ -799,11 +779,9 @@ class Item
|
|||
return $parent;
|
||||
}
|
||||
|
||||
$condition = [
|
||||
'uri-id' => $parent['parent-uri-id'],
|
||||
$condition = ['uri-id' => $parent['parent-uri-id'],
|
||||
'parent-uri-id' => $parent['parent-uri-id'],
|
||||
'uid' => $parent['uid']
|
||||
];
|
||||
'uid' => $parent['uid']];
|
||||
$params = ['order' => ['id' => false]];
|
||||
$toplevel_parent = Post::selectFirst($fields, $condition, $params);
|
||||
|
||||
|
|
@ -968,7 +946,7 @@ class Item
|
|||
// Communities aren't working with the Diaspora protocol
|
||||
if (($uid != 0) && ($item['network'] == Protocol::DIASPORA)) {
|
||||
$user = User::getById($uid, ['account-type']);
|
||||
if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
|
||||
if ($user['account-type'] == Contact::TYPE_COMMUNITY) {
|
||||
Logger::info('Community posts are not supported via Diaspora');
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -988,16 +966,12 @@ class Item
|
|||
|
||||
$item['gravity'] = self::getGravity($item);
|
||||
|
||||
$default = [
|
||||
'url' => $item['author-link'], 'name' => $item['author-name'],
|
||||
'photo' => $item['author-avatar'], 'network' => $item['network']
|
||||
];
|
||||
$default = ['url' => $item['author-link'], 'name' => $item['author-name'],
|
||||
'photo' => $item['author-avatar'], 'network' => $item['network']];
|
||||
$item['author-id'] = ($item['author-id'] ?? 0) ?: Contact::getIdForURL($item['author-link'], 0, null, $default);
|
||||
|
||||
$default = [
|
||||
'url' => $item['owner-link'], 'name' => $item['owner-name'],
|
||||
'photo' => $item['owner-avatar'], 'network' => $item['network']
|
||||
];
|
||||
$default = ['url' => $item['owner-link'], 'name' => $item['owner-name'],
|
||||
'photo' => $item['owner-avatar'], 'network' => $item['network']];
|
||||
$item['owner-id'] = ($item['owner-id'] ?? 0) ?: Contact::getIdForURL($item['owner-link'], 0, null, $default);
|
||||
|
||||
$item['post-reason'] = self::getPostReason($item);
|
||||
|
|
@ -1008,10 +982,8 @@ class Item
|
|||
|
||||
$item['contact-id'] = self::contactId($item);
|
||||
|
||||
if (
|
||||
!empty($item['direction']) && in_array($item['direction'], [Conversation::PUSH, Conversation::RELAY]) &&
|
||||
empty($item['origin']) && self::isTooOld($item)
|
||||
) {
|
||||
if (!empty($item['direction']) && in_array($item['direction'], [Conversation::PUSH, Conversation::RELAY]) &&
|
||||
empty($item['origin']) && self::isTooOld($item)) {
|
||||
Logger::info('Item is too old', ['item' => $item]);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1032,7 +1004,7 @@ class Item
|
|||
$item['deleted'] = $toplevel_parent['deleted'];
|
||||
$item['wall'] = $toplevel_parent['wall'];
|
||||
|
||||
// Reshares have to keep their permissions to allow groups to work
|
||||
// Reshares have to keep their permissions to allow forums to work
|
||||
if (!$defined_permissions && (!$item['origin'] || ($item['verb'] != Activity::ANNOUNCE))) {
|
||||
$item['allow_cid'] = $toplevel_parent['allow_cid'];
|
||||
$item['allow_gid'] = $toplevel_parent['allow_gid'];
|
||||
|
|
@ -1152,8 +1124,7 @@ class Item
|
|||
$item['allow_gid'],
|
||||
$item['deny_cid'],
|
||||
$item['deny_gid']
|
||||
)
|
||||
)->id;
|
||||
))->id;
|
||||
|
||||
if (!empty($item['extid'])) {
|
||||
$item['external-id'] = ItemURI::getIdByURI($item['extid']);
|
||||
|
|
@ -1336,7 +1307,7 @@ class Item
|
|||
|
||||
Post::update($fields, ['uri-id' => $posted_item['parent-uri-id'], 'uid' => $posted_item['uid']]);
|
||||
|
||||
// In that function we check if this is a group post. Additionally we delete the item under certain circumstances
|
||||
// In that function we check if this is a forum post. Additionally we delete the item under certain circumstances
|
||||
if (self::tagDeliver($posted_item['uid'], $post_user_id)) {
|
||||
// Get the user information for the logging
|
||||
$user = User::getById($uid);
|
||||
|
|
@ -1385,8 +1356,7 @@ class Item
|
|||
|
||||
// Don't relay participation messages
|
||||
if (($posted_item['verb'] == Activity::FOLLOW) &&
|
||||
(!$posted_item['origin'] || ($posted_item['author-id'] != Contact::getPublicIdByUserId($uid)))
|
||||
) {
|
||||
(!$posted_item['origin'] || ($posted_item['author-id'] != Contact::getPublicIdByUserId($uid)))) {
|
||||
Logger::info('Participation messages will not be relayed', ['item' => $posted_item['id'], 'uri' => $posted_item['uri'], 'verb' => $posted_item['verb']]);
|
||||
$transmit = false;
|
||||
}
|
||||
|
|
@ -1448,21 +1418,19 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Change the owner of a parent item if it had been shared by a group
|
||||
* Change the owner of a parent item if it had been shared by a forum
|
||||
*
|
||||
* (public) group posts in the new format consist of the regular post by the author
|
||||
* followed by an announce message sent from the group account.
|
||||
* Changing the owner helps in grouping group posts.
|
||||
* (public) forum posts in the new format consist of the regular post by the author
|
||||
* followed by an announce message sent from the forum account.
|
||||
* Changing the owner helps in grouping forum posts.
|
||||
*
|
||||
* @param array $item
|
||||
* @return void
|
||||
*/
|
||||
private static function setOwnerforResharedItem(array $item)
|
||||
{
|
||||
$parent = Post::selectFirst(
|
||||
['id', 'causer-id', 'owner-id', 'author-id', 'author-link', 'origin', 'post-reason'],
|
||||
['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]
|
||||
);
|
||||
$parent = Post::selectFirst(['id', 'causer-id', 'owner-id', 'author-id', 'author-link', 'origin', 'post-reason'],
|
||||
['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
|
||||
if (!DBA::isResult($parent)) {
|
||||
Logger::error('Parent not found', ['uri-id' => $item['thr-parent-id'], 'uid' => $item['uid']]);
|
||||
return;
|
||||
|
|
@ -1490,7 +1458,7 @@ class Item
|
|||
}
|
||||
|
||||
if (Contact::isSharing($parent['owner-id'], $item['uid'])) {
|
||||
Logger::info('The resharer is no group: quit', ['resharer' => $item['author-id'], 'owner' => $parent['owner-id'], 'author' => $parent['author-id'], 'uid' => $item['uid']]);
|
||||
Logger::info('The resharer is no forum: quit', ['resharer' => $item['author-id'], 'owner' => $parent['owner-id'], 'author' => $parent['author-id'], 'uid' => $item['uid']]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1510,13 +1478,10 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
foreach (Tag::getUIDListByURIId($item['uri-id']) as $uid => $tags) {
|
||||
$uids = Tag::getUIDListByURIId($item['uri-id']);
|
||||
foreach ($uids as $uid) {
|
||||
$stored = self::storeForUserByUriId($item['uri-id'], $uid, ['post-reason' => self::PR_TAG]);
|
||||
Logger::info('Stored item for users', ['uri-id' => $item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
|
||||
foreach ($tags as $tag) {
|
||||
$stored = Category::storeFileByURIId($item['uri-id'], $uid, Category::SUBCRIPTION, $tag);
|
||||
Logger::debug('Stored tag subscription for user', ['uri-id' => $item['uri-id'], 'uid' => $uid, $tag, 'stored' => $stored]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1537,11 +1502,9 @@ class Item
|
|||
}
|
||||
|
||||
// Only distribute public items from native networks
|
||||
$condition = [
|
||||
'id' => $itemid, 'uid' => 0,
|
||||
'network' => array_merge(Protocol::FEDERATED, ['']),
|
||||
'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]
|
||||
];
|
||||
$condition = ['id' => $itemid, 'uid' => 0,
|
||||
'network' => array_merge(Protocol::FEDERATED ,['']),
|
||||
'visible' => true, 'deleted' => false, 'private' => [self::PUBLIC, self::UNLISTED]];
|
||||
$item = Post::selectFirst(array_merge(self::ITEM_FIELDLIST, ['protocol']), $condition);
|
||||
if (!DBA::isResult($item)) {
|
||||
Logger::warning('Item not found', ['condition' => $condition]);
|
||||
|
|
@ -1640,7 +1603,7 @@ class Item
|
|||
if (($uid != 0) && ($item['gravity'] == self::GRAVITY_PARENT)) {
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (($owner['contact-type'] == User::ACCOUNT_TYPE_COMMUNITY) && !Tag::isMentioned($uri_id, $owner['url'])) {
|
||||
Logger::info('Target user is a group but is not mentioned here, thread will not be stored', ['uid' => $uid, 'uri-id' => $uri_id]);
|
||||
Logger::info('Target user is a forum but is not mentioned here, thread will not be stored', ['uid' => $uid, 'uri-id' => $uri_id]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1662,8 +1625,7 @@ class Item
|
|||
|
||||
if (($uid != 0) && (($item['gravity'] == self::GRAVITY_PARENT) || $is_reshare) &&
|
||||
DI::pConfig()->get($uid, 'system', 'accept_only_sharer') == self::COMPLETION_NONE &&
|
||||
!in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY, self::PR_AUDIENCE])
|
||||
) {
|
||||
!in_array($item['post-reason'], [self::PR_FOLLOWER, self::PR_TAG, self::PR_TO, self::PR_CC, self::PR_ACTIVITY, self::PR_AUDIENCE])) {
|
||||
Logger::info('Contact is not a follower, thread will not be stored', ['author' => $item['author-link'], 'uid' => $uid, 'uri-id' => $uri_id, 'post-reason' => $item['post-reason']]);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1747,7 +1709,7 @@ class Item
|
|||
return 0;
|
||||
}
|
||||
|
||||
// When the post belongs to a a group then all group users are allowed to access it
|
||||
// When the post belongs to a a forum then all forum users are allowed to access it
|
||||
foreach (Tag::getByURIId($uriid, [Tag::MENTION, Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => $uid, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
$target_uid = User::getIdForURL($tag['url']);
|
||||
|
|
@ -1859,7 +1821,7 @@ class Item
|
|||
}
|
||||
|
||||
// is it an entry from a connector? Only add an entry for natively connected networks
|
||||
if (!in_array($item["network"], array_merge(Protocol::FEDERATED, ['']))) {
|
||||
if (!in_array($item["network"], array_merge(Protocol::FEDERATED ,['']))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2082,17 +2044,13 @@ class Item
|
|||
// Remove the scheme to make sure that "https" and "http" doesn't make a difference
|
||||
unset($parsed['scheme']);
|
||||
|
||||
$hostPart = $host ?: $parsed['host'] ?? '';
|
||||
$hostPart = $host ?? $parsed['host'] ?? '';
|
||||
if (!$hostPart) {
|
||||
Logger::warning('Empty host GUID part', ['uri' => $uri, 'host' => $host, 'parsed' => $parsed, 'callstack' => System::callstack(10)]);
|
||||
}
|
||||
|
||||
// Glue it together to be able to make a hash from it
|
||||
if (!empty($parsed)) {
|
||||
$host_id = implode('/', $parsed);
|
||||
} else {
|
||||
$host_id = $uri;
|
||||
}
|
||||
$host_id = implode('/', $parsed);
|
||||
|
||||
// Use a mixture of several hashes to provide some GUID like experience
|
||||
return hash('crc32', $hostPart) . '-' . hash('joaat', $host_id) . '-' . hash('fnv164', $host_id);
|
||||
|
|
@ -2142,7 +2100,7 @@ class Item
|
|||
/// @todo On private posts we could obfuscate the date
|
||||
$update = ($arr['private'] != self::PRIVATE) || in_array($arr['network'], Protocol::FEDERATED);
|
||||
|
||||
// Is it a group? Then we don't care about the rules from above
|
||||
// Is it a forum? Then we don't care about the rules from above
|
||||
if (!$update && in_array($arr["network"], [Protocol::ACTIVITYPUB, Protocol::DFRN]) && ($arr["parent-uri-id"] === $arr["uri-id"])) {
|
||||
if (DBA::exists('contact', ['id' => $arr['contact-id'], 'forum' => true])) {
|
||||
$update = true;
|
||||
|
|
@ -2162,16 +2120,12 @@ class Item
|
|||
}
|
||||
// Now do the same for the system wide contacts with uid=0
|
||||
if ($arr['private'] != self::PRIVATE) {
|
||||
Contact::update(
|
||||
['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
|
||||
['id' => $arr['owner-id']]
|
||||
);
|
||||
Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
|
||||
['id' => $arr['owner-id']]);
|
||||
|
||||
if ($arr['owner-id'] != $arr['author-id']) {
|
||||
Contact::update(
|
||||
['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
|
||||
['id' => $arr['author-id']]
|
||||
);
|
||||
Contact::update(['failed' => false, 'local-data' => true, 'success_update' => $arr['received'], 'last-item' => $arr['received']],
|
||||
['id' => $arr['author-id']]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2197,44 +2151,29 @@ class Item
|
|||
|
||||
// All hashtags should point to the home server if "local_tags" is activated
|
||||
if (DI::config()->get('system', 'local_tags')) {
|
||||
$body = preg_replace(
|
||||
"/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]",
|
||||
$body
|
||||
);
|
||||
$body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#[url=" . DI::baseUrl() . "/search?tag=$2]$2[/url]", $body);
|
||||
}
|
||||
|
||||
// mask hashtags inside of url, bookmarks and attachments to avoid urls in urls
|
||||
$body = preg_replace_callback(
|
||||
"/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
$body = preg_replace_callback("/\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
function ($match) {
|
||||
return ("[url=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/url]");
|
||||
},
|
||||
$body
|
||||
);
|
||||
}, $body);
|
||||
|
||||
$body = preg_replace_callback(
|
||||
"/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
|
||||
$body = preg_replace_callback("/\[bookmark\=([$URLSearchString]*)\](.*?)\[\/bookmark\]/ism",
|
||||
function ($match) {
|
||||
return ("[bookmark=" . str_replace("#", "#", $match[1]) . "]" . str_replace("#", "#", $match[2]) . "[/bookmark]");
|
||||
},
|
||||
$body
|
||||
);
|
||||
}, $body);
|
||||
|
||||
$body = preg_replace_callback(
|
||||
"/\[attachment (.*?)\](.*?)\[\/attachment\]/ism",
|
||||
$body = preg_replace_callback("/\[attachment (.*?)\](.*?)\[\/attachment\]/ism",
|
||||
function ($match) {
|
||||
return ("[attachment " . str_replace("#", "#", $match[1]) . "]" . $match[2] . "[/attachment]");
|
||||
},
|
||||
$body
|
||||
);
|
||||
}, $body);
|
||||
|
||||
// Repair recursive urls
|
||||
$body = preg_replace(
|
||||
"/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#$2",
|
||||
$body
|
||||
);
|
||||
$body = preg_replace("/#\[url\=([$URLSearchString]*)\](.*?)\[\/url\]/ism",
|
||||
"#$2", $body);
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ((strpos($tag, '#') !== 0) || strpos($tag, '[url=') || strlen($tag) < 2 || $tag[1] == '#') {
|
||||
|
|
@ -2257,7 +2196,7 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* look for mention tags and setup a second delivery chain for group/community posts if appropriate
|
||||
* look for mention tags and setup a second delivery chain for forum/community posts if appropriate
|
||||
*
|
||||
* @param int $uid
|
||||
* @param int $item_id
|
||||
|
|
@ -2267,6 +2206,8 @@ class Item
|
|||
*/
|
||||
private static function tagDeliver(int $uid, int $item_id): bool
|
||||
{
|
||||
$mention = false;
|
||||
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
if (!DBA::isResult($owner)) {
|
||||
Logger::warning('User not found, quitting here.', ['uid' => $uid]);
|
||||
|
|
@ -2309,7 +2250,7 @@ class Item
|
|||
|
||||
if ($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) {
|
||||
$allow_cid = '';
|
||||
$allow_gid = '<' . Circle::FOLLOWERS . '>';
|
||||
$allow_gid = '<' . Group::FOLLOWERS . '>';
|
||||
$deny_cid = '';
|
||||
$deny_gid = '';
|
||||
self::performActivity($item['id'], 'announce', $uid, $allow_cid, $allow_gid, $deny_cid, $deny_gid);
|
||||
|
|
@ -2338,7 +2279,12 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
if (!DBA::exists('contact', ['id' => $cdata['user'], 'remote_self' => LocalRelationship::MIRROR_NATIVE_RESHARE])) {
|
||||
$cdata = Contact::getPublicAndUserContactID($item['author-id'], $item['uid']);
|
||||
if (empty($cdata['user']) || ($cdata['user'] != $item['contact-id'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!DBA::exists('contact', ['id' => $cdata['user'], 'remote_self' => Contact::MIRROR_NATIVE_RESHARE])) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2346,10 +2292,6 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
if (User::getById($item['uid'], ['blocked'])['blocked'] ?? false) {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger::info('Automatically reshare item', ['uid' => $item['uid'], 'id' => $item['id'], 'guid' => $item['guid'], 'uri-id' => $item['uri-id']]);
|
||||
|
||||
self::performActivity($item['id'], 'announce', $item['uid']);
|
||||
|
|
@ -2357,7 +2299,7 @@ class Item
|
|||
|
||||
public static function isRemoteSelf(array $contact, array &$datarray): bool
|
||||
{
|
||||
if ($contact['remote_self'] != LocalRelationship::MIRROR_OWN_POST) {
|
||||
if ($contact['remote_self'] != Contact::MIRROR_OWN_POST) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -2384,19 +2326,11 @@ class Item
|
|||
return false;
|
||||
}
|
||||
|
||||
if (User::getById($contact['uid'], ['blocked'])['blocked'] ?? false) {
|
||||
Logger::info('User is blocked', ['contact' => $contact]);
|
||||
return false;
|
||||
}
|
||||
|
||||
$datarray2 = $datarray;
|
||||
Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self' => $contact['remote_self'], 'item' => $datarray]);
|
||||
Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self'=> $contact['remote_self'], 'item' => $datarray]);
|
||||
|
||||
$self = DBA::selectFirst(
|
||||
'contact',
|
||||
['id', 'name', 'url', 'thumb'],
|
||||
['uid' => $contact['uid'], 'self' => true]
|
||||
);
|
||||
$self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'],
|
||||
['uid' => $contact['uid'], 'self' => true]);
|
||||
if (!DBA::isResult($self)) {
|
||||
Logger::error('Self contact not found', ['uid' => $contact['uid']]);
|
||||
return false;
|
||||
|
|
@ -2432,7 +2366,7 @@ class Item
|
|||
|
||||
// Store the original post
|
||||
$result = self::insert($datarray2);
|
||||
Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result' => $result, 'item' => $datarray2]);
|
||||
Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result'=> $result, 'item' => $datarray2]);
|
||||
} else {
|
||||
$datarray['private'] = self::PUBLIC;
|
||||
$datarray['app'] = 'Feed';
|
||||
|
|
@ -2559,8 +2493,7 @@ class Item
|
|||
if (($obj1['allow_cid'] == $obj2['allow_cid'])
|
||||
&& ($obj1['allow_gid'] == $obj2['allow_gid'])
|
||||
&& ($obj1['deny_cid'] == $obj2['deny_cid'])
|
||||
&& ($obj1['deny_gid'] == $obj2['deny_gid'])
|
||||
) {
|
||||
&& ($obj1['deny_gid'] == $obj2['deny_gid'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -2591,13 +2524,13 @@ class Item
|
|||
$expand_followers = true;
|
||||
}
|
||||
|
||||
$allow_people = $aclFormatter->expand($obj['allow_cid']);
|
||||
$allow_circles = Circle::expand($obj['uid'], $aclFormatter->expand($obj['allow_gid']), $check_dead, $expand_followers);
|
||||
$deny_people = $aclFormatter->expand($obj['deny_cid']);
|
||||
$deny_circles = Circle::expand($obj['uid'], $aclFormatter->expand($obj['deny_gid']), $check_dead);
|
||||
$recipients = array_unique(array_merge($allow_people, $allow_circles));
|
||||
$deny = array_unique(array_merge($deny_people, $deny_circles));
|
||||
$recipients = array_diff($recipients, $deny);
|
||||
$allow_people = $aclFormatter->expand($obj['allow_cid']);
|
||||
$allow_groups = Group::expand($obj['uid'], $aclFormatter->expand($obj['allow_gid']), $check_dead, $expand_followers);
|
||||
$deny_people = $aclFormatter->expand($obj['deny_cid']);
|
||||
$deny_groups = Group::expand($obj['uid'], $aclFormatter->expand($obj['deny_gid']), $check_dead);
|
||||
$recipients = array_unique(array_merge($allow_people, $allow_groups));
|
||||
$deny = array_unique(array_merge($deny_people, $deny_groups));
|
||||
$recipients = array_diff($recipients, $deny);
|
||||
return $recipients;
|
||||
}
|
||||
|
||||
|
|
@ -2607,10 +2540,8 @@ class Item
|
|||
return;
|
||||
}
|
||||
|
||||
$condition = [
|
||||
"`uid` = ? AND NOT `deleted` AND `gravity` = ?",
|
||||
$uid, self::GRAVITY_PARENT
|
||||
];
|
||||
$condition = ["`uid` = ? AND NOT `deleted` AND `gravity` = ?",
|
||||
$uid, self::GRAVITY_PARENT];
|
||||
|
||||
/*
|
||||
* $expire_network_only = save your own wall posts
|
||||
|
|
@ -2684,10 +2615,8 @@ class Item
|
|||
return false;
|
||||
}
|
||||
|
||||
$condition = [
|
||||
"`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?",
|
||||
$uid, $wall, $user['register_date']
|
||||
];
|
||||
$condition = ["`uid` = ? AND `wall` = ? AND NOT `deleted` AND `visible` AND `received` >= ?",
|
||||
$uid, $wall, $user['register_date']];
|
||||
$params = ['order' => ['received' => false]];
|
||||
$thread = Post::selectFirstThread(['received'], $condition, $params);
|
||||
if (DBA::isResult($thread)) {
|
||||
|
|
@ -2819,10 +2748,8 @@ class Item
|
|||
$vids = Verb::getID($activity);
|
||||
}
|
||||
|
||||
$condition = [
|
||||
'vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY,
|
||||
'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id
|
||||
];
|
||||
$condition = ['vid' => $vids, 'deleted' => false, 'gravity' => self::GRAVITY_ACTIVITY,
|
||||
'author-id' => $author_id, 'uid' => $uid, 'thr-parent-id' => $uri_id];
|
||||
$like_item = Post::selectFirst(['id', 'guid', 'verb'], $condition);
|
||||
|
||||
if (DBA::isResult($like_item)) {
|
||||
|
|
@ -2926,14 +2853,12 @@ class Item
|
|||
// Profile owner - everything is visible
|
||||
$condition = [];
|
||||
} elseif ($remote_user) {
|
||||
// Authenticated visitor - fetch the matching permissionsets
|
||||
// Authenticated visitor - fetch the matching permissionsets
|
||||
$permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id);
|
||||
if (!empty($set)) {
|
||||
$condition = [
|
||||
"(`private` != ? OR (`private` = ? AND `wall`
|
||||
$condition = ["(`private` != ? OR (`private` = ? AND `wall`
|
||||
AND `psid` IN (" . implode(', ', array_fill(0, count($set), '?')) . ")))",
|
||||
self::PRIVATE, self::PRIVATE
|
||||
];
|
||||
self::PRIVATE, self::PRIVATE];
|
||||
$condition = array_merge($condition, $permissionSets->column('id'));
|
||||
}
|
||||
}
|
||||
|
|
@ -2971,9 +2896,9 @@ class Item
|
|||
/*
|
||||
* Authenticated visitor. Unless pre-verified,
|
||||
* check that the contact belongs to this $owner_id
|
||||
* and load the circles the visitor belongs to.
|
||||
* and load the groups the visitor belongs to.
|
||||
* If pre-verified, the caller is expected to have already
|
||||
* done this and passed the circles into this function.
|
||||
* done this and passed the groups into this function.
|
||||
*/
|
||||
$permissionSets = DI::permissionSet()->selectByContactId($remote_user, $owner_id);
|
||||
|
||||
|
|
@ -3029,8 +2954,7 @@ class Item
|
|||
$rendered_hash = $item['rendered-hash'] ?? '';
|
||||
$rendered_html = $item['rendered-html'] ?? '';
|
||||
|
||||
if (
|
||||
$rendered_hash == ''
|
||||
if ($rendered_hash == ''
|
||||
|| $rendered_html == ''
|
||||
|| $rendered_hash != hash('md5', BBCode::VERSION . '::' . $body)
|
||||
|| DI::config()->get('system', 'ignore_cache')
|
||||
|
|
@ -3147,7 +3071,7 @@ class Item
|
|||
|
||||
if (!empty($quote_uri_id)) {
|
||||
if (isset($shared_item['plink'])) {
|
||||
$item['body'] .= "\n" . DI::contentItem()->createSharedBlockByArray($shared_item, false, true);
|
||||
$item['body'] .= "\n" . DI::contentItem()->createSharedBlockByArray($shared_item);
|
||||
} else {
|
||||
DI::logger()->warning('Missing plink in shared item', ['item' => $item, 'shared' => $shared, 'quote_uri_id' => $quote_uri_id, 'shared_item' => $shared_item]);
|
||||
}
|
||||
|
|
@ -3226,12 +3150,6 @@ class Item
|
|||
$body = BBCode::removeSharedData($body);
|
||||
}
|
||||
|
||||
$pos = strpos($s, BBCode::SHARED_ANCHOR);
|
||||
if ($pos) {
|
||||
$shared_html = substr($s, $pos + strlen(BBCode::SHARED_ANCHOR));
|
||||
$s = substr($s, 0, $pos);
|
||||
}
|
||||
|
||||
$s = self::addGallery($s, $attachments, $item['uri-id']);
|
||||
$s = self::addVisualAttachments($attachments, $item, $s, false);
|
||||
$s = self::addLinkAttachment($item['uri-id'], $attachments, $body, $s, false, $shared_links);
|
||||
|
|
@ -3252,10 +3170,6 @@ class Item
|
|||
$s = preg_replace('|(<img[^>]+src="[^"]+/photo/[0-9a-f]+)-[0-9]|', "$1-" . $ps, $s);
|
||||
}
|
||||
|
||||
if (!empty($shared_html)) {
|
||||
$s .= $shared_html;
|
||||
}
|
||||
|
||||
$s = HTML::applyContentFilter($s, $filter_reasons);
|
||||
|
||||
$hook_data = ['item' => $item, 'html' => $s];
|
||||
|
|
@ -3345,10 +3259,8 @@ class Item
|
|||
}
|
||||
|
||||
foreach ([0, 1, 2] as $size) {
|
||||
if (
|
||||
preg_match('#/photo/.*-' . $size . '\.#ism', $url) &&
|
||||
strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url)
|
||||
) {
|
||||
if (preg_match('#/photo/.*-' . $size . '\.#ism', $url) &&
|
||||
strpos(preg_replace('#(/photo/.*)-[012]\.#ism', '$1-' . $size . '.', $body), $url)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3545,8 +3457,7 @@ class Item
|
|||
'text' => '',
|
||||
'title' => $attachment['name'] ?? '',
|
||||
'type' => 'link',
|
||||
'url' => $attachment['url']
|
||||
];
|
||||
'url' => $attachment['url']];
|
||||
|
||||
if ($preview && !empty($attachment['preview'])) {
|
||||
if ($attachment['preview-width'] >= 500) {
|
||||
|
|
@ -3636,13 +3547,8 @@ class Item
|
|||
continue;
|
||||
}
|
||||
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias']
|
||||
];
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$the_url = Contact::magicLinkByContact($author, $attachment['url']);
|
||||
|
||||
$title = Strings::escapeHtml(trim(($attachment['description'] ?? '') ?: $attachment['url']));
|
||||
|
|
@ -3691,7 +3597,7 @@ class Item
|
|||
$summary = DI::l10n()->tt('%d voter.', '%d voters.', $question['voters']);
|
||||
} elseif (!empty($question['endtime'])) {
|
||||
$summary = DI::l10n()->t('Poll end: %s', Temporal::getRelativeDate($question['endtime']));
|
||||
} else {
|
||||
} else {
|
||||
$summary = '';
|
||||
}
|
||||
|
||||
|
|
@ -3700,7 +3606,7 @@ class Item
|
|||
'$options' => $options,
|
||||
'$summary' => $summary,
|
||||
]);
|
||||
}
|
||||
}
|
||||
DI::profiler()->stopRecording();
|
||||
return $content;
|
||||
}
|
||||
|
|
@ -3729,13 +3635,8 @@ class Item
|
|||
];
|
||||
|
||||
if (!empty($plink) && ($item['private'] == self::PRIVATE)) {
|
||||
$author = [
|
||||
'uid' => 0,
|
||||
'id' => $item['author-id'],
|
||||
'network' => $item['author-network'],
|
||||
'url' => $item['author-link'],
|
||||
'alias' => $item['author-alias'],
|
||||
];
|
||||
$author = ['uid' => 0, 'id' => $item['author-id'],
|
||||
'network' => $item['author-network'], 'url' => $item['author-link']];
|
||||
$plink = Contact::magicLinkByContact($author, $plink);
|
||||
}
|
||||
|
||||
|
|
@ -3758,22 +3659,15 @@ class Item
|
|||
}
|
||||
|
||||
/**
|
||||
* Does the given uri-id belongs to a post that is sent as starting post to a group?
|
||||
* This does apply to posts that are sent via ! and not in parallel to a group via @
|
||||
* Does the given uri-id belongs to a post that is sent as starting post to a forum?
|
||||
*
|
||||
* @param int $uri_id
|
||||
*
|
||||
* @return boolean "true" when it is a group post
|
||||
* @return boolean "true" when it is a forum post
|
||||
*/
|
||||
public static function isGroupPost(int $uri_id): bool
|
||||
public static function isForumPost(int $uri_id): bool
|
||||
{
|
||||
if (Post::exists(['private' => Item::PUBLIC, 'uri-id' => $uri_id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION, Tag::AUDIENCE]) as $tag) {
|
||||
// @todo Possibly check for a public audience in the future, see https://socialhub.activitypub.rocks/t/fep-1b12-group-federation/2724
|
||||
// and https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-1b12.md
|
||||
foreach (Tag::getByURIId($uri_id, [Tag::EXCLUSIVE_MENTION]) as $tag) {
|
||||
if (DBA::exists('contact', ['uid' => 0, 'nurl' => Strings::normaliseLink($tag['url']), 'contact-type' => Contact::TYPE_COMMUNITY])) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ class Mail
|
|||
foreach ($images as $image) {
|
||||
$image_rid = Photo::ridFromURI($image);
|
||||
if (!empty($image_rid)) {
|
||||
Photo::update(['allow_cid' => '<' . $recipient . '>'], ['resource-id' => $image_rid, 'album' => 'Wall Photos', 'uid' => $sender_uid]);
|
||||
Photo::update(['allow-cid' => '<' . $recipient . '>'], ['resource-id' => $image_rid, 'album' => 'Wall Photos', 'uid' => $sender_uid]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,9 +109,8 @@ class Nodeinfo
|
|||
'outbound' => [],
|
||||
];
|
||||
|
||||
if (Addon::isEnabled('bluesky')) {
|
||||
$services['inbound'][] = 'bluesky';
|
||||
$services['outbound'][] = 'bluesky';
|
||||
if (Addon::isEnabled('blogger')) {
|
||||
$services['outbound'][] = 'blogger';
|
||||
}
|
||||
if (Addon::isEnabled('dwpost')) {
|
||||
$services['outbound'][] = 'dreamwidth';
|
||||
|
|
@ -126,9 +125,18 @@ class Nodeinfo
|
|||
if (Addon::isEnabled('libertree')) {
|
||||
$services['outbound'][] = 'libertree';
|
||||
}
|
||||
if (Addon::isEnabled('buffer')) {
|
||||
$services['outbound'][] = 'linkedin';
|
||||
}
|
||||
if (Addon::isEnabled('ljpost')) {
|
||||
$services['outbound'][] = 'livejournal';
|
||||
}
|
||||
if (Addon::isEnabled('buffer')) {
|
||||
$services['outbound'][] = 'pinterest';
|
||||
}
|
||||
if (Addon::isEnabled('posterous')) {
|
||||
$services['outbound'][] = 'posterous';
|
||||
}
|
||||
if (Addon::isEnabled('pumpio')) {
|
||||
$services['inbound'][] = 'pumpio';
|
||||
$services['outbound'][] = 'pumpio';
|
||||
|
|
@ -139,7 +147,7 @@ class Nodeinfo
|
|||
if (Addon::isEnabled('tumblr')) {
|
||||
$services['outbound'][] = 'tumblr';
|
||||
}
|
||||
if (Addon::isEnabled('twitter')) {
|
||||
if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) {
|
||||
$services['outbound'][] = 'twitter';
|
||||
}
|
||||
if (Addon::isEnabled('wppost')) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class Type
|
|||
const TAG_SELF = 128;
|
||||
/** @var int Notification about getting poked/prodded/etc. (Obsolete) */
|
||||
const POKE = 512;
|
||||
/** @var int Notification about either a contact had posted something directly or the contact is a mentioned group */
|
||||
/** @var int Notification about either a contact had posted something directly or the contact is a mentioned forum */
|
||||
const SHARE = 1024;
|
||||
|
||||
/** @var int Global System notifications */
|
||||
|
|
|
|||
|
|
@ -414,9 +414,9 @@ class Photo
|
|||
* @param integer $scale Scale
|
||||
* @param integer $type Photo type, optional, default: Photo::DEFAULT
|
||||
* @param string $allow_cid Permissions, allowed contacts. optional, default = ""
|
||||
* @param string $allow_gid Permissions, allowed circles. optional, default = ""
|
||||
* @param string $deny_cid Permissions, denied contacts. optional, default = ""
|
||||
* @param string $deny_gid Permissions, denied circle. optional, default = ""
|
||||
* @param string $allow_gid Permissions, allowed groups. optional, default = ""
|
||||
* @param string $deny_cid Permissions, denied contacts.optional, default = ""
|
||||
* @param string $deny_gid Permissions, denied group.optional, default = ""
|
||||
* @param string $desc Photo caption. optional, default = ""
|
||||
*
|
||||
* @return boolean True on success
|
||||
|
|
@ -830,13 +830,13 @@ class Photo
|
|||
* Changes photo permissions that had been embedded in a post
|
||||
*
|
||||
* @todo This function currently does have some flaws:
|
||||
* - Sharing a post with a group will create a photo that only the group can see.
|
||||
* - Sharing a post with a forum will create a photo that only the forum can see.
|
||||
* - Sharing a photo again that been shared non public before doesn't alter the permissions.
|
||||
*
|
||||
* @return string
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function setPermissionFromBody($body, $uid, $original_contact_id, $str_contact_allow, $str_circle_allow, $str_contact_deny, $str_circle_deny)
|
||||
public static function setPermissionFromBody($body, $uid, $original_contact_id, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny)
|
||||
{
|
||||
// Simplify image codes
|
||||
$img_body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body);
|
||||
|
|
@ -877,11 +877,11 @@ class Photo
|
|||
/**
|
||||
* @todo Existing permissions need to be mixed with the new ones.
|
||||
* Otherwise this creates problems with sharing the same picture multiple times
|
||||
* Also check if $str_contact_allow does contain a public group.
|
||||
* Also check if $str_contact_allow does contain a public forum.
|
||||
* Then set the permissions to public.
|
||||
*/
|
||||
|
||||
self::setPermissionForResource($image_rid, $uid, $str_contact_allow, $str_circle_allow, $str_contact_deny, $str_circle_deny);
|
||||
self::setPermissionForResource($image_rid, $uid, $str_contact_allow, $str_group_allow, $str_contact_deny, $str_group_deny);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -894,15 +894,15 @@ class Photo
|
|||
* @param string $image_rid
|
||||
* @param integer $uid
|
||||
* @param string $str_contact_allow
|
||||
* @param string $str_circle_allow
|
||||
* @param string $str_group_allow
|
||||
* @param string $str_contact_deny
|
||||
* @param string $str_circle_deny
|
||||
* @param string $str_group_deny
|
||||
* @return void
|
||||
*/
|
||||
public static function setPermissionForResource(string $image_rid, int $uid, string $str_contact_allow, string $str_circle_allow, string $str_contact_deny, string $str_circle_deny)
|
||||
public static function setPermissionForResource(string $image_rid, int $uid, string $str_contact_allow, string $str_group_allow, string $str_contact_deny, string $str_group_deny)
|
||||
{
|
||||
$fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_circle_allow,
|
||||
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_circle_deny,
|
||||
$fields = ['allow_cid' => $str_contact_allow, 'allow_gid' => $str_group_allow,
|
||||
'deny_cid' => $str_contact_deny, 'deny_gid' => $str_group_deny,
|
||||
'accessible' => DI::pConfig()->get($uid, 'system', 'accessible-photos', false)];
|
||||
|
||||
$condition = ['resource-id' => $image_rid, 'uid' => $uid];
|
||||
|
|
@ -989,35 +989,6 @@ class Photo
|
|||
return DBA::exists('photo', ['resource-id' => $guid]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize to a given maximum file size
|
||||
*
|
||||
* @param Image $image
|
||||
* @param integer $maximagesize
|
||||
* @return Image
|
||||
*/
|
||||
public static function resizeToFileSize(Image $image, int $maximagesize): Image
|
||||
{
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
if ($maximagesize && ($filesize > $maximagesize)) {
|
||||
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
|
||||
foreach ([5120, 2560, 1280, 640, 320] as $pixels) {
|
||||
if (($filesize > $maximagesize) && (max($width, $height) > $pixels)) {
|
||||
Logger::info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to resize image to wanted maximum size
|
||||
*
|
||||
|
|
@ -1031,8 +1002,31 @@ class Photo
|
|||
$image->scaleDown($max_length);
|
||||
Logger::info('File upload: Scaling picture to new size', ['max-length' => $max_length]);
|
||||
}
|
||||
|
||||
return self::resizeToFileSize($image, Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize')));
|
||||
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));
|
||||
|
||||
if ($maximagesize && ($filesize > $maximagesize)) {
|
||||
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
|
||||
foreach ([5120, 2560, 1280, 640] as $pixels) {
|
||||
if (($filesize > $maximagesize) && (max($width, $height) > $pixels)) {
|
||||
Logger::info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
if ($filesize > $maximagesize) {
|
||||
Logger::notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1234,16 +1228,41 @@ class Photo
|
|||
* @param string $album Album name
|
||||
* @param string $description Photo caption
|
||||
* @param string $allow_cid Permissions, allowed contacts
|
||||
* @param string $allow_gid Permissions, allowed circles
|
||||
* @param string $allow_gid Permissions, allowed groups
|
||||
* @param string $deny_cid Permissions, denied contacts
|
||||
* @param string $deny_gid Permissions, denied circles
|
||||
* @param string $deny_gid Permissions, denied group
|
||||
*
|
||||
* @return integer preview photo size
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
*/
|
||||
public static function storeWithPreview(Image $image, int $uid, string $resource_id, string $filename, int $filesize, string $album, string $description, string $allow_cid, string $allow_gid, string $deny_cid, string $deny_gid): int
|
||||
{
|
||||
$image = self::resizeToFileSize($image, Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize')));
|
||||
if ($filesize == 0) {
|
||||
$filesize = strlen($image->asString());
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
||||
$maximagesize = Strings::getBytesFromShorthand(DI::config()->get('system', 'maximagesize'));
|
||||
|
||||
if ($maximagesize && $filesize > $maximagesize) {
|
||||
// Scale down to multiples of 640 until the maximum size isn't exceeded anymore
|
||||
foreach ([5120, 2560, 1280, 640, 320] as $pixels) {
|
||||
if ($filesize > $maximagesize && max($width, $height) > $pixels) {
|
||||
DI::logger()->info('Resize', ['size' => $filesize, 'width' => $width, 'height' => $height, 'max' => $maximagesize, 'pixels' => $pixels]);
|
||||
$image->scaleDown($pixels);
|
||||
$filesize = strlen($image->asString());
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
if ($filesize > $maximagesize) {
|
||||
DI::logger()->notice('Image size is too big', ['size' => $filesize, 'max' => $maximagesize]);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
$width = $image->getWidth();
|
||||
$height = $image->getHeight();
|
||||
|
|
|
|||
|
|
@ -453,10 +453,12 @@ class Post
|
|||
AND (NOT `causer-blocked` OR `causer-id` = ? OR `causer-id` IS NULL) AND NOT `contact-blocked`
|
||||
AND ((NOT `contact-readonly` AND NOT `contact-pending` AND (`contact-rel` IN (?, ?)))
|
||||
OR `self` OR `contact-uid` = ?)
|
||||
AND NOT EXISTS(SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `uri-id` = " . DBA::quoteIdentifier($view) . ".`uri-id` AND `hidden`)
|
||||
AND NOT EXISTS(SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `cid` IN (`author-id`, `owner-id`) AND (`blocked` OR `ignored`))
|
||||
AND NOT EXISTS(SELECT `gsid` FROM `user-gserver` WHERE `uid` = ? AND `gsid` IN (`author-gsid`, `owner-gsid`, `causer-gsid`) AND `ignored`)",
|
||||
0, Contact::SHARING, Contact::FRIEND, 0, $uid, $uid, $uid]);
|
||||
AND NOT `" . $view . "`.`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `uid` = ? AND `hidden`)
|
||||
AND NOT `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `blocked` AND `cid` = `author-id`)
|
||||
AND NOT `owner-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `blocked` AND `cid` = `owner-id`)
|
||||
AND NOT `author-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `ignored` AND `cid` = `author-id`)
|
||||
AND NOT `owner-id` IN (SELECT `cid` FROM `user-contact` WHERE `uid` = ? AND `ignored` AND `cid` = `owner-id`)",
|
||||
0, Contact::SHARING, Contact::FRIEND, 0, $uid, $uid, $uid, $uid, $uid]);
|
||||
|
||||
$select_string = implode(', ', array_map([DBA::class, 'quoteIdentifier'], $selected));
|
||||
|
||||
|
|
|
|||
|
|
@ -33,10 +33,9 @@ use Friendica\Model\Tag;
|
|||
*/
|
||||
class Category
|
||||
{
|
||||
const UNKNOWN = 0;
|
||||
const CATEGORY = 3;
|
||||
const FILE = 5;
|
||||
const SUBCRIPTION = 10;
|
||||
const UNKNOWN = 0;
|
||||
const CATEGORY = 3;
|
||||
const FILE = 5;
|
||||
|
||||
/**
|
||||
* Delete all categories and files from a given uri-id and user
|
||||
|
|
@ -81,7 +80,7 @@ class Category
|
|||
{
|
||||
$file_text = '';
|
||||
|
||||
$tags = DBA::selectToArray('category-view', ['type', 'name'], ['uri-id' => $uri_id, 'uid' => $uid, 'type' => [Category::FILE, Category::CATEGORY]]);
|
||||
$tags = DBA::selectToArray('category-view', ['type', 'name'], ['uri-id' => $uri_id, 'uid' => $uid]);
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag['type'] == self::CATEGORY) {
|
||||
$file_text .= '<' . $tag['name'] . '>';
|
||||
|
|
@ -178,7 +177,12 @@ class Category
|
|||
continue;
|
||||
}
|
||||
|
||||
self::storeByURIId($uri_id, $uid, self::FILE, $tagid);
|
||||
DBA::replace('post-category', [
|
||||
'uri-id' => $uri_id,
|
||||
'uid' => $uid,
|
||||
'type' => self::FILE,
|
||||
'tid' => $tagid
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -189,18 +193,13 @@ class Category
|
|||
}
|
||||
}
|
||||
|
||||
public static function storeFileByURIId(int $uri_id, int $uid, int $type, string $file, string $url = ''): bool
|
||||
public static function storeFileByURIId(int $uri_id, int $uid, int $type, string $file)
|
||||
{
|
||||
$tagid = Tag::getID($file, $url);
|
||||
$tagid = Tag::getID($file);
|
||||
if (empty($tagid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return self::storeByURIId($uri_id, $uid, $type, $tagid);
|
||||
}
|
||||
|
||||
private static function storeByURIId(int $uri_id, int $uid, int $type, int $tagid): bool
|
||||
{
|
||||
return DBA::replace('post-category', [
|
||||
'uri-id' => $uri_id,
|
||||
'uid' => $uid,
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ use Friendica\Database\DBA;
|
|||
use \BadMethodCallException;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\DI;
|
||||
use Friendica\Model\Item;
|
||||
use Friendica\Protocol\Activity;
|
||||
|
||||
class User
|
||||
{
|
||||
|
|
@ -51,8 +49,8 @@ class User
|
|||
$fields['uri-id'] = $uri_id;
|
||||
$fields['uid'] = $uid;
|
||||
|
||||
// Public posts and activities (like, dislike, ...) are always seen
|
||||
if ($uid == 0 || (($data['gravity'] == Item::GRAVITY_ACTIVITY) && ($data['verb'] != Activity::ANNOUNCE))) {
|
||||
// Public posts are always seen
|
||||
if ($uid == 0) {
|
||||
$fields['unseen'] = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -133,14 +133,14 @@ class UserNotification
|
|||
public static function setNotification(int $uri_id, int $uid)
|
||||
{
|
||||
$fields = ['id', 'uri-id', 'parent-uri-id', 'uid', 'body', 'parent', 'gravity', 'vid', 'gravity',
|
||||
'contact-id', 'author-id', 'author-gsid', 'owner-id', 'owner-gsid', 'causer-id', 'causer-gsid',
|
||||
'contact-id', 'author-id', 'owner-id', 'causer-id',
|
||||
'private', 'thr-parent', 'thr-parent-id', 'parent-uri-id', 'parent-uri', 'verb'];
|
||||
$item = Post::selectFirst($fields, ['uri-id' => $uri_id, 'uid' => $uid, 'origin' => false]);
|
||||
if (!DBA::isResult($item)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent = Post::selectFirstPost(['author-id', 'author-gsid', 'owner-id', 'owner-gsid', 'causer-id', 'causer-gsid',], ['uri-id' => $item['parent-uri-id']]);
|
||||
$parent = Post::selectFirstPost(['author-id', 'owner-id', 'causer-id'], ['uri-id' => $item['parent-uri-id']]);
|
||||
if (!DBA::isResult($parent)) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -195,13 +195,6 @@ class UserNotification
|
|||
}
|
||||
}
|
||||
|
||||
foreach (array_unique([$parent['author-gsid'], $parent['owner-gsid'], $parent['causer-gsid'], $item['author-gsid'], $item['owner-gsid'], $item['causer-gsid']]) as $gsid) {
|
||||
if ($gsid && DI::userGServer()->isIgnoredByUser($uid, $gsid)) {
|
||||
Logger::debug('Server is ignored by user', ['uid' => $uid, 'gsid' => $gsid, 'uri-id' => $item['uri-id']]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$user = User::getById($uid, ['account-type', 'account_removed', 'account_expired']);
|
||||
if (in_array($user['account-type'], [User::ACCOUNT_TYPE_COMMUNITY, User::ACCOUNT_TYPE_RELAY])) {
|
||||
return;
|
||||
|
|
@ -405,23 +398,45 @@ class UserNotification
|
|||
*/
|
||||
private static function getProfileForUser(int $uid): array
|
||||
{
|
||||
$owner = User::getOwnerDataById($uid);
|
||||
$notification_data = ['uid' => $uid, 'profiles' => []];
|
||||
Hook::callAll('check_item_notification', $notification_data);
|
||||
|
||||
$profiles = $notification_data['profiles'];
|
||||
|
||||
$user = DBA::selectFirst('user', ['nickname'], ['uid' => $uid]);
|
||||
if (!DBA::isResult($user)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$owner = DBA::selectFirst('contact', ['url', 'alias'], ['self' => true, 'uid' => $uid]);
|
||||
if (!DBA::isResult($owner)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$profiles = [$owner['nurl']];
|
||||
// This is our regular URL format
|
||||
$profiles[] = $owner['url'];
|
||||
|
||||
$notification_data = ['uid' => $uid, 'profiles' => []];
|
||||
Hook::callAll('check_item_notification', $notification_data);
|
||||
// Now the alias
|
||||
$profiles[] = $owner['alias'];
|
||||
|
||||
// Normalize the connector profiles
|
||||
foreach ($notification_data['profiles'] as $profile) {
|
||||
// Notifications from Diaspora often have a URL in the Diaspora format
|
||||
$profiles[] = DI::baseUrl() . '/u/' . $user['nickname'];
|
||||
|
||||
// Validate and add profile links
|
||||
foreach ($profiles as $key => $profile) {
|
||||
// Check for invalid profile urls (without scheme, host or path) and remove them
|
||||
if (empty(parse_url($profile, PHP_URL_SCHEME)) || empty(parse_url($profile, PHP_URL_HOST)) || empty(parse_url($profile, PHP_URL_PATH))) {
|
||||
$profiles[] = $profile;
|
||||
} else {
|
||||
$profiles[] = Strings::normaliseLink($profile);
|
||||
unset($profiles[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Add the normalized form
|
||||
$profile = Strings::normaliseLink($profile);
|
||||
$profiles[] = $profile;
|
||||
|
||||
// Add the SSL form
|
||||
$profile = str_replace('http://', 'https://', $profile);
|
||||
$profiles[] = $profile;
|
||||
}
|
||||
|
||||
return array_unique($profiles);
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ use Friendica\DI;
|
|||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Security\PermissionSet\Entity\PermissionSet;
|
||||
|
|
@ -94,11 +93,10 @@ class Profile
|
|||
/**
|
||||
* Update a profile entry and distribute the changes if needed
|
||||
*
|
||||
* @param array $fields Profile fields to update
|
||||
* @param integer $uid User id
|
||||
* @param array $fields Profile fields to update
|
||||
* @param integer $uid User id
|
||||
*
|
||||
* @return boolean Whether update was successful
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function update(array $fields, int $uid): bool
|
||||
{
|
||||
|
|
@ -118,6 +116,10 @@ class Profile
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($old_owner['name'] != $owner['name']) {
|
||||
User::update(['username' => $owner['name']], $uid);
|
||||
}
|
||||
|
||||
$profile_fields = ['postal-code', 'dob', 'prv_keywords', 'homepage'];
|
||||
foreach ($profile_fields as $field) {
|
||||
if ($old_owner[$field] != $owner[$field]) {
|
||||
|
|
@ -638,13 +640,13 @@ class Profile
|
|||
$istoday = true;
|
||||
}
|
||||
|
||||
$title = BBCode::toPlaintext($rr['summary'], false);
|
||||
$title = strip_tags(html_entity_decode(BBCode::convertForUriId($rr['uri-id'], $rr['summary']), ENT_QUOTES, 'UTF-8'));
|
||||
|
||||
if (strlen($title) > 35) {
|
||||
$title = substr($title, 0, 32) . '... ';
|
||||
}
|
||||
|
||||
$description = BBCode::toPlaintext($rr['desc'], false) . '... ';
|
||||
$description = substr(strip_tags(BBCode::convertForUriId($rr['uri-id'], $rr['desc'])), 0, 32) . '... ';
|
||||
if (!$description) {
|
||||
$description = DI::l10n()->t('[No description]');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class Tag
|
|||
*/
|
||||
const IMPLICIT_MENTION = 8;
|
||||
/**
|
||||
* An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a group.
|
||||
* An exclusive mention transmits the post only to the target account without transmitting it to the followers, usually a forum.
|
||||
*/
|
||||
const EXCLUSIVE_MENTION = 9;
|
||||
|
||||
|
|
@ -487,7 +487,7 @@ class Tag
|
|||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isMentioned(int $uriId, string $url, array $type = [self::MENTION, self::EXCLUSIVE_MENTION, self::AUDIENCE]): bool
|
||||
public static function isMentioned(int $uriId, string $url, array $type = [self::MENTION, self::EXCLUSIVE_MENTION]): bool
|
||||
{
|
||||
$tags = self::getByURIId($uriId, $type);
|
||||
foreach ($tags as $tag) {
|
||||
|
|
@ -828,13 +828,12 @@ class Tag
|
|||
public static function getUIDListByURIId(int $uriId): array
|
||||
{
|
||||
$uids = [];
|
||||
$tags = self::getByURIId($uriId, [self::HASHTAG]);
|
||||
|
||||
foreach (self::getByURIId($uriId, [self::HASHTAG]) as $tag) {
|
||||
foreach (self::getUIDListByTag(self::TAG_CHARACTER[self::HASHTAG] . $tag['name']) as $uid) {
|
||||
$uids[$uid][] = $tag['name'];
|
||||
}
|
||||
foreach ($tags as $tag) {
|
||||
$uids = array_merge($uids, self::getUIDListByTag(self::TAG_CHARACTER[self::HASHTAG] . $tag['name']));
|
||||
}
|
||||
|
||||
return $uids;
|
||||
return array_unique($uids);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ use Friendica\Database\DBA;
|
|||
use Friendica\DI;
|
||||
use Friendica\Module;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Security\TwoFactor\Model\AppSpecificPassword;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Object\Image;
|
||||
|
|
@ -89,7 +88,7 @@ class User
|
|||
* ACCOUNT_TYPE_NEWS - the account is a news reflector
|
||||
* Associated page type: PAGE_FLAGS_SOAPBOX
|
||||
*
|
||||
* ACCOUNT_TYPE_COMMUNITY - the account is community group
|
||||
* ACCOUNT_TYPE_COMMUNITY - the account is community forum
|
||||
* Associated page types: PAGE_COMMUNITY, PAGE_FLAGS_PRVGROUP
|
||||
*
|
||||
* ACCOUNT_TYPE_RELAY - the account is a relay
|
||||
|
|
@ -133,17 +132,6 @@ class User
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Uri-Id of the system account
|
||||
*
|
||||
* @return integer
|
||||
*/
|
||||
public static function getSystemUriId(): int
|
||||
{
|
||||
$system = self::getSystemAccount();
|
||||
return $system['uri-id'] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the system account
|
||||
*
|
||||
|
|
@ -495,41 +483,23 @@ class User
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the default circle for a given user
|
||||
* Returns the default group for a given user and network
|
||||
*
|
||||
* @param int $uid User id
|
||||
*
|
||||
* @return int circle id
|
||||
* @return int group id
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getDefaultCircle(int $uid): int
|
||||
public static function getDefaultGroup(int $uid): int
|
||||
{
|
||||
$user = DBA::selectFirst('user', ['def_gid'], ['uid' => $uid]);
|
||||
if (DBA::isResult($user)) {
|
||||
$default_circle = $user['def_gid'];
|
||||
$default_group = $user["def_gid"];
|
||||
} else {
|
||||
$default_circle = 0;
|
||||
$default_group = 0;
|
||||
}
|
||||
|
||||
return $default_circle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default circle for groups for a given user
|
||||
*
|
||||
* @param int $uid User id
|
||||
*
|
||||
* @return int circle id
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function getDefaultGroupCircle(int $uid): int
|
||||
{
|
||||
$default_circle = DI::pConfig()->get($uid, 'system', 'default-group-gid');
|
||||
if (empty($default_circle)) {
|
||||
$default_circle = self::getDefaultCircle($uid);
|
||||
}
|
||||
|
||||
return $default_circle;
|
||||
return $default_group;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -705,10 +675,6 @@ class User
|
|||
*/
|
||||
public static function updateLastActivity(int $uid)
|
||||
{
|
||||
if (!$uid) {
|
||||
return;
|
||||
}
|
||||
|
||||
$user = User::getById($uid, ['last-activity']);
|
||||
if (empty($user)) {
|
||||
return;
|
||||
|
|
@ -881,20 +847,6 @@ class User
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the given uid is valid and a moderator
|
||||
*
|
||||
* @param int $uid
|
||||
*
|
||||
* @return bool
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function isModerator(int $uid): bool
|
||||
{
|
||||
// @todo Replace with a moderator check in the future
|
||||
return self::isSiteAdmin($uid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a nickname is in the list of the forbidden nicknames
|
||||
*
|
||||
|
|
@ -1236,13 +1188,13 @@ class User
|
|||
throw new Exception(DI::l10n()->t('An error occurred creating your self contact. Please try again.'));
|
||||
}
|
||||
|
||||
// Create a circle with no members. This allows somebody to use it
|
||||
// right away as a default circle for new contacts.
|
||||
$def_gid = Circle::create($uid, DI::l10n()->t('Friends'));
|
||||
// Create a group with no members. This allows somebody to use it
|
||||
// right away as a default group for new contacts.
|
||||
$def_gid = Group::create($uid, DI::l10n()->t('Friends'));
|
||||
if (!$def_gid) {
|
||||
DBA::delete('user', ['uid' => $uid]);
|
||||
|
||||
throw new Exception(DI::l10n()->t('An error occurred creating your default contact circle. Please try again.'));
|
||||
throw new Exception(DI::l10n()->t('An error occurred creating your default contact group. Please try again.'));
|
||||
}
|
||||
|
||||
$fields = ['def_gid' => $def_gid];
|
||||
|
|
@ -1252,11 +1204,6 @@ class User
|
|||
|
||||
DBA::update('user', $fields, ['uid' => $uid]);
|
||||
|
||||
$def_gid_groups = Circle::create($uid, DI::l10n()->t('Groups'));
|
||||
if ($def_gid_groups) {
|
||||
DI::pConfig()->set($uid, 'system', 'default-group-gid', $def_gid_groups);
|
||||
}
|
||||
|
||||
// if we have no OpenID photo try to look up an avatar
|
||||
if (!strlen($photo)) {
|
||||
$photo = Network::lookupAvatarByEmail($email);
|
||||
|
|
@ -1329,18 +1276,33 @@ class User
|
|||
/**
|
||||
* Update a user entry and distribute the changes if needed
|
||||
*
|
||||
* @param array $fields
|
||||
* @param array $fields
|
||||
* @param integer $uid
|
||||
* @return boolean
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function update(array $fields, int $uid): bool
|
||||
{
|
||||
$old_owner = self::getOwnerDataById($uid);
|
||||
if (empty($old_owner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DBA::update('user', $fields, ['uid' => $uid])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Contact::updateSelfFromUserID($uid)) {
|
||||
$update = Contact::updateSelfFromUserID($uid);
|
||||
|
||||
$owner = self::getOwnerDataById($uid);
|
||||
if (empty($owner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($old_owner['name'] != $owner['name']) {
|
||||
Profile::update(['name' => $owner['name']], $uid);
|
||||
}
|
||||
|
||||
if ($update) {
|
||||
Profile::publishUpdate($uid);
|
||||
}
|
||||
|
||||
|
|
@ -1678,7 +1640,7 @@ class User
|
|||
*/
|
||||
public static function identities(int $uid): array
|
||||
{
|
||||
if (!$uid) {
|
||||
if (empty($uid)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -1689,7 +1651,7 @@ class User
|
|||
return $identities;
|
||||
}
|
||||
|
||||
if (!$user['parent-uid']) {
|
||||
if ($user['parent-uid'] == 0) {
|
||||
// First add our own entry
|
||||
$identities = [[
|
||||
'uid' => $user['uid'],
|
||||
|
|
@ -1750,7 +1712,7 @@ class User
|
|||
*/
|
||||
public static function hasIdentities(int $uid): bool
|
||||
{
|
||||
if (!$uid) {
|
||||
if (empty($uid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1759,7 +1721,7 @@ class User
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($user['parent-uid']) {
|
||||
if ($user['parent-uid'] != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1886,8 +1848,8 @@ class User
|
|||
{
|
||||
$condition = [
|
||||
'email' => self::getAdminEmailList(),
|
||||
'parent-uid' => null,
|
||||
'blocked' => false,
|
||||
'parent-uid' => 0,
|
||||
'blocked' => 0,
|
||||
'verified' => true,
|
||||
'account_removed' => false,
|
||||
'account_expired' => false,
|
||||
|
|
|
|||
|
|
@ -1,33 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Moderation\Collection\Report;
|
||||
|
||||
class Rules extends \Friendica\BaseCollection
|
||||
{
|
||||
/**
|
||||
* @return \Friendica\Moderation\Entity\Report\Rule
|
||||
*/
|
||||
public function current(): \Friendica\Moderation\Entity\Report\Rule
|
||||
{
|
||||
return parent::current();
|
||||
}
|
||||
}
|
||||
|
|
@ -21,126 +21,51 @@
|
|||
|
||||
namespace Friendica\Moderation\Entity;
|
||||
|
||||
use Friendica\Moderation\Collection;
|
||||
|
||||
/**
|
||||
* @property-read int $id
|
||||
* @property-read int $reporterCid
|
||||
* @property-read int $cid
|
||||
* @property-read int $gsid
|
||||
* @property-read string $comment
|
||||
* @property-read string $publicRemarks
|
||||
* @property-read string $privateRemarks
|
||||
* @property-read bool $forward
|
||||
* @property-read int $category
|
||||
* @property-read int $status
|
||||
* @property-read int|null $resolution
|
||||
* @property-read int $reporterUid
|
||||
* @property-read int|null $lastEditorUid
|
||||
* @property-read int|null $assignedUid
|
||||
* @property-read \DateTimeImmutable $created
|
||||
* @property-read \DateTimeImmutable|null $edited
|
||||
* @property-read Collection\Report\Posts $posts
|
||||
* @property-read Collection\Report\Rules $rules
|
||||
* @property-read int $id
|
||||
* @property-read int $reporterId
|
||||
* @property-read int $cid
|
||||
* @property-read string $comment
|
||||
* @property-read string|null $category
|
||||
* @property-read bool $forward
|
||||
* @property-read array $postUriIds
|
||||
* @property-read int $uid
|
||||
* @property-read \DateTime|null $created
|
||||
*/
|
||||
final class Report extends \Friendica\BaseEntity
|
||||
class Report extends \Friendica\BaseEntity
|
||||
{
|
||||
const CATEGORY_OTHER = 1;
|
||||
const CATEGORY_SPAM = 2;
|
||||
const CATEGORY_ILLEGAL = 4;
|
||||
const CATEGORY_SAFETY = 8;
|
||||
const CATEGORY_UNWANTED = 16;
|
||||
const CATEGORY_VIOLATION = 32;
|
||||
|
||||
const CATEGORIES = [
|
||||
self::CATEGORY_OTHER,
|
||||
self::CATEGORY_SPAM,
|
||||
self::CATEGORY_ILLEGAL,
|
||||
self::CATEGORY_SAFETY,
|
||||
self::CATEGORY_UNWANTED,
|
||||
self::CATEGORY_VIOLATION,
|
||||
];
|
||||
|
||||
const STATUS_CLOSED = 0;
|
||||
const STATUS_OPEN = 1;
|
||||
|
||||
const RESOLUTION_ACCEPTED = 0;
|
||||
const RESOLUTION_REJECTED = 1;
|
||||
|
||||
/** @var int|null */
|
||||
protected $id;
|
||||
/** @var int ID of the contact making a moderation report */
|
||||
protected $reporterCid;
|
||||
/** @var int ID of the contact being reported */
|
||||
/** @var int ID of the contact making a moderation report*/
|
||||
protected $reporterId;
|
||||
/** @var int ID of the contact being reported*/
|
||||
protected $cid;
|
||||
/** @var int ID of the gserver of the contact being reported */
|
||||
protected $gsid;
|
||||
/** @var string Reporter comment */
|
||||
/** @var string Optional comment */
|
||||
protected $comment;
|
||||
/** @var int One of CATEGORY_* */
|
||||
/** @var string Optional category */
|
||||
protected $category;
|
||||
/** @var int ID of the user making a moderation report, null in case of an incoming forwarded report */
|
||||
protected $reporterUid;
|
||||
/** @var string Violated rules */
|
||||
protected $rules;
|
||||
/** @var bool Whether this report should be forwarded to the remote server */
|
||||
protected $forward;
|
||||
/** @var \DateTimeImmutable When the report was created */
|
||||
/** @var \DateTime|null When the report was created */
|
||||
protected $created;
|
||||
/** @var Collection\Report\Rules List of terms of service rule lines being possibly violated */
|
||||
protected $rules;
|
||||
/** @var Collection\Report\Posts List of URI IDs of posts supporting the report */
|
||||
protected $posts;
|
||||
/** @var string Remarks shared with the reporter */
|
||||
protected $publicRemarks;
|
||||
/** @var string Remarks shared with the moderation team */
|
||||
protected $privateRemarks;
|
||||
/** @var \DateTimeImmutable|null When the report was last edited */
|
||||
protected $edited;
|
||||
/** @var int One of STATUS_* */
|
||||
protected $status;
|
||||
/** @var int|null One of RESOLUTION_* if any */
|
||||
protected $resolution;
|
||||
/** @var int|null Assigned moderator user id if any */
|
||||
protected $assignedUid;
|
||||
/** @var int|null Last editor user ID if any */
|
||||
protected $lastEditorUid;
|
||||
/** @var array Optional list of URI IDs of posts supporting the report*/
|
||||
protected $postUriIds;
|
||||
/** @var int ID of the user making a moderation report*/
|
||||
protected $uid;
|
||||
|
||||
public function __construct(
|
||||
int $reporterCid,
|
||||
int $cid,
|
||||
int $gsid,
|
||||
\DateTimeImmutable $created,
|
||||
int $category,
|
||||
int $reporterUid = null,
|
||||
string $comment = '',
|
||||
bool $forward = false,
|
||||
Collection\Report\Posts $posts = null,
|
||||
Collection\Report\Rules $rules = null,
|
||||
string $publicRemarks = '',
|
||||
string $privateRemarks = '',
|
||||
\DateTimeImmutable $edited = null,
|
||||
int $status = self::STATUS_OPEN,
|
||||
int $resolution = null,
|
||||
int $assignedUid = null,
|
||||
int $lastEditorUid = null,
|
||||
int $id = null
|
||||
) {
|
||||
$this->reporterCid = $reporterCid;
|
||||
$this->cid = $cid;
|
||||
$this->gsid = $gsid;
|
||||
$this->created = $created;
|
||||
$this->category = $category;
|
||||
$this->reporterUid = $reporterUid;
|
||||
$this->comment = $comment;
|
||||
$this->forward = $forward;
|
||||
$this->posts = $posts ?? new Collection\Report\Posts();
|
||||
$this->rules = $rules ?? new Collection\Report\Rules();
|
||||
$this->publicRemarks = $publicRemarks;
|
||||
$this->privateRemarks = $privateRemarks;
|
||||
$this->edited = $edited;
|
||||
$this->status = $status;
|
||||
$this->resolution = $resolution;
|
||||
$this->assignedUid = $assignedUid;
|
||||
$this->lastEditorUid = $lastEditorUid;
|
||||
$this->id = $id;
|
||||
public function __construct(int $reporterId, int $cid, \DateTime $created, string $comment = '', string $category = null, string $rules = '', bool $forward = false, array $postUriIds = [], int $uid = null, int $id = null)
|
||||
{
|
||||
$this->reporterId = $reporterId;
|
||||
$this->cid = $cid;
|
||||
$this->created = $created;
|
||||
$this->comment = $comment;
|
||||
$this->category = $category;
|
||||
$this->rules = $rules;
|
||||
$this->forward = $forward;
|
||||
$this->postUriIds = $postUriIds;
|
||||
$this->uid = $uid;
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,44 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Moderation\Entity\Report;
|
||||
|
||||
/**
|
||||
* @property-read int $uriId URI Id of the reported post
|
||||
* @property-read int $status One of STATUS_*
|
||||
*/
|
||||
final class Post extends \Friendica\BaseEntity
|
||||
{
|
||||
const STATUS_NO_ACTION = 0;
|
||||
const STATUS_UNLISTED = 1;
|
||||
const STATUS_DELETED = 2;
|
||||
|
||||
/** @var int */
|
||||
protected $uriId;
|
||||
/** @var int|null */
|
||||
protected $status;
|
||||
|
||||
public function __construct(int $uriId, int $status = self::STATUS_NO_ACTION)
|
||||
{
|
||||
$this->uriId = $uriId;
|
||||
$this->status = $status;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Moderation\Entity\Report;
|
||||
|
||||
/**
|
||||
* @property-read int $lineId Terms of service text line number
|
||||
* @property-read string $text Terms of service rule text
|
||||
*/
|
||||
final class Rule extends \Friendica\BaseEntity
|
||||
{
|
||||
/** @var int */
|
||||
protected $lineId;
|
||||
/** @var string */
|
||||
protected $text;
|
||||
|
||||
public function __construct(int $lineId, string $text)
|
||||
{
|
||||
$this->lineId = $lineId;
|
||||
$this->text = $text;
|
||||
}
|
||||
}
|
||||
|
|
@ -22,52 +22,28 @@
|
|||
namespace Friendica\Moderation\Factory;
|
||||
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Model\Contact;
|
||||
use Friendica\Moderation\Collection;
|
||||
use Friendica\Moderation\Entity;
|
||||
use Psr\Clock\ClockInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class Report extends \Friendica\BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
/** @var ClockInterface */
|
||||
private $clock;
|
||||
|
||||
public function __construct(LoggerInterface $logger, ClockInterface $clock)
|
||||
{
|
||||
parent::__construct($logger);
|
||||
|
||||
$this->clock = $clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $row `report` table row
|
||||
* @param Collection\Report\Posts|null $posts List of posts attached to the report
|
||||
* @param Collection\Report\Rules|null $rules List of rules from the terms of service, see System::getRules()
|
||||
* @param array $row `report` table row
|
||||
* @param array $postUriIds List of post URI ids from the `report-post` table
|
||||
* @return Entity\Report
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createFromTableRow(array $row, Collection\Report\Posts $posts = null, Collection\Report\Rules $rules = null): Entity\Report
|
||||
public function createFromTableRow(array $row, array $postUriIds = []): Entity\Report
|
||||
{
|
||||
return new Entity\Report(
|
||||
$row['reporter-id'],
|
||||
$row['cid'],
|
||||
$row['gsid'],
|
||||
new \DateTimeImmutable($row['created'], new \DateTimeZone('UTC')),
|
||||
$row['category-id'],
|
||||
$row['uid'],
|
||||
new \DateTime($row['created'] ?? 'now', new \DateTimeZone('UTC')),
|
||||
$row['comment'],
|
||||
$row['category'],
|
||||
$row['rules'],
|
||||
$row['forward'],
|
||||
$posts ?? new Collection\Report\Posts(),
|
||||
$rules ?? new Collection\Report\Rules(),
|
||||
$row['public-remarks'],
|
||||
$row['private-remarks'],
|
||||
$row['edited'] ? new \DateTimeImmutable($row['edited'], new \DateTimeZone('UTC')) : null,
|
||||
$row['status'],
|
||||
$row['resolution'],
|
||||
$row['assigned-uid'],
|
||||
$row['last-editor-uid'],
|
||||
$postUriIds,
|
||||
$row['uid'],
|
||||
$row['id'],
|
||||
);
|
||||
}
|
||||
|
|
@ -75,73 +51,29 @@ class Report extends \Friendica\BaseFactory implements ICanCreateFromTableRow
|
|||
/**
|
||||
* Creates a Report entity from a Mastodon API /reports request
|
||||
*
|
||||
* @param array $rules Line-number indexed node rules array, see System::getRules(true)
|
||||
* @see \Friendica\Module\Api\Mastodon\Reports::post()
|
||||
*
|
||||
* @param int $uid
|
||||
* @param int $reporterId
|
||||
* @param int $cid
|
||||
* @param int $gsid
|
||||
* @param string $comment
|
||||
* @param string $category
|
||||
* @param bool $forward
|
||||
* @param array $postUriIds
|
||||
* @param array $ruleIds
|
||||
* @param ?int $uid
|
||||
* @return Entity\Report
|
||||
* @see \Friendica\Module\Api\Mastodon\Reports::post()
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function createFromReportsRequest(array $rules, int $reporterId, int $cid, int $gsid, string $comment = '', string $category = '', bool $forward = false, array $postUriIds = [], array $ruleIds = [], int $uid = null): Entity\Report
|
||||
public function createFromReportsRequest(int $reporterId, int $cid, string $comment = '', string $category = null, string $rules = '', bool $forward = false, array $postUriIds = [], int $uid = null): Entity\Report
|
||||
{
|
||||
if (count($ruleIds)) {
|
||||
$categoryId = Entity\Report::CATEGORY_VIOLATION;
|
||||
} elseif ($category == 'spam') {
|
||||
$categoryId = Entity\Report::CATEGORY_SPAM;
|
||||
} else {
|
||||
$categoryId = Entity\Report::CATEGORY_OTHER;
|
||||
}
|
||||
|
||||
return new Entity\Report(
|
||||
$reporterId,
|
||||
$cid,
|
||||
$gsid,
|
||||
$this->clock->now(),
|
||||
$categoryId,
|
||||
new \DateTime('now', new \DateTimeZone('UTC')),
|
||||
$comment,
|
||||
$category,
|
||||
$rules,
|
||||
$forward,
|
||||
$postUriIds,
|
||||
$uid,
|
||||
$comment,
|
||||
$forward,
|
||||
new Collection\Report\Posts(array_map(function ($uriId) {
|
||||
return new Entity\Report\Post($uriId);
|
||||
}, $postUriIds)),
|
||||
new Collection\Report\Rules(array_map(function ($lineId) use ($rules) {
|
||||
return new Entity\Report\Rule($lineId, $rules[$lineId] ?? '');
|
||||
}, $ruleIds)),
|
||||
);
|
||||
}
|
||||
|
||||
public function createFromForm(array $rules, int $cid, int $reporterId, int $categoryId, array $ruleIds, string $comment, array $uriIds, bool $forward): Entity\Report
|
||||
{
|
||||
$contact = Contact::getById($cid, ['gsid']);
|
||||
if (!$contact) {
|
||||
throw new \InvalidArgumentException('Contact with id: ' . $cid . ' not found');
|
||||
}
|
||||
|
||||
if (!in_array($categoryId, Entity\Report::CATEGORIES)) {
|
||||
throw new \OutOfBoundsException('Category with id: ' . $categoryId . ' not found in set: [' . implode(', ', Entity\Report::CATEGORIES) . ']');
|
||||
}
|
||||
|
||||
return new Entity\Report(
|
||||
Contact::getPublicIdByUserId($reporterId),
|
||||
$cid,
|
||||
$contact['gsid'],
|
||||
$this->clock->now(),
|
||||
$categoryId,
|
||||
$reporterId,
|
||||
$comment,
|
||||
$forward,
|
||||
new Collection\Report\Posts(array_map(function ($uriId) {
|
||||
return new Entity\Report\Post($uriId);
|
||||
}, $uriIds)),
|
||||
new Collection\Report\Rules(array_map(function ($lineId) use ($rules) {
|
||||
return new Entity\Report\Rule($lineId, $rules[$lineId] ?? '');
|
||||
}, $ruleIds)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Moderation\Factory\Report;
|
||||
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
|
||||
class Post extends \Friendica\BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
public function createFromTableRow(array $row): \Friendica\Moderation\Entity\Report\Post
|
||||
{
|
||||
return new \Friendica\Moderation\Entity\Report\Post(
|
||||
$row['uri-id'],
|
||||
$row['status']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, 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\Moderation\Factory\Report;
|
||||
|
||||
use Friendica\Capabilities\ICanCreateFromTableRow;
|
||||
|
||||
class Rule extends \Friendica\BaseFactory implements ICanCreateFromTableRow
|
||||
{
|
||||
public function createFromTableRow(array $row): \Friendica\Moderation\Entity\Report\Rule
|
||||
{
|
||||
return new \Friendica\Moderation\Entity\Report\Rule(
|
||||
$row['line-id'],
|
||||
$row['text']
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -25,30 +25,24 @@ use Friendica\BaseEntity;
|
|||
use Friendica\Core\Logger;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Model\Post;
|
||||
use Friendica\Moderation\Factory;
|
||||
use Friendica\Moderation\Collection;
|
||||
use Friendica\Network\HTTPException\NotFoundException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
final class Report extends \Friendica\BaseRepository
|
||||
class Report extends \Friendica\BaseRepository
|
||||
{
|
||||
protected static $table_name = 'report';
|
||||
|
||||
/** @var Factory\Report */
|
||||
/**
|
||||
* @var \Friendica\Moderation\Factory\Report
|
||||
*/
|
||||
protected $factory;
|
||||
/** @var Factory\Report\Post */
|
||||
protected $postFactory;
|
||||
/** @var Factory\Report\Rule */
|
||||
protected $ruleFactory;
|
||||
|
||||
public function __construct(Database $database, LoggerInterface $logger, Factory\Report $factory, Factory\Report\Post $postFactory, Factory\Report\Rule $ruleFactory)
|
||||
public function __construct(Database $database, LoggerInterface $logger, \Friendica\Moderation\Factory\Report $factory)
|
||||
{
|
||||
parent::__construct($database, $logger, $factory);
|
||||
|
||||
$this->factory = $factory;
|
||||
$this->postFactory = $postFactory;
|
||||
$this->ruleFactory = $ruleFactory;
|
||||
$this->factory = $factory;
|
||||
}
|
||||
|
||||
public function selectOneById(int $lastInsertId): \Friendica\Moderation\Entity\Report
|
||||
|
|
@ -56,46 +50,37 @@ final class Report extends \Friendica\BaseRepository
|
|||
return $this->_selectOne(['id' => $lastInsertId]);
|
||||
}
|
||||
|
||||
public function save(\Friendica\Moderation\Entity\Report $Report): \Friendica\Moderation\Entity\Report
|
||||
public function save(\Friendica\Moderation\Entity\Report $Report)
|
||||
{
|
||||
$fields = [
|
||||
'reporter-id' => $Report->reporterCid,
|
||||
'uid' => $Report->reporterUid,
|
||||
'cid' => $Report->cid,
|
||||
'gsid' => $Report->gsid,
|
||||
'comment' => $Report->comment,
|
||||
'forward' => $Report->forward,
|
||||
'category-id' => $Report->category,
|
||||
'public-remarks' => $Report->publicRemarks,
|
||||
'private-remarks' => $Report->privateRemarks,
|
||||
'last-editor-uid' => $Report->lastEditorUid,
|
||||
'assigned-uid' => $Report->assignedUid,
|
||||
'status' => $Report->status,
|
||||
'resolution' => $Report->resolution,
|
||||
'created' => $Report->created->format(DateTimeFormat::MYSQL),
|
||||
'edited' => $Report->edited ? $Report->edited->format(DateTimeFormat::MYSQL) : null,
|
||||
'uid' => $Report->uid,
|
||||
'reporter-id' => $Report->reporterId,
|
||||
'cid' => $Report->cid,
|
||||
'comment' => $Report->comment,
|
||||
'category' => $Report->category,
|
||||
'rules' => $Report->rules,
|
||||
'forward' => $Report->forward,
|
||||
];
|
||||
|
||||
$postUriIds = $Report->postUriIds;
|
||||
|
||||
if ($Report->id) {
|
||||
$this->db->update(self::$table_name, $fields, ['id' => $Report->id]);
|
||||
} else {
|
||||
$fields['created'] = DateTimeFormat::utcNow();
|
||||
$this->db->insert(self::$table_name, $fields, Database::INSERT_IGNORE);
|
||||
|
||||
$newReportId = $this->db->lastInsertId();
|
||||
$Report = $this->selectOneById($this->db->lastInsertId());
|
||||
}
|
||||
|
||||
foreach ($Report->posts as $post) {
|
||||
if (Post::exists(['uri-id' => $post->uriId])) {
|
||||
$this->db->insert('report-post', ['rid' => $newReportId, 'uri-id' => $post->uriId, 'status' => $post->status]);
|
||||
} else {
|
||||
Logger::notice('Post does not exist', ['uri-id' => $post->uriId, 'report' => $Report]);
|
||||
}
|
||||
$this->db->delete('report-post', ['rid' => $Report->id]);
|
||||
|
||||
foreach ($postUriIds as $uriId) {
|
||||
if (Post::exists(['uri-id' => $uriId])) {
|
||||
$this->db->insert('report-post', ['rid' => $Report->id, 'uri-id' => $uriId]);
|
||||
} else {
|
||||
Logger::notice('Post does not exist', ['uri-id' => $uriId, 'report' => $Report]);
|
||||
}
|
||||
|
||||
foreach ($Report->rules as $rule) {
|
||||
$this->db->insert('report-rule', ['rid' => $newReportId, 'line-id' => $rule->lineId, 'text' => $rule->text]);
|
||||
}
|
||||
|
||||
$Report = $this->selectOneById($newReportId);
|
||||
}
|
||||
|
||||
return $Report;
|
||||
|
|
@ -103,14 +88,13 @@ final class Report extends \Friendica\BaseRepository
|
|||
|
||||
protected function _selectOne(array $condition, array $params = []): BaseEntity
|
||||
{
|
||||
$fields = $this->db->selectFirst(self::$table_name, [], $condition, $params);
|
||||
$fields = $this->db->selectFirst(static::$table_name, [], $condition, $params);
|
||||
if (!$this->db->isResult($fields)) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
$reportPosts = new Collection\Report\Posts(array_map([$this->postFactory, 'createFromTableRow'], $this->db->selectToArray('report-post', ['uri-id', 'status'], ['rid' => $condition['id'] ?? 0])));
|
||||
$reportRules = new Collection\Report\Rules(array_map([$this->ruleFactory, 'createFromTableRow'], $this->db->selectToArray('report-rule', ['line-id', 'text'], ['rid' => $condition['id'] ?? 0])));
|
||||
$postUriIds = array_column($this->db->selectToArray('report-post', ['uri-id'], ['rid' => $condition['id'] ?? 0]), 'uri-id');
|
||||
|
||||
return $this->factory->createFromTableRow($fields, $reportPosts, $reportRules);
|
||||
return $this->factory->createFromTableRow($fields, $postUriIds);
|
||||
}
|
||||
}
|
||||
|
|
|
|||