Merge branch '2020.09-rc' into stable

This commit is contained in:
Tobias Diekershoff 2020-09-20 21:11:33 +02:00
commit c6b45a958e
443 changed files with 110168 additions and 107725 deletions

100
.github/workflows/php.yml vendored Normal file
View File

@ -0,0 +1,100 @@
name: Testing Friendica
on: [push, pull_request]
jobs:
friendica:
name: Friendica (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
services:
mariadb:
image: mariadb:latest
env:
MYSQL_ALLOW_EMPTY_PASSWORD: true
MYSQL_DATABASE: test
MYSQL_PASSWORD: test
MYSQL_USER: test
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
redis:
image: redis
ports:
- 6379/tcp
options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3
memcached:
image: memcached
ports:
- 11211/tcp
strategy:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4']
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Setup PHP, with composer and extensions
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: pecl
extensions: pdo_mysql, gd, zip, opcache, ctype, pcntl, ldap, apcu, memcached, redis, imagick, memcache
coverage: xdebug
ini-values: apc.enabled=1, apc.enable_cli=1
- name: Start mysql service
run: sudo /etc/init.d/mysql start
- name: Validate composer.json and composer.lock
run: composer validate
- name: Get composer cache directory
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v2
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --prefer-dist
- name: Copy default Friendica config
run: cp config/local-sample.config.php config/local.config.php
- name: Verify MariaDB connection
env:
PORT: ${{ job.services.mariadb.ports[3306] }}
run: |
while ! mysqladmin ping -h"127.0.0.1" -P"$PORT" --silent; do
sleep 1
done
- name: Setup MYSQL database
env:
PORT: ${{ job.services.mariadb.ports[3306] }}
run: |
mysql -h"127.0.0.1" -P"$PORT" -utest -ptest test < database.sql
- name: Test with Parallel-lint
run: vendor/bin/parallel-lint --exclude vendor/ --exclude view/asset/ .
- name: Test with phpunit
run: vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
env:
MYSQL_HOST: 127.0.0.1
MYSQL_PORT: ${{ job.services.mariadb.ports[3306] }}
MYSQL_DATABASE: test
MYSQL_PASSWORD: test
MYSQL_USER: test
REDIS_PORT: ${{ job.services.redis.ports[6379] }}
MEMCACHED_PORT: ${{ job.services.memcached.ports[11211] }}
MEMCACHE_PORT: ${{ job.services.memcached.ports[11211] }}
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
file: clover.xml

4
.gitignore vendored
View File

@ -71,8 +71,8 @@ venv/
/addons /addons
/addon /addon
#ignore .htaccess #ignore base .htaccess
.htaccess /.htaccess
#ignore filesystem storage default path #ignore filesystem storage default path
/storage /storage

View File

@ -1,3 +1,6 @@
# This file is meant to be copied to ".htaccess" on Apache-powered web servers.
# The created .htaccess file can be edited manually and will not be overwritten by Friendica updates.
Options -Indexes Options -Indexes
AddType application/x-java-archive .jar AddType application/x-java-archive .jar
AddType audio/ogg .oga AddType audio/ogg .oga

View File

@ -1,31 +0,0 @@
---
language: php
## Friendica officially supports PHP version >= 7.1
php:
- 7.1
- 7.2
- 7.3
services:
- mysql
- redis
- memcached
env:
- MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=travis MYSQL_PASSWORD="" MYSQL_DATABASE=test
install:
- composer install
before_script:
- cp config/local-sample.config.php config/local.config.php
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
- mysql -utravis test < database.sql
- pecl channel-update pecl.php.net
- pecl config-set preferred_state beta
- phpenv config-add .travis/redis.ini
- phpenv config-add .travis/memcached.ini
script:
- vendor/bin/parallel-lint --exclude vendor/ --exclude view/asset/ .
- vendor/bin/phpunit --configuration tests/phpunit.xml --coverage-clover clover.xml
after_success: bash <(curl -s https://codecov.io/bash)

View File

@ -1,4 +0,0 @@
extension="apcu.so"
apc.enabled = 1
apc.enable_cli = 1

View File

@ -1 +0,0 @@
extension="memcached.so"

View File

@ -1 +0,0 @@
extension="redis.so"

View File

@ -1,6 +1,78 @@
Version 2020.09 (2020-09-20)
Friendica Core:
Updates to the translations: DE, EN GB, EN US, ES, FR, IT, NL, PL, RU, ZH_CN [translation teams]
Updates to the themes (all) [MrPetovan, tobiasd]
Updates to the documentation [annando, mpanhans, realkinetix, tobiasd]
General code cleanup and refactoring [annando, MrPetovan, nupplaphil]
Enhanced the API [annando]
Enhanced the processing of background jobs [annando]
Enhanced federation of activities [annando, vpzomtrrfrt]
Enhanced the user notifications[annando]
Enhanced database usage [annando, MrPetovan]
Enhanced ActivityPub support for forums [annando]
Enhanced the utilization of the cache [annando, MrPetovan]
Enhanced the performance of the daemon [annando]
Enhanced the communication with the directory servers [annando]
Enhanced the re-sharing of items [annando]
Enhanced sample lighttpd and nginx configs [MrPetovan, tobiasd]
Enhanced the checks for incoming postings using ActivityPub [annando, Roger Meyer]
Enhanced the import of RSS feeds by removing tracking pixels [annando]
Enhanced the speed of the full text search [annando]
Replaced library used for text completion [MrPetovan]
Fixed a problem that prevented recipients of direct messages to be selected [MrPetovan]
Fixed a problem that prevented new email contacts from being added [annando]
Fixed a problem with the console command search [tobiasd]
Fixed a problem during the search for contacts [annando]
Fixed a problem with the JOT of private notes [MrPetovan]
Fixed missing HTML encoding [MrPetovan]
Fixed a layout problem with the frio composer for new postings [MrPetovan]
Fixed some composer notices [nupplaphil]
Fixed a problem for empty preview data when importing feed posts [annando]
Fixed a problem with the pager on search result pages [annando]
Fixed some templates to show the correct un-/follow button for contacts [annando]
Fixed a problem with the generation of the Message-ID of notification emails [nupplaphil]
Added nodeinfo2 support [annando]
Added CSV export and import of blocked servers to the console [tobiasd]
Added new admin debug module for ActivityPub [MrPetovan]
Added the automatic determination of frequency to pull feeds [annando]
Added signed fetching from system users for ActivityPub [annando]
Added the discovery of new peers from contacts [annando]
Added the directory API endpoint [annando]
Added support for signed outbox requests [annando]
Added direction functionality for clarification of posting flow [annando]
Added the ability to set the database version [annando]
Added support for ActivityPub relay server [annando]
By default display of re-sharer information is now flattened [annando]
Removed some unused POCO functionality [annando]
Removed the unused rating functionality [annando]
Removed unneeded network request for local stuff [annando]
Removed some useless info messages [annando]
Reworked some additional features according to a user voting [MrPetovan]
Friendica Addons:
Updates to the translations: DE, EN GB, EN US, IT, NL, RU, ZH_CN [translation teams]
Updates to the docs [SpencerDub]
General code cleanup and maintenance [annando, MrPetovan]
blockbot:
added some "good" bots [annando]
forumdirectory:
fixed some SQL queries [MrPetovan]
phpmailer:
fixed a problem leading to double message ID headers [nupplaphil]
qcomment:
restructured the addon and fixed a bug preventing the addon from working [MrPetovan]
Closed Issues:
2811, 4606, 5742, 5782, 7660, 8676, 8788, 8797, 8798, 8847, 8860,
8874, 8882, 8885, 8906, 8914, 8922, 8928, 8929, 8935, 8940, 8941,
8956, 8958, 8961, 8967, 8989, 8993, 8994, 8995, 8997, 8999, 9000,
9004, 9013, 9015, 9051, 9064, 9065, 9072, 9081, 9090, 9091, 9099,
9107, 9135, 9136, 9137, 9138, 9140, 9142, 9150, 9153, 9154, 9163,
9164, 9172, 9182, 9192, 9193, 9204, 9210, 9229, 9231, 9246
Version 2020.07-1 (2020-09-08) Version 2020.07-1 (2020-09-08)
Friendica Core Friendica Core
Fixed a problem that leakted sensitive information [Roger Meyer, MrPetovan] Fixed a problem that leaked sensitive information [Roger Meyer, MrPetovan]
Version 2020.07 (2020-07-12) Version 2020.07 (2020-07-12)
Friendica Core: Friendica Core:
@ -670,7 +742,7 @@ Version 2018.09 (2018-09-23)
Version 2018.05 (2018-06-01) Version 2018.05 (2018-06-01)
Friendica Core: Friendica Core:
Update to the translations (DE, EN-GB, EN-US, FI, IS, IT, NL, PL, RU, ZN CH) [translation teams] Update to the translations (DE, EN-GB, EN-US, FI, IS, IT, NL, PL, RU, ZN CH) [translation teams]
Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPedovan, rudloff, tobiasd] Update to the documentation [andyhee, annando, fabrixxm, M-arcus, MrPetovan, rudloff, tobiasd]
Enhancements to the DB handling [annando] Enhancements to the DB handling [annando]
Enhancements to the relay system [annando] Enhancements to the relay system [annando]
Enhancements to the handling of URL that contain unicode characters [annando] Enhancements to the handling of URL that contain unicode characters [annando]

View File

@ -55,6 +55,7 @@ Chris Case
Christian González Christian González
Christian M. Grube Christian M. Grube
Christian Vogeley Christian Vogeley
Christian Wiwie
Cohan Robinson Cohan Robinson
Copiis Praeesse Copiis Praeesse
CrystalStiletto CrystalStiletto
@ -114,7 +115,6 @@ Hypolite Petovan
Ilmari Ilmari
ImgBotApp ImgBotApp
irhen irhen
Jak
Jakob Jakob
Jens Tautenhahn Jens Tautenhahn
jensp jensp
@ -122,6 +122,7 @@ Jeroen De Meerleer
jeroenpraat jeroenpraat
Joan Bar Joan Bar
JOduMonT JOduMonT
joe slam
Johannes Schwab Johannes Schwab
John Brazil John Brazil
Jonatan Nyberg Jonatan Nyberg
@ -143,7 +144,6 @@ Leberwurscht
Leonard Lausen Leonard Lausen
Lionel Triay Lionel Triay
loma-one loma-one
loma1
Lorem Ipsum Lorem Ipsum
Ludovic Grossard Ludovic Grossard
Lynn Stephenson Lynn Stephenson
@ -173,6 +173,7 @@ Michal Šupler
Michalina Michalina
Mike Macgirvin Mike Macgirvin
miqrogroove miqrogroove
mpanhans
mytbk mytbk
nathilia-peirce nathilia-peirce
Nicola Spanti Nicola Spanti
@ -231,7 +232,7 @@ Simon L'nu
Simó Albert i Beltran Simó Albert i Beltran
softmetz softmetz
soko1 soko1
SpencerDub Spencer Dub
St John Karp St John Karp
Stanislav N. Stanislav N.
Steffen K9 Steffen K9
@ -269,7 +270,6 @@ U-SOUND\mike
ufic ufic
Ulf Rompe Ulf Rompe
Unknown Unknown
Valvin
Valvin A Valvin A
Vasudev Kamath Vasudev Kamath
Vasya Novikov Vasya Novikov

View File

@ -40,3 +40,7 @@ Have a look at the [installation documentation](doc/Install.md) for further info
|*Vier theme, desktop browser. Public timeline view.*| |*Vier theme, desktop browser. Public timeline view.*|
|![Vier theme in desktop browser](images/screenshots/friendica-vier-community.png?raw=true "Vier theme in desktop browser") |![Vier theme in desktop browser](images/screenshots/friendica-vier-community.png?raw=true "Vier theme in desktop browser")
|*Vier theme, desktop browser. Community post displayed.*| |*Vier theme, desktop browser. Community post displayed.*|
## Endorsements
- [![Awesome Humane Tech](images/humane-tech-badge.svg)](https://github.com/humanetech-community/awesome-humane-tech) On August 12th 2020, Friendica was added to [the curated Awesome Humane Tech directory](https://github.com/humanetech-community/awesome-humane-tech) in [the "Fediverse" category](https://github.com/humanetech-community/awesome-humane-tech#fediverse).

View File

@ -1 +1 @@
2020.07-1 2020.09-rc

10
bin/.htaccess Normal file
View File

@ -0,0 +1,10 @@
# This file prevents browser access to Friendica command-line scripts on Apache-powered web servers.
# It isn't meant to be edited manually, please check the base Friendica folder for the .htaccess-dist file instead.
<IfModule authz_host_module>
Require all denied
</IfModule>
<IfModule !authz_host_module>
Order Allow,Deny
Deny from all
</IfModule>

View File

@ -51,6 +51,11 @@
* *
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
use Dice\Dice; use Dice\Dice;
use Friendica\App\Mode; use Friendica\App\Mode;
use Friendica\Util\ExAuth; use Friendica\Util\ExAuth;
@ -80,6 +85,7 @@ $dice = $dice->addRule(LoggerInterface::class,['constructParams' => ['auth_ejabb
$appMode = $dice->create(Mode::class); $appMode = $dice->create(Mode::class);
if ($appMode->isNormal()) { if ($appMode->isNormal()) {
$oAuth = new ExAuth(); /** @var ExAuth $oAuth */
$oAuth = $dice->create(ExAuth::class);
$oAuth->readStdin(); $oAuth->readStdin();
} }

View File

@ -20,6 +20,11 @@
* *
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
use Dice\Dice; use Dice\Dice;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;

View File

@ -23,6 +23,11 @@
* This script was taken from http://php.net/manual/en/function.pcntl-fork.php * This script was taken from http://php.net/manual/en/function.pcntl-fork.php
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
use Dice\Dice; use Dice\Dice;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Worker; use Friendica\Core\Worker;
@ -185,7 +190,12 @@ while (true) {
$do_cron = true; $do_cron = true;
} }
Worker::spawnWorker($do_cron); if ($do_cron || (!DI::process()->isMaxLoadReached() && Worker::entriesExists() && Worker::isReady())) {
Worker::spawnWorker($do_cron);
} else {
Logger::info('Cool down', ['pid' => $pid]);
sleep(10);
}
if ($do_cron) { if ($do_cron) {
// We force a reconnect of the database connection. // We force a reconnect of the database connection.

View File

@ -34,7 +34,7 @@ dontinclude = ['root', 'friendica', 'bavatar', 'tony baldwin', 'Taek', 'silke m'
path = os.path.abspath(argv[0].split('bin/dev/make_credits.py')[0]) path = os.path.abspath(argv[0].split('bin/dev/make_credits.py')[0])
print('> base directory is assumed to be: '+path) print('> base directory is assumed to be: '+path)
# a place to store contributors # a place to store contributors
contributors = ["Andi Stadler", "Ratten", "Vít Šesták 'v6ak'"] contributors = ["Andi Stadler", "Ratten", "Roger Meyer", "Vít Šesták 'v6ak'"]
# get the contributors # get the contributors
print('> getting contributors to the friendica core repository') print('> getting contributors to the friendica core repository')
p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'], p = subprocess.Popen(['git', 'shortlog', '--no-merges', '-s'],

View File

@ -26,6 +26,10 @@
* *
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
if (($_SERVER["argc"] > 1) && isset($_SERVER["argv"][1])) { if (($_SERVER["argc"] > 1) && isset($_SERVER["argv"][1])) {
echo $_SERVER["argv"][1]; echo $_SERVER["argv"][1];

View File

@ -24,6 +24,11 @@
* Usage: php bin/wait-for-connection {HOST} {PORT} [{TIMEOUT}] * Usage: php bin/wait-for-connection {HOST} {PORT} [{TIMEOUT}]
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
$timeout = 60; $timeout = 60;
switch ($argc) { switch ($argc) {
case 4: case 4:

View File

@ -21,6 +21,11 @@
* Starts the background processing * Starts the background processing
*/ */
if (php_sapi_name() !== 'cli') {
header($_SERVER["SERVER_PROTOCOL"] . ' 403 Forbidden');
exit();
}
use Dice\Dice; use Dice\Dice;
use Friendica\App; use Friendica\App;
use Friendica\Core\Update; use Friendica\Core\Update;

View File

@ -38,7 +38,7 @@ use Friendica\Util\DateTimeFormat;
define('FRIENDICA_PLATFORM', 'Friendica'); define('FRIENDICA_PLATFORM', 'Friendica');
define('FRIENDICA_CODENAME', 'Red Hot Poker'); define('FRIENDICA_CODENAME', 'Red Hot Poker');
define('FRIENDICA_VERSION', '2020.07-1'); define('FRIENDICA_VERSION', '2020.09-rc');
define('DFRN_PROTOCOL_VERSION', '2.23'); define('DFRN_PROTOCOL_VERSION', '2.23');
define('NEW_UPDATE_ROUTINE_VERSION', 1170); define('NEW_UPDATE_ROUTINE_VERSION', 1170);
@ -253,10 +253,10 @@ function public_contact()
if (!$public_contact_id && !empty($_SESSION['authenticated'])) { if (!$public_contact_id && !empty($_SESSION['authenticated'])) {
if (!empty($_SESSION['my_address'])) { if (!empty($_SESSION['my_address'])) {
// Local user // Local user
$public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, true)); $public_contact_id = intval(Contact::getIdForURL($_SESSION['my_address'], 0, false));
} elseif (!empty($_SESSION['visitor_home'])) { } elseif (!empty($_SESSION['visitor_home'])) {
// Remote user // Remote user
$public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, true)); $public_contact_id = intval(Contact::getIdForURL($_SESSION['visitor_home'], 0, false));
} }
} elseif (empty($_SESSION['authenticated'])) { } elseif (empty($_SESSION['authenticated'])) {
$public_contact_id = false; $public_contact_id = false;
@ -266,7 +266,7 @@ function public_contact()
} }
/** /**
* Returns contact id of authenticated site visitor or false * Returns public contact id of authenticated site visitor or false
* *
* @return int|bool visitor_id or false * @return int|bool visitor_id or false
*/ */
@ -382,38 +382,6 @@ function is_site_admin()
return local_user() && $admin_email && in_array($a->user['email'] ?? '', $adminlist); return local_user() && $admin_email && in_array($a->user['email'] ?? '', $adminlist);
} }
function explode_querystring($query)
{
$arg_st = strpos($query, '?');
if ($arg_st !== false) {
$base = substr($query, 0, $arg_st);
$arg_st += 1;
} else {
$base = '';
$arg_st = 0;
}
$args = explode('&', substr($query, $arg_st));
foreach ($args as $k => $arg) {
/// @TODO really compare type-safe here?
if ($arg === '') {
unset($args[$k]);
}
}
$args = array_values($args);
if (!$base) {
$base = $args[0];
unset($args[0]);
$args = array_values($args);
}
return [
'base' => $base,
'args' => $args,
];
}
/** /**
* Returns the complete URL of the current page, e.g.: http(s)://something.com/network * Returns the complete URL of the current page, e.g.: http(s)://something.com/network
* *

View File

@ -62,6 +62,7 @@
"npm-asset/jgrowl": "^1.4", "npm-asset/jgrowl": "^1.4",
"npm-asset/moment": "^2.24", "npm-asset/moment": "^2.24",
"npm-asset/perfect-scrollbar": "0.6.16", "npm-asset/perfect-scrollbar": "0.6.16",
"npm-asset/textcomplete": "^0.18.2",
"npm-asset/typeahead.js": "^0.11.1" "npm-asset/typeahead.js": "^0.11.1"
}, },
"repositories": [ "repositories": [
@ -82,7 +83,6 @@
"include/conversation.php", "include/conversation.php",
"include/dba.php", "include/dba.php",
"include/enotify.php", "include/enotify.php",
"include/items.php",
"boot.php" "boot.php"
] ]
}, },
@ -128,7 +128,7 @@
"mikey179/vfsstream": "^1.6", "mikey179/vfsstream": "^1.6",
"mockery/mockery": "^1.2", "mockery/mockery": "^1.2",
"johnkary/phpunit-speedtrap": "1.1", "johnkary/phpunit-speedtrap": "1.1",
"jakub-onderka/php-parallel-lint": "^1.0" "php-parallel-lint/php-parallel-lint": "^1.2"
}, },
"scripts": { "scripts": {
"test": "phpunit" "test": "phpunit"

372
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "ded67f7e680a122d0cd3512c2738be97", "content-hash": "27203cc7da01ff668ba15e03fd17dc94",
"packages": [ "packages": [
{ {
"name": "asika/simple-console", "name": "asika/simple-console",
@ -1276,6 +1276,63 @@
], ],
"time": "2017-07-06T13:46:38+00:00" "time": "2017-07-06T13:46:38+00:00"
}, },
{
"name": "npm-asset/eventemitter3",
"version": "2.0.3",
"dist": {
"type": "tar",
"url": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"shasum": "b5e1079b59fb5e1ba2771c0a993be060a58c99ba"
},
"type": "npm-asset-library",
"extra": {
"npm-asset-bugs": {
"url": "https://github.com/primus/eventemitter3/issues"
},
"npm-asset-main": "index.js",
"npm-asset-directories": [],
"npm-asset-repository": {
"type": "git",
"url": "git://github.com/primus/eventemitter3.git"
},
"npm-asset-scripts": {
"build": "mkdir -p umd && browserify index.js -s EventEmitter3 | uglifyjs -m -o umd/eventemitter3.min.js",
"benchmark": "find benchmarks/run -name '*.js' -exec benchmarks/start.sh {} \\;",
"test": "nyc --reporter=html --reporter=text mocha",
"test-browser": "zuul -- test.js",
"prepublish": "npm run build",
"sync": "node versions.js"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Arnout Kazemier"
}
],
"description": "EventEmitter3 focuses on performance while maintaining a Node.js AND browser compatible interface.",
"homepage": "https://github.com/primus/eventemitter3#readme",
"keywords": [
"EventEmitter",
"EventEmitter2",
"EventEmitter3",
"Events",
"addEventListener",
"addListener",
"emit",
"emits",
"emitter",
"event",
"once",
"pub/sub",
"publish",
"reactor",
"subscribe"
],
"time": "2017-03-31T14:51:09+00:00"
},
{ {
"name": "npm-asset/fullcalendar", "name": "npm-asset/fullcalendar",
"version": "3.10.2", "version": "3.10.2",
@ -1792,64 +1849,6 @@
], ],
"time": "2017-01-10T01:03:05+00:00" "time": "2017-01-10T01:03:05+00:00"
}, },
{
"name": "npm-asset/perfect-scrollbar",
"version": "0.6.16",
"dist": {
"type": "tar",
"url": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-0.6.16.tgz",
"shasum": "b1d61a5245cf3962bb9a8407a3fc669d923212fc"
},
"type": "npm-asset-library",
"extra": {
"npm-asset-bugs": {
"url": "https://github.com/noraesae/perfect-scrollbar/issues"
},
"npm-asset-files": [
"dist",
"src",
"index.js",
"jquery.js",
"perfect-scrollbar.d.ts"
],
"npm-asset-main": "./index.js",
"npm-asset-directories": [],
"npm-asset-repository": {
"type": "git",
"url": "git+https://github.com/noraesae/perfect-scrollbar.git"
},
"npm-asset-scripts": {
"test": "gulp",
"before-deploy": "gulp && gulp compress",
"release": "rm -rf dist && gulp && npm publish"
},
"npm-asset-engines": {
"node": ">= 0.12.0"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Hyunje Jun",
"email": "me@noraesae.net"
},
{
"name": "Hyunje Jun",
"email": "me@noraesae.net"
}
],
"description": "Minimalistic but perfect custom scrollbar plugin",
"homepage": "https://github.com/noraesae/perfect-scrollbar#readme",
"keywords": [
"frontend",
"jquery-plugin",
"scroll",
"scrollbar"
],
"time": "2017-01-10T01:03:05+00:00"
},
{ {
"name": "npm-asset/php-date-formatter", "name": "npm-asset/php-date-formatter",
"version": "v1.3.6", "version": "v1.3.6",
@ -1888,6 +1887,100 @@
"homepage": "https://github.com/kartik-v/php-date-formatter", "homepage": "https://github.com/kartik-v/php-date-formatter",
"time": "2020-04-14T10:16:32+00:00" "time": "2020-04-14T10:16:32+00:00"
}, },
{
"name": "npm-asset/textarea-caret",
"version": "3.1.0",
"dist": {
"type": "tar",
"url": "https://registry.npmjs.org/textarea-caret/-/textarea-caret-3.1.0.tgz",
"shasum": "5d5a35bb035fd06b2ff0e25d5359e97f2655087f"
},
"type": "npm-asset-library",
"extra": {
"npm-asset-bugs": {
"url": "https://github.com/component/textarea-caret-position/issues"
},
"npm-asset-files": [
"index.js"
],
"npm-asset-main": "index.js",
"npm-asset-directories": [],
"npm-asset-repository": {
"type": "git",
"url": "git+https://github.com/component/textarea-caret-position.git"
}
},
"license": [
"MIT"
],
"description": "(x, y) coordinates of the caret in a textarea or input type='text'",
"homepage": "https://github.com/component/textarea-caret-position#readme",
"keywords": [
"caret",
"position",
"textarea"
],
"time": "2018-02-20T06:11:03+00:00"
},
{
"name": "npm-asset/textcomplete",
"version": "0.18.2",
"dist": {
"type": "tar",
"url": "https://registry.npmjs.org/textcomplete/-/textcomplete-0.18.2.tgz",
"shasum": "de0d806567102f7e32daffcbcc3db05af1515eb5"
},
"require": {
"npm-asset/eventemitter3": ">=2.0.3,<3.0.0",
"npm-asset/textarea-caret": ">=3.0.1,<4.0.0",
"npm-asset/undate": ">=0.2.3,<0.3.0"
},
"type": "npm-asset-library",
"extra": {
"npm-asset-bugs": {
"url": "https://github.com/yuku-t/textcomplete/issues"
},
"npm-asset-main": "lib/index.js",
"npm-asset-directories": [],
"npm-asset-repository": {
"type": "git",
"url": "git+ssh://git@github.com/yuku-t/textcomplete.git"
},
"npm-asset-scripts": {
"build": "yarn run clean && run-p build:*",
"build:dist": "webpack && webpack --env=min && run-p print-dist-gz-size",
"build:docs": "run-p build:docs:*",
"build:docs:html": "webpack --config webpack.doc.config.js && pug -o docs src/doc/index.pug",
"build:docs:md": "documentation build src/*.js -f md -o doc/api.md",
"build:lib": "babel src -d lib -s && for js in src/*.js; do cp $js lib/${js##*/}.flow; done",
"clean": "rm -fr dist docs lib",
"format": "prettier --no-semi --trailing-comma all --write 'src/*.js' 'test/**/*.js'",
"gh-release": "npm pack textcomplete && gh-release -a textcomplete-$(cat package.json|jq -r .version).tgz",
"opener": "wait-on http://localhost:8082 && opener http://localhost:8082",
"print-dist-gz-size": "printf 'dist/textcomplete.min.js.gz: %d bytes\\n' \"$(gzip -9kc dist/textcomplete.min.js | wc -c)\"",
"start": "run-p watch opener",
"test": "run-p test:*",
"test:bundlesize": "yarn run build:dist && bundlesize",
"test:e2e": "NODE_ENV=test karma start --single-run",
"test:lint": "eslint src/*.js test/**/*.js",
"test:typecheck": "flow check",
"watch": "run-p watch:*",
"watch:webpack": "webpack-dev-server --config webpack.doc.config.js",
"watch:pug": "pug -o docs --watch src/doc/index.pug"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Yuku Takahashi"
}
],
"description": "Autocomplete for textarea elements",
"homepage": "https://github.com/yuku-t/textcomplete#readme",
"time": "2020-06-10T06:11:00+00:00"
},
{ {
"name": "npm-asset/typeahead.js", "name": "npm-asset/typeahead.js",
"version": "0.11.1", "version": "0.11.1",
@ -1940,6 +2033,48 @@
], ],
"time": "2015-04-27T04:03:42+00:00" "time": "2015-04-27T04:03:42+00:00"
}, },
{
"name": "npm-asset/undate",
"version": "0.2.4",
"dist": {
"type": "tar",
"url": "https://registry.npmjs.org/undate/-/undate-0.2.4.tgz",
"shasum": "ccb2a8cf38edc035d1006fcb2909c4c6024a8400"
},
"type": "npm-asset-library",
"extra": {
"npm-asset-bugs": {
"url": "https://github.com/yuku-t/undate/issues"
},
"npm-asset-main": "lib/index.js",
"npm-asset-directories": [],
"npm-asset-repository": {
"type": "git",
"url": "git+https://github.com/yuku-t/undate.git"
},
"npm-asset-scripts": {
"build": "babel src -d lib && for js in src/*.js; do cp $js lib/${js##*/}.flow; done",
"test": "run-p test:*",
"test:eslint": "eslint src/*.js test/*.js",
"test:flow": "flow check",
"test:karma": "karma start --single-run"
}
},
"license": [
"MIT"
],
"authors": [
{
"name": "Yuku Takahashi"
}
],
"description": "Undoable update for HTMLTextAreaElement",
"homepage": "https://github.com/yuku-t/undate#readme",
"keywords": [
"textarea"
],
"time": "2018-01-24T10:49:39+00:00"
},
{ {
"name": "paragonie/certainty", "name": "paragonie/certainty",
"version": "v2.6.1", "version": "v2.6.1",
@ -3247,55 +3382,6 @@
], ],
"time": "2016-01-20T08:20:44+00:00" "time": "2016-01-20T08:20:44+00:00"
}, },
{
"name": "jakub-onderka/php-parallel-lint",
"version": "v1.0.0",
"source": {
"type": "git",
"url": "https://github.com/JakubOnderka/PHP-Parallel-Lint.git",
"reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/JakubOnderka/PHP-Parallel-Lint/zipball/04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
"reference": "04fbd3f5fb1c83f08724aa58a23db90bd9086ee8",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
},
"require-dev": {
"jakub-onderka/php-console-highlighter": "~0.3",
"nette/tester": "~1.3",
"squizlabs/php_codesniffer": "~2.7"
},
"suggest": {
"jakub-onderka/php-console-highlighter": "Highlight syntax in code snippet"
},
"bin": [
"parallel-lint"
],
"type": "library",
"autoload": {
"classmap": [
"./"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Jakub Onderka",
"email": "ahoj@jakubonderka.cz"
}
],
"description": "This tool check syntax of PHP files about 20x faster than serial check.",
"homepage": "https://github.com/JakubOnderka/PHP-Parallel-Lint",
"abandoned": "php-parallel-lint/php-parallel-lint",
"time": "2018-02-24T15:31:20+00:00"
},
{ {
"name": "johnkary/phpunit-speedtrap", "name": "johnkary/phpunit-speedtrap",
"version": "v1.1.0", "version": "v1.1.0",
@ -3500,6 +3586,59 @@
], ],
"time": "2017-10-19T19:58:43+00:00" "time": "2017-10-19T19:58:43+00:00"
}, },
{
"name": "php-parallel-lint/php-parallel-lint",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
"reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/474f18bc6cc6aca61ca40bfab55139de614e51ca",
"reference": "474f18bc6cc6aca61ca40bfab55139de614e51ca",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": ">=5.4.0"
},
"replace": {
"grogy/php-parallel-lint": "*",
"jakub-onderka/php-parallel-lint": "*"
},
"require-dev": {
"nette/tester": "^1.3 || ^2.0",
"php-parallel-lint/php-console-highlighter": "~0.3",
"squizlabs/php_codesniffer": "~3.0"
},
"suggest": {
"php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
},
"bin": [
"parallel-lint"
],
"type": "library",
"autoload": {
"classmap": [
"./"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-2-Clause"
],
"authors": [
{
"name": "Jakub Onderka",
"email": "ahoj@jakubonderka.cz"
}
],
"description": "This tool check syntax of PHP files about 20x faster than serial check.",
"homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
"time": "2020-04-04T12:18:32+00:00"
},
{ {
"name": "phpdocumentor/reflection-common", "name": "phpdocumentor/reflection-common",
"version": "1.0.1", "version": "1.0.1",
@ -4669,6 +4808,20 @@
"polyfill", "polyfill",
"portable" "portable"
], ],
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-05-12T16:14:59+00:00" "time": "2020-05-12T16:14:59+00:00"
}, },
{ {
@ -4801,5 +4954,6 @@
"platform-dev": [], "platform-dev": [],
"platform-overrides": { "platform-overrides": {
"php": "7.0" "php": "7.0"
} },
"plugin-api-version": "1.1.0"
} }

View File

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2020.06-dev (Red Hot Poker) -- Friendica 2020.09-rc (Red Hot Poker)
-- DB_UPDATE_VERSION 1353 -- DB_UPDATE_VERSION 1368
-- ------------------------------------------ -- ------------------------------------------
@ -28,23 +28,11 @@ CREATE TABLE IF NOT EXISTS `gserver` (
`last_poco_query` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_poco_query` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`failed` boolean COMMENT 'Connection failed',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `nurl` (`nurl`(190)) UNIQUE INDEX `nurl` (`nurl`(190))
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Global servers';
--
-- TABLE clients
--
CREATE TABLE IF NOT EXISTS `clients` (
`client_id` varchar(20) NOT NULL COMMENT '',
`pw` varchar(20) NOT NULL DEFAULT '' COMMENT '',
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
`name` text COMMENT '',
`icon` text COMMENT '',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
PRIMARY KEY(`client_id`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
-- --
-- TABLE contact -- TABLE contact
-- --
@ -95,11 +83,13 @@ CREATE TABLE IF NOT EXISTS `contact` (
`last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info', `last-update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last try to update the contact info',
`success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update', `success_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last successful contact update',
`failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update', `failure_update` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last failed update',
`failed` boolean COMMENT 'Connection failed',
`name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `name-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `uri-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `avatar-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `term-date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post', `last-item` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last post',
`last-discovery` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'date of the last follower discovery',
`priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `priority` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status', `blocked` boolean NOT NULL DEFAULT '1' COMMENT 'Node-wide block status',
`block_reason` text COMMENT 'Node-wide block reason', `block_reason` text COMMENT 'Node-wide block reason',
@ -108,6 +98,7 @@ CREATE TABLE IF NOT EXISTS `contact` (
`forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum', `forum` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a forum',
`prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group', `prv` boolean NOT NULL DEFAULT '0' COMMENT 'contact is a private group',
`contact-type` tinyint NOT NULL DEFAULT 0 COMMENT '', `contact-type` tinyint NOT NULL DEFAULT 0 COMMENT '',
`manually-approve` boolean COMMENT '',
`hidden` boolean NOT NULL DEFAULT '0' COMMENT '', `hidden` boolean NOT NULL DEFAULT '0' COMMENT '',
`archive` boolean NOT NULL DEFAULT '0' COMMENT '', `archive` boolean NOT NULL DEFAULT '0' COMMENT '',
`pending` boolean NOT NULL DEFAULT '1' COMMENT '', `pending` boolean NOT NULL DEFAULT '1' COMMENT '',
@ -129,16 +120,20 @@ CREATE TABLE IF NOT EXISTS `contact` (
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid_name` (`uid`,`name`(190)), INDEX `uid_name` (`uid`,`name`(190)),
INDEX `self_uid` (`self`,`uid`), INDEX `self_uid` (`self`,`uid`),
INDEX `alias_uid` (`alias`(32),`uid`), INDEX `alias_uid` (`alias`(96),`uid`),
INDEX `pending_uid` (`pending`,`uid`), INDEX `pending_uid` (`pending`,`uid`),
INDEX `blocked_uid` (`blocked`,`uid`), INDEX `blocked_uid` (`blocked`,`uid`),
INDEX `uid_rel_network_poll` (`uid`,`rel`,`network`,`poll`(64),`archive`), INDEX `uid_rel_network_poll` (`uid`,`rel`,`network`,`poll`(64),`archive`),
INDEX `uid_network_batch` (`uid`,`network`,`batch`(64)), INDEX `uid_network_batch` (`uid`,`network`,`batch`(64)),
INDEX `addr_uid` (`addr`(32),`uid`), INDEX `addr_uid` (`addr`(96),`uid`),
INDEX `nurl_uid` (`nurl`(32),`uid`), INDEX `nurl_uid` (`nurl`(96),`uid`),
INDEX `nick_uid` (`nick`(32),`uid`), INDEX `nick_uid` (`nick`(32),`uid`),
INDEX `attag_uid` (`attag`(96),`uid`),
INDEX `dfrn-id` (`dfrn-id`(64)), INDEX `dfrn-id` (`dfrn-id`(64)),
INDEX `issued-id` (`issued-id`(64)), INDEX `issued-id` (`issued-id`(64)),
INDEX `network_uid_lastupdate` (`network`,`uid`,`last-update`),
INDEX `uid_network_self_lastupdate` (`uid`,`network`,`self`,`last-update`),
INDEX `uid_lastitem` (`uid`,`last-item`),
INDEX `gsid` (`gsid`), INDEX `gsid` (`gsid`),
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contact table';
@ -155,6 +150,86 @@ CREATE TABLE IF NOT EXISTS `item-uri` (
INDEX `guid` (`guid`) INDEX `guid` (`guid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='URI and GUID for items';
--
-- TABLE tag
--
CREATE TABLE IF NOT EXISTS `tag` (
`id` int unsigned NOT NULL auto_increment COMMENT '',
`name` varchar(96) NOT NULL DEFAULT '' COMMENT '',
`url` varbinary(255) NOT NULL DEFAULT '' COMMENT '',
PRIMARY KEY(`id`),
UNIQUE INDEX `type_name_url` (`name`,`url`),
INDEX `url` (`url`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions';
--
-- TABLE user
--
CREATE TABLE IF NOT EXISTS `user` (
`uid` mediumint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`parent-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'The parent user that has full control about this user',
`guid` varchar(64) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this user',
`username` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this user is known by',
`password` varchar(255) NOT NULL DEFAULT '' COMMENT 'encrypted password',
`legacy_password` boolean NOT NULL DEFAULT '0' COMMENT 'Is the password hash double-hashed?',
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT 'nick- and user name',
`email` varchar(255) NOT NULL DEFAULT '' COMMENT 'the users email address',
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`timezone` varchar(128) NOT NULL DEFAULT '' COMMENT 'PHP-legal timezone',
`language` varchar(32) NOT NULL DEFAULT 'en' COMMENT 'default language',
`register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of registration',
`login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last login',
`default-location` varchar(255) NOT NULL DEFAULT '' COMMENT 'Default for item.location',
`allow_location` boolean NOT NULL DEFAULT '0' COMMENT '1 allows to display the location',
`theme` varchar(255) NOT NULL DEFAULT '' COMMENT 'user theme preference',
`pubkey` text COMMENT 'RSA public key 4096 bit',
`prvkey` text COMMENT 'RSA private key 4096 bit',
`spubkey` text COMMENT '',
`sprvkey` text COMMENT '',
`verified` boolean NOT NULL DEFAULT '0' COMMENT 'user is verified through email',
`blocked` boolean NOT NULL DEFAULT '0' COMMENT '1 for user is blocked',
`blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user',
`hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unkown viewers',
`blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user',
`unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user',
`cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '',
`notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options',
`page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type',
`account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`prvnets` boolean NOT NULL DEFAULT '0' COMMENT '',
`pwdreset` varchar(255) COMMENT 'Password reset request token',
`pwdreset_time` datetime COMMENT 'Timestamp of the last password reset request',
`maxreq` int unsigned NOT NULL DEFAULT 10 COMMENT '',
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`account_removed` boolean NOT NULL DEFAULT '0' COMMENT 'if 1 the account is removed',
`account_expired` boolean NOT NULL DEFAULT '0' COMMENT '',
`account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp when account expires and will be deleted',
`expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last warning of account expiration',
`def_gid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`allow_cid` mediumtext COMMENT 'default permission for this user',
`allow_gid` mediumtext COMMENT 'default permission for this user',
`deny_cid` mediumtext COMMENT 'default permission for this user',
`deny_gid` mediumtext COMMENT 'default permission for this user',
`openidserver` text COMMENT '',
PRIMARY KEY(`uid`),
INDEX `nickname` (`nickname`(32))
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
--
-- TABLE clients
--
CREATE TABLE IF NOT EXISTS `clients` (
`client_id` varchar(20) NOT NULL COMMENT '',
`pw` varchar(20) NOT NULL DEFAULT '' COMMENT '',
`redirect_uri` varchar(200) NOT NULL DEFAULT '' COMMENT '',
`name` text COMMENT '',
`icon` text COMMENT '',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
PRIMARY KEY(`client_id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
-- --
-- TABLE permissionset -- TABLE permissionset
-- --
@ -169,18 +244,6 @@ CREATE TABLE IF NOT EXISTS `permissionset` (
INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30)) INDEX `uid_allow_cid_allow_gid_deny_cid_deny_gid` (`allow_cid`(50),`allow_gid`(30),`deny_cid`(50),`deny_gid`(30))
) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
--
-- TABLE tag
--
CREATE TABLE IF NOT EXISTS `tag` (
`id` int unsigned NOT NULL auto_increment COMMENT '',
`name` varchar(96) NOT NULL DEFAULT '' COMMENT '',
`url` varbinary(255) NOT NULL DEFAULT '' COMMENT '',
PRIMARY KEY(`id`),
UNIQUE INDEX `type_name_url` (`name`,`url`),
INDEX `url` (`url`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='tags and mentions';
-- --
-- TABLE 2fa_app_specific_password -- TABLE 2fa_app_specific_password
-- --
@ -192,7 +255,8 @@ CREATE TABLE IF NOT EXISTS `2fa_app_specific_password` (
`generated` datetime NOT NULL COMMENT 'Datetime the password was generated', `generated` datetime NOT NULL COMMENT 'Datetime the password was generated',
`last_used` datetime COMMENT 'Datetime the password was last used', `last_used` datetime COMMENT 'Datetime the password was last used',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid_description` (`uid`,`description`(190)) INDEX `uid_description` (`uid`,`description`(190)),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor app-specific _password'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor app-specific _password';
-- --
@ -203,7 +267,8 @@ CREATE TABLE IF NOT EXISTS `2fa_recovery_codes` (
`code` varchar(50) NOT NULL COMMENT 'Recovery code string', `code` varchar(50) NOT NULL COMMENT 'Recovery code string',
`generated` datetime NOT NULL COMMENT 'Datetime the code was generated', `generated` datetime NOT NULL COMMENT 'Datetime the code was generated',
`used` datetime COMMENT 'Datetime the code was used', `used` datetime COMMENT 'Datetime the code was used',
PRIMARY KEY(`uid`,`code`) PRIMARY KEY(`uid`,`code`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor authentication recovery codes'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Two-factor authentication recovery codes';
-- --
@ -253,6 +318,7 @@ CREATE TABLE IF NOT EXISTS `apcontact` (
INDEX `addr` (`addr`(32)), INDEX `addr` (`addr`(32)),
INDEX `alias` (`alias`(190)), INDEX `alias` (`alias`(190)),
INDEX `followers` (`followers`(190)), INDEX `followers` (`followers`(190)),
INDEX `baseurl` (`baseurl`(190)),
INDEX `gsid` (`gsid`), INDEX `gsid` (`gsid`),
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation';
@ -276,7 +342,9 @@ CREATE TABLE IF NOT EXISTS `attach` (
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
`backend-class` tinytext COMMENT 'Storage backend class', `backend-class` tinytext COMMENT 'Storage backend class',
`backend-ref` text COMMENT 'Storage backend data reference', `backend-ref` text COMMENT 'Storage backend data reference',
PRIMARY KEY(`id`) PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='file attachments'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='file attachments';
-- --
@ -337,8 +405,12 @@ CREATE TABLE IF NOT EXISTS `contact-relation` (
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact the related contact had interacted with', `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact the related contact had interacted with',
`relation-cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'related contact who had interacted with the contact', `relation-cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'related contact who had interacted with the contact',
`last-interaction` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last interaction', `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 '',
PRIMARY KEY(`cid`,`relation-cid`), PRIMARY KEY(`cid`,`relation-cid`),
INDEX `relation-cid` (`relation-cid`) INDEX `relation-cid` (`relation-cid`),
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`relation-cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Contact relations'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Contact relations';
-- --
@ -354,7 +426,8 @@ CREATE TABLE IF NOT EXISTS `conv` (
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'edited timestamp', `updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'edited timestamp',
`subject` text COMMENT 'subject of initial message', `subject` text COMMENT 'subject of initial message',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid` (`uid`) INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages';
-- --
@ -409,7 +482,9 @@ CREATE TABLE IF NOT EXISTS `event` (
`deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id', `deny_cid` mediumtext COMMENT 'Access Control - list of denied contact.id',
`deny_gid` mediumtext COMMENT 'Access Control - list of denied groups', `deny_gid` mediumtext COMMENT 'Access Control - list of denied groups',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid_start` (`uid`,`start`) INDEX `uid_start` (`uid`,`start`),
INDEX `cid` (`cid`),
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
-- --
@ -451,91 +526,12 @@ CREATE TABLE IF NOT EXISTS `fsuggest` (
`photo` varchar(255) NOT NULL DEFAULT '' COMMENT '', `photo` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`note` text COMMENT '', `note` text COMMENT '',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
PRIMARY KEY(`id`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff';
--
-- TABLE gcign
--
CREATE TABLE IF NOT EXISTS `gcign` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Local User id',
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'gcontact.id of ignored contact',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `cid` (`cid`),
INDEX `uid` (`uid`), INDEX `uid` (`uid`),
INDEX `gcid` (`gcid`) FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='contacts ignored by friend suggestions'; FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='friend suggestion stuff';
--
-- TABLE gcontact
--
CREATE TABLE IF NOT EXISTS `gcontact` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this contact is known by',
`nick` varchar(255) NOT NULL DEFAULT '' COMMENT 'Nick- and user name of the contact',
`url` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the contacts profile page',
`nurl` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`photo` varchar(255) NOT NULL DEFAULT '' COMMENT 'Link to the profile photo',
`connect` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`updated` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last_contact` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last_failure` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`last_discovery` datetime DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of the last contact discovery',
`archive_date` datetime DEFAULT '0001-01-01 00:00:00' COMMENT '',
`archived` boolean NOT NULL DEFAULT '0' COMMENT '',
`location` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`about` text COMMENT '',
`keywords` text COMMENT 'puplic keywords (interests)',
`gender` varchar(32) NOT NULL DEFAULT '' COMMENT 'Deprecated',
`birthday` varchar(32) NOT NULL DEFAULT '0001-01-01' COMMENT '',
`community` boolean NOT NULL DEFAULT '0' COMMENT '1 if contact is forum account',
`contact-type` tinyint NOT NULL DEFAULT -1 COMMENT '',
`hide` boolean NOT NULL DEFAULT '0' COMMENT '1 = should be hidden from search',
`nsfw` boolean NOT NULL DEFAULT '0' COMMENT '1 = contact posts nsfw content',
`network` char(4) NOT NULL DEFAULT '' COMMENT 'social network protocol',
`addr` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`notify` varchar(255) COMMENT '',
`alias` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`generation` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`server_url` varchar(255) NOT NULL DEFAULT '' COMMENT 'baseurl of the contacts server',
`gsid` int unsigned COMMENT 'Global Server ID',
PRIMARY KEY(`id`),
UNIQUE INDEX `nurl` (`nurl`(190)),
INDEX `name` (`name`(64)),
INDEX `nick` (`nick`(32)),
INDEX `addr` (`addr`(64)),
INDEX `hide_network_updated` (`hide`,`network`,`updated`),
INDEX `updated` (`updated`),
INDEX `gsid` (`gsid`),
FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='global contacts';
--
-- TABLE gfollower
--
CREATE TABLE IF NOT EXISTS `gfollower` (
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact',
`follower-gcid` int unsigned NOT NULL DEFAULT 0 COMMENT 'global contact of the follower',
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates that the connection has been deleted',
PRIMARY KEY(`gcid`,`follower-gcid`),
INDEX `follower-gcid` (`follower-gcid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Followers of global contacts';
--
-- TABLE glink
--
CREATE TABLE IF NOT EXISTS `glink` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`cid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
`gcid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`zcid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`updated` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
PRIMARY KEY(`id`),
UNIQUE INDEX `cid_uid_gcid_zcid` (`cid`,`uid`,`gcid`,`zcid`),
INDEX `gcid` (`gcid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='\'friends of friends\' linkages derived from poco';
-- --
-- TABLE group -- TABLE group
@ -547,7 +543,8 @@ CREATE TABLE IF NOT EXISTS `group` (
`deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted', `deleted` boolean NOT NULL DEFAULT '0' COMMENT '1 indicates the group has been deleted',
`name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group', `name` varchar(255) NOT NULL DEFAULT '' COMMENT 'human readable name of group',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid` (`uid`) INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, group info';
-- --
@ -559,7 +556,9 @@ CREATE TABLE IF NOT EXISTS `group_member` (
`contact-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact.id of the member assigned to 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`), PRIMARY KEY(`id`),
INDEX `contactid` (`contact-id`), INDEX `contactid` (`contact-id`),
UNIQUE INDEX `gid_contactid` (`gid`,`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 groups, member info'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='privacy groups, member info';
-- --
@ -569,7 +568,8 @@ CREATE TABLE IF NOT EXISTS `gserver-tag` (
`gserver-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'The id of the gserver', `gserver-id` int unsigned NOT NULL DEFAULT 0 COMMENT 'The id of the gserver',
`tag` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tag that the server has subscribed', `tag` varchar(100) NOT NULL DEFAULT '' COMMENT 'Tag that the server has subscribed',
PRIMARY KEY(`gserver-id`,`tag`), PRIMARY KEY(`gserver-id`,`tag`),
INDEX `tag` (`tag`) INDEX `tag` (`tag`),
FOREIGN KEY (`gserver-id`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Tags that the server has subscribed'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Tags that the server has subscribed';
-- --
@ -585,6 +585,16 @@ CREATE TABLE IF NOT EXISTS `hook` (
UNIQUE INDEX `hook_file_function` (`hook`,`file`,`function`) UNIQUE INDEX `hook_file_function` (`hook`,`file`,`function`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='addon hook registry'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='addon hook registry';
--
-- TABLE host
--
CREATE TABLE IF NOT EXISTS `host` (
`id` tinyint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`name` varchar(128) NOT NULL DEFAULT '' COMMENT 'The hostname',
PRIMARY KEY(`id`),
UNIQUE INDEX `name` (`name`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Hostname';
-- --
-- TABLE inbox-status -- TABLE inbox-status
-- --
@ -614,7 +624,11 @@ CREATE TABLE IF NOT EXISTS `intro` (
`datetime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `datetime` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
`blocked` boolean NOT NULL DEFAULT '1' COMMENT '', `blocked` boolean NOT NULL DEFAULT '1' COMMENT '',
`ignore` boolean NOT NULL DEFAULT '0' COMMENT '', `ignore` boolean NOT NULL DEFAULT '0' COMMENT '',
PRIMARY KEY(`id`) PRIMARY KEY(`id`),
INDEX `contact-id` (`contact-id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) 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=''; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
-- --
@ -716,6 +730,7 @@ CREATE TABLE IF NOT EXISTS `item` (
INDEX `resource-id` (`resource-id`), INDEX `resource-id` (`resource-id`),
INDEX `deleted_changed` (`deleted`,`changed`), INDEX `deleted_changed` (`deleted`,`changed`),
INDEX `uid_wall_changed` (`uid`,`wall`,`changed`), INDEX `uid_wall_changed` (`uid`,`wall`,`changed`),
INDEX `uid_unseen_wall` (`uid`,`unseen`,`wall`),
INDEX `mention_uid_id` (`mention`,`uid`,`id`), INDEX `mention_uid_id` (`mention`,`uid`,`id`),
INDEX `uid_eventid` (`uid`,`event-id`), INDEX `uid_eventid` (`uid`,`event-id`),
INDEX `icid` (`icid`), INDEX `icid` (`icid`),
@ -771,6 +786,7 @@ CREATE TABLE IF NOT EXISTS `item-content` (
`verb` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams verb', `verb` varchar(100) NOT NULL DEFAULT '' COMMENT 'ActivityStreams verb',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `uri-plink-hash` (`uri-plink-hash`), UNIQUE INDEX `uri-plink-hash` (`uri-plink-hash`),
FULLTEXT INDEX `title-content-warning-body` (`title`,`content-warning`,`body`),
INDEX `uri` (`uri`(191)), INDEX `uri` (`uri`(191)),
INDEX `plink` (`plink`(191)), INDEX `plink` (`plink`(191)),
INDEX `uri-id` (`uri-id`), INDEX `uri-id` (`uri-id`),
@ -816,7 +832,8 @@ CREATE TABLE IF NOT EXISTS `mail` (
INDEX `convid` (`convid`), INDEX `convid` (`convid`),
INDEX `uri` (`uri`(64)), INDEX `uri` (`uri`(64)),
INDEX `parent-uri` (`parent-uri`(64)), INDEX `parent-uri` (`parent-uri`(64)),
INDEX `contactid` (`contact-id`(32)) INDEX `contactid` (`contact-id`(32)),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='private messages';
-- --
@ -836,7 +853,9 @@ CREATE TABLE IF NOT EXISTS `mailacct` (
`movetofolder` varchar(255) NOT NULL DEFAULT '' COMMENT '', `movetofolder` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`pubmail` boolean NOT NULL DEFAULT '0' COMMENT '', `pubmail` boolean NOT NULL DEFAULT '0' COMMENT '',
`last_check` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '', `last_check` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT '',
PRIMARY KEY(`id`) PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Mail account data for fetching mails'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Mail account data for fetching mails';
-- --
@ -847,7 +866,10 @@ CREATE TABLE IF NOT EXISTS `manage` (
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
`mid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `mid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `uid_mid` (`uid`,`mid`) UNIQUE INDEX `uid_mid` (`uid`,`mid`),
INDEX `mid` (`mid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`mid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='table of accounts that can manage each other';
-- --
@ -875,7 +897,8 @@ CREATE TABLE IF NOT EXISTS `notify` (
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `seen_uid_date` (`seen`,`uid`,`date`), INDEX `seen_uid_date` (`seen`,`uid`,`date`),
INDEX `uid_date` (`uid`,`date`), INDEX `uid_date` (`uid`,`date`),
INDEX `uid_type_link` (`uid`,`type`,`link`(190)) INDEX `uid_type_link` (`uid`,`type`,`link`(190)),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='notifications';
-- --
@ -889,7 +912,11 @@ CREATE TABLE IF NOT EXISTS `notify-threads` (
`parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '', `parent-item` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`receiver-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `receiver-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `master-parent-uri-id` (`master-parent-uri-id`) INDEX `master-parent-uri-id` (`master-parent-uri-id`),
INDEX `receiver-uid` (`receiver-uid`),
INDEX `notify-id` (`notify-id`),
FOREIGN KEY (`notify-id`) REFERENCES `notify` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`receiver-uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
-- --
@ -909,12 +936,13 @@ CREATE TABLE IF NOT EXISTS `oembed` (
-- --
CREATE TABLE IF NOT EXISTS `openwebauth-token` ( CREATE TABLE IF NOT EXISTS `openwebauth-token` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id - currently unused',
`type` varchar(32) NOT NULL DEFAULT '' COMMENT 'Verify type', `type` varchar(32) NOT NULL DEFAULT '' COMMENT 'Verify type',
`token` varchar(255) NOT NULL DEFAULT '' COMMENT 'A generated token', `token` varchar(255) NOT NULL DEFAULT '' COMMENT 'A generated token',
`meta` varchar(255) NOT NULL DEFAULT '' COMMENT '', `meta` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation', `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of creation',
PRIMARY KEY(`id`) PRIMARY KEY(`id`),
INDEX `uid` (`uid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Store OpenWebAuth token to verify contacts'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Store OpenWebAuth token to verify contacts';
-- --
@ -940,7 +968,10 @@ CREATE TABLE IF NOT EXISTS `participation` (
`fid` int unsigned NOT NULL COMMENT '', `fid` int unsigned NOT NULL COMMENT '',
PRIMARY KEY(`iid`,`server`), PRIMARY KEY(`iid`,`server`),
INDEX `cid` (`cid`), INDEX `cid` (`cid`),
INDEX `fid` (`fid`) INDEX `fid` (`fid`),
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`fid`) REFERENCES `fcontact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Storage for participation messages from Diaspora'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Storage for participation messages from Diaspora';
-- --
@ -953,7 +984,8 @@ CREATE TABLE IF NOT EXISTS `pconfig` (
`k` varbinary(100) NOT NULL DEFAULT '' COMMENT '', `k` varbinary(100) NOT NULL DEFAULT '' COMMENT '',
`v` mediumtext COMMENT '', `v` mediumtext COMMENT '',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `uid_cat_k` (`uid`,`cat`,`k`) UNIQUE INDEX `uid_cat_k` (`uid`,`cat`,`k`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='personal (per user) configuration storage'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='personal (per user) configuration storage';
-- --
@ -992,40 +1024,10 @@ CREATE TABLE IF NOT EXISTS `photo` (
INDEX `uid_profile` (`uid`,`profile`), INDEX `uid_profile` (`uid`,`profile`),
INDEX `uid_album_scale_created` (`uid`,`album`(32),`scale`,`created`), INDEX `uid_album_scale_created` (`uid`,`album`(32),`scale`,`created`),
INDEX `uid_album_resource-id_created` (`uid`,`album`(32),`resource-id`,`created`), INDEX `uid_album_resource-id_created` (`uid`,`album`(32),`resource-id`,`created`),
INDEX `resource-id` (`resource-id`) INDEX `resource-id` (`resource-id`),
FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='photo storage'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='photo storage';
--
-- TABLE poll
--
CREATE TABLE IF NOT EXISTS `poll` (
`id` int unsigned NOT NULL auto_increment COMMENT '',
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
`q0` text COMMENT '',
`q1` text COMMENT '',
`q2` text COMMENT '',
`q3` text COMMENT '',
`q4` text COMMENT '',
`q5` text COMMENT '',
`q6` text COMMENT '',
`q7` text COMMENT '',
`q8` text COMMENT '',
`q9` text COMMENT '',
PRIMARY KEY(`id`),
INDEX `uid` (`uid`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Currently unused table for storing poll results';
--
-- TABLE poll_result
--
CREATE TABLE IF NOT EXISTS `poll_result` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`poll_id` int unsigned NOT NULL DEFAULT 0,
`choice` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY(`id`),
INDEX `poll_id` (`poll_id`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='data for polls - currently unused';
-- --
-- TABLE post-category -- TABLE post-category
-- --
@ -1134,7 +1136,8 @@ CREATE TABLE IF NOT EXISTS `profile` (
`net-publish` boolean NOT NULL DEFAULT '0' COMMENT 'publish profile in global directory', `net-publish` boolean NOT NULL DEFAULT '0' COMMENT 'publish profile in global directory',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid_is-default` (`uid`,`is-default`), INDEX `uid_is-default` (`uid`,`is-default`),
FULLTEXT INDEX `pub_keywords` (`pub_keywords`) FULLTEXT INDEX `pub_keywords` (`pub_keywords`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='user profiles data'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='user profiles data';
-- --
@ -1147,7 +1150,11 @@ CREATE TABLE IF NOT EXISTS `profile_check` (
`dfrn_id` varchar(255) NOT NULL DEFAULT '' COMMENT '', `dfrn_id` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`sec` varchar(255) NOT NULL DEFAULT '' COMMENT '', `sec` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '', `expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY(`id`) 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='DFRN remote auth use'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='DFRN remote auth use';
-- --
@ -1166,6 +1173,7 @@ CREATE TABLE IF NOT EXISTS `profile_field` (
INDEX `uid` (`uid`), INDEX `uid` (`uid`),
INDEX `order` (`order`), INDEX `order` (`order`),
INDEX `psid` (`psid`), INDEX `psid` (`psid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT FOREIGN KEY (`psid`) REFERENCES `permissionset` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Custom profile fields'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Custom profile fields';
@ -1184,7 +1192,9 @@ CREATE TABLE IF NOT EXISTS `push_subscriber` (
`renewed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last subscription renewal', `renewed` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'Date of last subscription renewal',
`secret` varchar(255) NOT NULL DEFAULT '' COMMENT '', `secret` varchar(255) NOT NULL DEFAULT '' COMMENT '',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `next_try` (`next_try`) INDEX `next_try` (`next_try`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Used for OStatus: Contains feed subscribers'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Used for OStatus: Contains feed subscribers';
-- --
@ -1198,7 +1208,9 @@ CREATE TABLE IF NOT EXISTS `register` (
`password` varchar(255) NOT NULL DEFAULT '' COMMENT '', `password` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`language` varchar(16) NOT NULL DEFAULT '' COMMENT '', `language` varchar(16) NOT NULL DEFAULT '' COMMENT '',
`note` text COMMENT '', `note` text COMMENT '',
PRIMARY KEY(`id`) PRIMARY KEY(`id`),
INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registrations requiring admin approval'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='registrations requiring admin approval';
-- --
@ -1209,7 +1221,8 @@ CREATE TABLE IF NOT EXISTS `search` (
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
`term` varchar(255) NOT NULL DEFAULT '' COMMENT '', `term` varchar(255) NOT NULL DEFAULT '' COMMENT '',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `uid` (`uid`) INDEX `uid` (`uid`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
-- --
@ -1277,6 +1290,7 @@ CREATE TABLE IF NOT EXISTS `thread` (
INDEX `uid_wall_received` (`uid`,`wall`,`received`), INDEX `uid_wall_received` (`uid`,`wall`,`received`),
INDEX `private_wall_origin_commented` (`private`,`wall`,`origin`,`commented`), INDEX `private_wall_origin_commented` (`private`,`wall`,`origin`,`commented`),
INDEX `uri-id` (`uri-id`), INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`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='Thread related data'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Thread related data';
@ -1292,62 +1306,11 @@ CREATE TABLE IF NOT EXISTS `tokens` (
`uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id', `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'User id',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
INDEX `client_id` (`client_id`), INDEX `client_id` (`client_id`),
FOREIGN KEY (`client_id`) REFERENCES `clients` (`client_id`) ON UPDATE RESTRICT ON DELETE CASCADE INDEX `uid` (`uid`),
FOREIGN KEY (`client_id`) REFERENCES `clients` (`client_id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth usage';
--
-- TABLE user
--
CREATE TABLE IF NOT EXISTS `user` (
`uid` mediumint unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`parent-uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'The parent user that has full control about this user',
`guid` varchar(64) NOT NULL DEFAULT '' COMMENT 'A unique identifier for this user',
`username` varchar(255) NOT NULL DEFAULT '' COMMENT 'Name that this user is known by',
`password` varchar(255) NOT NULL DEFAULT '' COMMENT 'encrypted password',
`legacy_password` boolean NOT NULL DEFAULT '0' COMMENT 'Is the password hash double-hashed?',
`nickname` varchar(255) NOT NULL DEFAULT '' COMMENT 'nick- and user name',
`email` varchar(255) NOT NULL DEFAULT '' COMMENT 'the users email address',
`openid` varchar(255) NOT NULL DEFAULT '' COMMENT '',
`timezone` varchar(128) NOT NULL DEFAULT '' COMMENT 'PHP-legal timezone',
`language` varchar(32) NOT NULL DEFAULT 'en' COMMENT 'default language',
`register_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of registration',
`login_date` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last login',
`default-location` varchar(255) NOT NULL DEFAULT '' COMMENT 'Default for item.location',
`allow_location` boolean NOT NULL DEFAULT '0' COMMENT '1 allows to display the location',
`theme` varchar(255) NOT NULL DEFAULT '' COMMENT 'user theme preference',
`pubkey` text COMMENT 'RSA public key 4096 bit',
`prvkey` text COMMENT 'RSA private key 4096 bit',
`spubkey` text COMMENT '',
`sprvkey` text COMMENT '',
`verified` boolean NOT NULL DEFAULT '0' COMMENT 'user is verified through email',
`blocked` boolean NOT NULL DEFAULT '0' COMMENT '1 for user is blocked',
`blockwall` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to post to the profile page of the user',
`hidewall` boolean NOT NULL DEFAULT '0' COMMENT 'Hide profile details from unkown viewers',
`blocktags` boolean NOT NULL DEFAULT '0' COMMENT 'Prohibit contacts to tag the post of this user',
`unkmail` boolean NOT NULL DEFAULT '0' COMMENT 'Permit unknown people to send private mails to this user',
`cntunkmail` int unsigned NOT NULL DEFAULT 10 COMMENT '',
`notify-flags` smallint unsigned NOT NULL DEFAULT 65535 COMMENT 'email notification options',
`page-flags` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'page/profile type',
`account-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`prvnets` boolean NOT NULL DEFAULT '0' COMMENT '',
`pwdreset` varchar(255) COMMENT 'Password reset request token',
`pwdreset_time` datetime COMMENT 'Timestamp of the last password reset request',
`maxreq` int unsigned NOT NULL DEFAULT 10 COMMENT '',
`expire` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`account_removed` boolean NOT NULL DEFAULT '0' COMMENT 'if 1 the account is removed',
`account_expired` boolean NOT NULL DEFAULT '0' COMMENT '',
`account_expires_on` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp when account expires and will be deleted',
`expire_notification_sent` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'timestamp of last warning of account expiration',
`def_gid` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`allow_cid` mediumtext COMMENT 'default permission for this user',
`allow_gid` mediumtext COMMENT 'default permission for this user',
`deny_cid` mediumtext COMMENT 'default permission for this user',
`deny_gid` mediumtext COMMENT 'default permission for this user',
`openidserver` text COMMENT '',
PRIMARY KEY(`uid`),
INDEX `nickname` (`nickname`(32))
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='The local users';
-- --
-- TABLE userd -- TABLE userd
-- --
@ -1367,7 +1330,10 @@ CREATE TABLE IF NOT EXISTS `user-contact` (
`blocked` boolean COMMENT 'Contact is completely blocked for this user', `blocked` boolean COMMENT 'Contact is completely blocked for this user',
`ignored` boolean COMMENT 'Posts from this contact are ignored', `ignored` boolean COMMENT 'Posts from this contact are ignored',
`collapsed` boolean COMMENT 'Posts from this contact are collapsed', `collapsed` boolean COMMENT 'Posts from this contact are collapsed',
PRIMARY KEY(`uid`,`cid`) PRIMARY KEY(`uid`,`cid`),
INDEX `cid` (`cid`),
FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific public contact data';
-- --
@ -1382,7 +1348,9 @@ CREATE TABLE IF NOT EXISTS `user-item` (
`notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '', `notification-type` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
PRIMARY KEY(`uid`,`iid`), PRIMARY KEY(`uid`,`iid`),
INDEX `uid_pinned` (`uid`,`pinned`), INDEX `uid_pinned` (`uid`,`pinned`),
INDEX `iid_uid` (`iid`,`uid`) INDEX `iid_uid` (`iid`,`uid`),
FOREIGN KEY (`iid`) REFERENCES `item` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='User specific item data';
-- --
@ -1422,6 +1390,7 @@ CREATE TABLE IF NOT EXISTS `workerqueue` (
INDEX `done_priority_created` (`done`,`priority`,`created`), INDEX `done_priority_created` (`done`,`priority`,`created`),
INDEX `done_priority_next_try` (`done`,`priority`,`next_try`), INDEX `done_priority_next_try` (`done`,`priority`,`next_try`),
INDEX `done_pid_next_try` (`done`,`pid`,`next_try`), INDEX `done_pid_next_try` (`done`,`pid`,`next_try`),
INDEX `done_pid_retrial` (`done`,`pid`,`retrial`),
INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`) INDEX `done_pid_priority_created` (`done`,`pid`,`priority`,`created`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Background tasks queue entries';
@ -1460,6 +1429,65 @@ CREATE VIEW `tag-view` AS SELECT
LEFT JOIN `tag` ON `post-tag`.`tid` = `tag`.`id` LEFT JOIN `tag` ON `post-tag`.`tid` = `tag`.`id`
LEFT JOIN `contact` ON `post-tag`.`cid` = `contact`.`id`; LEFT JOIN `contact` ON `post-tag`.`cid` = `contact`.`id`;
--
-- VIEW network-item-view
--
DROP VIEW IF EXISTS `network-item-view`;
CREATE VIEW `network-item-view` AS SELECT
`item`.`parent-uri-id` AS `uri-id`,
`item`.`parent-uri` AS `uri`,
`item`.`parent` AS `parent`,
`item`.`received` AS `received`,
`item`.`commented` AS `commented`,
`item`.`created` AS `created`,
`item`.`uid` AS `uid`,
`item`.`starred` AS `starred`,
`item`.`mention` AS `mention`,
`item`.`network` AS `network`,
`item`.`unseen` AS `unseen`,
`item`.`gravity` AS `gravity`,
`item`.`contact-id` AS `contact-id`
FROM `item`
INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid`
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id`
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id`
WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated`
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
--
-- VIEW network-thread-view
--
DROP VIEW IF EXISTS `network-thread-view`;
CREATE VIEW `network-thread-view` AS SELECT
`item`.`uri-id` AS `uri-id`,
`item`.`uri` AS `uri`,
`item`.`parent-uri-id` AS `parent-uri-id`,
`thread`.`iid` AS `parent`,
`thread`.`received` AS `received`,
`thread`.`commented` AS `commented`,
`thread`.`created` AS `created`,
`thread`.`uid` AS `uid`,
`thread`.`starred` AS `starred`,
`thread`.`mention` AS `mention`,
`thread`.`network` AS `network`,
`thread`.`contact-id` AS `contact-id`
FROM `thread`
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid`
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = `thread`.`uid`
LEFT JOIN `user-contact` AS `author` ON `author`.`uid` = `thread`.`uid` AND `author`.`cid` = `thread`.`author-id`
LEFT JOIN `user-contact` AS `owner` ON `owner`.`uid` = `thread`.`uid` AND `owner`.`cid` = `thread`.`owner-id`
WHERE `thread`.`visible` AND NOT `thread`.`deleted` AND NOT `thread`.`moderated`
AND (NOT `contact`.`readonly` AND NOT `contact`.`blocked` AND NOT `contact`.`pending`)
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
AND (`author`.`blocked` IS NULL OR NOT `author`.`blocked`)
AND (`owner`.`blocked` IS NULL OR NOT `owner`.`blocked`);
-- --
-- VIEW owner-view -- VIEW owner-view
-- --
@ -1524,11 +1552,11 @@ CREATE VIEW `owner-view` AS SELECT
`contact`.`forum` AS `forum`, `contact`.`forum` AS `forum`,
`contact`.`prv` AS `prv`, `contact`.`prv` AS `prv`,
`contact`.`contact-type` AS `contact-type`, `contact`.`contact-type` AS `contact-type`,
`contact`.`manually-approve` AS `manually-approve`,
`contact`.`hidden` AS `hidden`, `contact`.`hidden` AS `hidden`,
`contact`.`archive` AS `archive`, `contact`.`archive` AS `archive`,
`contact`.`pending` AS `pending`, `contact`.`pending` AS `pending`,
`contact`.`deleted` AS `deleted`, `contact`.`deleted` AS `deleted`,
`contact`.`rating` AS `rating`,
`contact`.`unsearchable` AS `unsearchable`, `contact`.`unsearchable` AS `unsearchable`,
`contact`.`sensitive` AS `sensitive`, `contact`.`sensitive` AS `sensitive`,
`contact`.`baseurl` AS `baseurl`, `contact`.`baseurl` AS `baseurl`,

View File

@ -518,10 +518,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('item_photo_menu', $args); Hook::callAll('item_photo_menu', $args);
Hook::callAll('jot_tool', $jotplugins); Hook::callAll('jot_tool', $jotplugins);
### include/items.php
Hook::callAll('page_info_data', $data);
### mod/directory.php ### mod/directory.php
Hook::callAll('directory_item', $arr); Hook::callAll('directory_item', $arr);
@ -608,10 +604,6 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('post_local_end', $arr); Hook::callAll('post_local_end', $arr);
### mod/lockview.php
Hook::callAll('lockview_content', $item);
### mod/uexport.php ### mod/uexport.php
Hook::callAll('uexport_options', $options); Hook::callAll('uexport_options', $options);
@ -683,6 +675,10 @@ Here is a complete list of all hook callbacks with file locations (as of 24-Sep-
Hook::callAll('register_account', $uid); Hook::callAll('register_account', $uid);
Hook::callAll('remove_user', $user); Hook::callAll('remove_user', $user);
### src/Module/PermissionTooltip.php
Hook::callAll('lockview_content', $item);
### src/Content/ContactBlock.php ### src/Content/ContactBlock.php
Hook::callAll('contact_block_end', $arr); Hook::callAll('contact_block_end', $arr);

View File

@ -4,49 +4,46 @@ Forums
* [Home](help) * [Home](help)
Friendica also lets you create forums and/or celebrity accounts. Friendica also lets you create community forums and other types of accounts that can function as discussion forums, celebrity accounts, announcement channels, news reflectors, or organization pages, depending on how you want to interact with others. Management of these pages can be delegated to other accounts, or a parent account can be designated to easily toggle multiple identities.
Every page in Friendica has a nickname and these must all be unique. 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.
This applies to all forums, whether they are normal profiles or forum profiles.
Therefore the first thing you need to do to create a new forum is to register a new account for the forum. Managing Accounts
Please note that the site administrator can restrict and/or regulate the registration of new accounts.
If you create a second account on a system and use the same email address or OpenID account as an existing account, you will no longer be able to use the email address (or OpenID) to log in to the account.
You should log in using the account nickname instead.
On the new account, visit the 'Settings' page.
Towards the end of the page are "Advanced Account/Page Type Settings".
Typically you would use "Normal Account" for a normal personal account.
This is the default selection.
Community Forum/Celebrity Accounts provide the ability for people to become friends/fans of the forum without requiring approval.
The exact setting you would use depends on how you wish to interact with people who join the page.
The "Soapbox" setting lets the page owner control all communications.
Everything you post will go out to the forum members, but there will be no opportunity for interaction.
This setting would typically be used for announcements or corporate communications.
The most common setting is the "Community Forum".
This creates a forum page where all members can freely interact.
The "Automatic Friend Account" is typically used for personal profile forums where you wish to automatically approve any friendship/connection requests.
Managing Multiple forums
--- ---
We recommend that you create group forums with the same email address and password as your normal account. 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.
If you do this, you will find a new "Manage" tab on the menu bar which lets you toggle identities easily and manage your forums. Here you can register additional accounts with new nicknames that will be linked to your primary account.
You are not required to do this, but the alternative is to log out and log back into the other account to manage alternate forums.
This could get cumbersome if you manage several different forums/identities.
You may also appoint a delegate to manage your forum. You may appoint a delegate to manage your new account (e.g. forum page).
Do this by visiting the [Delegation Setup Page](settings/delegation). The Delegates section of Manage Accounts page will provide you with a list of contacts on this instance under "Potential Delegates".
This will provide you with a list of contacts on this system under "Potential Delegates".
Selecting one or more persons will give them access to manage your forum. 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. They will be able to edit contacts, profiles, and all content for this account/page.
Please use this facility wisely. Please use this facility wisely.
Delegated managers will not be able to alter basic account settings such as passwords or page types and/or remove the account. Delegated managers will not be able to alter basic account settings, such as passwords or page types, or remove the account.
Additionally, this page is also where you can choose to designate an account as a parent user.
If your primary account is designated as the parent user, you will be able to easily toggle identities and manage your forums or other types of accounts.
Types of Accounts
---
On the new account, visit the Settings > Account page.
Towards the end of the page is a section for "Advanced account types".
Typically you would use "Personal Page - Standard" for a normal personal account with manual approval of “friends” and “followers.”
This is the default selection.
On this page you can change the type of account if desired.
The other subtypes of a Personal Page are “Soapbox” and “Love-all.”
A Soapbox account is an announcement channel that automatically approvals follower requests.
Everything posted by the account will go out to the followers, but there will be no opportunity for interaction.
This setting would typically be used for announcements or corporate communications.
“Love-all” automatically approves contacts as friends.
In addition to Personal Page, there are options for Organization Page, News Page, and Community Forum.
Organization and New Pages automatically approve contact requests as followers.
Community Forum provide the ability for people to become friends/fans of the forum without requiring approval.
This creates a forum page where all members can freely interact.
Posting to Community forums Posting to Community forums
--- ---

View File

@ -33,7 +33,7 @@ The account will expire after 7 days, but you can ask the server admin to keep y
* Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file * Apache with mod-rewrite enabled and "Options All" so you can use a local `.htaccess` file
* PHP 7+ (PHP 7.1+ is recommended for performance and official support) * PHP 7+ (PHP 7.1+ is recommended for performance and official support)
* PHP *command line* access with register_argc_argv set to true in the php.ini file * PHP *command line* access with register_argc_argv set to true in the php.ini file
* Curl, GD, PDO, MySQLi, hash, xml, zip and OpenSSL extensions * Curl, GD, PDO, mbstrings, MySQLi, hash, xml, zip and OpenSSL extensions
* The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it) * The POSIX module of PHP needs to be activated (e.g. [RHEL, CentOS](http://www.bigsoft.co.uk/blog/index.php/2014/12/08/posix-php-commands-not-working-under-centos-7) have disabled it)
* some form of email server or email gateway such that PHP mail() works * some form of email server or email gateway such that PHP mail() works
* MySQL 5.6+ or an equivalent alternative for MySQL (MariaDB, Percona Server etc.) * MySQL 5.6+ or an equivalent alternative for MySQL (MariaDB, Percona Server etc.)
@ -47,7 +47,6 @@ For alternative server configurations (such as Nginx server and MariaDB database
### Optional ### Optional
* PHP ImageMagick extension (php-imagick) for animated GIF support. * PHP ImageMagick extension (php-imagick) for animated GIF support.
* [Composer](https://getcomposer.org/) for a git install
## Installation procedure ## Installation procedure
@ -61,6 +60,8 @@ If this is nothing for you, you might be interested in
### Get Friendica ### Get Friendica
Download the full archive of the stable release of Friendica core and the addons from [the project homepage](https://friendi.ca/resources/download-files/).
Make sure that the version of the Friendica archive and the addons match.
Unpack the Friendica files into the root of your web server document area. Unpack the Friendica files into the root of your web server document area.
If you copy the directory tree to your webserver, make sure that you also copy `.htaccess-dist` - as "dot" files are often hidden and aren't normally copied. If you copy the directory tree to your webserver, make sure that you also copy `.htaccess-dist` - as "dot" files are often hidden and aren't normally copied.

View File

@ -6,8 +6,6 @@ There are multiple paths, using multiple protocols and message formats.
Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub). Those attempting to understand these message flows should become familiar with (at the minimum) the [DFRN protocol document](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf) and the message passing elements of the OStatus stack (salmon and Pubsubhubbub).
Most message passing involves the file include/items.php, which has functions for several feed-related import/export activities.
When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message. When a message is posted, all immediate deliveries to all networks are made using include/notifier.php, which chooses how (and to whom) to deliver the message.
This file also invokes the local side of all deliveries including DFRN-notify. This file also invokes the local side of all deliveries including DFRN-notify.

View File

@ -18,9 +18,6 @@ Database Tables
| [event](help/database/db_event) | Events | | [event](help/database/db_event) | Events |
| [fcontact](help/database/db_fcontact) | friend suggestion stuff | | [fcontact](help/database/db_fcontact) | friend suggestion stuff |
| [fsuggest](help/database/db_fsuggest) | friend suggestion stuff | | [fsuggest](help/database/db_fsuggest) | friend suggestion stuff |
| [gcign](help/database/db_gcign) | contacts ignored by friend suggestions |
| [gcontact](help/database/db_gcontact) | global contacts |
| [glink](help/database/db_glink) | "friends of friends" linkages derived from poco |
| [group](help/database/db_group) | privacy groups, group info | | [group](help/database/db_group) | privacy groups, group info |
| [group_member](help/database/db_group_member) | privacy groups, member info | | [group_member](help/database/db_group_member) | privacy groups, member info |
| [gserver](help/database/db_gserver) | | | [gserver](help/database/db_gserver) | |

View File

@ -1,10 +0,0 @@
Table gcign
===========
| Field | Description | Type | Null | Key | Default | Extra |
| ----- | ------------------------------ | ------- | ---- | --- | ------- | --------------- |
| id | sequential ID | int(11) | NO | PRI | NULL | auto_increment |
| uid | local user.id | int(11) | NO | MUL | 0 | |
| gcid | gcontact.id of ignored contact | int(11) | NO | MUL | 0 | |
Return to [database documentation](help/database)

View File

@ -1,32 +0,0 @@
Table gcontact
==============
| Field |Description | Type | Null | Key | Default | Extra |
|--------------|------------------------------------|------------------|------|-----|---------------------|----------------|
| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | Name that this contact is known by | varchar(255) | NO | | | |
| nick | Nick- and user name of the contact | varchar(255) | NO | | | |
| url | Link to the contacts profile page | varchar(255) | NO | | | |
| nurl | | varchar(255) | NO | MUL | | |
| photo | Link to the profile photo | varchar(255) | NO | | | |
| connect | | varchar(255) | NO | | | |
| created | | datetime | NO | | 0001-01-01 00:00:00 | |
| updated | | datetime | YES | MUL | 0001-01-01 00:00:00 | |
| last_contact | | datetime | YES | | 0001-01-01 00:00:00 | |
| last_failure | | datetime | YES | | 0001-01-01 00:00:00 | |
| location | | varchar(255) | NO | | | |
| about | | text | NO | | NULL | |
| keywords | puplic keywords (interests) | text | NO | | NULL | |
| gender | | varchar(32) | NO | | | |
| birthday | | varchar(32) | NO | | 0001-01-01 | |
| community | 1 if contact is forum account | tinyint(1) | NO | | 0 | |
| hide | 1 = should be hidden from search | tinyint(1) | NO | | 0 | |
| nsfw | 1 = contact posts nsfw content | tinyint(1) | NO | | 0 | |
| network | social network protocol | varchar(255) | NO | | | |
| addr | | varchar(255) | NO | | | |
| notify | | text | NO | | | |
| alias | | varchar(255) | NO | | | |
| generation | | tinyint(3) | NO | | 0 | |
| server_url | baseurl of the contacts server | varchar(255) | NO | | | |
Return to [database documentation](help/database)

View File

@ -1,13 +0,0 @@
Table glink
===========
| Field | Description | Type | Null | Key | Default | Extra |
|---------|------------------|------------------|------|-----|---------------------|----------------|
| id | sequential ID | int(10) unsigned | NO | PRI | NULL | auto_increment |
| cid | | int(11) | NO | MUL | 0 | |
| uid | | int(11) | NO | | 0 | |
| gcid | | int(11) | NO | MUL | 0 | |
| zcid | | int(11) | NO | MUL | 0 | |
| updated | | datetime | NO | | 0001-01-01 00:00:00 | |
Return to [database documentation](help/database)

View File

@ -226,10 +226,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('item_photo_menu', $args); Hook::callAll('item_photo_menu', $args);
Hook::callAll('jot_tool', $jotplugins); Hook::callAll('jot_tool', $jotplugins);
### include/items.php
Hook::callAll('page_info_data', $data);
### mod/directory.php ### mod/directory.php
Hook::callAll('directory_item', $arr); Hook::callAll('directory_item', $arr);
@ -316,10 +312,6 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('post_local_end', $arr); Hook::callAll('post_local_end', $arr);
### mod/lockview.php
Hook::callAll('lockview_content', $item);
### mod/uexport.php ### mod/uexport.php
Hook::callAll('uexport_options', $options); Hook::callAll('uexport_options', $options);
@ -426,6 +418,10 @@ Eine komplette Liste aller Hook-Callbacks mit den zugehörigen Dateien (am 01-Ap
Hook::callAll('storage_instance', $data); Hook::callAll('storage_instance', $data);
### src/Module/PermissionTooltip.php
Hook::callAll('lockview_content', $item);
### src/Worker/Directory.php ### src/Worker/Directory.php
Hook::callAll('globaldir_update', $arr); Hook::callAll('globaldir_update', $arr);

View File

@ -8,8 +8,6 @@ Es gibt verschiedene Pfade, die verschiedene Protokolle und Nachrichtenformate n
Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub). Diejenigen, die den Nachrichtenfluss genauer verstehen wollen, sollten sich mindestens mit dem DFRN-Protokoll ([Dokument mit den DFRN Spezifikationen](https://github.com/friendica/friendica/blob/stable/spec/dfrn2.pdf)) und den Elementen zur Nachrichtenverarbeitung des OStatus Stack informieren (salmon und Pubsubhubbub).
Der Großteil der Nachrichtenverarbeitung nutzt die Datei include/items.php, welche Funktionen für verschiedene Feed-bezogene Import-/Exportaktivitäten liefert.
Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird. Wenn eine Nachricht veröffentlicht wird, werden alle Übermittlungen an alle Netzwerke mit include/notifier.php durchgeführt, welche entscheidet, wie und an wen die Nachricht geliefert wird.
Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify. Diese Datei bindet dabei die lokale Bearbeitung aller Übertragungen ein inkl. dfrn-notify.

View File

@ -27,6 +27,7 @@ The console provides the following commands:
* typo: Checks for parse errors in Friendica files * typo: Checks for parse errors in Friendica files
* postupdate: Execute pending post update scripts (can last days) * postupdate: Execute pending post update scripts (can last days)
* storage: Manage storage backend * storage: Manage storage backend
* relay: Manage ActivityPub relay servers
Please consult *bin/console help* on the command line interface of your server for details about the commands. Please consult *bin/console help* on the command line interface of your server for details about the commands.

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="110" height="20" viewBox="0 0 110 20" id="humane-tech-badge" version="1.1" inkscape:version="0.91 r13725" sodipodi:docname="humane-tech-badge.svg">
<defs id="defs4" />
<sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="4" inkscape:cx="133.140625" inkscape:cy="8.8445333895091" inkscape:document-units="px" inkscape:current-layer="layer1" showgrid="false" inkscape:window-width="1983" inkscape:window-height="1128" inkscape:window-x="65" inkscape:window-y="24" inkscape:window-maximized="1" />
<metadata id="metadata7">
<rdf:RDF>
<cc:Work rdf:about="https://humanetech.com">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<cc:license rdf:resource="http://creativecommons.org/licenses/by-nc-nd/4.0/" />
<dc:subject>Humane Technology Badge</dc:subject>
<dc:description>This badge when displayed on a code repository indicates the software is considered to be 'humane', as defined by The Center for Humane Technology (https://humanetech.com).</dc:description>
<dc:creator rdf:href="https://github.com/aschrijver">Arnold Schrijver</dc:creator>
</cc:Work>
<cc:License rdf:about="http://creativecommons.org/licenses/by-nc-nd/4.0/">
<cc:permits rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:prohibits rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1">
<rect style="fill:none;fill-opacity:1;stroke:#f33;stroke-width:10.3667802811;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect4139" width="98.63321685791" height="10.133219718933" x="6.1833915710449" y="5.1833901405334" />
<rect style="fill:#fcc;fill-opacity:1;stroke:#fcc;stroke-width:8.7014474869;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect4139-1" width="20.125642776489" height="11.875650405884" x="4.4371752738953" y="4.3476815223694" />
<g style="font-style:normal;font-weight:normal;font-size:26px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#f33;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="text4171" transform="matrix(0.81571369806664,0,0,0.84575215906809,2.5866412998766,2.2115384615385)">
<path d="M 15.392578125,17.5791015625 Q 13.6533203125,14.544921875 8.359375,11.47265625 5.896484375,10.0380859375 5.896484375,7.7783203125 5.896484375,6.826171875 6.2392578125,5.9501953125 6.5947265625,5.07421875 7.29296875,4.388671875 7.9658203125,3.728515625 8.841796875,3.34765625 9.6669921875,2.9921875 10.64453125,2.9921875 11.6220703125,2.9921875 12.447265625,3.34765625 13.310546875,3.7158203125 13.99609375,4.388671875 14.6689453125,5.048828125 15.0244140625,5.9248046875 15.392578125,6.7880859375 15.392578125,7.7783203125 15.392578125,6.7880859375 15.748046875,5.9248046875 16.1162109375,5.048828125 16.7890625,4.388671875 17.474609375,3.7158203125 18.337890625,3.34765625 19.1630859375,2.9921875 20.140625,2.9921875 21.1181640625,2.9921875 21.943359375,3.34765625 22.8193359375,3.728515625 23.4921875,4.388671875 24.1904296875,5.07421875 24.533203125,5.9501953125 24.888671875,6.826171875 24.888671875,7.7783203125 24.888671875,10.0634765625 22.42578125,11.47265625 17.1318359375,14.544921875 15.392578125,17.5791015625 Z" id="path4266" inkscape:connector-curvature="0" />
</g>
<rect style="fill:#fcc;fill-opacity:1;stroke:none;stroke-width:6.0879998207;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" id="rect4145" width="6.25" height="20.5" x="23.75" y="0" />
<g transform="scale(0.98208273317227,1.0182441521702)" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:black;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="text4147">
<path d="M 35.842449240386,9.4841952230781 Q 36.180519717745,8.5922646019608 36.684028939344,8.189457224682 37.194731149822,7.7866498474032 37.89964406006,7.7866498474032 38.475083170459,7.7866498474032 38.870697558857,8.0168254915625 39.273504936136,8.2470011357218 39.525259546936,8.6498085130006 39.777014157735,9.0526158902794 39.884908990934,9.5992830451578 39.999996813014,10.145950200036 39.999996813014,10.778933221474 L 39.999996813014,15.71332359314 38.345609370619,15.71332359314 38.345609370619,11.354372331873 Q 38.345609370619,10.901214032434 38.288065459579,10.527178610675 38.230521548539,10.145950200036 38.09385475982,9.879809611477 37.9571879711,9.6064760340378 37.734205315821,9.4554232675582 37.518415649422,9.3043705010787 37.209117127582,9.3043705010787 36.907011594623,9.3043705010787 36.662449972704,9.4698092453182 36.417888350785,9.6280550006777 36.238063628785,9.9301605336368 36.065431895666,10.225073077716 35.964730051346,10.642266432755 35.871221195906,11.052266798913 35.871221195906,11.548583031632 L 35.871221195906,15.71332359314 34.209640764631,15.71332359314 34.209640764631,5.0389280952513 35.871221195906,5.0389280952513 35.871221195906,7.9520885916427 Q 35.871221195906,8.182264235802 35.864028207026,8.4196328688413 35.856835218146,8.6498085130006 35.849642229266,8.85840519052 35.842449240386,9.0598088791594 35.835256251507,9.2252476233989 35.828063262627,9.3906863676384 35.820870273747,9.4841952230781 L 35.842449240386,9.4841952230781 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4224" />
<path d="M 43.172104830854,7.9305096250027 43.172104830854,12.29665387515 Q 43.172104830854,12.749812174588 43.229648741893,13.131040585227 43.287192652933,13.505076006986 43.423859441653,13.778409584425 43.560526230372,14.044550172985 43.776315896772,14.195602939464 43.992105563171,14.346655705944 44.30140408501,14.346655705944 44.603509617969,14.346655705944 44.848071239889,14.188409950584 45.092632861808,14.022971206345 45.265264594927,13.728058662266 45.445089316927,13.425953129306 45.538598172367,13.015952763148 45.639300016686,12.598759408109 45.639300016686,12.10244317539 L 45.639300016686,7.9305096250027 47.300880447961,7.9305096250027 47.300880447961,13.972620284185 Q 47.300880447961,14.217181906104 47.300880447961,14.490515483543 47.308073436841,14.756656072102 47.315266425721,15.001217694022 47.322459414601,15.238586327061 47.329652403481,15.43279702682 47.336845392361,15.6198147377 47.344038381241,15.71332359314 L 45.761580827646,15.71332359314 Q 45.754387838766,15.62700772658 45.740001861006,15.4399900157 45.732808872126,15.245779315941 45.718422894366,15.015603671782 45.711229905486,14.785428027622 45.704036916606,14.555252383463 45.696843927726,14.325076739304 45.696843927726,14.166830983944 L 45.668071972206,14.166830983944 Q 45.337194483727,15.051568616182 44.826492273249,15.45437599346 44.31579006277,15.857183370739 43.610877152532,15.857183370739 43.035438042134,15.857183370739 42.632630664855,15.62700772658 42.237016276456,15.396832082421 41.985261665657,15.001217694022 41.733507054858,14.598410316743 41.618419232778,14.051743161865 41.510524399579,13.505076006986 41.510524399579,12.872092985548 L 41.510524399579,7.9305096250027 43.172104830854,7.9305096250027 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4226" />
<path d="M 52.760358929634,15.71332359314 52.760358929634,11.347179342993 Q 52.760358929634,10.894021043554 52.710008007474,10.519985621795 52.659657085314,10.138757211156 52.544569263235,9.872616622597 52.429481441155,9.5992830451578 52.242463730276,9.4482302786782 52.055446019396,9.2971775121987 51.789305430837,9.2971775121987 51.537550820038,9.2971775121987 51.328954142518,9.4626162564382 51.120357464999,9.6208620117977 50.969304698519,9.9229675447568 50.82544492092,10.217880088836 50.73912905436,10.635073443875 50.66000617668,11.045073810033 50.66000617668,11.541390042752 L 50.66000617668,15.71332359314 48.998425745405,15.71332359314 48.998425745405,9.6712129339576 Q 48.998425745405,9.4194583231583 48.991232756525,9.1533177345991 48.991232756525,8.8871771460399 48.984039767645,8.6498085130006 48.976846778765,8.4052468910813 48.969653789885,8.2182291802019 48.962460801005,8.0240184804425 48.955267812125,7.9305096250027 L 50.530532376841,7.9305096250027 Q 50.544918354601,8.0168254915625 50.552111343481,8.2110361913219 50.566497321241,8.3980539022014 50.580883299001,8.6282295463607 50.595269276761,8.85840519052 50.60246226564,9.0885808346793 50.60965525452,9.3187564788386 50.60965525452,9.4770022341982 L 50.63123422116,9.4770022341982 Q 50.940532742999,8.5850716130808 51.393691042438,8.182264235802 51.854042330757,7.7794568585232 52.494218341075,7.7794568585232 53.227903206833,7.7794568585232 53.666675528511,8.2182291802019 54.11264083907,8.6498085130006 54.26369360555,9.4770022341982 L 54.29965854995,9.4770022341982 Q 54.472290283069,8.9950719792396 54.666500982828,8.6713874796405 54.867904671468,8.3477029800415 55.105273304507,8.1534922802821 55.342641937546,7.9520885916427 55.623168503866,7.8657727250829 55.903695070185,7.7794568585232 56.234572558664,7.7794568585232 56.759660746902,7.7794568585232 57.119310190901,8.0096325026825 57.48615262378,8.2398081468418 57.709135279059,8.6426155241206 57.939310923219,9.0454229013994 58.040012767538,9.5920900562778 58.147907600738,10.138757211156 58.147907600738,10.771740232594 L 58.147907600738,15.71332359314 56.500713147223,15.71332359314 56.500713147223,11.347179342993 Q 56.500713147223,10.894021043554 56.450362225063,10.519985621795 56.400011302903,10.138757211156 56.284923480824,9.872616622597 56.169835658744,9.5992830451578 55.982817947865,9.4482302786782 55.802993225865,9.2971775121987 55.536852637306,9.2971775121987 55.292291015387,9.2971775121987 55.083694337867,9.4554232675582 54.882290649228,9.6064760340378 54.731237882748,9.8941955892369 54.580185116269,10.174722155556 54.493869249709,10.570336543955 54.414746372029,10.965950932354 54.400360394269,11.447881187312 L 54.400360394269,15.71332359314 52.760358929634,15.71332359314 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4228" />
<path d="M 61.204928735271,15.857183370739 Q 60.744577446952,15.857183370739 60.377735014074,15.70613060426 60.018085570075,15.5478848489 59.766330959275,15.252972304821 59.514576348476,14.950866771862 59.377909559757,14.512094450183 59.241242771037,14.073322128505 59.241242771037,13.512268995866 59.241242771037,12.821742063388 59.435453470796,12.35419778619 59.636857159436,11.879460520111 59.974927636795,11.591740964912 60.320191103034,11.296828420833 60.780542391352,11.167354620993 61.240893679671,11.030687832274 61.758788879029,11.023494843394 L 63.132649755105,10.994722887874 63.132649755105,10.599108499475 Q 63.132649755105,10.174722155556 63.067912855186,9.8941955892369 63.010368944146,9.6064760340378 62.888088133186,9.4266513120383 62.773000311106,9.2468265900388 62.600368577987,9.174896701239 62.427736844867,9.0957738235593 62.204754189588,9.0957738235593 61.996157512069,9.0957738235593 61.830718767829,9.1461247457191 61.67247301247,9.196475667879 61.55019220151,9.3259494677186 61.43510437943,9.4482302786782 61.363174490631,9.6640199450776 61.291244601831,9.872616622597 61.262472646311,10.196301122196 L 59.528962326236,10.095599277876 Q 59.600892215036,9.5848970673978 59.787909925915,9.1677037123591 59.982120625675,8.7433173684403 60.312998114154,8.4340188466012 60.643875602633,8.1247203247622 61.132998846471,7.9592815805227 61.62212209031,7.7866498474032 62.276684078388,7.7866498474032 62.873702155426,7.7866498474032 63.334053443745,7.9664745694026 63.801597720943,8.1462992914021 64.125282220542,8.505948735401 64.448966720141,8.85840519052 64.621598453261,9.3763003898785 64.79423018638,9.8941955892369 64.79423018638,10.577529532835 L 64.79423018638,13.411567151546 Q 64.79423018638,13.684900728986 64.81580915302,13.900690395385 64.83738811966,14.116480061784 64.88773904182,14.267532828264 64.94528295286,14.411392605864 65.04598479718,14.490515483543 65.153879630379,14.562445372343 65.319318374619,14.562445372343 65.506336085498,14.562445372343 65.686160807498,14.519287439063 L 65.686160807498,15.61262174882 Q 65.686160807498,15.61262174882 65.420020218939,15.6917446265 65.297739407979,15.7277095709 65.182651585899,15.74928853754 65.06756376382,15.770867504179 64.9308969751,15.785253481939 64.80142317526,15.799639459699 64.621598453261,15.799639459699 63.995808420703,15.799639459699 63.693702887744,15.425604037941 63.398790343665,15.051568616182 63.341246432625,14.325076739304 L 63.305281488225,14.325076739304 Q 62.974403999746,15.051568616182 62.456508800387,15.45437599346 61.938613601029,15.857183370739 61.204928735271,15.857183370739 Z M 63.132649755105,12.10963616427 62.283877067268,12.12402214203 Q 62.017736478709,12.13840811979 61.780367845669,12.18156605307 61.54299921263,12.21753099747 61.363174490631,12.34700479731 61.183349768631,12.469285608269 61.075454935431,12.706654241309 60.967560102232,12.944022874348 60.967560102232,13.354023240507 60.967560102232,13.907883384265 61.176156779751,14.181216961704 61.391946446151,14.447357550263 61.73720991239,14.447357550263 62.053701423109,14.447357550263 62.312649022788,14.281918806024 62.578789611347,14.116480061784 62.758614333346,13.850339473225 62.938439055346,13.577005895786 63.031947910786,13.224549440667 63.132649755105,12.872092985548 63.132649755105,12.505250552669 L 63.132649755105,12.10963616427 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4230" />
<path d="M 70.59897011891,15.71332359314 70.59897011891,11.347179342993 Q 70.59897011891,10.894021043554 70.54142620787,10.519985621795 70.48388229683,10.138757211156 70.347215508111,9.872616622597 70.210548719391,9.5992830451578 69.987566064112,9.4482302786782 69.771776397713,9.2971775121987 69.462477875873,9.2971775121987 69.160372342914,9.2971775121987 68.915810720995,9.4626162564382 68.671249099076,9.6208620117977 68.491424377076,9.9229675447568 68.318792643957,10.217880088836 68.218090799637,10.635073443875 68.124581944197,11.045073810033 68.124581944197,11.541390042752 L 68.124581944197,15.71332359314 66.463001512922,15.71332359314 66.463001512922,9.6712129339576 Q 66.463001512922,9.4194583231583 66.455808524042,9.1533177345991 66.455808524042,8.8871771460399 66.448615535162,8.6498085130006 66.441422546282,8.4052468910813 66.434229557402,8.2182291802019 66.427036568522,8.0240184804425 66.419843579642,7.9305096250027 L 68.002301133238,7.9305096250027 Q 68.016687110998,8.0168254915625 68.023880099878,8.2110361913219 68.031073088758,8.3980539022014 68.045459066518,8.6282295463607 68.059845044278,8.85840519052 68.067038033158,9.0885808346793 68.074231022038,9.3187564788386 68.074231022038,9.4770022341982 L 68.095809988677,9.4770022341982 Q 68.433880466036,8.5850716130808 68.937389687635,8.182264235802 69.448091898113,7.7794568585232 70.153004808351,7.7794568585232 70.72844391875,7.7794568585232 71.124058307149,8.0096325026825 71.526865684427,8.2398081468418 71.778620295227,8.6426155241206 72.030374906026,9.0454229013994 72.138269739226,9.5920900562778 72.253357561305,10.138757211156 72.253357561305,10.771740232594 L 72.253357561305,15.71332359314 70.59897011891,15.71332359314 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4232" />
<path d="M 76.475643386133,15.857183370739 Q 75.785116453655,15.857183370739 75.231256309897,15.61262174882 74.677396166138,15.360867138021 74.28897476662,14.857357916422 73.900553367101,14.346655705944 73.691956689581,13.584198884666 73.490553000942,12.814549074508 73.490553000942,11.785951664671 73.490553000942,10.671038388275 73.735114622861,9.9085815669969 73.97967624478,9.1461247457191 74.389676610939,8.6785804685205 74.806869965978,8.2038432024419 75.346344131976,7.9952465249225 75.893011286855,7.7866498474032 76.497222352773,7.7866498474032 77.252486185171,7.7866498474032 77.784767362289,8.1103343470022 78.317048539408,8.4268258577213 78.655119016767,9.0022649681196 79.000382483006,9.5777040785179 79.158628238365,10.383318833075 79.324066982605,11.181740598753 79.324066982605,12.15279409755 L 79.324066982605,12.21033800859 75.231256309897,12.21033800859 Q 75.231256309897,12.699461252429 75.295993209817,13.123847596347 75.367923098616,13.541040951386 75.526168853976,13.850339473225 75.684414609335,14.152445006184 75.936169220135,14.332269728184 76.195116819814,14.504901461303 76.554766263813,14.504901461303 76.993538585491,14.504901461303 77.266872162931,14.281918806024 77.54739872925,14.051743161865 77.662486551329,13.577005895786 L 79.223365138285,13.742444640025 Q 79.115470305085,14.073322128505 78.921259605326,14.447357550263 78.734241894446,14.821392972022 78.417750383727,15.137884482741 78.101258873008,15.44718300458 77.62652160693,15.6557796821 77.151784340851,15.857183370739 76.475643386133,15.857183370739 Z M 76.475643386133,9.0598088791594 Q 76.223888775334,9.0598088791594 76.000906120054,9.1677037123591 75.777923464775,9.2684055566788 75.612484720536,9.4985812008381 75.454238965176,9.7215638561174 75.353537120856,10.081213300116 75.252835276537,10.440862744115 75.238449298777,10.944371965714 L 77.720030462369,10.944371965714 Q 77.669679540209,10.002090422437 77.34599504061,9.534546145238 77.022310541011,9.0598088791594 76.475643386133,9.0598088791594 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4234" />
<path d="M 85.589160971344,15.842797392979 Q 84.855476105586,15.842797392979 84.459861717187,15.360867138021 84.071440317668,14.871743894182 84.071440317668,13.886304417625 L 84.071440317668,9.2971775121987 83.25863257423,9.2971775121987 83.25863257423,7.9305096250027 84.150563195348,7.9305096250027 84.668458394706,6.1034904494882 85.711441782303,6.1034904494882 85.711441782303,7.9305096250027 86.91986391414,7.9305096250027 86.91986391414,9.2971775121987 85.711441782303,9.2971775121987 85.711441782303,13.339637262747 Q 85.711441782303,13.907883384265 85.884073515423,14.181216961704 86.063898237422,14.447357550263 86.430740670301,14.447357550263 86.581793436781,14.447357550263 86.70407424774,14.418585594743 86.83354804758,14.389813639224 86.991793802939,14.346655705944 L 86.991793802939,15.59823577106 Q 86.6824952811,15.72051658202 86.337231814861,15.778060493059 85.999161337502,15.842797392979 85.589160971344,15.842797392979 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4236" />
<path d="M 90.602672805078,15.857183370739 Q 89.9121458726,15.857183370739 89.358285728842,15.61262174882 88.804425585084,15.360867138021 88.416004185565,14.857357916422 88.027582786046,14.346655705944 87.818986108527,13.584198884666 87.617582419887,12.814549074508 87.617582419887,11.785951664671 87.617582419887,10.671038388275 87.862144041806,9.9085815669969 88.106705663726,9.1461247457191 88.516706029885,8.6785804685205 88.933899384923,8.2038432024419 89.473373550922,7.9952465249225 90.0200407058,7.7866498474032 90.624251771718,7.7866498474032 91.379515604116,7.7866498474032 91.911796781234,8.1103343470022 92.444077958353,8.4268258577213 92.782148435712,9.0022649681196 93.127411901951,9.5777040785179 93.28565765731,10.383318833075 93.45109640155,11.181740598753 93.45109640155,12.15279409755 L 93.45109640155,12.21033800859 89.358285728842,12.21033800859 Q 89.358285728842,12.699461252429 89.423022628762,13.123847596347 89.494952517562,13.541040951386 89.653198272921,13.850339473225 89.811444028281,14.152445006184 90.06319863908,14.332269728184 90.322146238759,14.504901461303 90.681795682758,14.504901461303 91.120568004437,14.504901461303 91.393901581876,14.281918806024 91.674428148195,14.051743161865 91.789515970275,13.577005895786 L 93.35039455723,13.742444640025 Q 93.24249972403,14.073322128505 93.048289024271,14.447357550263 92.861271313392,14.821392972022 92.544779802673,15.137884482741 92.228288291954,15.44718300458 91.753551025875,15.6557796821 91.278813759796,15.857183370739 90.602672805078,15.857183370739 Z M 90.602672805078,9.0598088791594 Q 90.350918194279,9.0598088791594 90.127935539,9.1677037123591 89.90495288372,9.2684055566788 89.739514139481,9.4985812008381 89.581268384121,9.7215638561174 89.480566539802,10.081213300116 89.379864695482,10.440862744115 89.365478717722,10.944371965714 L 91.847059881315,10.944371965714 Q 91.796708959155,10.002090422437 91.473024459556,9.534546145238 91.149339959957,9.0598088791594 90.602672805078,9.0598088791594 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4238" />
<path d="M 97.378470051102,15.857183370739 Q 96.616013229825,15.857183370739 96.047767108306,15.56946381554 95.479520986788,15.281744260341 95.098292576149,14.756656072102 94.72425715439,14.231567883864 94.537239443511,13.497883018106 94.350221732631,12.757005163468 94.350221732631,11.865074542351 94.350221732631,10.894021043554 94.551625421271,10.138757211156 94.76022209879,9.3763003898785 95.148643498309,8.85840519052 95.537064897828,8.3333170022815 96.105311019346,8.0599834248424 96.680750129744,7.7866498474032 97.400049017742,7.7866498474032 98.01864606142,7.7866498474032 98.500576316379,7.9880535360426 98.982506571338,8.189457224682 99.327770037577,8.5419136798009 99.680226492696,8.8871771460399 99.896016159095,9.3619144121185 100.11180582549,9.8366516781971 100.19092870317,10.383318833075 L 98.522155283019,10.484020677395 Q 98.450225394219,9.8870026003569 98.1696988279,9.534546145238 97.889172261581,9.174896701239 97.364084073342,9.174896701239 96.695136107504,9.174896701239 96.393030574545,9.8438446670771 96.090925041586,10.512792632915 96.090925041586,11.785951664671 96.090925041586,14.476129505783 97.392856028862,14.476129505783 97.867593294941,14.476129505783 98.18408480566,14.116480061784 98.500576316379,13.749637628905 98.579699194059,13.030338740908 L 100.24127962533,13.123847596347 Q 100.18373571429,13.663321762346 99.967946047895,14.159637995064 99.759349370375,14.648761238903 99.399699926376,15.029989649542 99.047243471257,15.404025071301 98.536541260779,15.63420071546 98.03303203918,15.857183370739 97.378470051102,15.857183370739 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4240" />
<path d="M 103.08250813931,9.4841952230781 Q 103.42057861667,8.5922646019608 103.92408783827,8.189457224682 104.43479004875,7.7866498474032 105.13970295899,7.7866498474032 105.71514206938,7.7866498474032 106.11075645778,8.0168254915625 106.51356383506,8.2470011357218 106.76531844586,8.6498085130006 107.01707305666,9.0526158902794 107.12496788986,9.5992830451578 107.24005571194,10.145950200036 107.24005571194,10.778933221474 L 107.24005571194,15.71332359314 105.58566826954,15.71332359314 105.58566826954,11.354372331873 Q 105.58566826954,10.901214032434 105.52812435851,10.527178610675 105.47058044747,10.145950200036 105.33391365875,9.879809611477 105.19724687003,9.6064760340378 104.97426421475,9.4554232675582 104.75847454835,9.3043705010787 104.44917602651,9.3043705010787 104.14707049355,9.3043705010787 103.90250887163,9.4698092453182 103.65794724971,9.6280550006777 103.47812252771,9.9301605336368 103.30549079459,10.225073077716 103.20478895027,10.642266432755 103.11128009483,11.052266798913 103.11128009483,11.548583031632 L 103.11128009483,15.71332359314 101.44969966356,15.71332359314 101.44969966356,5.0389280952513 103.11128009483,5.0389280952513 103.11128009483,7.9520885916427 Q 103.11128009483,8.182264235802 103.10408710595,8.4196328688413 103.09689411707,8.6498085130006 103.08970112819,8.85840519052 103.08250813931,9.0598088791594 103.07531515043,9.2252476233989 103.06812216155,9.3906863676384 103.06092917267,9.4841952230781 L 103.08250813931,9.4841952230781 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start" id="path4242" />
</g>
<g transform="scale(0.98208273317227,1.0182441521702)" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" id="text4147-1">
<path d="M 35.559284262359,8.8569664862007 Q 35.897354739718,7.9650358650833 36.400863961317,7.5622284878045 36.911566171795,7.1594211105257 37.616479082033,7.1594211105257 38.191918192431,7.1594211105257 38.58753258083,7.389596754685 38.990339958109,7.6197723988444 39.242094568908,8.0225797761232 39.493849179707,8.425387153402 39.601744012907,8.9720543082803 39.716831834987,9.5187214631587 39.716831834987,10.151704484597 L 39.716831834987,15.086094856262 38.062444392592,15.086094856262 38.062444392592,10.727143594995 Q 38.062444392592,10.273985295556 38.004900481552,9.8999498737976 37.947356570512,9.5187214631587 37.810689781792,9.2525808745995 37.674022993073,8.9792472971603 37.451040337794,8.8281945306808 37.235250671394,8.6771417642012 36.925952149555,8.6771417642012 36.623846616596,8.6771417642012 36.379284994677,8.8425805084407 36.134723372757,9.0008262638003 35.954898650758,9.3029317967594 35.782266917638,9.5978443408385 35.681565073319,10.015037695877 35.588056217879,10.425038062036 35.588056217879,10.921354294755 L 35.588056217879,15.086094856262 33.926475786604,15.086094856262 33.926475786604,4.4116993583739 35.588056217879,4.4116993583739 35.588056217879,7.3248598547652 Q 35.588056217879,7.5550354989246 35.580863228999,7.7924041319638 35.573670240119,8.0225797761232 35.566477251239,8.2311764536425 35.559284262359,8.4325801422819 35.552091273479,8.5980188865215 35.544898284599,8.763457630761 35.537705295719,8.8569664862007 L 35.559284262359,8.8569664862007 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4245" />
<path d="M 42.888939852826,7.3032808881253 42.888939852826,11.669425138272 Q 42.888939852826,12.122583437711 42.946483763866,12.50381184835 43.004027674906,12.877847270109 43.140694463626,13.151180847548 43.277361252345,13.417321436107 43.493150918745,13.568374202587 43.708940585144,13.719426969066 44.018239106983,13.719426969066 44.320344639942,13.719426969066 44.564906261861,13.561181213707 44.809467883781,13.395742469467 44.9820996169,13.100829925388 45.1619243389,12.798724392429 45.255433194339,12.38872402627 45.356135038659,11.971530671231 45.356135038659,11.475214438513 L 45.356135038659,7.3032808881253 47.017715469934,7.3032808881253 47.017715469934,13.345391547307 Q 47.017715469934,13.589953169227 47.017715469934,13.863286746666 47.024908458814,14.129427335225 47.032101447694,14.373988957144 47.039294436574,14.611357590184 47.046487425454,14.805568289943 47.053680414334,14.992586000822 47.060873403214,15.086094856262 L 45.478415849619,15.086094856262 Q 45.471222860739,14.999778989702 45.456836882979,14.812761278823 45.449643894099,14.618550579064 45.435257916339,14.388374934904 45.428064927459,14.158199290745 45.420871938579,13.928023646586 45.413678949699,13.697848002426 45.413678949699,13.539602247067 L 45.384906994179,13.539602247067 Q 45.0540295057,14.424339879304 44.543327295221,14.827147256583 44.032625084743,15.229954633862 43.327712174505,15.229954633862 42.752273064107,15.229954633862 42.349465686828,14.999778989702 41.953851298429,14.769603345543 41.70209668763,14.373988957144 41.450342076831,13.971181579866 41.335254254751,13.424514424987 41.227359421551,12.877847270109 41.227359421551,12.244864248671 L 41.227359421551,7.3032808881253 42.888939852826,7.3032808881253 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4247" />
<path d="M 52.477193951607,15.086094856262 52.477193951607,10.719950606115 Q 52.477193951607,10.266792306677 52.426843029447,9.8927568849176 52.376492107287,9.5115284742787 52.261404285207,9.2453878857195 52.146316463128,8.9720543082803 51.959298752248,8.8210015418008 51.772281041369,8.6699487753212 51.50614045281,8.6699487753212 51.25438584201,8.6699487753212 51.045789164491,8.8353875195608 50.837192486972,8.9936332749203 50.686139720492,9.2957388078794 50.542279942892,9.5906513519585 50.455964076333,10.007844706997 50.376841198653,10.417845073156 50.376841198653,10.914161305875 L 50.376841198653,15.086094856262 48.715260767378,15.086094856262 48.715260767378,9.0439841970801 Q 48.715260767378,8.7922295862809 48.708067778498,8.5260889977217 48.708067778498,8.2599484091625 48.700874789618,8.0225797761232 48.693681800738,7.7780181542039 48.686488811858,7.5910004433244 48.679295822978,7.396789743565 48.672102834098,7.3032808881253 L 50.247367398813,7.3032808881253 Q 50.261753376573,7.389596754685 50.268946365453,7.5838074544445 50.283332343213,7.7708251653239 50.297718320973,8.0010008094832 50.312104298733,8.2311764536425 50.319297287613,8.4613520978019 50.326490276493,8.6915277419612 50.326490276493,8.8497734973207 L 50.348069243133,8.8497734973207 Q 50.657367764972,7.9578428762034 51.110526064411,7.5550354989246 51.570877352729,7.1522281216457 52.211053363048,7.1522281216457 52.944738228805,7.1522281216457 53.383510550484,7.5910004433244 53.829475861043,8.0225797761232 53.980528627522,8.8497734973207 L 54.016493571922,8.8497734973207 Q 54.189125305042,8.3678432423621 54.383336004801,8.0441587427631 54.58473969344,7.7204742431641 54.82210832648,7.5262635434046 55.059476959519,7.3248598547652 55.340003525838,7.2385439882055 55.620530092157,7.1522281216457 55.951407580636,7.1522281216457 56.476495768875,7.1522281216457 56.836145212874,7.3824037658051 57.202987645753,7.6125794099644 57.425970301032,8.0153867872432 57.656145945191,8.418194164522 57.756847789511,8.9648613194004 57.864742622711,9.5115284742787 57.864742622711,10.144511495717 L 57.864742622711,15.086094856262 56.217548169196,15.086094856262 56.217548169196,10.719950606115 Q 56.217548169196,10.266792306677 56.167197247036,9.8927568849176 56.116846324876,9.5115284742787 56.001758502796,9.2453878857195 55.886670680717,8.9720543082803 55.699652969837,8.8210015418008 55.519828247838,8.6699487753212 55.253687659279,8.6699487753212 55.009126037359,8.6699487753212 54.80052935984,8.8281945306808 54.5991256712,8.9792472971603 54.448072904721,9.2669668523595 54.297020138241,9.5474934186786 54.210704271682,9.9431078070775 54.131581394002,10.338722195476 54.117195416242,10.820652450435 L 54.117195416242,15.086094856262 52.477193951607,15.086094856262 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4249" />
<path d="M 60.921763757244,15.229954633862 Q 60.461412468925,15.229954633862 60.094570036046,15.078901867382 59.734920592047,14.920656112023 59.483165981248,14.625743567944 59.231411370449,14.323638034984 59.094744581729,13.884865713306 58.95807779301,13.446093391627 58.95807779301,12.885040258989 58.95807779301,12.194513326511 59.152288492769,11.726969049312 59.353692181408,11.252231783234 59.691762658767,10.964512228034 60.037026125006,10.669599683955 60.497377413325,10.540125884116 60.957728701644,10.403459095396 61.475623901002,10.396266106516 L 62.849484777078,10.367494150996 62.849484777078,9.9718797625974 Q 62.849484777078,9.5474934186786 62.784747877158,9.2669668523595 62.727203966118,8.9792472971603 62.604923155159,8.7994225751609 62.489835333079,8.6195978531614 62.31720359996,8.5476679643616 62.14457186684,8.4685450866818 61.921589211561,8.4685450866818 61.712992534041,8.4685450866818 61.547553789802,8.5188960088417 61.389308034442,8.5692469310015 61.267027223483,8.6987207308412 61.151939401403,8.8210015418008 61.080009512603,9.0367912082002 61.008079623803,9.2453878857195 60.979307668284,9.5690723853186 L 59.245797348209,9.4683705409989 Q 59.317727237009,8.9576683305204 59.504744947888,8.5404749754816 59.698955647647,8.1160886315629 60.029833136126,7.8067901097238 60.360710624605,7.4974915878847 60.849833868444,7.3320528436452 61.338957112283,7.1594211105257 61.993519100361,7.1594211105257 62.590537177399,7.1594211105257 63.050888465717,7.3392458325252 63.518432742916,7.5190705545247 63.842117242515,7.8787199985236 64.165801742114,8.2311764536425 64.338433475234,8.749071653001 64.511065208353,9.2669668523595 64.511065208353,9.9503007959574 L 64.511065208353,12.784338414669 Q 64.511065208353,13.057671992108 64.532644174993,13.273461658508 64.554223141633,13.489251324907 64.604574063793,13.640304091386 64.662117974833,13.784163868986 64.762819819152,13.863286746666 64.870714652352,13.935216635466 65.036153396592,13.935216635466 65.223171107471,13.935216635466 65.40299582947,13.892058702186 L 65.40299582947,14.985393011943 Q 65.40299582947,14.985393011943 65.136855240911,15.064515889622 65.014574429952,15.100480834022 64.899486607872,15.122059800662 64.784398785792,15.143638767302 64.647731997073,15.158024745062 64.518258197233,15.172410722822 64.338433475234,15.172410722822 63.712643442675,15.172410722822 63.410537909716,14.798375301063 63.115625365637,14.424339879304 63.058081454597,13.697848002426 L 63.022116510198,13.697848002426 Q 62.691239021718,14.424339879304 62.17334382236,14.827147256583 61.655448623002,15.229954633862 60.921763757244,15.229954633862 Z M 62.849484777078,11.482407427393 62.000712089241,11.496793405153 Q 61.734571500681,11.511179382913 61.497202867642,11.554337316193 61.259834234603,11.590302260593 61.080009512603,11.719776060432 60.900184790604,11.842056871392 60.792289957404,12.079425504431 60.684395124204,12.31679413747 60.684395124204,12.726794503629 60.684395124204,13.280654647388 60.892991801724,13.553988224827 61.108781468123,13.820128813386 61.454044934362,13.820128813386 61.770536445081,13.820128813386 62.02948404476,13.654690069146 62.29562463332,13.489251324907 62.475449355319,13.223110736348 62.655274077319,12.949777158909 62.748782932758,12.59732070379 62.849484777078,12.244864248671 62.849484777078,11.878021815792 L 62.849484777078,11.482407427393 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4251" />
<path d="M 70.315805140883,15.086094856262 70.315805140883,10.719950606115 Q 70.315805140883,10.266792306677 70.258261229843,9.8927568849176 70.200717318803,9.5115284742787 70.064050530083,9.2453878857195 69.927383741364,8.9720543082803 69.704401086085,8.8210015418008 69.488611419685,8.6699487753212 69.179312897846,8.6699487753212 68.877207364887,8.6699487753212 68.632645742968,8.8353875195608 68.388084121048,8.9936332749203 68.208259399049,9.2957388078794 68.035627665929,9.5906513519585 67.93492582161,10.007844706997 67.84141696617,10.417845073156 67.84141696617,10.914161305875 L 67.84141696617,15.086094856262 66.179836534895,15.086094856262 66.179836534895,9.0439841970801 Q 66.179836534895,8.7922295862809 66.172643546015,8.5260889977217 66.172643546015,8.2599484091625 66.165450557135,8.0225797761232 66.158257568255,7.7780181542039 66.151064579375,7.5910004433244 66.143871590495,7.396789743565 66.136678601615,7.3032808881253 L 67.71913615521,7.3032808881253 Q 67.73352213297,7.389596754685 67.74071512185,7.5838074544445 67.74790811073,7.7708251653239 67.76229408849,8.0010008094832 67.77668006625,8.2311764536425 67.78387305513,8.4613520978019 67.79106604401,8.6915277419612 67.79106604401,8.8497734973207 L 67.81264501065,8.8497734973207 Q 68.150715488009,7.9578428762034 68.654224709608,7.5550354989246 69.164926920086,7.1522281216457 69.869839830324,7.1522281216457 70.445278940722,7.1522281216457 70.840893329121,7.3824037658051 71.2437007064,7.6125794099644 71.495455317199,8.0153867872432 71.747209927998,8.418194164522 71.855104761198,8.9648613194004 71.970192583278,9.5115284742787 71.970192583278,10.144511495717 L 71.970192583278,15.086094856262 70.315805140883,15.086094856262 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4253" />
<path d="M 76.192478408106,15.229954633862 Q 75.501951475628,15.229954633862 74.948091331869,14.985393011943 74.394231188111,14.733638401143 74.005809788592,14.230129179545 73.617388389073,13.719426969066 73.408791711554,12.956970147789 73.207388022915,12.187320337631 73.207388022915,11.158722927794 73.207388022915,10.043809651397 73.451949644834,9.2813528301194 73.696511266753,8.5188960088417 74.106511632912,8.0513517316431 74.523704987951,7.5766144655645 75.063179153949,7.3680177880451 75.609846308827,7.1594211105257 76.214057374746,7.1594211105257 76.969321207143,7.1594211105257 77.501602384262,7.4831056101248 78.03388356138,7.7995971208438 78.371954038739,8.3750362312421 78.717217504978,8.9504753416404 78.875463260338,9.756090096198 79.040902004577,10.554511861876 79.040902004577,11.525565360673 L 79.040902004577,11.583109271713 74.948091331869,11.583109271713 Q 74.948091331869,12.072232515551 75.012828231789,12.49661885947 75.084758120589,12.913812214509 75.243003875948,13.223110736348 75.401249631308,13.525216269307 75.653004242107,13.705040991306 75.911951841787,13.877672724426 76.271601285785,13.877672724426 76.710373607464,13.877672724426 76.983707184903,13.654690069146 77.264233751222,13.424514424987 77.379321573302,12.949777158909 L 78.940200160258,13.115215903148 Q 78.832305327058,13.446093391627 78.638094627298,13.820128813386 78.451076916419,14.194164235145 78.1345854057,14.510655745864 77.818093894981,14.819954267703 77.343356628902,15.028550945222 76.868619362824,15.229954633862 76.192478408106,15.229954633862 Z M 76.192478408106,8.4325801422819 Q 75.940723797306,8.4325801422819 75.717741142027,8.5404749754816 75.494758486748,8.6411768198013 75.329319742508,8.8713524639606 75.171073987149,9.09433511924 75.070372142829,9.4539845632389 74.969670298509,9.8136340072379 74.955284320749,10.317143228836 L 77.436865484342,10.317143228836 Q 77.386514562182,9.3748616855592 77.062830062583,8.9073174083605 76.739145562984,8.4325801422819 76.192478408106,8.4325801422819 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4255" />
<path d="M 85.305995993316,15.215568656102 Q 84.572311127558,15.215568656102 84.17669673916,14.733638401143 83.788275339641,14.244515157305 83.788275339641,13.259075680748 L 83.788275339641,8.6699487753212 82.975467596203,8.6699487753212 82.975467596203,7.3032808881253 83.86739821732,7.3032808881253 84.385293416679,5.4762617126107 85.428276804276,5.4762617126107 85.428276804276,7.3032808881253 86.636698936112,7.3032808881253 86.636698936112,8.6699487753212 85.428276804276,8.6699487753212 85.428276804276,12.712408525869 Q 85.428276804276,13.280654647388 85.600908537395,13.553988224827 85.780733259395,13.820128813386 86.147575692274,13.820128813386 86.298628458753,13.820128813386 86.420909269713,13.791356857866 86.550383069552,13.762584902346 86.708628824912,13.719426969066 L 86.708628824912,14.971007034183 Q 86.399330303073,15.093287845142 86.054066836834,15.150831756182 85.715996359475,15.215568656102 85.305995993316,15.215568656102 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4257" />
<path d="M 90.319507827051,15.229954633862 Q 89.628980894573,15.229954633862 89.075120750815,14.985393011943 88.521260607056,14.733638401143 88.132839207537,14.230129179545 87.744417808019,13.719426969066 87.535821130499,12.956970147789 87.33441744186,12.187320337631 87.33441744186,11.158722927794 87.33441744186,10.043809651397 87.578979063779,9.2813528301194 87.823540685698,8.5188960088417 88.233541051857,8.0513517316431 88.650734406896,7.5766144655645 89.190208572894,7.3680177880451 89.736875727773,7.1594211105257 90.341086793691,7.1594211105257 91.096350626089,7.1594211105257 91.628631803207,7.4831056101248 92.160912980326,7.7995971208438 92.498983457685,8.3750362312421 92.844246923923,8.9504753416404 93.002492679283,9.756090096198 93.167931423523,10.554511861876 93.167931423523,11.525565360673 L 93.167931423523,11.583109271713 89.075120750815,11.583109271713 Q 89.075120750815,12.072232515551 89.139857650734,12.49661885947 89.211787539534,12.913812214509 89.370033294894,13.223110736348 89.528279050253,13.525216269307 89.780033661053,13.705040991306 90.038981260732,13.877672724426 90.398630704731,13.877672724426 90.837403026409,13.877672724426 91.110736603849,13.654690069146 91.391263170168,13.424514424987 91.506350992247,12.949777158909 L 93.067229579203,13.115215903148 Q 92.959334746003,13.446093391627 92.765124046244,13.820128813386 92.578106335364,14.194164235145 92.261614824645,14.510655745864 91.945123313926,14.819954267703 91.470386047848,15.028550945222 90.995648781769,15.229954633862 90.319507827051,15.229954633862 Z M 90.319507827051,8.4325801422819 Q 90.067753216252,8.4325801422819 89.844770560972,8.5404749754816 89.621787905693,8.6411768198013 89.456349161454,8.8713524639606 89.298103406094,9.09433511924 89.197401561774,9.4539845632389 89.096699717455,9.8136340072379 89.082313739695,10.317143228836 L 91.563894903287,10.317143228836 Q 91.513543981127,9.3748616855592 91.189859481528,8.9073174083605 90.866174981929,8.4325801422819 90.319507827051,8.4325801422819 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4259" />
<path d="M 97.095305073075,15.229954633862 Q 96.332848251797,15.229954633862 95.764602130279,14.942235078663 95.196356008761,14.654515523463 94.815127598122,14.129427335225 94.441092176363,13.604339146987 94.254074465483,12.870654281229 94.067056754604,12.129776426591 94.067056754604,11.237845805474 94.067056754604,10.266792306677 94.268460443243,9.5115284742787 94.477057120763,8.749071653001 94.865478520282,8.2311764536425 95.2538999198,7.7060882654041 95.822146041319,7.4327546879649 96.397585151717,7.1594211105257 97.116884039715,7.1594211105257 97.735481083393,7.1594211105257 98.217411338352,7.3608247991651 98.69934159331,7.5622284878045 99.044605059549,7.9146849429235 99.397061514668,8.2599484091625 99.612851181068,8.7346856752411 99.828640847467,9.2094229413196 99.907763725147,9.756090096198 L 98.238990304992,9.8567919405177 Q 98.167060416192,9.2597738634795 97.886533849873,8.9073174083605 97.606007283553,8.5476679643616 97.080919095315,8.5476679643616 96.411971129477,8.5476679643616 96.109865596518,9.2166159301996 95.807760063559,9.8855638960376 95.807760063559,11.158722927794 95.807760063559,13.848900768906 97.109691050835,13.848900768906 97.584428316914,13.848900768906 97.900919827633,13.489251324907 98.217411338352,13.122408892028 98.296534216031,12.40311000403 L 99.958114647306,12.49661885947 Q 99.900570736267,13.036093025468 99.684781069867,13.532409258187 99.476184392348,14.021532502025 99.116534948349,14.402760912664 98.76407849323,14.776796334423 98.253376282752,15.006971978582 97.749867061153,15.229954633862 97.095305073075,15.229954633862 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4261" />
<path d="M 102.79934316128,8.8569664862007 Q 103.13741363864,7.9650358650833 103.64092286024,7.5622284878045 104.15162507072,7.1594211105257 104.85653798096,7.1594211105257 105.43197709136,7.1594211105257 105.82759147976,7.389596754685 106.23039885703,7.6197723988444 106.48215346783,8.0225797761232 106.73390807863,8.425387153402 106.84180291183,8.9720543082803 106.95689073391,9.5187214631587 106.95689073391,10.151704484597 L 106.95689073391,15.086094856262 105.30250329152,15.086094856262 105.30250329152,10.727143594995 Q 105.30250329152,10.273985295556 105.24495938048,9.8999498737976 105.18741546944,9.5187214631587 105.05074868072,9.2525808745995 104.914081892,8.9792472971603 104.69109923672,8.8281945306808 104.47530957032,8.6771417642012 104.16601104848,8.6771417642012 103.86390551552,8.6771417642012 103.6193438936,8.8425805084407 103.37478227168,9.0008262638003 103.19495754968,9.3029317967594 103.02232581656,9.5978443408385 102.92162397224,10.015037695877 102.8281151168,10.425038062036 102.8281151168,10.921354294755 L 102.8281151168,15.086094856262 101.16653468553,15.086094856262 101.16653468553,4.4116993583739 102.8281151168,4.4116993583739 102.8281151168,7.3248598547652 Q 102.8281151168,7.5550354989246 102.82092212792,7.7924041319638 102.81372913904,8.0225797761232 102.80653615016,8.2311764536425 102.79934316128,8.4325801422819 102.7921501724,8.5980188865215 102.78495718352,8.763457630761 102.77776419464,8.8569664862007 L 102.79934316128,8.8569664862007 Z" style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:condensed;font-size:14.7312412262px;line-height:125%;font-family:'Liberation Sans Narrow';-inkscape-font-specification:'Liberation Sans Narrow, Bold Condensed';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:white;fill-opacity:0.9411764706" id="path4263" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -311,22 +311,22 @@ function api_call(App $a, App\Arguments $args = null)
} }
$type = "json"; $type = "json";
if (strpos($args->getQueryString(), ".xml") > 0) { if (strpos($args->getCommand(), ".xml") > 0) {
$type = "xml"; $type = "xml";
} }
if (strpos($args->getQueryString(), ".json") > 0) { if (strpos($args->getCommand(), ".json") > 0) {
$type = "json"; $type = "json";
} }
if (strpos($args->getQueryString(), ".rss") > 0) { if (strpos($args->getCommand(), ".rss") > 0) {
$type = "rss"; $type = "rss";
} }
if (strpos($args->getQueryString(), ".atom") > 0) { if (strpos($args->getCommand(), ".atom") > 0) {
$type = "atom"; $type = "atom";
} }
try { try {
foreach ($API as $p => $info) { foreach ($API as $p => $info) {
if (strpos($args->getQueryString(), $p) === 0) { if (strpos($args->getCommand(), $p) === 0) {
if (!api_check_method($info['method'])) { if (!api_check_method($info['method'])) {
throw new MethodNotAllowedException(); throw new MethodNotAllowedException();
} }
@ -654,8 +654,8 @@ function api_get_user(App $a, $contact_id = null)
'notifications' => false, 'notifications' => false,
'statusnet_profile_url' => $contact["url"], 'statusnet_profile_url' => $contact["url"],
'uid' => 0, 'uid' => 0,
'cid' => Contact::getIdForURL($contact["url"], api_user(), true), 'cid' => Contact::getIdForURL($contact["url"], api_user(), false),
'pid' => Contact::getIdForURL($contact["url"], 0, true), 'pid' => Contact::getIdForURL($contact["url"], 0, false),
'self' => 0, 'self' => 0,
'network' => $contact["network"], 'network' => $contact["network"],
]; ];
@ -679,7 +679,7 @@ function api_get_user(App $a, $contact_id = null)
$countfollowers = 0; $countfollowers = 0;
$starred = 0; $starred = 0;
$pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, true); $pcontact_id = Contact::getIdForURL($uinfo[0]['url'], 0, false);
if (!empty($profile['about'])) { if (!empty($profile['about'])) {
$description = $profile['about']; $description = $profile['about'];
@ -731,7 +731,7 @@ function api_get_user(App $a, $contact_id = null)
'statusnet_profile_url' => $uinfo[0]['url'], 'statusnet_profile_url' => $uinfo[0]['url'],
'uid' => intval($uinfo[0]['uid']), 'uid' => intval($uinfo[0]['uid']),
'cid' => intval($uinfo[0]['cid']), 'cid' => intval($uinfo[0]['cid']),
'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, true), 'pid' => Contact::getIdForURL($uinfo[0]["url"], 0, false),
'self' => $uinfo[0]['self'], 'self' => $uinfo[0]['self'],
'network' => $uinfo[0]['network'], 'network' => $uinfo[0]['network'],
]; ];
@ -5052,7 +5052,7 @@ function api_share_as_retweet(&$item)
$reshared_item["share-pre-body"] = $reshared['comment']; $reshared_item["share-pre-body"] = $reshared['comment'];
$reshared_item["body"] = $reshared['shared']; $reshared_item["body"] = $reshared['shared'];
$reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, true); $reshared_item["author-id"] = Contact::getIdForURL($reshared['profile'], 0, false);
$reshared_item["author-name"] = $reshared['author']; $reshared_item["author-name"] = $reshared['author'];
$reshared_item["author-link"] = $reshared['profile']; $reshared_item["author-link"] = $reshared['profile'];
$reshared_item["author-avatar"] = $reshared['avatar']; $reshared_item["author-avatar"] = $reshared['avatar'];
@ -5271,7 +5271,7 @@ function api_friendica_group_show($type)
// loop through all groups and retrieve all members for adding data in the user array // loop through all groups and retrieve all members for adding data in the user array
$grps = []; $grps = [];
foreach ($r as $rr) { foreach ($r as $rr) {
$members = Contact::getByGroupId($rr['id']); $members = Contact\Group::getById($rr['id']);
$users = []; $users = [];
if ($type == "xml") { if ($type == "xml") {
@ -5596,7 +5596,7 @@ function api_friendica_group_update($type)
} }
// remove members // remove members
$members = Contact::getByGroupId($gid); $members = Contact\Group::getById($gid);
foreach ($members as $member) { foreach ($members as $member) {
$cid = $member['id']; $cid = $member['id'];
foreach ($users as $user) { foreach ($users as $user) {
@ -5710,7 +5710,7 @@ function api_friendica_activity($type)
$id = $_REQUEST['id'] ?? 0; $id = $_REQUEST['id'] ?? 0;
$res = Item::performActivity($id, $verb); $res = Item::performActivity($id, $verb, api_user());
if ($res) { if ($res) {
if ($type == "xml") { if ($type == "xml") {

View File

@ -28,6 +28,7 @@ use Friendica\Core\Logger;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\Session; use Friendica\Core\Session;
use Friendica\Core\Theme;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -40,7 +41,6 @@ use Friendica\Object\Thread;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\Temporal; use Friendica\Util\Temporal;
use Friendica\Util\XML; use Friendica\Util\XML;
@ -316,7 +316,7 @@ function conv_get_blocklist()
return []; return [];
} }
$str_blocked = DI::pConfig()->get(local_user(), 'system', 'blocked'); $str_blocked = str_replace(["\n", "\r"], ",", DI::pConfig()->get(local_user(), 'system', 'blocked'));
if (empty($str_blocked)) { if (empty($str_blocked)) {
return []; return [];
} }
@ -325,7 +325,7 @@ function conv_get_blocklist()
foreach (explode(',', $str_blocked) as $entry) { foreach (explode(',', $str_blocked) as $entry) {
// The 4th parameter guarantees that there always will be a public contact entry // The 4th parameter guarantees that there always will be a public contact entry
$cid = Contact::getIdForURL(trim($entry), 0, true, ['url' => trim($entry)]); $cid = Contact::getIdForURL(trim($entry), 0, false, ['url' => trim($entry)]);
if (!empty($cid)) { if (!empty($cid)) {
$blocklist[] = $cid; $blocklist[] = $cid;
} }
@ -355,6 +355,13 @@ function conv_get_blocklist()
*/ */
function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0) function conversation(App $a, array $items, $mode, $update, $preview = false, $order = 'commented', $uid = 0)
{ {
$page = DI::page();
$page->registerFooterScript(Theme::getPathForFile('asset/typeahead.js/dist/typeahead.bundle.js'));
$page->registerFooterScript(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.js'));
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput.css'));
$page->registerStylesheet(Theme::getPathForFile('js/friendica-tagsinput/friendica-tagsinput-typeahead.css'));
$ssl_state = (local_user() ? true : false); $ssl_state = (local_user() ? true : false);
$profile_owner = 0; $profile_owner = 0;
@ -513,10 +520,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
$threadsid++; $threadsid++;
$owner_url = '';
$owner_name = '';
$sparkle = '';
// prevent private email from leaking. // prevent private email from leaking.
if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) { if ($item['network'] === Protocol::MAIL && local_user() != $item['uid']) {
continue; continue;
@ -533,14 +536,14 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'network' => $item['author-network'], 'url' => $item['author-link']]; 'network' => $item['author-network'], 'url' => $item['author-link']];
$profile_link = Contact::magicLinkByContact($author); $profile_link = Contact::magicLinkByContact($author);
$sparkle = '';
if (strpos($profile_link, 'redir/') === 0) { if (strpos($profile_link, 'redir/') === 0) {
$sparkle = ' sparkle'; $sparkle = ' sparkle';
} }
$locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => '']; $locate = ['location' => $item['location'], 'coord' => $item['coord'], 'html' => ''];
Hook::callAll('render_location',$locate); Hook::callAll('render_location',$locate);
$location_html = $locate['html'] ?: Strings::escapeHtml($locate['location'] ?: $locate['coord'] ?: '');
$location = ((strlen($locate['html'])) ? $locate['html'] : render_location_dummy($locate));
localize_item($item); localize_item($item);
if ($mode === 'network-new') { if ($mode === 'network-new') {
@ -556,10 +559,6 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'delete' => DI::l10n()->t('Delete'), 'delete' => DI::l10n()->t('Delete'),
]; ];
$star = false;
$isstarred = "unstarred";
$lock = false;
$likebuttons = [ $likebuttons = [
'like' => null, 'like' => null,
'dislike' => null, 'dislike' => null,
@ -570,7 +569,7 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
unset($likebuttons['dislike']); unset($likebuttons['dislike']);
} }
$body = Item::prepareBody($item, true, $preview); $body_html = Item::prepareBody($item, true, $preview);
list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item); list($categories, $folders) = DI::contentItem()->determineCategoriesTerms($item);
@ -589,13 +588,13 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']), 'network_icon' => ContactSelector::networkToIcon($item['network'], $item['author-link']),
'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']), 'linktitle' => DI::l10n()->t('View %s\'s profile @ %s', $profile_name, $item['author-link']),
'profile_url' => $profile_link, 'profile_url' => $profile_link,
'item_photo_menu' => item_photo_menu($item), 'item_photo_menu_html' => item_photo_menu($item),
'name' => $profile_name, 'name' => $profile_name,
'sparkle' => $sparkle, 'sparkle' => $sparkle,
'lock' => $lock, 'lock' => false,
'thumb' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['author-avatar'], false, ProxyUtils::SIZE_THUMB)), 'thumb' => DI::baseUrl()->remove($item['author-avatar']),
'title' => $title, 'title' => $title,
'body' => $body, 'body_html' => $body_html,
'tags' => $tags['tags'], 'tags' => $tags['tags'],
'hashtags' => $tags['hashtags'], 'hashtags' => $tags['hashtags'],
'mentions' => $tags['mentions'], 'mentions' => $tags['mentions'],
@ -606,23 +605,23 @@ function conversation(App $a, array $items, $mode, $update, $preview = false, $o
'has_folders' => ((count($folders)) ? 'true' : ''), 'has_folders' => ((count($folders)) ? 'true' : ''),
'categories' => $categories, 'categories' => $categories,
'folders' => $folders, 'folders' => $folders,
'text' => strip_tags($body), 'text' => strip_tags($body_html),
'localtime' => DateTimeFormat::local($item['created'], 'r'), 'localtime' => DateTimeFormat::local($item['created'], 'r'),
'ago' => (($item['app']) ? DI::l10n()->t('%s from %s', Temporal::getRelativeDate($item['created']),$item['app']) : Temporal::getRelativeDate($item['created'])), 'ago' => (($item['app']) ? DI::l10n()->t('%s from %s', Temporal::getRelativeDate($item['created']),$item['app']) : Temporal::getRelativeDate($item['created'])),
'location' => $location, 'location_html' => $location_html,
'indent' => '', 'indent' => '',
'owner_name' => $owner_name, 'owner_name' => '',
'owner_url' => $owner_url, 'owner_url' => '',
'owner_photo' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($item['owner-avatar'], false, ProxyUtils::SIZE_THUMB)), 'owner_photo' => DI::baseUrl()->remove($item['owner-avatar']),
'plink' => Item::getPlink($item), 'plink' => Item::getPlink($item),
'edpost' => false, 'edpost' => false,
'isstarred' => $isstarred, 'isstarred' => 'unstarred',
'star' => $star, 'star' => false,
'drop' => $drop, 'drop' => $drop,
'vote' => $likebuttons, 'vote' => $likebuttons,
'like' => '', 'like_html' => '',
'dislike' => '', 'dislike_html' => '',
'comment' => '', 'comment_html' => '',
'conv' => (($preview) ? '' : ['href'=> 'display/'.$item['guid'], 'title'=> DI::l10n()->t('View in context')]), 'conv' => (($preview) ? '' : ['href'=> 'display/'.$item['guid'], 'title'=> DI::l10n()->t('View in context')]),
'previewing' => $previewing, 'previewing' => $previewing,
'wait' => DI::l10n()->t('Please wait'), 'wait' => DI::l10n()->t('Please wait'),
@ -711,17 +710,68 @@ function conversation_fetch_comments($thread_items, $pinned) {
$comments = []; $comments = [];
$parentlines = []; $parentlines = [];
$lineno = 0; $lineno = 0;
$direction = [];
$actor = []; $actor = [];
$received = ''; $received = '';
while ($row = Item::fetch($thread_items)) { while ($row = Item::fetch($thread_items)) {
if (($row['verb'] == Activity::ANNOUNCE) && !empty($row['contact-uid']) && ($row['received'] > $received) && ($row['thr-parent'] == $row['parent-uri'])) { if (!empty($parentlines) && ($row['verb'] == Activity::ANNOUNCE)
$actor = ['link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']]; && ($row['thr-parent'] == $row['parent-uri']) && ($row['received'] > $received)
&& Contact::isSharing($row['author-id'], $row['uid'])) {
$direction = ['direction' => 3, 'title' => DI::l10n()->t('%s reshared this.', $row['author-name'])];
$author = ['uid' => 0, 'id' => $row['author-id'],
'network' => $row['author-network'], 'url' => $row['author-link']];
$url = '<a href="'. htmlentities(Contact::magicLinkByContact($author)) .'">' . htmlentities($row['author-name']) . '</a>';
$actor = ['url' => $url, 'link' => $row['author-link'], 'avatar' => $row['author-avatar'], 'name' => $row['author-name']];
$received = $row['received']; $received = $row['received'];
} }
if ((($row['gravity'] == GRAVITY_PARENT) && !$row['origin'] && !in_array($row['network'], [Protocol::DIASPORA])) && if (!empty($parentlines) && empty($direction) && ($row['gravity'] == GRAVITY_COMMENT)
(empty($row['contact-uid']) || !in_array($row['network'], Protocol::NATIVE_SUPPORT))) { && Contact::isSharing($row['author-id'], $row['uid'])) {
$direction = ['direction' => 5, 'title' => DI::l10n()->t('%s commented on this.', $row['author-name'])];
}
switch ($row['post-type']) {
case Item::PT_TO:
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'to')];
break;
case Item::PT_CC:
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'cc')];
break;
case Item::PT_BTO:
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bto')];
break;
case Item::PT_BCC:
$row['direction'] = ['direction' => 7, 'title' => DI::l10n()->t('You had been addressed (%s).', 'bcc')];
break;
case Item::PT_FOLLOWER:
$row['direction'] = ['direction' => 6, 'title' => DI::l10n()->t('You are following %s.', $row['author-name'])];
break;
case Item::PT_TAG:
$row['direction'] = ['direction' => 4, 'title' => DI::l10n()->t('Tagged')];
break;
case Item::PT_ANNOUNCEMENT:
$row['direction'] = ['direction' => 3, 'title' => DI::l10n()->t('Reshared')];
break;
case Item::PT_COMMENT:
$row['direction'] = ['direction' => 5, 'title' => DI::l10n()->t('%s is participating in this thread.', $row['author-name'])];
break;
case Item::PT_STORED:
$row['direction'] = ['direction' => 8, 'title' => DI::l10n()->t('Stored')];
break;
case Item::PT_GLOBAL:
$row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
break;
default:
if ($row['uid'] == 0) {
$row['direction'] = ['direction' => 9, 'title' => DI::l10n()->t('Global')];
}
}
if (($row['gravity'] == GRAVITY_PARENT) && !$row['origin'] && ($row['author-id'] == $row['owner-id']) &&
!Contact::isSharing($row['author-id'], $row['uid'])) {
$parentlines[] = $lineno; $parentlines[] = $lineno;
} }
@ -735,11 +785,17 @@ function conversation_fetch_comments($thread_items, $pinned) {
DBA::close($thread_items); DBA::close($thread_items);
if (!empty($actor)) { if (!empty($direction)) {
foreach ($parentlines as $line) { foreach ($parentlines as $line) {
$comments[$line]['owner-link'] = $actor['link']; $comments[$line]['direction'] = $direction;
$comments[$line]['owner-avatar'] = $actor['avatar']; if (!empty($actor)) {
$comments[$line]['owner-name'] = $actor['name']; $comments[$line]['reshared'] = DI::l10n()->t('%s reshared this.', $actor['url']);
if (DI::pConfig()->get(local_user(), 'system', 'display_resharer') ) {
$comments[$line]['owner-link'] = $actor['link'];
$comments[$line]['owner-avatar'] = $actor['avatar'];
$comments[$line]['owner-name'] = $actor['name'];
}
}
} }
} }
return $comments; return $comments;
@ -766,7 +822,7 @@ function conversation_add_children(array $parents, $block_authors, $order, $uid)
$max_comments = DI::config()->get('system', 'max_display_comments', 1000); $max_comments = DI::config()->get('system', 'max_display_comments', 1000);
} }
$params = ['order' => ['uid', 'commented' => true]]; $params = ['order' => ['gravity', 'uid', 'commented' => true]];
if ($max_comments > 0) { if ($max_comments > 0) {
$params['limit'] = $max_comments; $params['limit'] = $max_comments;
@ -806,7 +862,7 @@ function conversation_fetch_items(array $parent, array $items, array $condition,
$condition[0] .= " AND NOT `author`.`hidden`"; $condition[0] .= " AND NOT `author`.`hidden`";
} }
$thread_items = Item::selectForUser(local_user(), array_merge(Item::DISPLAY_FIELDLIST, ['contact-uid', 'gravity']), $condition, $params); $thread_items = Item::selectForUser(local_user(), array_merge(Item::DISPLAY_FIELDLIST, ['contact-uid', 'gravity', 'post-type']), $condition, $params);
$comments = conversation_fetch_comments($thread_items, $parent['pinned'] ?? false); $comments = conversation_fetch_comments($thread_items, $parent['pinned'] ?? false);
@ -837,7 +893,7 @@ function item_photo_menu($item) {
$sparkle = (strpos($profile_link, 'redir/') === 0); $sparkle = (strpos($profile_link, 'redir/') === 0);
$cid = 0; $cid = 0;
$pcid = Contact::getIdForURL($item['author-link'], 0, true); $pcid = Contact::getIdForURL($item['author-link'], 0, false);
$network = ''; $network = '';
$rel = 0; $rel = 0;
$condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])]; $condition = ['uid' => local_user(), 'nurl' => Strings::normaliseLink($item['author-link'])];
@ -1114,34 +1170,12 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
$jotplugins = ''; $jotplugins = '';
Hook::callAll('jot_tool', $jotplugins); Hook::callAll('jot_tool', $jotplugins);
// Private/public post links for the non-JS ACL form
$private_post = 1;
if (!empty($_REQUEST['public'])) {
$private_post = 0;
}
$query_str = DI::args()->getQueryString();
if (strpos($query_str, 'public=1') !== false) {
$query_str = str_replace(['?public=1', '&public=1'], ['', ''], $query_str);
}
/*
* I think $a->query_string may never have ? in it, but I could be wrong
* It looks like it's from the index.php?q=[etc] rewrite that the web
* server does, which converts any ? to &, e.g. suggest&ignore=61 for suggest?ignore=61
*/
if (strpos($query_str, '?') === false) {
$public_post_link = '?public=1';
} else {
$public_post_link = '&public=1';
}
// $tpl = Renderer::replaceMacros($tpl,array('$jotplugins' => $jotplugins)); // $tpl = Renderer::replaceMacros($tpl,array('$jotplugins' => $jotplugins));
$tpl = Renderer::getMarkupTemplate("jot.tpl"); $tpl = Renderer::getMarkupTemplate("jot.tpl");
$o .= Renderer::replaceMacros($tpl,[ $o .= Renderer::replaceMacros($tpl,[
'$new_post' => DI::l10n()->t('New Post'), '$new_post' => DI::l10n()->t('New Post'),
'$return_path' => $query_str, '$return_path' => DI::args()->getQueryString(),
'$action' => 'item', '$action' => 'item',
'$share' => ($x['button'] ?? '') ?: DI::l10n()->t('Share'), '$share' => ($x['button'] ?? '') ?: DI::l10n()->t('Share'),
'$loading' => DI::l10n()->t('Loading...'), '$loading' => DI::l10n()->t('Loading...'),
@ -1167,7 +1201,7 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
'$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? DI::l10n()->t("Categories \x28comma-separated list\x29") : '', '$placeholdercategory' => Feature::isEnabled(local_user(), 'categories') ? DI::l10n()->t("Categories \x28comma-separated list\x29") : '',
'$wait' => DI::l10n()->t('Please wait'), '$wait' => DI::l10n()->t('Please wait'),
'$permset' => DI::l10n()->t('Permission settings'), '$permset' => DI::l10n()->t('Permission settings'),
'$shortpermset' => DI::l10n()->t('permissions'), '$shortpermset' => DI::l10n()->t('Permissions'),
'$wall' => $notes_cid ? 0 : 1, '$wall' => $notes_cid ? 0 : 1,
'$posttype' => $notes_cid ? Item::PT_PERSONAL_NOTE : Item::PT_ARTICLE, '$posttype' => $notes_cid ? Item::PT_PERSONAL_NOTE : Item::PT_ARTICLE,
'$content' => $x['content'] ?? '', '$content' => $x['content'] ?? '',
@ -1189,11 +1223,6 @@ function status_editor(App $a, $x, $notes_cid = 0, $popup = false)
// ACL permissions box // ACL permissions box
'$acl' => $x['acl'], '$acl' => $x['acl'],
'$group_perms' => DI::l10n()->t('Post to Groups'),
'$contact_perms' => DI::l10n()->t('Post to Contacts'),
'$private' => DI::l10n()->t('Private post'),
'$is_private' => $private_post,
'$public_link' => $public_post_link,
//jot nav tab (used in some themes) //jot nav tab (used in some themes)
'$message' => DI::l10n()->t('Message'), '$message' => DI::l10n()->t('Message'),
@ -1467,13 +1496,3 @@ function sort_thr_commented(array $a, array $b)
{ {
return strcmp($b['commented'], $a['commented']); return strcmp($b['commented'], $a['commented']);
} }
function render_location_dummy(array $item) {
if (!empty($item['location']) && !empty($item['location'])) {
return $item['location'];
}
if (!empty($item['coord']) && !empty($item['coord'])) {
return $item['coord'];
}
}

View File

@ -87,12 +87,15 @@ function notification($params)
} }
$nickname = $user["nickname"]; $nickname = $user["nickname"];
// Creates a new email builder for the notification email
$emailBuilder = DI::emailer()->newNotifyMail();
// with $params['show_in_notification_page'] == false, the notification isn't inserted into // with $params['show_in_notification_page'] == false, the notification isn't inserted into
// the database, and an email is sent if applicable. // the database, and an email is sent if applicable.
// default, if not specified: true // default, if not specified: true
$show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true; $show_in_notification_page = isset($params['show_in_notification_page']) ? $params['show_in_notification_page'] : true;
$additional_mail_header = "X-Friendica-Account: <".$nickname."@".$hostname.">\n"; $emailBuilder->setHeader('X-Friendica-Account', '<' . $nickname . '@' . $hostname . '>');
if (array_key_exists('item', $params)) { if (array_key_exists('item', $params)) {
$title = $params['item']['title']; $title = $params['item']['title'];
@ -259,13 +262,23 @@ function notification($params)
} }
if ($params['type'] == Notify\Type::SHARE) { if ($params['type'] == Notify\Type::SHARE) {
$subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $params['source_name']); if ($params['origin_link'] == $params['source_link']) {
$subject = $l10n->t('%s %s shared a new post', $subjectPrefix, $params['source_name']);
$preamble = $l10n->t('%1$s shared a new post at %2$s', $params['source_name'], $sitename); $preamble = $l10n->t('%1$s shared a new post at %2$s', $params['source_name'], $sitename);
$epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url].', $epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url].',
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
$params['link'] $params['link']
); );
} else {
$subject = $l10n->t('%s %s shared a post from %s', $subjectPrefix, $params['source_name'], $params['origin_name']);
$preamble = $l10n->t('%1$s shared a post from %2$s at %3$s', $params['source_name'], $params['origin_name'], $sitename);
$epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url] from %3$s.',
'[url='.$params['source_link'].']'.$params['source_name'].'[/url]',
$params['link'], '[url='.$params['origin_link'].']'.$params['origin_name'].'[/url]'
);
}
$sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.');
$tsitelink = sprintf($sitelink, $siteurl); $tsitelink = sprintf($sitelink, $siteurl);
@ -499,7 +512,8 @@ function notification($params)
Logger::log('sending notification email'); Logger::log('sending notification email');
if (isset($params['parent']) && (intval($params['parent']) != 0)) { if (isset($params['parent']) && (intval($params['parent']) != 0)) {
$id_for_parent = $params['parent'] . "@" . $hostname; $parent = Item::selectFirst(['guid'], ['id' => $params['parent']]);
$message_id = "<" . $parent['guid'] . "@" . gethostname() . ">";
// Is this the first email notification for this parent item and user? // Is this the first email notification for this parent item and user?
if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) { if (!DBA::exists('notify-threads', ['master-parent-item' => $params['parent'], 'receiver-uid' => $params['uid']])) {
@ -510,13 +524,14 @@ function notification($params)
'receiver-uid' => $params['uid'], 'parent-item' => 0]; 'receiver-uid' => $params['uid'], 'parent-item' => 0];
DBA::insert('notify-threads', $fields); DBA::insert('notify-threads', $fields);
$additional_mail_header .= "Message-ID: <${id_for_parent}>\n"; $emailBuilder->setHeader('Message-ID', $message_id);
$log_msg = "include/enotify: No previous notification found for this parent:\n" . $log_msg = "include/enotify: No previous notification found for this parent:\n" .
" parent: ${params['parent']}\n" . " uid : ${params['uid']}\n"; " parent: ${params['parent']}\n" . " uid : ${params['uid']}\n";
Logger::log($log_msg, Logger::DEBUG); Logger::log($log_msg, Logger::DEBUG);
} else { } else {
// If not, just "follow" the thread. // If not, just "follow" the thread.
$additional_mail_header .= "References: <${id_for_parent}>\nIn-Reply-To: <${id_for_parent}>\n"; $emailBuilder->setHeader('References', $message_id);
$emailBuilder->setHeader('In-Reply-To', $message_id);
Logger::log("There's already a notification for this parent.", Logger::DEBUG); Logger::log("There's already a notification for this parent.", Logger::DEBUG);
} }
} }
@ -535,7 +550,6 @@ function notification($params)
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'subject' => $subject, 'subject' => $subject,
'headers' => $additional_mail_header,
]; ];
Hook::callAll('enotify_mail', $datarray); Hook::callAll('enotify_mail', $datarray);
@ -554,13 +568,13 @@ function notification($params)
// If a photo is present, add it to the email // If a photo is present, add it to the email
if (!empty($datarray['source_photo'])) { if (!empty($datarray['source_photo'])) {
$builder->withPhoto( $emailBuilder->withPhoto(
$datarray['source_photo'], $datarray['source_photo'],
$datarray['source_link'] ?? $sitelink, $datarray['source_link'] ?? $sitelink,
$datarray['source_name'] ?? $sitename); $datarray['source_name'] ?? $sitename);
} }
$email = $builder->build(); $email = $emailBuilder->build();
// use the Emailer class to send the message // use the Emailer class to send the message
return DI::emailer()->send($email); return DI::emailer()->send($email);
@ -594,10 +608,10 @@ function check_user_notification($itemid) {
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
function check_item_notification($itemid, $uid, $notification_type) { function check_item_notification($itemid, $uid, $notification_type) {
$fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'title', 'body', $fields = ['id', 'uri-id', 'mention', 'parent', 'parent-uri-id', 'thr-parent-id',
'author-link', 'author-name', 'author-avatar', 'author-id', 'title', 'body', 'author-link', 'author-name', 'author-avatar', 'author-id',
'guid', 'parent-uri', 'uri', 'contact-id', 'network']; 'gravity', 'guid', 'parent-uri', 'uri', 'contact-id', 'network'];
$condition = ['id' => $itemid, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'deleted' => false]; $condition = ['id' => $itemid, 'deleted' => false];
$item = Item::selectFirstForUser($uid, $fields, $condition); $item = Item::selectFirstForUser($uid, $fields, $condition);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
return false; return false;
@ -610,9 +624,9 @@ function check_item_notification($itemid, $uid, $notification_type) {
$params['parent'] = $item['parent']; $params['parent'] = $item['parent'];
$params['link'] = DI::baseUrl() . '/display/' . urlencode($item['guid']); $params['link'] = DI::baseUrl() . '/display/' . urlencode($item['guid']);
$params['otype'] = 'item'; $params['otype'] = 'item';
$params['source_name'] = $item['author-name']; $params['origin_name'] = $params['source_name'] = $item['author-name'];
$params['source_link'] = $item['author-link']; $params['origin_link'] = $params['source_link'] = $item['author-link'];
$params['source_photo'] = $item['author-avatar']; $params['origin_photo'] = $params['source_photo'] = $item['author-avatar'];
// Set the activity flags // Set the activity flags
$params['activity']['explicit_tagged'] = ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED); $params['activity']['explicit_tagged'] = ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED);
@ -630,6 +644,22 @@ function check_item_notification($itemid, $uid, $notification_type) {
if ($notification_type & UserItem::NOTIF_SHARED) { if ($notification_type & UserItem::NOTIF_SHARED) {
$params['type'] = Notify\Type::SHARE; $params['type'] = Notify\Type::SHARE;
$params['verb'] = Activity::POST; $params['verb'] = Activity::POST;
// Special treatment for posts that had been shared via "announce"
if ($item['gravity'] == GRAVITY_ACTIVITY) {
$parent_item = Item::selectFirst($fields, ['uri-id' => $item['thr-parent-id'], 'uid' => [$uid, 0]]);
if (DBA::isResult($parent_item)) {
// Don't notify on own entries
if (User::getIdForURL($parent_item['author-link']) == $uid) {
return false;
}
$params['origin_name'] = $parent_item['author-name'];
$params['origin_link'] = $parent_item['author-link'];
$params['origin_photo'] = $parent_item['author-avatar'];
$params['item'] = $parent_item;
}
}
} elseif ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED) { } elseif ($notification_type & UserItem::NOTIF_EXPLICIT_TAGGED) {
$params['type'] = Notify\Type::TAG_SELF; $params['type'] = Notify\Type::TAG_SELF;
$params['verb'] = Activity::TAG; $params['verb'] = Activity::TAG;

View File

@ -1,74 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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/>.
*
*/
/**
* @deprecated since 2020.06
* @see \Friendica\Content\PageInfo::getFooterFromData
*/
function add_page_info_data(array $data, $no_photos = false)
{
return "\n" . \Friendica\Content\PageInfo::getFooterFromData($data, $no_photos);
}
/**
* @deprecated since 2020.06
* @see \Friendica\Content\PageInfo::queryUrl
*/
function query_page_info($url, $photo = "", $keywords = false, $keyword_denylist = "")
{
return \Friendica\Content\PageInfo::queryUrl($url, $photo, $keywords, $keyword_denylist);
}
/**
* @deprecated since 2020.06
* @see \Friendica\Content\PageInfo::getTagsFromUrl()
*/
function get_page_keywords($url, $photo = "", $keywords = false, $keyword_denylist = "")
{
return $keywords ? \Friendica\Content\PageInfo::getTagsFromUrl($url, $photo, $keyword_denylist) : [];
}
/**
* @deprecated since 2020.06
* @see \Friendica\Content\PageInfo::getFooterFromUrl
*/
function add_page_info($url, $no_photos = false, $photo = "", $keywords = false, $keyword_denylist = "")
{
return "\n" . \Friendica\Content\PageInfo::getFooterFromUrl($url, $no_photos, $photo, $keywords, $keyword_denylist);
}
/**
* @deprecated since 2020.06
* @see \Friendica\Content\PageInfo::appendToBody
*/
function add_page_info_to_body($body, $texturl = false, $no_photos = false)
{
return \Friendica\Content\PageInfo::appendToBody($body, $texturl, $no_photos);
}
/**
* @deprecated since 2020.06
* @see \Friendica\Protocol\Feed::consume
*/
function consume_feed($xml, array $importer, array $contact, &$hub)
{
\Friendica\Protocol\Feed::consume($xml, $importer, $contact, $hub);
}

View File

@ -47,12 +47,12 @@ function oauth_get_client(OAuthRequest $request)
function api_post(App $a) function api_post(App $a)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) { if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
} }
@ -107,7 +107,7 @@ function api_content(App $a)
if (!local_user()) { if (!local_user()) {
/// @TODO We need login form to redirect to this page /// @TODO We need login form to redirect to this page
notice(DI::l10n()->t('Please login to continue.') . EOL); notice(DI::l10n()->t('Please login to continue.'));
return Login::form(DI::args()->getQueryString(), false, $request->get_parameters()); return Login::form(DI::args()->getQueryString(), false, $request->get_parameters());
} }
//FKOAuth1::loginUser(4); //FKOAuth1::loginUser(4);

View File

@ -105,6 +105,11 @@ function cal_content(App $a)
// get the translation strings for the callendar // get the translation strings for the callendar
$i18n = Event::getStrings(); $i18n = Event::getStrings();
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css');
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print');
DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
$htpl = Renderer::getMarkupTemplate('event_head.tpl'); $htpl = Renderer::getMarkupTemplate('event_head.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [ DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [
'$module_url' => '/cal/' . $a->data['user']['nickname'], '$module_url' => '/cal/' . $a->data['user']['nickname'],
@ -134,7 +139,7 @@ function cal_content(App $a)
$is_owner = local_user() == $a->profile['uid']; $is_owner = local_user() == $a->profile['uid'];
if ($a->profile['hidewall'] && !$is_owner && !$remote_contact) { if ($a->profile['hidewall'] && !$is_owner && !$remote_contact) {
notice(DI::l10n()->t('Access to this profile has been restricted.') . EOL); notice(DI::l10n()->t('Access to this profile has been restricted.'));
return; return;
} }
@ -292,13 +297,6 @@ function cal_content(App $a)
return; return;
} }
// Test permissions
// Respect the export feature setting for all other /cal pages if it's not the own profile
if ((local_user() !== $owner_uid) && !Feature::isEnabled($owner_uid, "export_calendar")) {
notice(DI::l10n()->t('Permission denied.') . EOL);
DI::baseUrl()->redirect('cal/' . $nick);
}
// Get the export data by uid // Get the export data by uid
$evexport = Event::exportListByUserId($owner_uid, $format); $evexport = Event::exportListByUserId($owner_uid, $format);

View File

@ -1,170 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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/>.
*
*/
use Friendica\App;
use Friendica\Content\ContactSelector;
use Friendica\Content\Pager;
use Friendica\Core\Renderer;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model;
use Friendica\Module;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings;
function common_content(App $a)
{
$o = '';
$cmd = $a->argv[1];
$uid = intval($a->argv[2]);
$cid = intval($a->argv[3]);
$zcid = 0;
if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL);
return;
}
if ($cmd !== 'loc' && $cmd != 'rem') {
return;
}
if (!$uid) {
return;
}
if ($cmd === 'loc' && $cid) {
$contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['id' => $cid, 'uid' => $uid]);
if (DBA::isResult($contact)) {
DI::page()['aside'] = "";
Model\Profile::load($a, "", Model\Contact::getDetailsByURL($contact["url"]));
}
} else {
$contact = DBA::selectFirst('contact', ['name', 'url', 'photo', 'uid', 'id'], ['self' => true, 'uid' => $uid]);
if (DBA::isResult($contact)) {
$vcard_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('widget/vcard.tpl'), [
'$name' => $contact['name'],
'$photo' => $contact['photo'],
'url' => 'contact/' . $cid
]);
if (empty(DI::page()['aside'])) {
DI::page()['aside'] = '';
}
DI::page()['aside'] .= $vcard_widget;
}
}
if (!DBA::isResult($contact)) {
return;
}
if (!$cid && Model\Profile::getMyURL()) {
$contact = DBA::selectFirst('contact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL()), 'uid' => $uid]);
if (DBA::isResult($contact)) {
$cid = $contact['id'];
} else {
$gcontact = DBA::selectFirst('gcontact', ['id'], ['nurl' => Strings::normaliseLink(Model\Profile::getMyURL())]);
if (DBA::isResult($gcontact)) {
$zcid = $gcontact['id'];
}
}
}
if ($cid == 0 && $zcid == 0) {
return;
}
if ($cid) {
$total = Model\GContact::countCommonFriends($uid, $cid);
} else {
$total = Model\GContact::countCommonFriendsZcid($uid, $zcid);
}
if ($total < 1) {
notice(DI::l10n()->t('No contacts in common.') . EOL);
return $o;
}
$pager = new Pager(DI::l10n(), DI::args()->getQueryString());
if ($cid) {
$common_friends = Model\GContact::commonFriends($uid, $cid, $pager->getStart(), $pager->getItemsPerPage());
} else {
$common_friends = Model\GContact::commonFriendsZcid($uid, $zcid, $pager->getStart(), $pager->getItemsPerPage());
}
if (!DBA::isResult($common_friends)) {
return $o;
}
$id = 0;
$entries = [];
foreach ($common_friends as $common_friend) {
//get further details of the contact
$contact_details = Model\Contact::getDetailsByURL($common_friend['url'], $uid);
// $rr['id'] is needed to use contact_photo_menu()
/// @TODO Adding '/" here avoids E_NOTICE on missing constants
$common_friend['id'] = $common_friend['cid'];
$photo_menu = Model\Contact::photoMenu($common_friend);
$entry = [
'url' => Model\Contact::magicLink($common_friend['url']),
'itemurl' => ($contact_details['addr'] ?? '') ?: $common_friend['url'],
'name' => $contact_details['name'],
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'img_hover' => $contact_details['name'],
'details' => $contact_details['location'],
'tags' => $contact_details['keywords'],
'about' => $contact_details['about'],
'account_type' => Model\Contact::getAccountType($contact_details),
'network' => ContactSelector::networkToName($contact_details['network'], $contact_details['url']),
'photo_menu' => $photo_menu,
'id' => ++$id,
];
$entries[] = $entry;
}
$title = '';
$tab_str = '';
if ($cmd === 'loc' && $cid && local_user() == $uid) {
$tab_str = Module\Contact::getTabsHTML($a, $contact, 5);
} else {
$title = DI::l10n()->t('Common Friends');
}
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');
$o .= Renderer::replaceMacros($tpl, [
'$title' => $title,
'$tab_str' => $tab_str,
'$contacts' => $entries,
'$paginate' => $pager->renderFull($total),
]);
return $o;
}

View File

@ -45,7 +45,6 @@ use Friendica\Model\User;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\Crypto; use Friendica\Util\Crypto;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\XML; use Friendica\Util\XML;
@ -76,13 +75,13 @@ function dfrn_confirm_post(App $a, $handsfree = null)
if (empty($_POST['source_url'])) { if (empty($_POST['source_url'])) {
$uid = ($handsfree['uid'] ?? 0) ?: local_user(); $uid = ($handsfree['uid'] ?? 0) ?: local_user();
if (!$uid) { if (!$uid) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
$user = DBA::selectFirst('user', [], ['uid' => $uid]); $user = DBA::selectFirst('user', [], ['uid' => $uid]);
if (!DBA::isResult($user)) { if (!DBA::isResult($user)) {
notice(DI::l10n()->t('Profile not found.') . EOL); notice(DI::l10n()->t('Profile not found.'));
return; return;
} }
@ -137,8 +136,8 @@ function dfrn_confirm_post(App $a, $handsfree = null)
); );
if (!DBA::isResult($r)) { if (!DBA::isResult($r)) {
Logger::log('Contact not found in DB.'); Logger::log('Contact not found in DB.');
notice(DI::l10n()->t('Contact not found.') . EOL); notice(DI::l10n()->t('Contact not found.'));
notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.') . EOL); notice(DI::l10n()->t('This may occasionally happen if contact was requested by both persons and it has already been approved.'));
return; return;
} }
@ -224,7 +223,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
* *
*/ */
$res = Network::post($dfrn_confirm, $params, [], 120)->getBody(); $res = DI::httpRequest()->post($dfrn_confirm, $params, [], 120)->getBody();
Logger::log(' Confirm: received data: ' . $res, Logger::DATA); Logger::log(' Confirm: received data: ' . $res, Logger::DATA);
@ -239,20 +238,20 @@ function dfrn_confirm_post(App $a, $handsfree = null)
// We shouldn't proceed, because the xml parser might choke, // We shouldn't proceed, because the xml parser might choke,
// and $status is going to be zero, which indicates success. // and $status is going to be zero, which indicates success.
// We can hardly call this a success. // We can hardly call this a success.
notice(DI::l10n()->t('Response from remote site was not understood.') . EOL); notice(DI::l10n()->t('Response from remote site was not understood.'));
return; return;
} }
if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) { if (strlen($leading_junk) && DI::config()->get('system', 'debugging')) {
// This might be more common. Mixed error text and some XML. // This might be more common. Mixed error text and some XML.
// If we're configured for debugging, show the text. Proceed in either case. // If we're configured for debugging, show the text. Proceed in either case.
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . $leading_junk . EOL); notice(DI::l10n()->t('Unexpected response from remote site: ') . $leading_junk);
} }
if (stristr($res, "<status") === false) { if (stristr($res, "<status") === false) {
// wrong xml! stop here! // wrong xml! stop here!
Logger::log('Unexpected response posting to ' . $dfrn_confirm); Logger::log('Unexpected response posting to ' . $dfrn_confirm);
notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res) . EOL); notice(DI::l10n()->t('Unexpected response from remote site: ') . EOL . htmlspecialchars($res));
return; return;
} }
@ -261,7 +260,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
$message = XML::unescape($xml->message); // human readable text of what may have gone wrong. $message = XML::unescape($xml->message); // human readable text of what may have gone wrong.
switch ($status) { switch ($status) {
case 0: case 0:
info(DI::l10n()->t("Confirmation completed successfully.") . EOL); info(DI::l10n()->t("Confirmation completed successfully."));
break; break;
case 1: case 1:
// birthday paradox - generate new dfrn-id and fall through. // birthday paradox - generate new dfrn-id and fall through.
@ -273,15 +272,15 @@ function dfrn_confirm_post(App $a, $handsfree = null)
); );
case 2: case 2:
notice(DI::l10n()->t("Temporary failure. Please wait and try again.") . EOL); notice(DI::l10n()->t("Temporary failure. Please wait and try again."));
break; break;
case 3: case 3:
notice(DI::l10n()->t("Introduction failed or was revoked.") . EOL); notice(DI::l10n()->t("Introduction failed or was revoked."));
break; break;
} }
if (strlen($message)) { if (strlen($message)) {
notice(DI::l10n()->t('Remote site reported: ') . $message . EOL); notice(DI::l10n()->t('Remote site reported: ') . $message);
} }
if (($status == 0) && $intro_id) { if (($status == 0) && $intro_id) {
@ -305,7 +304,7 @@ function dfrn_confirm_post(App $a, $handsfree = null)
* *
* We will also update the contact record with the nature and scope of the relationship. * We will also update the contact record with the nature and scope of the relationship.
*/ */
Contact::updateAvatar($contact['photo'], $uid, $contact_id); Contact::updateAvatar($contact_id, $contact['photo']);
Logger::log('dfrn_confirm: confirm - imported photos'); Logger::log('dfrn_confirm: confirm - imported photos');
@ -482,10 +481,10 @@ function dfrn_confirm_post(App $a, $handsfree = null)
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$photo = $contact['photo']; $photo = $contact['photo'];
} else { } else {
$photo = DI::baseUrl() . '/images/person-300.jpg'; $photo = DI::baseUrl() . Contact::DEFAULT_AVATAR_PHOTO;
} }
Contact::updateAvatar($photo, $local_uid, $dfrn_record); Contact::updateAvatar($dfrn_record, $photo);
Logger::log('dfrn_confirm: request - photos imported'); Logger::log('dfrn_confirm: request - photos imported');

View File

@ -21,13 +21,12 @@
use Friendica\App; use Friendica\App;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\Core\Session; use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Protocol\DFRN; use Friendica\Protocol\DFRN;
use Friendica\Protocol\OStatus; use Friendica\Protocol\OStatus;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\XML; use Friendica\Util\XML;
@ -115,7 +114,7 @@ function dfrn_poll_init(App $a)
); );
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$s = Network::fetchUrl($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check'); $s = DI::httpRequest()->fetch($r[0]['poll'] . '?dfrn_id=' . $my_id . '&type=profile-check');
Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA); Logger::log("dfrn_poll: old profile returns " . $s, Logger::DATA);
@ -133,7 +132,7 @@ function dfrn_poll_init(App $a)
Session::setVisitorsContacts(); Session::setVisitorsContacts();
if (!$quiet) { if (!$quiet) {
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL); info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
} }
// Visitors get 1 day session. // Visitors get 1 day session.
@ -240,7 +239,6 @@ function dfrn_poll_post(App $a)
{ {
$dfrn_id = $_POST['dfrn_id'] ?? ''; $dfrn_id = $_POST['dfrn_id'] ?? '';
$challenge = $_POST['challenge'] ?? ''; $challenge = $_POST['challenge'] ?? '';
$url = $_POST['url'] ?? '';
$sec = $_POST['sec'] ?? ''; $sec = $_POST['sec'] ?? '';
$ptype = $_POST['type'] ?? ''; $ptype = $_POST['type'] ?? '';
$perm = ($_POST['perm'] ?? '') ?: 'r'; $perm = ($_POST['perm'] ?? '') ?: 'r';
@ -320,7 +318,6 @@ function dfrn_poll_post(App $a)
exit(); exit();
} }
$type = $r[0]['type'];
$last_update = $r[0]['last_update']; $last_update = $r[0]['last_update'];
DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]); DBA::delete('challenge', ['dfrn-id' => $dfrn_id, 'challenge' => $challenge]);
@ -347,59 +344,29 @@ function dfrn_poll_post(App $a)
} }
$contact = $r[0]; $contact = $r[0];
$owner_uid = $r[0]['uid'];
$contact_id = $r[0]['id']; $contact_id = $r[0]['id'];
if ($type === 'reputation' && strlen($url)) { // Update the writable flag if it changed
$r = q("SELECT * FROM `contact` WHERE `url` = '%s' AND `uid` = %d LIMIT 1", Logger::debug('post request feed', ['post' => $_POST]);
DBA::escape($url), if ($dfrn_version >= 2.21) {
intval($owner_uid) if ($perm === 'rw') {
); $writable = 1;
$reputation = 0; } else {
$text = ''; $writable = 0;
if (DBA::isResult($r)) {
$reputation = $r[0]['rating'];
$text = $r[0]['reason'];
if ($r[0]['id'] == $contact_id) { // inquiring about own reputation not allowed
$reputation = 0;
$text = '';
}
} }
echo "<?xml version=\"1.0\" encoding=\"UTF-8\"?> if ($writable != $contact['writable']) {
<reputation> q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
<url>$url</url> intval($writable),
<rating>$reputation</rating> intval($contact_id)
<description>$text</description> );
</reputation>
";
exit();
// NOTREACHED
} else {
// Update the writable flag if it changed
Logger::debug('post request feed', ['post' => $_POST]);
if ($dfrn_version >= 2.21) {
if ($perm === 'rw') {
$writable = 1;
} else {
$writable = 0;
}
if ($writable != $contact['writable']) {
q("UPDATE `contact` SET `writable` = %d WHERE `id` = %d",
intval($writable),
intval($contact_id)
);
}
} }
header("Content-type: application/atom+xml");
$o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
echo $o;
exit();
} }
header("Content-type: application/atom+xml");
$o = DFRN::feed($dfrn_id, $a->argv[1], $last_update, $direction);
echo $o;
exit();
} }
function dfrn_poll_content(App $a) function dfrn_poll_content(App $a)
@ -499,20 +466,20 @@ function dfrn_poll_content(App $a)
// URL reply // URL reply
if ($dfrn_version < 2.2) { if ($dfrn_version < 2.2) {
$s = Network::fetchUrl($r[0]['poll'] $s = DI::httpRequest()->fetch($r[0]['poll']
. '?dfrn_id=' . $encrypted_id . '?dfrn_id=' . $encrypted_id
. '&type=profile-check' . '&type=profile-check'
. '&dfrn_version=' . DFRN_PROTOCOL_VERSION . '&dfrn_version=' . DFRN_PROTOCOL_VERSION
. '&challenge=' . $challenge . '&challenge=' . $challenge
. '&sec=' . $sec . '&sec=' . $sec
); );
} else { } else {
$s = Network::post($r[0]['poll'], [ $s = DI::httpRequest()->post($r[0]['poll'], [
'dfrn_id' => $encrypted_id, 'dfrn_id' => $encrypted_id,
'type' => 'profile-check', 'type' => 'profile-check',
'dfrn_version' => DFRN_PROTOCOL_VERSION, 'dfrn_version' => DFRN_PROTOCOL_VERSION,
'challenge' => $challenge, 'challenge' => $challenge,
'sec' => $sec 'sec' => $sec
])->getBody(); ])->getBody();
} }
@ -536,7 +503,7 @@ function dfrn_poll_content(App $a)
Session::setVisitorsContacts(); Session::setVisitorsContacts();
if (!$quiet) { if (!$quiet) {
info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']) . EOL); info(DI::l10n()->t('%1$s welcomes %2$s', $r[0]['username'], $r[0]['name']));
} }
// Visitors get 1 day session. // Visitors get 1 day session.

View File

@ -29,8 +29,8 @@ use Friendica\Core\Logger;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\Search; use Friendica\Core\Search;
use Friendica\Core\System;
use Friendica\Core\Session; use Friendica\Core\Session;
use Friendica\Core\System;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -110,7 +110,7 @@ function dfrn_request_post(App $a)
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
if (strlen($r[0]['dfrn-id'])) { if (strlen($r[0]['dfrn-id'])) {
// We don't need to be here. It has already happened. // We don't need to be here. It has already happened.
notice(DI::l10n()->t("This introduction has already been accepted.") . EOL); notice(DI::l10n()->t("This introduction has already been accepted."));
return; return;
} else { } else {
$contact_record = $r[0]; $contact_record = $r[0];
@ -128,18 +128,18 @@ function dfrn_request_post(App $a)
$parms = Probe::profile($dfrn_url); $parms = Probe::profile($dfrn_url);
if (!count($parms)) { if (!count($parms)) {
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL); notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
return; return;
} else { } else {
if (empty($parms['fn'])) { if (empty($parms['fn'])) {
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL); notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
} }
if (empty($parms['photo'])) { if (empty($parms['photo'])) {
notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL); notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
} }
$invalid = Probe::validDfrn($parms); $invalid = Probe::validDfrn($parms);
if ($invalid) { if ($invalid) {
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL); notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
return; return;
} }
} }
@ -177,7 +177,7 @@ function dfrn_request_post(App $a)
} }
if ($r) { if ($r) {
info(DI::l10n()->t("Introduction complete.") . EOL); info(DI::l10n()->t("Introduction complete."));
} }
$r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1", $r = q("SELECT `id`, `network` FROM `contact` WHERE `uid` = %d AND `url` = '%s' AND `site-pubkey` = '%s' LIMIT 1",
@ -189,7 +189,7 @@ function dfrn_request_post(App $a)
Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']); Group::addMember(User::getDefaultGroup(local_user(), $r[0]["network"]), $r[0]['id']);
if (isset($photo)) { if (isset($photo)) {
Contact::updateAvatar($photo, local_user(), $r[0]["id"], true); Contact::updateAvatar($r[0]["id"], $photo, true);
} }
$forward_path = "contact/" . $r[0]['id']; $forward_path = "contact/" . $r[0]['id'];
@ -203,7 +203,7 @@ function dfrn_request_post(App $a)
} }
if (!empty($dfrn_request) && strlen($confirm_key)) { if (!empty($dfrn_request) && strlen($confirm_key)) {
Network::fetchUrl($dfrn_request . '?confirm_key=' . $confirm_key); DI::httpRequest()->fetch($dfrn_request . '?confirm_key=' . $confirm_key);
} }
// (ignore reply, nothing we can do it failed) // (ignore reply, nothing we can do it failed)
@ -213,7 +213,7 @@ function dfrn_request_post(App $a)
} }
// invalid/bogus request // invalid/bogus request
notice(DI::l10n()->t('Unrecoverable protocol error.') . EOL); notice(DI::l10n()->t('Unrecoverable protocol error.'));
DI::baseUrl()->redirect(); DI::baseUrl()->redirect();
return; // NOTREACHED return; // NOTREACHED
} }
@ -240,7 +240,7 @@ function dfrn_request_post(App $a)
* *
*/ */
if (empty($a->profile['uid'])) { if (empty($a->profile['uid'])) {
notice(DI::l10n()->t('Profile unavailable.') . EOL); notice(DI::l10n()->t('Profile unavailable.'));
return; return;
} }
@ -261,9 +261,9 @@ function dfrn_request_post(App $a)
intval($uid) intval($uid)
); );
if (DBA::isResult($r) && count($r) > $maxreq) { if (DBA::isResult($r) && count($r) > $maxreq) {
notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']) . EOL); notice(DI::l10n()->t('%s has received too many connection requests today.', $a->profile['name']));
notice(DI::l10n()->t('Spam protection measures have been invoked.') . EOL); notice(DI::l10n()->t('Spam protection measures have been invoked.'));
notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.') . EOL); notice(DI::l10n()->t('Friends are advised to please try again in 24 hours.'));
return; return;
} }
} }
@ -287,14 +287,14 @@ function dfrn_request_post(App $a)
$url = trim($_POST['dfrn_url']); $url = trim($_POST['dfrn_url']);
if (!strlen($url)) { if (!strlen($url)) {
notice(DI::l10n()->t("Invalid locator") . EOL); notice(DI::l10n()->t("Invalid locator"));
return; return;
} }
$hcard = ''; $hcard = '';
// Detect the network // Detect the network
$data = Probe::uri($url); $data = Contact::getByURL($url);
$network = $data["network"]; $network = $data["network"];
// Canonicalize email-style profile locator // Canonicalize email-style profile locator
@ -323,10 +323,10 @@ function dfrn_request_post(App $a)
if (DBA::isResult($ret)) { if (DBA::isResult($ret)) {
if (strlen($ret[0]['issued-id'])) { if (strlen($ret[0]['issued-id'])) {
notice(DI::l10n()->t('You have already introduced yourself here.') . EOL); notice(DI::l10n()->t('You have already introduced yourself here.'));
return; return;
} elseif ($ret[0]['rel'] == Contact::FRIEND) { } elseif ($ret[0]['rel'] == Contact::FRIEND) {
notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']) . EOL); notice(DI::l10n()->t('Apparently you are already friends with %s.', $a->profile['name']));
return; return;
} else { } else {
$contact_record = $ret[0]; $contact_record = $ret[0];
@ -346,19 +346,19 @@ function dfrn_request_post(App $a)
} else { } else {
$url = Network::isUrlValid($url); $url = Network::isUrlValid($url);
if (!$url) { if (!$url) {
notice(DI::l10n()->t('Invalid profile URL.') . EOL); notice(DI::l10n()->t('Invalid profile URL.'));
DI::baseUrl()->redirect(DI::args()->getCommand()); DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED return; // NOTREACHED
} }
if (!Network::isUrlAllowed($url)) { if (!Network::isUrlAllowed($url)) {
notice(DI::l10n()->t('Disallowed profile URL.') . EOL); notice(DI::l10n()->t('Disallowed profile URL.'));
DI::baseUrl()->redirect(DI::args()->getCommand()); DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED return; // NOTREACHED
} }
if (Network::isUrlBlocked($url)) { if (Network::isUrlBlocked($url)) {
notice(DI::l10n()->t('Blocked domain') . EOL); notice(DI::l10n()->t('Blocked domain'));
DI::baseUrl()->redirect(DI::args()->getCommand()); DI::baseUrl()->redirect(DI::args()->getCommand());
return; // NOTREACHED return; // NOTREACHED
} }
@ -366,18 +366,18 @@ function dfrn_request_post(App $a)
$parms = Probe::profile(($hcard) ? $hcard : $url); $parms = Probe::profile(($hcard) ? $hcard : $url);
if (!count($parms)) { if (!count($parms)) {
notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.') . EOL); notice(DI::l10n()->t('Profile location is not valid or does not contain profile information.'));
DI::baseUrl()->redirect(DI::args()->getCommand()); DI::baseUrl()->redirect(DI::args()->getCommand());
} else { } else {
if (empty($parms['fn'])) { if (empty($parms['fn'])) {
notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.') . EOL); notice(DI::l10n()->t('Warning: profile location has no identifiable owner name.'));
} }
if (empty($parms['photo'])) { if (empty($parms['photo'])) {
notice(DI::l10n()->t('Warning: profile location has no profile photo.') . EOL); notice(DI::l10n()->t('Warning: profile location has no profile photo.'));
} }
$invalid = Probe::validDfrn($parms); $invalid = Probe::validDfrn($parms);
if ($invalid) { if ($invalid) {
notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid) . EOL); notice(DI::l10n()->tt("%d required parameter was not found at the given location", "%d required parameters were not found at the given location", $invalid));
return; return;
} }
@ -420,12 +420,12 @@ function dfrn_request_post(App $a)
); );
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$contact_record = $r[0]; $contact_record = $r[0];
Contact::updateAvatar($photo, $uid, $contact_record["id"], true); Contact::updateAvatar($contact_record["id"], $photo, true);
} }
} }
} }
if ($r === false) { if ($r === false) {
notice(DI::l10n()->t('Failed to update contact record.') . EOL); notice(DI::l10n()->t('Failed to update contact record.'));
return; return;
} }
@ -445,7 +445,7 @@ function dfrn_request_post(App $a)
// This notice will only be seen by the requestor if the requestor and requestee are on the same server. // This notice will only be seen by the requestor if the requestor and requestee are on the same server.
if (!$failed) { if (!$failed) {
info(DI::l10n()->t('Your introduction has been sent.') . EOL); info(DI::l10n()->t('Your introduction has been sent.'));
} }
// "Homecoming" - send the requestor back to their site to record the introduction. // "Homecoming" - send the requestor back to their site to record the introduction.
@ -477,7 +477,7 @@ function dfrn_request_post(App $a)
// NOTREACHED // NOTREACHED
// END $network != Protocol::PHANTOM // END $network != Protocol::PHANTOM
} else { } else {
notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system.") . EOL); notice(DI::l10n()->t("Remote subscription can't be done for your network. Please subscribe directly on your system."));
return; return;
} }
} return; } return;
@ -493,7 +493,7 @@ function dfrn_request_content(App $a)
// to send us to the post section to record the introduction. // to send us to the post section to record the introduction.
if (!empty($_GET['dfrn_url'])) { if (!empty($_GET['dfrn_url'])) {
if (!local_user()) { if (!local_user()) {
info(DI::l10n()->t("Please login to confirm introduction.") . EOL); info(DI::l10n()->t("Please login to confirm introduction."));
/* setup the return URL to come back to this page if they use openid */ /* setup the return URL to come back to this page if they use openid */
return Login::form(); return Login::form();
} }
@ -501,7 +501,7 @@ function dfrn_request_content(App $a)
// Edge case, but can easily happen in the wild. This person is authenticated, // Edge case, but can easily happen in the wild. This person is authenticated,
// but not as the person who needs to deal with this request. // but not as the person who needs to deal with this request.
if ($a->user['nickname'] != $a->argv[1]) { if ($a->user['nickname'] != $a->argv[1]) {
notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile.") . EOL); notice(DI::l10n()->t("Incorrect identity currently logged in. Please login to <strong>this</strong> profile."));
return Login::form(); return Login::form();
} }
@ -603,7 +603,7 @@ function dfrn_request_content(App $a)
// Normal web request. Display our user's introduction form. // Normal web request. Display our user's introduction form.
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
if (!DI::config()->get('system', 'local_block')) { if (!DI::config()->get('system', 'local_block')) {
notice(DI::l10n()->t('Public access denied.') . EOL); notice(DI::l10n()->t('Public access denied.'));
return; return;
} }
} }

View File

@ -164,7 +164,7 @@ function display_fetchauthor($a, $item)
$profiledata["about"] = ""; $profiledata["about"] = "";
} }
$profiledata = Contact::getDetailsByURL($profiledata["url"], local_user(), $profiledata); $profiledata = Contact::getByURLForUser($profiledata["url"], local_user()) ?: $profiledata;
if (!empty($profiledata["photo"])) { if (!empty($profiledata["photo"])) {
$profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]); $profiledata["photo"] = DI::baseUrl()->remove($profiledata["photo"]);

View File

@ -35,14 +35,14 @@ function editpost_content(App $a)
$o = ''; $o = '';
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
$post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0); $post_id = (($a->argc > 1) ? intval($a->argv[1]) : 0);
if (!$post_id) { if (!$post_id) {
notice(DI::l10n()->t('Item not found') . EOL); notice(DI::l10n()->t('Item not found'));
return; return;
} }
@ -52,7 +52,7 @@ function editpost_content(App $a)
$item = Item::selectFirstForUser(local_user(), $fields, ['id' => $post_id, 'uid' => local_user()]); $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $post_id, 'uid' => local_user()]);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
notice(DI::l10n()->t('Item not found') . EOL); notice(DI::l10n()->t('Item not found'));
return; return;
} }
@ -131,7 +131,7 @@ function editpost_content(App $a)
//jot nav tab (used in some themes) //jot nav tab (used in some themes)
'$message' => DI::l10n()->t('Message'), '$message' => DI::l10n()->t('Message'),
'$browser' => DI::l10n()->t('Browser'), '$browser' => DI::l10n()->t('Browser'),
'$shortpermset' => DI::l10n()->t('permissions'), '$shortpermset' => DI::l10n()->t('Permissions'),
'$compose_link_title' => DI::l10n()->t('Open Compose page'), '$compose_link_title' => DI::l10n()->t('Open Compose page'),
]); ]);
@ -145,7 +145,7 @@ function undo_post_tagging($s) {
if ($cnt) { if ($cnt) {
foreach ($matches as $mtch) { foreach ($matches as $mtch) {
if (in_array($mtch[1], ['!', '@'])) { if (in_array($mtch[1], ['!', '@'])) {
$contact = Contact::getDetailsByURL($mtch[2]); $contact = Contact::getByURL($mtch[2], false, ['addr']);
$mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr']; $mtch[3] = empty($contact['addr']) ? $mtch[2] : $contact['addr'];
} }
$s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s); $s = str_replace($mtch[0], $mtch[1] . $mtch[3],$s);

View File

@ -132,7 +132,7 @@ function events_post(App $a)
$onerror_path = 'events/' . $action . '?' . http_build_query($params, null, null, PHP_QUERY_RFC3986); $onerror_path = 'events/' . $action . '?' . http_build_query($params, null, null, PHP_QUERY_RFC3986);
if (strcmp($finish, $start) < 0 && !$nofinish) { if (strcmp($finish, $start) < 0 && !$nofinish) {
notice(DI::l10n()->t('Event can not end before it has started.') . EOL); notice(DI::l10n()->t('Event can not end before it has started.'));
if (intval($_REQUEST['preview'])) { if (intval($_REQUEST['preview'])) {
echo DI::l10n()->t('Event can not end before it has started.'); echo DI::l10n()->t('Event can not end before it has started.');
exit(); exit();
@ -141,7 +141,7 @@ function events_post(App $a)
} }
if (!$summary || ($start === DBA::NULL_DATETIME)) { if (!$summary || ($start === DBA::NULL_DATETIME)) {
notice(DI::l10n()->t('Event title and start time are required.') . EOL); notice(DI::l10n()->t('Event title and start time are required.'));
if (intval($_REQUEST['preview'])) { if (intval($_REQUEST['preview'])) {
echo DI::l10n()->t('Event title and start time are required.'); echo DI::l10n()->t('Event title and start time are required.');
exit(); exit();
@ -225,7 +225,7 @@ function events_post(App $a)
function events_content(App $a) function events_content(App $a)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return Login::form(); return Login::form();
} }
@ -256,6 +256,11 @@ function events_content(App $a)
// get the translation strings for the callendar // get the translation strings for the callendar
$i18n = Event::getStrings(); $i18n = Event::getStrings();
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.min.css');
DI::page()->registerStylesheet('view/asset/fullcalendar/dist/fullcalendar.print.min.css', 'print');
DI::page()->registerFooterScript('view/asset/moment/min/moment-with-locales.min.js');
DI::page()->registerFooterScript('view/asset/fullcalendar/dist/fullcalendar.min.js');
$htpl = Renderer::getMarkupTemplate('event_head.tpl'); $htpl = Renderer::getMarkupTemplate('event_head.tpl');
DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [ DI::page()['htmlhead'] .= Renderer::replaceMacros($htpl, [
'$module_url' => '/events', '$module_url' => '/events',
@ -469,16 +474,16 @@ function events_content(App $a)
$t_orig = $orig_event['summary'] ?? ''; $t_orig = $orig_event['summary'] ?? '';
$d_orig = $orig_event['desc'] ?? ''; $d_orig = $orig_event['desc'] ?? '';
$l_orig = $orig_event['location'] ?? ''; $l_orig = $orig_event['location'] ?? '';
$eid = !empty($orig_event) ? $orig_event['id'] : 0; $eid = $orig_event['id'] ?? 0;
$cid = !empty($orig_event) ? $orig_event['cid'] : 0; $cid = $orig_event['cid'] ?? 0;
$uri = !empty($orig_event) ? $orig_event['uri'] : ''; $uri = $orig_event['uri'] ?? '';
if ($cid || $mode === 'edit') { if ($cid || $mode === 'edit') {
$share_disabled = 'disabled="disabled"'; $share_disabled = 'disabled="disabled"';
} }
$sdt = !empty($orig_event) ? $orig_event['start'] : 'now'; $sdt = $orig_event['start'] ?? 'now';
$fdt = !empty($orig_event) ? $orig_event['finish'] : 'now'; $fdt = $orig_event['finish'] ?? 'now';
$tz = date_default_timezone_get(); $tz = date_default_timezone_get();
if (!empty($orig_event)) { if (!empty($orig_event)) {
@ -583,9 +588,7 @@ function events_content(App $a)
} }
if (Item::exists(['id' => $ev[0]['itemid']])) { if (Item::exists(['id' => $ev[0]['itemid']])) {
notice(DI::l10n()->t('Failed to remove event') . EOL); notice(DI::l10n()->t('Failed to remove event'));
} else {
info(DI::l10n()->t('Event removed') . EOL);
} }
DI::baseUrl()->redirect('events'); DI::baseUrl()->redirect('events');

View File

@ -9,6 +9,7 @@ use Friendica\App;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Photo;
use Friendica\Util\Images; use Friendica\Util\Images;
use Friendica\Util\Strings; use Friendica\Util\Strings;
@ -47,8 +48,8 @@ function fbrowser_content(App $a)
if ($a->argc==2) { if ($a->argc==2) {
$photos = q("SELECT distinct(`album`) AS `album` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' ", $photos = q("SELECT distinct(`album`) AS `album` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' ",
intval(local_user()), intval(local_user()),
DBA::escape('Contact Photos'), DBA::escape(Photo::CONTACT_PHOTOS),
DBA::escape(DI::l10n()->t('Contact Photos')) DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
); );
$albums = array_column($photos, 'album'); $albums = array_column($photos, 'album');
@ -66,8 +67,8 @@ function fbrowser_content(App $a)
FROM `photo` WHERE `uid` = %d $sql_extra AND `album` != '%s' AND `album` != '%s' FROM `photo` WHERE `uid` = %d $sql_extra AND `album` != '%s' AND `album` != '%s'
GROUP BY `resource-id` $sql_extra2", GROUP BY `resource-id` $sql_extra2",
intval(local_user()), intval(local_user()),
DBA::escape('Contact Photos'), DBA::escape(Photo::CONTACT_PHOTOS),
DBA::escape(DI::l10n()->t('Contact Photos')) DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
); );
function _map_files1($rr) function _map_files1($rr)

View File

@ -62,7 +62,7 @@ function follow_post(App $a)
DI::baseUrl()->redirect('contact/' . $result['cid']); DI::baseUrl()->redirect('contact/' . $result['cid']);
} }
info(DI::l10n()->t('The contact could not be added.')); notice(DI::l10n()->t('The contact could not be added.'));
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
// NOTREACHED // NOTREACHED
@ -107,8 +107,14 @@ function follow_content(App $a)
} }
} }
$contact = Contact::getByURL($url, 0, [], true); $contact = Contact::getByURL($url, true);
// Possibly it is a mail contact
if (empty($contact)) { if (empty($contact)) {
$contact = Probe::uri($url, Protocol::MAIL, $uid);
}
if (empty($contact) || ($contact['network'] == Protocol::PHANTOM)) {
// Possibly it is a remote item and not an account // Possibly it is a remote item and not an account
follow_remote_item($url); follow_remote_item($url);

View File

@ -30,6 +30,7 @@
use Friendica\App; use Friendica\App;
use Friendica\Content\Item as ItemHelper; use Friendica\Content\Item as ItemHelper;
use Friendica\Content\PageInfo;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
@ -57,8 +58,6 @@ use Friendica\Util\Security;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Worker\Delivery; use Friendica\Worker\Delivery;
require_once __DIR__ . '/../include/items.php';
function item_post(App $a) { function item_post(App $a) {
if (!Session::isAuthenticated()) { if (!Session::isAuthenticated()) {
throw new HTTPException\ForbiddenException(); throw new HTTPException\ForbiddenException();
@ -137,6 +136,16 @@ function item_post(App $a) {
throw new HTTPException\NotFoundException(DI::l10n()->t('Unable to locate original post.')); throw new HTTPException\NotFoundException(DI::l10n()->t('Unable to locate original post.'));
} }
// When commenting on a public post then store the post for the current user
// This enables interaction like starring and saving into folders
if ($toplevel_item['uid'] == 0) {
$stored = Item::storeForUserByUriId($toplevel_item['uri-id'], local_user());
Logger::info('Public item stored for user', ['uri-id' => $toplevel_item['uri-id'], 'uid' => $uid, 'stored' => $stored]);
if ($stored) {
$toplevel_item = Item::selectFirst([], ['id' => $stored]);
}
}
$toplevel_item_id = $toplevel_item['id']; $toplevel_item_id = $toplevel_item['id'];
$parent_user = $toplevel_item['uid']; $parent_user = $toplevel_item['uid'];
@ -233,7 +242,7 @@ function item_post(App $a) {
]; ];
} }
$att_bbcode = add_page_info_data($attachment); $att_bbcode = "\n" . PageInfo::getFooterFromData($attachment);
$body .= $att_bbcode; $body .= $att_bbcode;
} }
@ -251,7 +260,7 @@ function item_post(App $a) {
$objecttype = $orig_post['object-type']; $objecttype = $orig_post['object-type'];
$app = $orig_post['app']; $app = $orig_post['app'];
$categories = $orig_post['file'] ?? ''; $categories = $orig_post['file'] ?? '';
$title = Strings::escapeTags(trim($_REQUEST['title'])); $title = trim($_REQUEST['title'] ?? '');
$body = trim($body); $body = trim($body);
$private = $orig_post['private']; $private = $orig_post['private'];
$pubmail_enabled = $orig_post['pubmail']; $pubmail_enabled = $orig_post['pubmail'];
@ -272,13 +281,13 @@ function item_post(App $a) {
$str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? ''; $str_group_deny = isset($_REQUEST['group_deny']) ? $aclFormatter->toString($_REQUEST['group_deny']) : $user['deny_gid'] ?? '';
} }
$title = Strings::escapeTags(trim($_REQUEST['title'] ?? '')); $title = trim($_REQUEST['title'] ?? '');
$location = Strings::escapeTags(trim($_REQUEST['location'] ?? '')); $location = trim($_REQUEST['location'] ?? '');
$coord = Strings::escapeTags(trim($_REQUEST['coord'] ?? '')); $coord = trim($_REQUEST['coord'] ?? '');
$verb = Strings::escapeTags(trim($_REQUEST['verb'] ?? '')); $verb = trim($_REQUEST['verb'] ?? '');
$emailcc = Strings::escapeTags(trim($_REQUEST['emailcc'] ?? '')); $emailcc = trim($_REQUEST['emailcc'] ?? '');
$body = trim($body); $body = trim($body);
$network = Strings::escapeTags(trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN)); $network = trim(($_REQUEST['network'] ?? '') ?: Protocol::DFRN);
$guid = System::createUUID(); $guid = System::createUUID();
$postopts = $_REQUEST['postopts'] ?? ''; $postopts = $_REQUEST['postopts'] ?? '';
@ -324,7 +333,7 @@ function item_post(App $a) {
System::jsonExit(['preview' => '']); System::jsonExit(['preview' => '']);
} }
info(DI::l10n()->t('Empty post discarded.')); notice(DI::l10n()->t('Empty post discarded.'));
if ($return_path) { if ($return_path) {
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
} }
@ -694,7 +703,6 @@ function item_post(App $a) {
// update filetags in pconfig // update filetags in pconfig
FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category'); FileTag::updatePconfig($uid, $categories_old, $categories_new, 'category');
info(DI::l10n()->t('Post updated.'));
if ($return_path) { if ($return_path) {
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
} }
@ -716,7 +724,7 @@ function item_post(App $a) {
$post_id = Item::insert($datarray); $post_id = Item::insert($datarray);
if (!$post_id) { if (!$post_id) {
info(DI::l10n()->t('Item wasn\'t stored.')); notice(DI::l10n()->t('Item wasn\'t stored.'));
if ($return_path) { if ($return_path) {
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
} }
@ -817,7 +825,6 @@ function item_post(App $a) {
return $post_id; return $post_id;
} }
info(DI::l10n()->t('Post published.'));
item_post_return(DI::baseUrl(), $api_source, $return_path); item_post_return(DI::baseUrl(), $api_source, $return_path);
// NOTREACHED // NOTREACHED
} }
@ -881,7 +888,7 @@ function drop_item(int $id, string $return = '')
$item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]); $item = Item::selectFirstForUser(local_user(), $fields, ['id' => $id]);
if (!DBA::isResult($item)) { if (!DBA::isResult($item)) {
notice(DI::l10n()->t('Item not found.') . EOL); notice(DI::l10n()->t('Item not found.'));
DI::baseUrl()->redirect('network'); DI::baseUrl()->redirect('network');
} }
@ -897,40 +904,8 @@ function drop_item(int $id, string $return = '')
} }
if ((local_user() == $item['uid']) || $contact_id) { if ((local_user() == $item['uid']) || $contact_id) {
// Check if we should do HTML-based delete confirmation
if (!empty($_REQUEST['confirm'])) {
// <form> can't take arguments in its "action" parameter
// so add any arguments as hidden inputs
$query = explode_querystring(DI::args()->getQueryString());
$inputs = [];
foreach ($query['args'] as $arg) {
if (strpos($arg, 'confirm=') === false) {
$arg_parts = explode('=', $arg);
$inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
}
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$method' => 'get',
'$message' => DI::l10n()->t('Do you really want to delete this item?'),
'$extra_inputs' => $inputs,
'$confirm' => DI::l10n()->t('Yes'),
'$confirm_url' => $query['base'],
'$confirm_name' => 'confirmed',
'$cancel' => DI::l10n()->t('Cancel'),
]);
}
// Now check how the user responded to the confirmation query
if (!empty($_REQUEST['canceled'])) {
DI::baseUrl()->redirect('display/' . $item['guid']);
}
$is_comment = $item['gravity'] == GRAVITY_COMMENT;
$parentitem = null;
if (!empty($item['parent'])) { if (!empty($item['parent'])) {
$fields = ['guid']; $parentitem = Item::selectFirstForUser(local_user(), ['guid'], ['id' => $item['parent']]);
$parentitem = Item::selectFirstForUser(local_user(), $fields, ['id' => $item['parent']]);
} }
// delete the item // delete the item
@ -942,7 +917,7 @@ function drop_item(int $id, string $return = '')
$return_url = str_replace("update_", "", $return_url); $return_url = str_replace("update_", "", $return_url);
// Check if delete a comment // Check if delete a comment
if ($is_comment) { if ($item['gravity'] == GRAVITY_COMMENT) {
// Return to parent guid // Return to parent guid
if (!empty($parentitem)) { if (!empty($parentitem)) {
DI::baseUrl()->redirect('display/' . $parentitem['guid']); DI::baseUrl()->redirect('display/' . $parentitem['guid']);

View File

@ -1,161 +0,0 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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/>.
*
*/
use Friendica\App;
use Friendica\Core\Hook;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Group;
use Friendica\Model\Item;
function lockview_content(App $a)
{
$type = (($a->argc > 1) ? $a->argv[1] : 0);
if (is_numeric($type)) {
$item_id = intval($type);
$type = 'item';
} else {
$item_id = (($a->argc > 2) ? intval($a->argv[2]) : 0);
}
if (!$item_id) {
exit();
}
if (!in_array($type, ['item','photo','event'])) {
exit();
}
$fields = ['uid', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid'];
$condition = ['id' => $item_id];
if ($type != 'item') {
$item = DBA::selectFirst($type, $fields, $condition);
} else {
$fields[] = 'private';
$item = Item::selectFirst($fields, $condition);
}
if (!DBA::isResult($item)) {
exit();
}
Hook::callAll('lockview_content', $item);
if ($item['uid'] != local_user()) {
echo DI::l10n()->t('Remote privacy information not available.') . '<br />';
exit();
}
if (isset($item['private'])
&& $item['private'] == Item::PRIVATE
&& empty($item['allow_cid'])
&& empty($item['allow_gid'])
&& empty($item['deny_cid'])
&& empty($item['deny_gid']))
{
echo DI::l10n()->t('Remote privacy information not available.') . '<br />';
exit();
}
$aclFormatter = DI::aclFormatter();
$allowed_users = $aclFormatter->expand($item['allow_cid']);
$allowed_groups = $aclFormatter->expand($item['allow_gid']);
$deny_users = $aclFormatter->expand($item['deny_cid']);
$deny_groups = $aclFormatter->expand($item['deny_gid']);
$o = DI::l10n()->t('Visible to:') . '<br />';
$l = [];
if (count($allowed_groups)) {
$key = array_search(Group::FOLLOWERS, $allowed_groups);
if ($key !== false) {
$l[] = '<b>' . DI::l10n()->t('Followers') . '</b>';
unset($allowed_groups[$key]);
}
$key = array_search(Group::MUTUALS, $allowed_groups);
if ($key !== false) {
$l[] = '<b>' . DI::l10n()->t('Mutuals') . '</b>';
unset($allowed_groups[$key]);
}
$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
DBA::escape(implode(', ', $allowed_groups))
);
if (DBA::isResult($r)) {
foreach ($r as $rr) {
$l[] = '<b>' . $rr['name'] . '</b>';
}
}
}
if (count($allowed_users)) {
$r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )",
DBA::escape(implode(', ', $allowed_users))
);
if (DBA::isResult($r)) {
foreach ($r as $rr) {
$l[] = $rr['name'];
}
}
}
if (count($deny_groups)) {
$key = array_search(Group::FOLLOWERS, $deny_groups);
if ($key !== false) {
$l[] = '<b><strike>' . DI::l10n()->t('Followers') . '</strike></b>';
unset($deny_groups[$key]);
}
$key = array_search(Group::MUTUALS, $deny_groups);
if ($key !== false) {
$l[] = '<b><strike>' . DI::l10n()->t('Mutuals') . '</strike></b>';
unset($deny_groups[$key]);
}
$r = q("SELECT `name` FROM `group` WHERE `id` IN ( %s )",
DBA::escape(implode(', ', $deny_groups))
);
if (DBA::isResult($r)) {
foreach ($r as $rr) {
$l[] = '<b><strike>' . $rr['name'] . '</strike></b>';
}
}
}
if (count($deny_users)) {
$r = q("SELECT `name` FROM `contact` WHERE `id` IN ( %s )",
DBA::escape(implode(', ', $deny_users))
);
if (DBA::isResult($r)) {
foreach ($r as $rr) {
$l[] = '<strike>' . $rr['name'] . '</strike>';
}
}
}
echo $o . implode(', ', $l);
exit();
}

View File

@ -37,7 +37,7 @@ function lostpass_post(App $a)
$condition = ['(`email` = ? OR `nickname` = ?) AND `verified` = 1 AND `blocked` = 0', $loginame, $loginame]; $condition = ['(`email` = ? OR `nickname` = ?) AND `verified` = 1 AND `blocked` = 0', $loginame, $loginame];
$user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition); $user = DBA::selectFirst('user', ['uid', 'username', 'nickname', 'email', 'language'], $condition);
if (!DBA::isResult($user)) { if (!DBA::isResult($user)) {
notice(DI::l10n()->t('No valid account found.') . EOL); notice(DI::l10n()->t('No valid account found.'));
DI::baseUrl()->redirect(); DI::baseUrl()->redirect();
} }
@ -49,7 +49,7 @@ function lostpass_post(App $a)
]; ];
$result = DBA::update('user', $fields, ['uid' => $user['uid']]); $result = DBA::update('user', $fields, ['uid' => $user['uid']]);
if ($result) { if ($result) {
info(DI::l10n()->t('Password reset request issued. Check your email.') . EOL); info(DI::l10n()->t('Password reset request issued. Check your email.'));
} }
$sitename = DI::config()->get('config', 'sitename'); $sitename = DI::config()->get('config', 'sitename');
@ -152,7 +152,7 @@ function lostpass_generate_password($user)
'$newpass' => $new_password, '$newpass' => $new_password,
]); ]);
info("Your password has been reset." . EOL); info(DI::l10n()->t("Your password has been reset."));
$sitename = DI::config()->get('config', 'sitename'); $sitename = DI::config()->get('config', 'sitename');
$preamble = Strings::deindent(DI::l10n()->t(' $preamble = Strings::deindent(DI::l10n()->t('

View File

@ -27,8 +27,7 @@ use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Util\Network; use Friendica\Module\Contact as ModuleContact;
use Friendica\Util\Proxy as ProxyUtils;
/** /**
* Controller for /match. * Controller for /match.
@ -60,7 +59,7 @@ function match_content(App $a)
return ''; return '';
} }
if (!$profile['pub_keywords'] && (!$profile['prv_keywords'])) { if (!$profile['pub_keywords'] && (!$profile['prv_keywords'])) {
notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.') . EOL); notice(DI::l10n()->t('No keywords to match. Please add keywords to your profile.'));
return ''; return '';
} }
@ -76,7 +75,7 @@ function match_content(App $a)
$host = DI::baseUrl(); $host = DI::baseUrl();
} }
$msearch_json = Network::post($host . '/msearch', $params)->getBody(); $msearch_json = DI::httpRequest()->post($host . '/msearch', $params)->getBody();
$msearch = json_decode($msearch_json); $msearch = json_decode($msearch_json);
@ -89,37 +88,14 @@ function match_content(App $a)
$profile = $msearch->results[$i]; $profile = $msearch->results[$i];
// Already known contact // Already known contact
if (!$profile || Contact::getIdForURL($profile->url, local_user(), true)) { if (!$profile || Contact::getIdForURL($profile->url, local_user())) {
continue; continue;
} }
// Workaround for wrong directory photo URL $contact = Contact::getByURLForUser($profile->url, local_user());
$profile->photo = str_replace('http:///photo/', Search::getGlobalDirectory() . '/photo/', $profile->photo); if (!empty($contact)) {
$entries[] = ModuleContact::getContactTemplateVars($contact);
$connlnk = DI::baseUrl() . '/follow/?url=' . $profile->url; }
$photo_menu = [
'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($profile->url)],
'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk]
];
$contact_details = Contact::getDetailsByURL($profile->url, 0);
$entry = [
'url' => Contact::magicLink($profile->url),
'itemurl' => $contact_details['addr'] ?? $profile->url,
'name' => $profile->name,
'details' => $contact_details['location'] ?? '',
'tags' => $contact_details['keywords'] ?? '',
'about' => $contact_details['about'] ?? '',
'account_type' => Contact::getAccountType($contact_details),
'thumb' => ProxyUtils::proxifyUrl($profile->photo, false, ProxyUtils::SIZE_THUMB),
'conntxt' => DI::l10n()->t('Connect'),
'connlnk' => $connlnk,
'img_hover' => $profile->tags,
'photo_menu' => $photo_menu,
'id' => $i,
];
$entries[] = $entry;
} }
$data = [ $data = [
@ -141,7 +117,7 @@ function match_content(App $a)
} }
if (empty($entries)) { if (empty($entries)) {
info(DI::l10n()->t('No matches') . EOL); info(DI::l10n()->t('No matches'));
} }
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');

View File

@ -32,7 +32,6 @@ use Friendica\Model\Mail;
use Friendica\Model\Notify\Type; use Friendica\Model\Notify\Type;
use Friendica\Module\Security\Login; use Friendica\Module\Security\Login;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\Temporal; use Friendica\Util\Temporal;
@ -68,34 +67,32 @@ function message_init(App $a)
function message_post(App $a) function message_post(App $a)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
$replyto = !empty($_REQUEST['replyto']) ? Strings::escapeTags(trim($_REQUEST['replyto'])) : ''; $replyto = !empty($_REQUEST['replyto']) ? Strings::escapeTags(trim($_REQUEST['replyto'])) : '';
$subject = !empty($_REQUEST['subject']) ? Strings::escapeTags(trim($_REQUEST['subject'])) : ''; $subject = !empty($_REQUEST['subject']) ? Strings::escapeTags(trim($_REQUEST['subject'])) : '';
$body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : ''; $body = !empty($_REQUEST['body']) ? Strings::escapeHtml(trim($_REQUEST['body'])) : '';
$recipient = !empty($_REQUEST['messageto']) ? intval($_REQUEST['messageto']) : 0; $recipient = !empty($_REQUEST['recipient']) ? intval($_REQUEST['recipient']) : 0;
$ret = Mail::send($recipient, $body, $subject, $replyto); $ret = Mail::send($recipient, $body, $subject, $replyto);
$norecip = false; $norecip = false;
switch ($ret) { switch ($ret) {
case -1: case -1:
notice(DI::l10n()->t('No recipient selected.') . EOL); notice(DI::l10n()->t('No recipient selected.'));
$norecip = true; $norecip = true;
break; break;
case -2: case -2:
notice(DI::l10n()->t('Unable to locate contact information.') . EOL); notice(DI::l10n()->t('Unable to locate contact information.'));
break; break;
case -3: case -3:
notice(DI::l10n()->t('Message could not be sent.') . EOL); notice(DI::l10n()->t('Message could not be sent.'));
break; break;
case -4: case -4:
notice(DI::l10n()->t('Message collection failure.') . EOL); notice(DI::l10n()->t('Message collection failure.'));
break; break;
default:
info(DI::l10n()->t('Message sent.') . EOL);
} }
// fake it to go back to the input form if no recipient listed // fake it to go back to the input form if no recipient listed
@ -113,7 +110,7 @@ function message_content(App $a)
Nav::setSelected('messages'); Nav::setSelected('messages');
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return Login::form(); return Login::form();
} }
@ -144,51 +141,20 @@ function message_content(App $a)
return; return;
} }
// Check if we should do HTML-based delete confirmation
if (!empty($_REQUEST['confirm'])) {
// <form> can't take arguments in its "action" parameter
// so add any arguments as hidden inputs
$query = explode_querystring(DI::args()->getQueryString());
$inputs = [];
foreach ($query['args'] as $arg) {
if (strpos($arg, 'confirm=') === false) {
$arg_parts = explode('=', $arg);
$inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
}
}
//DI::page()['aside'] = '';
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$method' => 'get',
'$message' => DI::l10n()->t('Do you really want to delete this message?'),
'$extra_inputs' => $inputs,
'$confirm' => DI::l10n()->t('Yes'),
'$confirm_url' => $query['base'],
'$confirm_name' => 'confirmed',
'$cancel' => DI::l10n()->t('Cancel'),
]);
}
// Now check how the user responded to the confirmation query
if (!empty($_REQUEST['canceled'])) {
DI::baseUrl()->redirect('message');
}
$cmd = $a->argv[1]; $cmd = $a->argv[1];
if ($cmd === 'drop') { if ($cmd === 'drop') {
$message = DBA::selectFirst('mail', ['convid'], ['id' => $a->argv[2], 'uid' => local_user()]); $message = DBA::selectFirst('mail', ['convid'], ['id' => $a->argv[2], 'uid' => local_user()]);
if(!DBA::isResult($message)){ if(!DBA::isResult($message)){
info(DI::l10n()->t('Conversation not found.') . EOL); notice(DI::l10n()->t('Conversation not found.'));
DI::baseUrl()->redirect('message'); DI::baseUrl()->redirect('message');
} }
if (DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) { if (!DBA::delete('mail', ['id' => $a->argv[2], 'uid' => local_user()])) {
info(DI::l10n()->t('Message deleted.') . EOL); notice(DI::l10n()->t('Message was not deleted.'));
} }
$conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => local_user()]); $conversation = DBA::selectFirst('mail', ['id'], ['convid' => $message['convid'], 'uid' => local_user()]);
if(!DBA::isResult($conversation)){ if(!DBA::isResult($conversation)){
info(DI::l10n()->t('Conversation removed.') . EOL);
DI::baseUrl()->redirect('message'); DI::baseUrl()->redirect('message');
} }
@ -201,8 +167,8 @@ function message_content(App $a)
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$parent = $r[0]['parent-uri']; $parent = $r[0]['parent-uri'];
if (DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) { if (!DBA::delete('mail', ['parent-uri' => $parent, 'uid' => local_user()])) {
info(DI::l10n()->t('Conversation removed.') . EOL); notice(DI::l10n()->t('Conversation was not removed.'));
} }
} }
DI::baseUrl()->redirect('message'); DI::baseUrl()->redirect('message');
@ -219,50 +185,14 @@ function message_content(App $a)
'$linkurl' => DI::l10n()->t('Please enter a link URL:') '$linkurl' => DI::l10n()->t('Please enter a link URL:')
]); ]);
$preselect = isset($a->argv[2]) ? [$a->argv[2]] : []; $recipientId = $a->argv[2] ?? null;
$prename = $preurl = $preid = ''; $select = ACL::getMessageContactSelectHTML($recipientId);
if ($preselect) {
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `id` = %d LIMIT 1",
intval(local_user()),
intval($a->argv[2])
);
if (!DBA::isResult($r)) {
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `nurl` = '%s' LIMIT 1",
intval(local_user()),
DBA::escape(Strings::normaliseLink(base64_decode($a->argv[2])))
);
}
if (!DBA::isResult($r)) {
$r = q("SELECT `name`, `url`, `id` FROM `contact` WHERE `uid` = %d AND `addr` = '%s' LIMIT 1",
intval(local_user()),
DBA::escape(base64_decode($a->argv[2]))
);
}
if (DBA::isResult($r)) {
$prename = $r[0]['name'];
$preid = $r[0]['id'];
$preselect = [$preid];
} else {
$preselect = [];
}
}
$prefill = $preselect ? $prename : '';
// the ugly select box
$select = ACL::getMessageContactSelectHTML('messageto', 'message-to-select', $preselect, 4, 10);
$tpl = Renderer::getMarkupTemplate('prv_message.tpl'); $tpl = Renderer::getMarkupTemplate('prv_message.tpl');
$o .= Renderer::replaceMacros($tpl, [ $o .= Renderer::replaceMacros($tpl, [
'$header' => DI::l10n()->t('Send Private Message'), '$header' => DI::l10n()->t('Send Private Message'),
'$to' => DI::l10n()->t('To:'), '$to' => DI::l10n()->t('To:'),
'$showinputs' => 'true',
'$prefill' => $prefill,
'$preid' => $preid,
'$subject' => DI::l10n()->t('Subject:'), '$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $_REQUEST['subject'] ?? '', '$subjtxt' => $_REQUEST['subject'] ?? '',
'$text' => $_REQUEST['body'] ?? '', '$text' => $_REQUEST['body'] ?? '',
@ -301,7 +231,7 @@ function message_content(App $a)
$r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage()); $r = get_messages(local_user(), $pager->getStart(), $pager->getItemsPerPage());
if (!DBA::isResult($r)) { if (!DBA::isResult($r)) {
info(DI::l10n()->t('No messages.') . EOL); notice(DI::l10n()->t('No messages.'));
return $o; return $o;
} }
@ -358,7 +288,7 @@ function message_content(App $a)
} }
if (!DBA::isResult($messages)) { if (!DBA::isResult($messages)) {
notice(DI::l10n()->t('Message not available.') . EOL); notice(DI::l10n()->t('Message not available.'));
return $o; return $o;
} }
@ -396,12 +326,8 @@ function message_content(App $a)
$body_e = BBCode::convert($message['body']); $body_e = BBCode::convert($message['body']);
$to_name_e = $message['name']; $to_name_e = $message['name'];
$contact = Contact::getDetailsByURL($message['from-url']); $contact = Contact::getByURL($message['from-url'], false, ['thumb', 'addr', 'id', 'avatar']);
if (isset($contact["thumb"])) { $from_photo = Contact::getThumb($contact, $message['from-photo']);
$from_photo = $contact["thumb"];
} else {
$from_photo = $message['from-photo'];
}
$mails[] = [ $mails[] = [
'id' => $message['id'], 'id' => $message['id'],
@ -409,7 +335,7 @@ function message_content(App $a)
'from_url' => $from_url, 'from_url' => $from_url,
'from_addr' => $contact['addr'], 'from_addr' => $contact['addr'],
'sparkle' => $sparkle, 'sparkle' => $sparkle,
'from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB), 'from_photo' => $from_photo,
'subject' => $subject_e, 'subject' => $subject_e,
'body' => $body_e, 'body' => $body_e,
'delete' => DI::l10n()->t('Delete message'), 'delete' => DI::l10n()->t('Delete message'),
@ -421,7 +347,7 @@ function message_content(App $a)
$seen = $message['seen']; $seen = $message['seen'];
} }
$select = $message['name'] . '<input type="hidden" name="messageto" value="' . $contact_id . '" />'; $select = $message['name'] . '<input type="hidden" name="recipient" value="' . $contact_id . '" />';
$parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />'; $parent = '<input type="hidden" name="replyto" value="' . $message['parent-uri'] . '" />';
$tpl = Renderer::getMarkupTemplate('mail_display.tpl'); $tpl = Renderer::getMarkupTemplate('mail_display.tpl');
@ -437,7 +363,6 @@ function message_content(App $a)
// reply // reply
'$header' => DI::l10n()->t('Send Reply'), '$header' => DI::l10n()->t('Send Reply'),
'$to' => DI::l10n()->t('To:'), '$to' => DI::l10n()->t('To:'),
'$showinputs' => '',
'$subject' => DI::l10n()->t('Subject:'), '$subject' => DI::l10n()->t('Subject:'),
'$subjtxt' => $message['title'], '$subjtxt' => $message['title'],
'$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ', '$readonly' => ' readonly="readonly" style="background: #BBBBBB;" ',
@ -528,12 +453,8 @@ function render_messages(array $msg, $t)
$body_e = $rr['body']; $body_e = $rr['body'];
$to_name_e = $rr['name']; $to_name_e = $rr['name'];
$contact = Contact::getDetailsByURL($rr['url']); $contact = Contact::getByURL($rr['url'], false, ['thumb', 'addr', 'id', 'avatar']);
if (isset($contact["thumb"])) { $from_photo = Contact::getThumb($contact, $rr['thumb'] ?: $rr['from-photo']);
$from_photo = $contact["thumb"];
} else {
$from_photo = (($rr['thumb']) ? $rr['thumb'] : $rr['from-photo']);
}
$rslt .= Renderer::replaceMacros($tpl, [ $rslt .= Renderer::replaceMacros($tpl, [
'$id' => $rr['id'], '$id' => $rr['id'],
@ -541,7 +462,7 @@ function render_messages(array $msg, $t)
'$from_url' => Contact::magicLink($rr['url']), '$from_url' => Contact::magicLink($rr['url']),
'$from_addr' => $contact['addr'] ?? '', '$from_addr' => $contact['addr'] ?? '',
'$sparkle' => ' sparkle', '$sparkle' => ' sparkle',
'$from_photo' => ProxyUtils::proxifyUrl($from_photo, false, ProxyUtils::SIZE_THUMB), '$from_photo' => $from_photo,
'$subject' => $rr['title'], '$subject' => $rr['title'],
'$delete' => DI::l10n()->t('Delete conversation'), '$delete' => DI::l10n()->t('Delete conversation'),
'$body' => $body_e, '$body' => $body_e,

View File

@ -20,7 +20,6 @@
*/ */
use Friendica\App; use Friendica\App;
use Friendica\Content\Feature;
use Friendica\Content\ForumManager; use Friendica\Content\ForumManager;
use Friendica\Content\Nav; use Friendica\Content\Nav;
use Friendica\Content\Pager; use Friendica\Content\Pager;
@ -29,9 +28,7 @@ use Friendica\Content\Text\HTML;
use Friendica\Core\ACL; use Friendica\Core\ACL;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Core\Session;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
@ -39,15 +36,15 @@ use Friendica\Model\Group;
use Friendica\Model\Item; use Friendica\Model\Item;
use Friendica\Model\Post\Category; use Friendica\Model\Post\Category;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Module\Contact as ModuleContact;
use Friendica\Module\Security\Login; use Friendica\Module\Security\Login;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\Strings; use Friendica\Util\Strings;
function network_init(App $a) function network_init(App $a)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -102,9 +99,7 @@ function network_init(App $a)
'order=activity', //all 'order=activity', //all
'order=post', //postord 'order=post', //postord
'conv=1', //conv 'conv=1', //conv
'new=1', //new
'star=1', //starred 'star=1', //starred
'bmark=1', //bookmarked
]; ];
$k = array_search('active', $last_sel_tabs); $k = array_search('active', $last_sel_tabs);
@ -154,40 +149,28 @@ function network_init(App $a)
* '/network?order=activity' => $activity_active = 'active' * '/network?order=activity' => $activity_active = 'active'
* '/network?order=post' => $postord_active = 'active' * '/network?order=post' => $postord_active = 'active'
* '/network?conv=1', => $conv_active = 'active' * '/network?conv=1', => $conv_active = 'active'
* '/network?new=1', => $new_active = 'active'
* '/network?star=1', => $starred_active = 'active' * '/network?star=1', => $starred_active = 'active'
* '/network?bmark=1', => $bookmarked_active = 'active'
* *
* @param App $a * @param App $a
* @return array ($no_active, $activity_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active); * @return array ($no_active, $activity_active, $postord_active, $conv_active, $starred_active);
*/ */
function network_query_get_sel_tab(App $a) function network_query_get_sel_tab(App $a)
{ {
$no_active = ''; $no_active = '';
$starred_active = ''; $starred_active = '';
$new_active = '';
$bookmarked_active = '';
$all_active = ''; $all_active = '';
$conv_active = ''; $conv_active = '';
$postord_active = ''; $postord_active = '';
if (!empty($_GET['new'])) {
$new_active = 'active';
}
if (!empty($_GET['star'])) { if (!empty($_GET['star'])) {
$starred_active = 'active'; $starred_active = 'active';
} }
if (!empty($_GET['bmark'])) {
$bookmarked_active = 'active';
}
if (!empty($_GET['conv'])) { if (!empty($_GET['conv'])) {
$conv_active = 'active'; $conv_active = 'active';
} }
if (($new_active == '') && ($starred_active == '') && ($bookmarked_active == '') && ($conv_active == '')) { if (($starred_active == '') && ($conv_active == '')) {
$no_active = 'active'; $no_active = 'active';
} }
@ -198,7 +181,7 @@ function network_query_get_sel_tab(App $a)
} }
} }
return [$no_active, $all_active, $postord_active, $conv_active, $new_active, $starred_active, $bookmarked_active]; return [$no_active, $all_active, $postord_active, $conv_active, $starred_active];
} }
function network_query_get_sel_group(App $a) function network_query_get_sel_group(App $a)
@ -217,17 +200,11 @@ function network_query_get_sel_group(App $a)
* *
* @param App $a The global App * @param App $a The global App
* @param Pager $pager * @param Pager $pager
* @param integer $update Used for the automatic reloading
* @return string SQL with the appropriate LIMIT clause * @return string SQL with the appropriate LIMIT clause
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/ */
function networkPager(App $a, Pager $pager, $update) function networkPager(App $a, Pager $pager)
{ {
if ($update) {
// only setup pagination on initial page view
return ' LIMIT 100';
}
if (DI::mode()->isMobile()) { if (DI::mode()->isMobile()) {
$itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network', $itemspage_network = DI::pConfig()->get(local_user(), 'system', 'itemspage_mobile_network',
DI::config()->get('system', 'itemspage_network_mobile')); DI::config()->get('system', 'itemspage_network_mobile'));
@ -243,8 +220,6 @@ function networkPager(App $a, Pager $pager, $update)
} }
$pager->setItemsPerPage($itemspage_network); $pager->setItemsPerPage($itemspage_network);
return sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
} }
/** /**
@ -285,7 +260,7 @@ function networkConversation(App $a, $items, Pager $pager, $mode, $update, $orde
$a->page_contact = $a->contact; $a->page_contact = $a->contact;
if (!is_array($items)) { if (!is_array($items)) {
Logger::log("Expecting items to be an array. Got " . print_r($items, true)); Logger::info('Expecting items to be an array.', ['items' => $items]);
$items = []; $items = [];
} }
@ -312,14 +287,14 @@ function network_content(App $a, $update = 0, $parent = 0)
$arr = ['query' => DI::args()->getQueryString()]; $arr = ['query' => DI::args()->getQueryString()];
Hook::callAll('network_content_init', $arr); Hook::callAll('network_content_init', $arr);
if (!empty($_GET['new']) || !empty($_GET['file'])) { if (!empty($_GET['file'])) {
$o = networkFlatView($a, $update); $o = networkFlatView($a, $update);
} else { } else {
$o = networkThreadedView($a, $update, $parent); $o = networkThreadedView($a, $update, $parent);
} }
if ($o === '') { if (!$update && ($o === '')) {
info("No items found"); notice(DI::l10n()->t("No items found"));
} }
return $o; return $o;
@ -377,8 +352,7 @@ function networkFlatView(App $a, $update = 0)
$pager = new Pager(DI::l10n(), DI::args()->getQueryString()); $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
networkPager($a, $pager, $update); networkPager($a, $pager);
if (strlen($file)) { if (strlen($file)) {
$item_params = ['order' => ['uri-id' => true]]; $item_params = ['order' => ['uri-id' => true]];
@ -428,19 +402,12 @@ function networkThreadedView(App $a, $update, $parent)
global $pager; global $pager;
// Rawmode is used for fetching new content at the end of the page // Rawmode is used for fetching new content at the end of the page
$rawmode = (isset($_GET['mode']) AND ( $_GET['mode'] == 'raw')); $rawmode = (isset($_GET['mode']) AND ($_GET['mode'] == 'raw'));
if (isset($_GET['last_received']) && isset($_GET['last_commented']) && isset($_GET['last_created']) && isset($_GET['last_id'])) { $last_received = isset($_GET['last_received']) ? DateTimeFormat::utc($_GET['last_received']) : '';
$last_received = DateTimeFormat::utc($_GET['last_received']); $last_commented = isset($_GET['last_commented']) ? DateTimeFormat::utc($_GET['last_commented']) : '';
$last_commented = DateTimeFormat::utc($_GET['last_commented']); $last_created = isset($_GET['last_created']) ? DateTimeFormat::utc($_GET['last_created']) : '';
$last_created = DateTimeFormat::utc($_GET['last_created']); $last_uriid = isset($_GET['last_uriid']) ? intval($_GET['last_uriid']) : 0;
$last_id = intval($_GET['last_id']);
} else {
$last_received = '';
$last_commented = '';
$last_created = '';
$last_id = 0;
}
$datequery = $datequery2 = ''; $datequery = $datequery2 = '';
@ -468,7 +435,6 @@ function networkThreadedView(App $a, $update, $parent)
$cid = intval($_GET['contactid'] ?? 0); $cid = intval($_GET['contactid'] ?? 0);
$star = intval($_GET['star'] ?? 0); $star = intval($_GET['star'] ?? 0);
$bmark = intval($_GET['bmark'] ?? 0);
$conv = intval($_GET['conv'] ?? 0); $conv = intval($_GET['conv'] ?? 0);
$order = Strings::escapeTags(($_GET['order'] ?? '') ?: 'activity'); $order = Strings::escapeTags(($_GET['order'] ?? '') ?: 'activity');
$nets = $_GET['nets'] ?? ''; $nets = $_GET['nets'] ?? '';
@ -508,13 +474,9 @@ function networkThreadedView(App $a, $update, $parent)
if ($cid) { if ($cid) {
// If $cid belongs to a communitity forum or a privat goup,.add a mention to the status editor // If $cid belongs to a communitity forum or a privat goup,.add a mention to the status editor
$condition = ["`id` = ? AND (`forum` OR `prv`)", $cid]; $condition = ["`id` = ? AND (`forum` OR `prv`)", $cid];
$contact = DBA::selectFirst('contact', ['addr', 'nick'], $condition); $contact = DBA::selectFirst('contact', ['addr'], $condition);
if (DBA::isResult($contact)) { if (!empty($contact['addr'])) {
if ($contact['addr'] != '') { $content = '!' . $contact['addr'];
$content = '!' . $contact['addr'];
} else {
$content = '!' . $contact['nick'] . '+' . $cid;
}
} }
} }
@ -537,27 +499,25 @@ function networkThreadedView(App $a, $update, $parent)
$o .= status_editor($a, $x); $o .= status_editor($a, $x);
} }
// We don't have to deal with ACLs on this page. You're looking at everything $conditionFields = ['uid' => local_user()];
// that belongs to you, hence you can see all of it. We will filter by group if $conditionStrings = [];
// desired.
$sql_post_table = ''; if ($star) {
$sql_options = ($star ? " AND `thread`.`starred` " : ''); $conditionFields['starred'] = true;
$sql_options .= ($bmark ? sprintf(" AND `thread`.`post-type` = %d ", Item::PT_PAGE) : ''); }
$sql_extra = $sql_options; if ($conv) {
$sql_extra2 = ''; $conditionFields['mention'] = true;
$sql_extra3 = ''; }
$sql_table = '`thread`'; if ($nets) {
$sql_parent = '`iid`'; $conditionFields['network'] = $nets;
if ($update) {
$sql_table = '`item`';
$sql_parent = '`parent`';
$sql_post_table = " INNER JOIN `thread` ON `thread`.`iid` = `item`.`parent`";
} }
$sql_nets = (($nets) ? sprintf(" AND $sql_table.`network` = '%s' ", DBA::escape($nets)) : ''); if ($datequery) {
$sql_tag_nets = (($nets) ? sprintf(" AND `item`.`network` = '%s' ", DBA::escape($nets)) : ''); $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` <= ? ", DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get())]);
}
if ($datequery2) {
$conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` >= ? ", DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get())]);
}
if ($gid) { if ($gid) {
$group = DBA::selectFirst('group', ['name'], ['id' => $gid, 'uid' => local_user()]); $group = DBA::selectFirst('group', ['name'], ['id' => $gid, 'uid' => local_user()]);
@ -565,80 +525,35 @@ function networkThreadedView(App $a, $update, $parent)
if ($update) { if ($update) {
exit(); exit();
} }
notice(DI::l10n()->t('No such group') . EOL); notice(DI::l10n()->t('No such group'));
DI::baseUrl()->redirect('network/0'); DI::baseUrl()->redirect('network/0');
// NOTREACHED // NOTREACHED
} }
$contacts = Group::expand(local_user(), [$gid]); $conditionStrings = DBA::mergeConditions($conditionStrings, ["`contact-id` IN (SELECT `contact-id` FROM `group_member` WHERE `gid` = ?)", $gid]);
if ((is_array($contacts)) && count($contacts)) {
$contact_str_self = '';
$contact_str = implode(',', $contacts);
$self = DBA::selectFirst('contact', ['id'], ['uid' => local_user(), 'self' => true]);
if (DBA::isResult($self)) {
$contact_str_self = $self['id'];
}
$sql_post_table .= " INNER JOIN `item` AS `temp1` ON `temp1`.`id` = " . $sql_table . "." . $sql_parent;
$sql_extra3 .= " AND (`thread`.`contact-id` IN ($contact_str) ";
$sql_extra3 .= " OR (`thread`.`contact-id` = '$contact_str_self' AND `temp1`.`allow_gid` LIKE '" . Strings::protectSprintf('%<' . intval($gid) . '>%') . "' AND `temp1`.`private`))";
} else {
$sql_extra3 .= " AND false ";
info(DI::l10n()->t('Group is empty'));
}
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [ $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), [
'$title' => DI::l10n()->t('Group: %s', $group['name']) '$title' => DI::l10n()->t('Group: %s', $group['name'])
]) . $o; ]) . $o;
} elseif ($cid) { } elseif ($cid) {
$fields = ['id', 'name', 'network', 'writable', 'nurl', $contact = Contact::getById($cid);
'forum', 'prv', 'contact-type', 'addr', 'thumb', 'location'];
$condition = ["`id` = ? AND (NOT `blocked` OR `pending`)", $cid];
$contact = DBA::selectFirst('contact', $fields, $condition);
if (DBA::isResult($contact)) { if (DBA::isResult($contact)) {
$sql_extra = " AND " . $sql_table . ".`contact-id` = " . intval($cid); $conditionFields['contact-id'] = $cid;
$entries[0] = [
'id' => 'network',
'name' => $contact['name'],
'itemurl' => ($contact['addr'] ?? '') ?: $contact['nurl'],
'thumb' => ProxyUtils::proxifyUrl($contact['thumb'], false, ProxyUtils::SIZE_THUMB),
'details' => $contact['location'],
];
$entries[0]['account_type'] = Contact::getAccountType($contact);
$o = Renderer::replaceMacros(Renderer::getMarkupTemplate('viewcontact_template.tpl'), [ $o = Renderer::replaceMacros(Renderer::getMarkupTemplate('viewcontact_template.tpl'), [
'contacts' => $entries, 'contacts' => [ModuleContact::getContactTemplateVars($contact)],
'id' => 'network', 'id' => 'network',
]) . $o; ]) . $o;
} else { } else {
notice(DI::l10n()->t('Invalid contact.') . EOL); notice(DI::l10n()->t('Invalid contact.'));
DI::baseUrl()->redirect('network'); DI::baseUrl()->redirect('network');
// NOTREACHED // NOTREACHED
} }
} } elseif (!$update && !DI::config()->get('theme', 'hide_eventlist')) {
if (!$gid && !$cid && !$update && !DI::config()->get('theme', 'hide_eventlist')) {
$o .= Profile::getBirthdays(); $o .= Profile::getBirthdays();
$o .= Profile::getEventsReminderHTML(); $o .= Profile::getEventsReminderHTML();
} }
if ($datequery) {
$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.received <= '%s' ",
DBA::escape(DateTimeFormat::convert($datequery, 'UTC', date_default_timezone_get()))));
}
if ($datequery2) {
$sql_extra3 .= Strings::protectSprintf(sprintf(" AND $sql_table.received >= '%s' ",
DBA::escape(DateTimeFormat::convert($datequery2, 'UTC', date_default_timezone_get()))));
}
if ($conv) {
$sql_extra3 .= " AND $sql_table.`mention`";
}
// Normal conversation view // Normal conversation view
if ($order === 'post') { if ($order === 'post') {
$ordering = '`received`'; $ordering = '`received`';
@ -648,50 +563,34 @@ function networkThreadedView(App $a, $update, $parent)
$order_mode = 'commented'; $order_mode = 'commented';
} }
$sql_order = "$sql_table.$ordering";
if (!empty($_GET['offset'])) {
$sql_range = sprintf(" AND $sql_order <= '%s'", DBA::escape($_GET['offset']));
} else {
$sql_range = '';
}
$pager = new Pager(DI::l10n(), DI::args()->getQueryString()); $pager = new Pager(DI::l10n(), DI::args()->getQueryString());
$pager_sql = networkPager($a, $pager, $update); networkPager($a, $pager);
$last_date = ''; if (DI::pConfig()->get(local_user(), 'system', 'infinite_scroll')) {
$pager->setPage(1);
}
// Currently only the order modes "received" and "commented" are in use
switch ($order_mode) { switch ($order_mode) {
case 'received': case 'received':
if ($last_received != '') { if ($last_received != '') {
$last_date = $last_received; $conditionStrings = DBA::mergeConditions($conditionStrings, ["`received` < ?", $last_received]);
$sql_range .= sprintf(" AND $sql_table.`received` < '%s'", DBA::escape($last_received));
$pager->setPage(1);
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
} }
break; break;
case 'commented': case 'commented':
if ($last_commented != '') { if ($last_commented != '') {
$last_date = $last_commented; $conditionStrings = DBA::mergeConditions($conditionStrings, ["`commented` < ?", $last_commented]);
$sql_range .= sprintf(" AND $sql_table.`commented` < '%s'", DBA::escape($last_commented));
$pager->setPage(1);
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
} }
break; break;
case 'created': case 'created':
if ($last_created != '') { if ($last_created != '') {
$last_date = $last_created; $conditionStrings = DBA::mergeConditions($conditionStrings, ["`created` < ?", $last_created]);
$sql_range .= sprintf(" AND $sql_table.`created` < '%s'", DBA::escape($last_created));
$pager->setPage(1);
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
} }
break; break;
case 'id': case 'uriid':
if (($last_id > 0) && ($sql_table == '`thread`')) { if ($last_uriid > 0) {
$sql_range .= sprintf(" AND $sql_table.`iid` < '%s'", DBA::escape($last_id)); $conditionStrings = DBA::mergeConditions($conditionStrings, ["`uri-id` < ?", $last_uriid]);
$pager->setPage(1);
$pager_sql = sprintf(" LIMIT %d, %d ", $pager->getStart(), $pager->getItemsPerPage());
} }
break; break;
} }
@ -700,168 +599,49 @@ function networkThreadedView(App $a, $update, $parent)
if ($update) { if ($update) {
if (!empty($parent)) { if (!empty($parent)) {
// Load only a single thread // Load only a single thread
$sql_extra4 = "`item`.`id` = ".intval($parent); $conditionFields['parent'] = $parent;
} elseif ($order === 'post') {
// Only load new toplevel posts
$conditionFields['unseen'] = true;
$conditionFields['gravity'] = GRAVITY_PARENT;
} else { } else {
// Load all unseen items // Load all unseen items
$sql_extra4 = "`item`.`unseen`"; $conditionFields['unseen'] = true;
if (DI::config()->get("system", "like_no_comment")) {
$sql_extra4 .= " AND `item`.`gravity` IN (" . GRAVITY_PARENT . "," . GRAVITY_COMMENT . ")";
}
if ($order === 'post') {
// Only show toplevel posts when updating posts in this order mode
$sql_extra4 .= " AND `item`.`gravity` = " . GRAVITY_PARENT;
}
} }
$r = q("SELECT `item`.`parent-uri` AS `uri`, `item`.`parent` AS `item_id`, $sql_order AS `order_date` $params = ['order' => [$order_mode => true], 'limit' => 100];
FROM `item` $sql_post_table $table = 'network-item-view';
STRAIGHT_JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
AND (`item`.`gravity` != %d
OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
WHERE `item`.`uid` = %d AND `item`.`visible` AND NOT `item`.`deleted`
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
AND NOT `item`.`moderated` AND $sql_extra4
$sql_extra3 $sql_extra $sql_range $sql_nets
ORDER BY `order_date` DESC LIMIT 100",
intval(GRAVITY_PARENT),
intval(Contact::SHARING),
intval(Contact::FRIEND),
intval(local_user()),
intval(local_user())
);
} else { } else {
$r = q("SELECT `item`.`uri`, `thread`.`iid` AS `item_id`, $sql_order AS `order_date` $params = ['order' => [$order_mode => true], 'limit' => [$pager->getStart(), $pager->getItemsPerPage()]];
FROM `thread` $sql_post_table $table = 'network-thread-view';
STRAIGHT_JOIN `contact` ON `contact`.`id` = `thread`.`contact-id`
AND (NOT `contact`.`blocked` OR `contact`.`pending`)
STRAIGHT_JOIN `item` ON `item`.`id` = `thread`.`iid`
AND (`item`.`gravity` != %d
OR `contact`.`uid` = `item`.`uid` AND `contact`.`self`
OR `contact`.`rel` IN (%d, %d) AND NOT `contact`.`readonly`)
LEFT JOIN `user-item` ON `user-item`.`iid` = `item`.`id` AND `user-item`.`uid` = %d
WHERE `thread`.`uid` = %d AND `thread`.`visible` AND NOT `thread`.`deleted`
AND NOT `thread`.`moderated`
AND (`user-item`.`hidden` IS NULL OR NOT `user-item`.`hidden`)
$sql_extra2 $sql_extra3 $sql_range $sql_extra $sql_nets
ORDER BY `order_date` DESC $pager_sql",
intval(GRAVITY_PARENT),
intval(Contact::SHARING),
intval(Contact::FRIEND),
intval(local_user()),
intval(local_user())
);
} }
$r = DBA::selectToArray($table, [], DBA::mergeConditions($conditionFields, $conditionStrings), $params);
// Only show it when unfiltered (no groups, no networks, ...) return $o . network_display_post($a, $pager, (!$gid && !$cid && !$star), $update, $ordering, $r);
if (in_array($nets, ['', Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]) && (strlen($sql_extra . $sql_extra2 . $sql_extra3) == 0)) { }
if (DBA::isResult($r)) {
$top_limit = current($r)['order_date'];
$bottom_limit = end($r)['order_date'];
if (empty($_SESSION['network_last_top_limit']) || ($_SESSION['network_last_top_limit'] < $top_limit)) {
$_SESSION['network_last_top_limit'] = $top_limit;
}
} else {
$top_limit = $bottom_limit = DateTimeFormat::utcNow();
}
// When checking for updates we need to fetch from the newest date to the newest date before
// Only do this, when the last stored date isn't too long ago (10 times the update interval)
$browser_update = DI::pConfig()->get(local_user(), 'system', 'update_interval', 40000) / 1000;
if (($browser_update > 0) && $update && !empty($_SESSION['network_last_date']) &&
(($bottom_limit < $_SESSION['network_last_date']) || ($top_limit == $bottom_limit)) &&
((time() - $_SESSION['network_last_date_timestamp']) < ($browser_update * 10))) {
$bottom_limit = $_SESSION['network_last_date'];
}
$_SESSION['network_last_date'] = Session::get('network_last_top_limit', $top_limit);
$_SESSION['network_last_date_timestamp'] = time();
if ($last_date > $top_limit) {
$top_limit = $last_date;
} elseif ($pager->getPage() == 1) {
// Highest possible top limit when we are on the first page
$top_limit = DateTimeFormat::utcNow();
}
// Handle bad performance situations when the distance between top and bottom is too high
// See issue https://github.com/friendica/friendica/issues/8619
if (strtotime($top_limit) - strtotime($bottom_limit) > 86400) {
// Set the bottom limit to one day in the past at maximum
$bottom_limit = DateTimeFormat::utc(date('c', strtotime($top_limit) - 86400));
}
$items = DBA::p("SELECT `item`.`parent-uri` AS `uri`, 0 AS `item_id`, `item`.$ordering AS `order_date`, `author`.`url` AS `author-link` FROM `item`
STRAIGHT_JOIN (SELECT `uri-id` FROM `tag-search-view` WHERE `name` IN
(SELECT SUBSTR(`term`, 2) FROM `search` WHERE `uid` = ? AND `term` LIKE '#%') AND `uid` = 0) AS `tag-search`
ON `item`.`uri-id` = `tag-search`.`uri-id`
STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = `item`.`author-id`
WHERE `item`.`uid` = 0 AND `item`.$ordering < ? AND `item`.$ordering > ? AND `item`.`gravity` = ?
AND NOT `author`.`hidden` AND NOT `author`.`blocked`" . $sql_tag_nets,
local_user(), $top_limit, $bottom_limit, GRAVITY_PARENT);
$data = DBA::toArray($items);
if (count($data) > 0) {
$tag_top_limit = current($data)['order_date'];
if ($_SESSION['network_last_date'] < $tag_top_limit) {
$_SESSION['network_last_date'] = $tag_top_limit;
}
Logger::log('Tagged items: ' . count($data) . ' - ' . $bottom_limit . ' - ' . $top_limit . ' - ' . local_user().' - '.(int)$update);
$s = [];
foreach ($r as $item) {
$s[$item['uri']] = $item;
}
foreach ($data as $item) {
// Don't show hash tag posts from blocked or ignored contacts
$condition = ["`nurl` = ? AND `uid` = ? AND (`blocked` OR `readonly`)",
Strings::normaliseLink($item['author-link']), local_user()];
if (!DBA::exists('contact', $condition)) {
$s[$item['uri']] = $item;
}
}
$r = $s;
}
}
function network_display_post($a, $pager, $mark_all, $update, $ordering, $items)
{
$parents_str = ''; $parents_str = '';
$date_offset = '';
$items = $r;
if (DBA::isResult($items)) { if (DBA::isResult($items)) {
$parents_arr = []; $parents_arr = [];
foreach ($items as $item) { foreach ($items as $item) {
if ($date_offset < $item['order_date']) { if (!in_array($item['parent'], $parents_arr) && ($item['parent'] > 0)) {
$date_offset = $item['order_date']; $parents_arr[] = $item['parent'];
}
if (!in_array($item['item_id'], $parents_arr) && ($item['item_id'] > 0)) {
$parents_arr[] = $item['item_id'];
} }
} }
$parents_str = implode(', ', $parents_arr); $parents_str = implode(', ', $parents_arr);
} }
if (!empty($_GET['offset'])) { $pager->setQueryString(DI::args()->getQueryString());
$date_offset = $_GET['offset'];
}
$query_string = DI::args()->getQueryString();
if ($date_offset && !preg_match('/[?&].offset=/', $query_string)) {
$query_string .= '&offset=' . urlencode($date_offset);
}
$pager->setQueryString($query_string);
// We aren't going to try and figure out at the item, group, and page // We aren't going to try and figure out at the item, group, and page
// level which items you've seen and which you haven't. If you're looking // level which items you've seen and which you haven't. If you're looking
// at the top level network page just mark everything seen. // at the top level network page just mark everything seen.
if (!$gid && !$cid && !$star) { if ($mark_all) {
$condition = ['unseen' => true, 'uid' => local_user()]; $condition = ['unseen' => true, 'uid' => local_user()];
networkSetSeen($condition); networkSetSeen($condition);
} elseif ($parents_str) { } elseif ($parents_str) {
@ -869,11 +649,7 @@ function networkThreadedView(App $a, $update, $parent)
networkSetSeen($condition); networkSetSeen($condition);
} }
return networkConversation($a, $items, $pager, 'network', $update, $ordering);
$mode = 'network';
$o .= networkConversation($a, $items, $pager, $mode, $update, $ordering);
return $o;
} }
/** /**
@ -888,7 +664,7 @@ function network_tabs(App $a)
// item filter tabs // item filter tabs
/// @TODO fix this logic, reduce duplication /// @TODO fix this logic, reduce duplication
/// DI::page()['content'] .= '<div class="tabs-wrapper">'; /// DI::page()['content'] .= '<div class="tabs-wrapper">';
list($no_active, $all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active) = network_query_get_sel_tab($a); list($no_active, $all_active, $post_active, $conv_active, $starred_active) = network_query_get_sel_tab($a);
// if no tabs are selected, defaults to activitys // if no tabs are selected, defaults to activitys
if ($no_active == 'active') { if ($no_active == 'active') {
@ -931,28 +707,6 @@ function network_tabs(App $a)
'accesskey' => 'r', 'accesskey' => 'r',
]; ];
if (Feature::isEnabled(local_user(), 'new_tab')) {
$tabs[] = [
'label' => DI::l10n()->t('New'),
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['new' => true])),
'sel' => $new_active,
'title' => DI::l10n()->t('Activity Stream - by date'),
'id' => 'activitiy-by-date-tab',
'accesskey' => 'w',
];
}
if (Feature::isEnabled(local_user(), 'link_tab')) {
$tabs[] = [
'label' => DI::l10n()->t('Shared Links'),
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['bmark' => true])),
'sel' => $bookmarked_active,
'title' => DI::l10n()->t('Interesting Links'),
'id' => 'shared-links-tab',
'accesskey' => 'b',
];
}
$tabs[] = [ $tabs[] = [
'label' => DI::l10n()->t('Starred'), 'label' => DI::l10n()->t('Starred'),
'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['star' => true])), 'url' => $cmd . '?' . http_build_query(array_merge($def_param, ['star' => true])),
@ -965,7 +719,7 @@ function network_tabs(App $a)
// save selected tab, but only if not in file mode // save selected tab, but only if not in file mode
if (empty($_GET['file'])) { if (empty($_GET['file'])) {
DI::pConfig()->set(local_user(), 'network.view', 'tab.selected', [ DI::pConfig()->set(local_user(), 'network.view', 'tab.selected', [
$all_active, $post_active, $conv_active, $new_active, $starred_active, $bookmarked_active $all_active, $post_active, $conv_active, $starred_active
]); ]);
} }

View File

@ -40,7 +40,7 @@ function notes_init(App $a)
function notes_content(App $a, $update = false) function notes_content(App $a, $update = false)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -55,7 +55,7 @@ function notes_content(App $a, $update = false)
'default_location' => $a->user['default-location'], 'default_location' => $a->user['default-location'],
'nickname' => $a->user['nickname'], 'nickname' => $a->user['nickname'],
'lockstate' => 'lock', 'lockstate' => 'lock',
'acl' => '', 'acl' => \Friendica\Core\ACL::getSelfOnlyHTML(local_user(), DI::l10n()->t('Personal notes are visible only by yourself.')),
'bang' => '', 'bang' => '',
'visitor' => 'block', 'visitor' => 'block',
'profile_uid' => local_user(), 'profile_uid' => local_user(),

View File

@ -23,7 +23,6 @@ use Friendica\App;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\DI; use Friendica\DI;
use Friendica\Module\Security\Login; use Friendica\Module\Security\Login;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
function oexchange_init(App $a) { function oexchange_init(App $a) {
@ -45,7 +44,6 @@ function oexchange_content(App $a) {
} }
if (($a->argc > 1) && $a->argv[1] === 'done') { if (($a->argc > 1) && $a->argv[1] === 'done') {
info(DI::l10n()->t('Post successful.') . EOL);
return; return;
} }
@ -58,7 +56,7 @@ function oexchange_content(App $a) {
$tags = ((!empty($_REQUEST['tags'])) $tags = ((!empty($_REQUEST['tags']))
? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : ''); ? '&tags=' . urlencode(Strings::escapeTags(trim($_REQUEST['tags']))) : '');
$s = Network::fetchUrl(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags); $s = DI::httpRequest()->fetch(DI::baseUrl() . '/parse_url?url=' . $url . $title . $description . $tags);
if (!strlen($s)) { if (!strlen($s)) {
return; return;

View File

@ -23,13 +23,11 @@ use Friendica\App;
use Friendica\Core\Protocol; use Friendica\Core\Protocol;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Network\Probe;
use Friendica\Util\Network;
function ostatus_subscribe_content(App $a) function ostatus_subscribe_content(App $a)
{ {
if (!local_user()) { if (!local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
DI::baseUrl()->redirect('ostatus_subscribe'); DI::baseUrl()->redirect('ostatus_subscribe');
// NOTREACHED // NOTREACHED
} }
@ -47,7 +45,7 @@ function ostatus_subscribe_content(App $a)
return $o . DI::l10n()->t('No contact provided.'); return $o . DI::l10n()->t('No contact provided.');
} }
$contact = Probe::uri($_REQUEST['url']); $contact = Contact::getByURL($_REQUEST['url']);
if (!$contact) { if (!$contact) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
return $o . DI::l10n()->t('Couldn\'t fetch information for contact.'); return $o . DI::l10n()->t('Couldn\'t fetch information for contact.');
@ -56,7 +54,7 @@ function ostatus_subscribe_content(App $a)
$api = $contact['baseurl'] . '/api/'; $api = $contact['baseurl'] . '/api/';
// Fetching friends // Fetching friends
$curlResult = Network::curl($api . 'statuses/friends.json?screen_name=' . $contact['nick']); $curlResult = DI::httpRequest()->get($api . 'statuses/friends.json?screen_name=' . $contact['nick']);
if (!$curlResult->isSuccess()) { if (!$curlResult->isSuccess()) {
DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact'); DI::pConfig()->delete($uid, 'ostatus', 'legacy_contact');
@ -88,7 +86,7 @@ function ostatus_subscribe_content(App $a)
$o .= '<p>' . $counter . '/' . $total . ': ' . $url; $o .= '<p>' . $counter . '/' . $total . ': ' . $url;
$probed = Probe::uri($url); $probed = Contact::getByURL($url);
if ($probed['network'] == Protocol::OSTATUS) { if ($probed['network'] == Protocol::OSTATUS) {
$result = Contact::createFromProbe($a->user, $probed['url'], true, Protocol::OSTATUS); $result = Contact::createFromProbe($a->user, $probed['url'], true, Protocol::OSTATUS);
if ($result['success']) { if ($result['success']) {

View File

@ -24,10 +24,11 @@
*/ */
use Friendica\App; use Friendica\App;
use Friendica\Content\PageInfo;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\Util\Network; use Friendica\DI;
use Friendica\Util\ParseUrl; use Friendica\Util\ParseUrl;
use Friendica\Util\Strings; use Friendica\Util\Strings;
@ -84,7 +85,7 @@ function parse_url_content(App $a)
// Check if the URL is an image, video or audio file. If so format // Check if the URL is an image, video or audio file. If so format
// the URL with the corresponding BBCode media tag // the URL with the corresponding BBCode media tag
// Fetch the header of the URL // Fetch the header of the URL
$curlResponse = Network::curl($url, false, ['novalidate' => true, 'nobody' => true]); $curlResponse = DI::httpRequest()->get($url, false, ['novalidate' => true, 'nobody' => true]);
if ($curlResponse->isSuccess()) { if ($curlResponse->isSuccess()) {
// Convert the header fields into an array // Convert the header fields into an array
@ -177,7 +178,7 @@ function parse_url_content(App $a)
} }
// Format it as BBCode attachment // Format it as BBCode attachment
$info = add_page_info_data($siteinfo); $info = "\n" . PageInfo::getFooterFromData($siteinfo);
echo $info; echo $info;

View File

@ -25,6 +25,7 @@ use Friendica\Content\Nav;
use Friendica\Content\Pager; use Friendica\Content\Pager;
use Friendica\Content\Text\BBCode; use Friendica\Content\Text\BBCode;
use Friendica\Core\ACL; use Friendica\Core\ACL;
use Friendica\Core\Addon;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
@ -175,14 +176,14 @@ function photos_post(App $a)
} }
if (!$can_post) { if (!$can_post) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
exit(); exit();
} }
$owner_record = User::getOwnerDataById($page_owner_uid); $owner_record = User::getOwnerDataById($page_owner_uid);
if (!$owner_record) { if (!$owner_record) {
notice(DI::l10n()->t('Contact information unavailable') . EOL); notice(DI::l10n()->t('Contact information unavailable'));
Logger::log('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid); Logger::log('photos_post: unable to locate contact record for page owner. uid=' . $page_owner_uid);
exit(); exit();
} }
@ -193,7 +194,7 @@ function photos_post(App $a)
} }
$album = hex2bin($a->argv[3]); $album = hex2bin($a->argv[3]);
if ($album === DI::l10n()->t('Profile Photos') || $album === 'Contact Photos' || $album === DI::l10n()->t('Contact Photos')) { if ($album === DI::l10n()->t('Profile Photos') || $album === Photo::CONTACT_PHOTOS || $album === DI::l10n()->t(Photo::CONTACT_PHOTOS)) {
DI::baseUrl()->redirect($_SESSION['photo_return']); DI::baseUrl()->redirect($_SESSION['photo_return']);
return; // NOTREACHED return; // NOTREACHED
} }
@ -204,7 +205,7 @@ function photos_post(App $a)
); );
if (!DBA::isResult($r)) { if (!DBA::isResult($r)) {
notice(DI::l10n()->t('Album not found.') . EOL); notice(DI::l10n()->t('Album not found.'));
DI::baseUrl()->redirect('photos/' . $a->data['user']['nickname'] . '/album'); DI::baseUrl()->redirect('photos/' . $a->data['user']['nickname'] . '/album');
return; // NOTREACHED return; // NOTREACHED
} }
@ -295,9 +296,8 @@ function photos_post(App $a)
// Update the photo albums cache // Update the photo albums cache
Photo::clearAlbumCache($page_owner_uid); Photo::clearAlbumCache($page_owner_uid);
notice('Successfully deleted the photo.');
} else { } else {
notice('Failed to delete the photo.'); notice(DI::l10n()->t('Failed to delete the photo.'));
DI::baseUrl()->redirect('photos/' . $a->argv[1] . '/image/' . $a->argv[3]); DI::baseUrl()->redirect('photos/' . $a->argv[1] . '/image/' . $a->argv[3]);
} }
@ -676,21 +676,21 @@ function photos_post(App $a)
if ($error !== UPLOAD_ERR_OK) { if ($error !== UPLOAD_ERR_OK) {
switch ($error) { switch ($error) {
case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_INI_SIZE:
notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize')) . EOL); notice(DI::l10n()->t('Image exceeds size limit of %s', ini_get('upload_max_filesize')));
break; break;
case UPLOAD_ERR_FORM_SIZE: case UPLOAD_ERR_FORM_SIZE:
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0)) . EOL); notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($_REQUEST['MAX_FILE_SIZE'] ?? 0)));
break; break;
case UPLOAD_ERR_PARTIAL: case UPLOAD_ERR_PARTIAL:
notice(DI::l10n()->t('Image upload didn\'t complete, please try again') . EOL); notice(DI::l10n()->t('Image upload didn\'t complete, please try again'));
break; break;
case UPLOAD_ERR_NO_FILE: case UPLOAD_ERR_NO_FILE:
notice(DI::l10n()->t('Image file is missing') . EOL); notice(DI::l10n()->t('Image file is missing'));
break; break;
case UPLOAD_ERR_NO_TMP_DIR: case UPLOAD_ERR_NO_TMP_DIR:
case UPLOAD_ERR_CANT_WRITE: case UPLOAD_ERR_CANT_WRITE:
case UPLOAD_ERR_EXTENSION: case UPLOAD_ERR_EXTENSION:
notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator') . EOL); notice(DI::l10n()->t('Server can\'t accept new file upload at this time, please contact your administrator'));
break; break;
} }
@unlink($src); @unlink($src);
@ -706,7 +706,7 @@ function photos_post(App $a)
$maximagesize = DI::config()->get('system', 'maximagesize'); $maximagesize = DI::config()->get('system', 'maximagesize');
if ($maximagesize && ($filesize > $maximagesize)) { if ($maximagesize && ($filesize > $maximagesize)) {
notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)) . EOL); notice(DI::l10n()->t('Image exceeds size limit of %s', Strings::formatBytes($maximagesize)));
@unlink($src); @unlink($src);
$foo = 0; $foo = 0;
Hook::callAll('photo_post_end', $foo); Hook::callAll('photo_post_end', $foo);
@ -714,7 +714,7 @@ function photos_post(App $a)
} }
if (!$filesize) { if (!$filesize) {
notice(DI::l10n()->t('Image file is empty.') . EOL); notice(DI::l10n()->t('Image file is empty.'));
@unlink($src); @unlink($src);
$foo = 0; $foo = 0;
Hook::callAll('photo_post_end', $foo); Hook::callAll('photo_post_end', $foo);
@ -729,7 +729,7 @@ function photos_post(App $a)
if (!$image->isValid()) { if (!$image->isValid()) {
Logger::log('mod/photos.php: photos_post(): unable to process image' , Logger::DEBUG); Logger::log('mod/photos.php: photos_post(): unable to process image' , Logger::DEBUG);
notice(DI::l10n()->t('Unable to process image.') . EOL); notice(DI::l10n()->t('Unable to process image.'));
@unlink($src); @unlink($src);
$foo = 0; $foo = 0;
Hook::callAll('photo_post_end',$foo); Hook::callAll('photo_post_end',$foo);
@ -758,7 +758,7 @@ function photos_post(App $a)
if (!$r) { if (!$r) {
Logger::log('mod/photos.php: photos_post(): image store failed', Logger::DEBUG); Logger::log('mod/photos.php: photos_post(): image store failed', Logger::DEBUG);
notice(DI::l10n()->t('Image upload failed.') . EOL); notice(DI::l10n()->t('Image upload failed.'));
return; return;
} }
@ -841,12 +841,12 @@ function photos_content(App $a)
// photos/name/image/xxxxx/drop // photos/name/image/xxxxx/drop
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
notice(DI::l10n()->t('Public access denied.') . EOL); notice(DI::l10n()->t('Public access denied.'));
return; return;
} }
if (empty($a->data['user'])) { if (empty($a->data['user'])) {
notice(DI::l10n()->t('No photos selected') . EOL); notice(DI::l10n()->t('No photos selected'));
return; return;
} }
@ -912,7 +912,7 @@ function photos_content(App $a)
} }
if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) { if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) {
notice(DI::l10n()->t('Access to this item is restricted.') . EOL); notice(DI::l10n()->t('Access to this item is restricted.'));
return; return;
} }
@ -938,7 +938,7 @@ function photos_content(App $a)
$albumselect .= '<option value="" ' . (!$selname ? ' selected="selected" ' : '') . '>&lt;current year&gt;</option>'; $albumselect .= '<option value="" ' . (!$selname ? ' selected="selected" ' : '') . '>&lt;current year&gt;</option>';
if (!empty($a->data['albums'])) { if (!empty($a->data['albums'])) {
foreach ($a->data['albums'] as $album) { foreach ($a->data['albums'] as $album) {
if (($album['album'] === '') || ($album['album'] === 'Contact Photos') || ($album['album'] === DI::l10n()->t('Contact Photos'))) { if (($album['album'] === '') || ($album['album'] === Photo::CONTACT_PHOTOS) || ($album['album'] === DI::l10n()->t(Photo::CONTACT_PHOTOS))) {
continue; continue;
} }
$selected = (($selname === $album['album']) ? ' selected="selected" ' : ''); $selected = (($selname === $album['album']) ? ' selected="selected" ' : '');
@ -988,8 +988,6 @@ function photos_content(App $a)
'$uploadurl' => $ret['post_url'], '$uploadurl' => $ret['post_url'],
// ACL permissions box // ACL permissions box
'$group_perms' => DI::l10n()->t('Show to Groups'),
'$contact_perms' => DI::l10n()->t('Show to Contacts'),
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
]); ]);
@ -1041,7 +1039,6 @@ function photos_content(App $a)
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$method' => 'post', '$method' => 'post',
'$message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'), '$message' => DI::l10n()->t('Do you really want to delete this photo album and all its photos?'),
'$extra_inputs' => [],
'$confirm' => DI::l10n()->t('Delete Album'), '$confirm' => DI::l10n()->t('Delete Album'),
'$confirm_url' => $drop_url, '$confirm_url' => $drop_url,
'$confirm_name' => 'dropalbum', '$confirm_name' => 'dropalbum',
@ -1051,7 +1048,7 @@ function photos_content(App $a)
// edit album name // edit album name
if ($cmd === 'edit') { if ($cmd === 'edit') {
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== DI::l10n()->t('Contact Photos'))) { if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== Photo::CONTACT_PHOTOS) && ($album !== DI::l10n()->t(Photo::CONTACT_PHOTOS))) {
if ($can_post) { if ($can_post) {
$edit_tpl = Renderer::getMarkupTemplate('album_edit.tpl'); $edit_tpl = Renderer::getMarkupTemplate('album_edit.tpl');
@ -1068,7 +1065,7 @@ function photos_content(App $a)
} }
} }
} else { } else {
if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== 'Contact Photos') && ($album !== DI::l10n()->t('Contact Photos')) && $can_post) { if (($album !== DI::l10n()->t('Profile Photos')) && ($album !== Photo::CONTACT_PHOTOS) && ($album !== DI::l10n()->t(Photo::CONTACT_PHOTOS)) && $can_post) {
$edit = [DI::l10n()->t('Edit Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit']; $edit = [DI::l10n()->t('Edit Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/edit'];
$drop = [DI::l10n()->t('Drop Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/drop']; $drop = [DI::l10n()->t('Drop Album'), 'photos/' . $a->data['user']['nickname'] . '/album/' . bin2hex($album) . '/drop'];
} }
@ -1137,7 +1134,7 @@ function photos_content(App $a)
if (DBA::exists('photo', ['resource-id' => $datum, 'uid' => $owner_uid])) { if (DBA::exists('photo', ['resource-id' => $datum, 'uid' => $owner_uid])) {
notice(DI::l10n()->t('Permission denied. Access to this item may be restricted.')); notice(DI::l10n()->t('Permission denied. Access to this item may be restricted.'));
} else { } else {
notice(DI::l10n()->t('Photo not available') . EOL); notice(DI::l10n()->t('Photo not available'));
} }
return; return;
} }
@ -1148,7 +1145,6 @@ function photos_content(App $a)
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [ return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$method' => 'post', '$method' => 'post',
'$message' => DI::l10n()->t('Do you really want to delete this photo?'), '$message' => DI::l10n()->t('Do you really want to delete this photo?'),
'$extra_inputs' => [],
'$confirm' => DI::l10n()->t('Delete Photo'), '$confirm' => DI::l10n()->t('Delete Photo'),
'$confirm_url' => $drop_url, '$confirm_url' => $drop_url,
'$confirm_name' => 'delete', '$confirm_name' => 'delete',
@ -1353,8 +1349,6 @@ function photos_content(App $a)
'$delete' => DI::l10n()->t('Delete Photo'), '$delete' => DI::l10n()->t('Delete Photo'),
// ACL permissions box // ACL permissions box
'$group_perms' => DI::l10n()->t('Show to Groups'),
'$contact_perms' => DI::l10n()->t('Show to Contacts'),
'$return_path' => DI::args()->getQueryString(), '$return_path' => DI::args()->getQueryString(),
]); ]);
} }
@ -1383,6 +1377,16 @@ function photos_content(App $a)
if (!DBA::isResult($items)) { if (!DBA::isResult($items)) {
if (($can_post || Security::canWriteToUserWall($owner_uid))) { if (($can_post || Security::canWriteToUserWall($owner_uid))) {
/*
* Hmmm, code depending on the presence of a particular addon?
* This should be better if done by a hook
*/
$qcomment = null;
if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl, [ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
@ -1397,7 +1401,7 @@ function photos_content(App $a)
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$loading' => DI::l10n()->t('Loading...'), '$loading' => DI::l10n()->t('Loading...'),
'$sourceapp' => DI::l10n()->t($a->sourcename), '$sourceapp' => DI::l10n()->t($a->sourcename),
'$ww' => '', '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12) '$rand_num' => Crypto::randomDigits(12)
]); ]);
} }
@ -1430,6 +1434,16 @@ function photos_content(App $a)
} }
if (($can_post || Security::canWriteToUserWall($owner_uid))) { if (($can_post || Security::canWriteToUserWall($owner_uid))) {
/*
* Hmmm, code depending on the presence of a particular addon?
* This should be better if done by a hook
*/
$qcomment = null;
if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl,[ $comments .= Renderer::replaceMacros($cmnt_tpl,[
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
@ -1443,7 +1457,7 @@ function photos_content(App $a)
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$sourceapp' => DI::l10n()->t($a->sourcename), '$sourceapp' => DI::l10n()->t($a->sourcename),
'$ww' => '', '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12) '$rand_num' => Crypto::randomDigits(12)
]); ]);
} }
@ -1493,6 +1507,16 @@ function photos_content(App $a)
]); ]);
if (($can_post || Security::canWriteToUserWall($owner_uid))) { if (($can_post || Security::canWriteToUserWall($owner_uid))) {
/*
* Hmmm, code depending on the presence of a particular addon?
* This should be better if done by a hook
*/
$qcomment = null;
if (Addon::isEnabled('qcomment')) {
$words = DI::pConfig()->get(local_user(), 'qcomment', 'words');
$qcomment = $words ? explode("\n", $words) : [];
}
$comments .= Renderer::replaceMacros($cmnt_tpl, [ $comments .= Renderer::replaceMacros($cmnt_tpl, [
'$return_path' => '', '$return_path' => '',
'$jsreload' => $return_path, '$jsreload' => $return_path,
@ -1506,7 +1530,7 @@ function photos_content(App $a)
'$submit' => DI::l10n()->t('Submit'), '$submit' => DI::l10n()->t('Submit'),
'$preview' => DI::l10n()->t('Preview'), '$preview' => DI::l10n()->t('Preview'),
'$sourceapp' => DI::l10n()->t($a->sourcename), '$sourceapp' => DI::l10n()->t($a->sourcename),
'$ww' => '', '$qcomment' => $qcomment,
'$rand_num' => Crypto::randomDigits(12) '$rand_num' => Crypto::randomDigits(12)
]); ]);
} }
@ -1551,8 +1575,8 @@ function photos_content(App $a)
$r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' $r = q("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s'
$sql_extra GROUP BY `resource-id`", $sql_extra GROUP BY `resource-id`",
intval($a->data['user']['uid']), intval($a->data['user']['uid']),
DBA::escape('Contact Photos'), DBA::escape(Photo::CONTACT_PHOTOS),
DBA::escape(DI::l10n()->t('Contact Photos')) DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS))
); );
if (DBA::isResult($r)) { if (DBA::isResult($r)) {
$total = count($r); $total = count($r);
@ -1566,8 +1590,8 @@ function photos_content(App $a)
WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s' WHERE `uid` = %d AND `album` != '%s' AND `album` != '%s'
$sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d", $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT %d , %d",
intval($a->data['user']['uid']), intval($a->data['user']['uid']),
DBA::escape('Contact Photos'), DBA::escape(Photo::CONTACT_PHOTOS),
DBA::escape(DI::l10n()->t('Contact Photos')), DBA::escape(DI::l10n()->t(Photo::CONTACT_PHOTOS)),
$pager->getStart(), $pager->getStart(),
$pager->getItemsPerPage() $pager->getItemsPerPage()
); );

View File

@ -34,7 +34,6 @@ use Friendica\Model\Verb;
use Friendica\Protocol\Activity; use Friendica\Protocol\Activity;
use Friendica\Util\DateTimeFormat; use Friendica\Util\DateTimeFormat;
use Friendica\Util\Temporal; use Friendica\Util\Temporal;
use Friendica\Util\Proxy as ProxyUtils;
use Friendica\Util\XML; use Friendica\Util\XML;
/** /**
@ -136,13 +135,9 @@ function ping_init(App $a)
$notifs = ping_get_notifications(local_user()); $notifs = ping_get_notifications(local_user());
$condition = ["`unseen` AND `uid` = ? AND `contact-id` != ? AND (`vid` != ? OR `vid` IS NULL)", $condition = ["`unseen` AND `uid` = ? AND NOT `origin` AND (`vid` != ? OR `vid` IS NULL)",
local_user(), local_user(), Verb::getID(Activity::FOLLOW)]; local_user(), Verb::getID(Activity::FOLLOW)];
$fields = ['id', 'parent', 'verb', 'author-name', 'unseen', 'author-link', 'author-avatar', 'contact-avatar', $items = Item::selectForUser(local_user(), ['wall', 'uid', 'uri-id'], $condition);
'network', 'created', 'object', 'parent-author-name', 'parent-author-link', 'parent-guid', 'wall', 'activity'];
$params = ['order' => ['received' => true]];
$items = Item::selectForUser(local_user(), $fields, $condition, $params);
if (DBA::isResult($items)) { if (DBA::isResult($items)) {
$items_unseen = Item::inArray($items); $items_unseen = Item::inArray($items);
$arr = ['items' => $items_unseen]; $arr = ['items' => $items_unseen];
@ -156,6 +151,7 @@ function ping_init(App $a)
} }
} }
} }
DBA::close($items);
if ($network_count) { if ($network_count) {
// Find out how unseen network posts are spread across groups // Find out how unseen network posts are spread across groups
@ -331,12 +327,8 @@ function ping_init(App $a)
if (DBA::isResult($notifs)) { if (DBA::isResult($notifs)) {
foreach ($notifs as $notif) { foreach ($notifs as $notif) {
$contact = Contact::getDetailsByURL($notif['url']); $contact = Contact::getByURL($notif['url'], false, ['micro', 'id', 'avatar']);
if (isset($contact['micro'])) { $notif['photo'] = Contact::getMicro($contact, $notif['photo']);
$notif['photo'] = ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO);
} else {
$notif['photo'] = ProxyUtils::proxifyUrl($notif['photo'], false, ProxyUtils::SIZE_MICRO);
}
$local_time = DateTimeFormat::local($notif['date']); $local_time = DateTimeFormat::local($notif['date']);

View File

@ -27,139 +27,37 @@ use Friendica\Core\Protocol;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Protocol\PortableContact;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\XML; use Friendica\Util\XML;
function poco_init(App $a) { function poco_init(App $a) {
$system_mode = false;
if (intval(DI::config()->get('system', 'block_public')) || (DI::config()->get('system', 'block_local_dir'))) { if (intval(DI::config()->get('system', 'block_public')) || (DI::config()->get('system', 'block_local_dir'))) {
throw new \Friendica\Network\HTTPException\ForbiddenException(); throw new \Friendica\Network\HTTPException\ForbiddenException();
} }
if ($a->argc > 1) { if ($a->argc > 1) {
$nickname = Strings::escapeTags(trim($a->argv[1])); // Only the system mode is supported
} throw new \Friendica\Network\HTTPException\NotFoundException();
if (empty($nickname)) {
if (!DBA::exists('profile', ['net-publish' => true])) {
throw new \Friendica\Network\HTTPException\ForbiddenException();
}
$system_mode = true;
} }
$format = ($_GET['format'] ?? '') ?: 'json'; $format = ($_GET['format'] ?? '') ?: 'json';
$justme = false; $totalResults = DBA::count('profile', ['net-publish' => true]);
$global = false; if ($totalResults == 0) {
throw new \Friendica\Network\HTTPException\ForbiddenException();
if ($a->argc > 1 && $a->argv[1] === '@server') {
// List of all servers that this server knows
$ret = PortableContact::serverlist();
header('Content-type: application/json');
echo json_encode($ret);
exit();
} }
if ($a->argc > 1 && $a->argv[1] === '@global') {
// List of all profiles that this server recently had data from
$global = true;
$update_limit = date(DateTimeFormat::MYSQL, time() - 30 * 86400);
}
if ($a->argc > 2 && $a->argv[2] === '@me') {
$justme = true;
}
if ($a->argc > 3 && $a->argv[3] === '@all') {
$justme = false;
}
if ($a->argc > 3 && $a->argv[3] === '@self') {
$justme = true;
}
if ($a->argc > 4 && intval($a->argv[4]) && $justme == false) {
$cid = intval($a->argv[4]);
}
if (!$system_mode && !$global) {
$user = DBA::selectFirst('owner-view', ['uid', 'nickname'], ['nickname' => $nickname, 'hide-friends' => false]);
if (!DBA::isResult($user)) {
throw new \Friendica\Network\HTTPException\NotFoundException();
}
}
if ($justme) {
$sql_extra = " AND `contact`.`self` = 1 ";
} else {
$sql_extra = "";
}
if (!empty($cid)) {
$sql_extra = sprintf(" AND `contact`.`id` = %d ", intval($cid));
}
if (!empty($_GET['updatedSince'])) {
$update_limit = date(DateTimeFormat::MYSQL, strtotime($_GET['updatedSince']));
}
if ($global) {
$contacts = q("SELECT count(*) AS `total` FROM `gcontact` WHERE `updated` >= '%s' AND `updated` >= `last_failure` AND NOT `hide` AND `network` IN ('%s', '%s', '%s')",
DBA::escape($update_limit),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS)
);
} elseif ($system_mode) {
$totalResults = DBA::count('profile', ['net-publish' => true]);
} else {
$contacts = q("SELECT count(*) AS `total` FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`)
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra",
intval($user['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::STATUSNET)
);
}
if (empty($totalResults) && DBA::isResult($contacts)) {
$totalResults = intval($contacts[0]['total']);
} elseif (empty($totalResults)) {
$totalResults = 0;
}
if (!empty($_GET['startIndex'])) { if (!empty($_GET['startIndex'])) {
$startIndex = intval($_GET['startIndex']); $startIndex = intval($_GET['startIndex']);
} else { } else {
$startIndex = 0; $startIndex = 0;
} }
$itemsPerPage = ((!empty($_GET['count'])) ? intval($_GET['count']) : $totalResults); $itemsPerPage = (!empty($_GET['count']) ? intval($_GET['count']) : $totalResults);
if ($global) { Logger::info("Start system mode query");
Logger::log("Start global query", Logger::DEBUG); $contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
$contacts = q("SELECT * FROM `gcontact` WHERE `updated` > '%s' AND NOT `hide` AND `network` IN ('%s', '%s', '%s') AND `updated` > `last_failure`
ORDER BY `updated` DESC LIMIT %d, %d", Logger::info("Query done");
DBA::escape($update_limit),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS),
intval($startIndex),
intval($itemsPerPage)
);
} elseif ($system_mode) {
Logger::log("Start system mode query", Logger::DEBUG);
$contacts = DBA::selectToArray('owner-view', [], ['net-publish' => true], ['limit' => [$startIndex, $itemsPerPage]]);
} else {
Logger::log("Start query for user " . $user['nickname'], Logger::DEBUG);
$contacts = q("SELECT * FROM `contact` WHERE `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `hidden` = 0 AND `archive` = 0
AND (`success_update` >= `failure_update` OR `last-item` >= `failure_update`)
AND `network` IN ('%s', '%s', '%s', '%s') $sql_extra LIMIT %d, %d",
intval($user['uid']),
DBA::escape(Protocol::DFRN),
DBA::escape(Protocol::DIASPORA),
DBA::escape(Protocol::OSTATUS),
DBA::escape(Protocol::STATUSNET),
intval($startIndex),
intval($itemsPerPage)
);
}
Logger::log("Query done", Logger::DEBUG);
$ret = []; $ret = [];
if (!empty($_GET['sorted'])) { if (!empty($_GET['sorted'])) {
@ -168,7 +66,7 @@ function poco_init(App $a) {
if (!empty($_GET['filtered'])) { if (!empty($_GET['filtered'])) {
$ret['filtered'] = false; $ret['filtered'] = false;
} }
if (!empty($_GET['updatedSince']) && ! $global) { if (!empty($_GET['updatedSince'])) {
$ret['updatedSince'] = false; $ret['updatedSince'] = false;
} }
$ret['startIndex'] = (int) $startIndex; $ret['startIndex'] = (int) $startIndex;
@ -176,7 +74,6 @@ function poco_init(App $a) {
$ret['totalResults'] = (int) $totalResults; $ret['totalResults'] = (int) $totalResults;
$ret['entry'] = []; $ret['entry'] = [];
$fields_ret = [ $fields_ret = [
'id' => false, 'id' => false,
'displayName' => false, 'displayName' => false,
@ -193,7 +90,7 @@ function poco_init(App $a) {
'generation' => false 'generation' => false
]; ];
if (empty($_GET['fields']) || ($_GET['fields'] === '@all')) { if (empty($_GET['fields'])) {
foreach ($fields_ret as $k => $v) { foreach ($fields_ret as $k => $v) {
$fields_ret[$k] = true; $fields_ret[$k] = true;
} }
@ -215,13 +112,7 @@ function poco_init(App $a) {
} }
if (! isset($contact['generation'])) { if (! isset($contact['generation'])) {
if ($global) { $contact['generation'] = 1;
$contact['generation'] = 3;
} elseif ($system_mode) {
$contact['generation'] = 1;
} else {
$contact['generation'] = 2;
}
} }
if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) { if (($contact['keywords'] == "") && isset($contact['pub_keywords'])) {
@ -268,20 +159,16 @@ function poco_init(App $a) {
$entry['preferredUsername'] = $contact['nick']; $entry['preferredUsername'] = $contact['nick'];
} }
if ($fields_ret['updated']) { if ($fields_ret['updated']) {
if (! $global) { $entry['updated'] = $contact['success_update'];
$entry['updated'] = $contact['success_update'];
if ($contact['name-date'] > $entry['updated']) { if ($contact['name-date'] > $entry['updated']) {
$entry['updated'] = $contact['name-date']; $entry['updated'] = $contact['name-date'];
} }
if ($contact['uri-date'] > $entry['updated']) { if ($contact['uri-date'] > $entry['updated']) {
$entry['updated'] = $contact['uri-date']; $entry['updated'] = $contact['uri-date'];
} }
if ($contact['avatar-date'] > $entry['updated']) { if ($contact['avatar-date'] > $entry['updated']) {
$entry['updated'] = $contact['avatar-date']; $entry['updated'] = $contact['avatar-date'];
}
} else {
$entry['updated'] = $contact['updated'];
} }
$entry['updated'] = date("c", strtotime($entry['updated'])); $entry['updated'] = date("c", strtotime($entry['updated']));
} }
@ -314,19 +201,13 @@ function poco_init(App $a) {
if ($fields_ret['address']) { if ($fields_ret['address']) {
$entry['address'] = []; $entry['address'] = [];
// Deactivated. It just reveals too much data. (Although its from the default profile)
//if (isset($rr['address']))
// $entry['address']['streetAddress'] = $rr['address'];
if (isset($contact['locality'])) { if (isset($contact['locality'])) {
$entry['address']['locality'] = $contact['locality']; $entry['address']['locality'] = $contact['locality'];
} }
if (isset($contact['region'])) { if (isset($contact['region'])) {
$entry['address']['region'] = $contact['region']; $entry['address']['region'] = $contact['region'];
} }
// See above
//if (isset($rr['postal-code']))
// $entry['address']['postalCode'] = $rr['postal-code'];
if (isset($contact['country'])) { if (isset($contact['country'])) {
$entry['address']['country'] = $contact['country']; $entry['address']['country'] = $contact['country'];
@ -342,7 +223,7 @@ function poco_init(App $a) {
$ret['entry'][] = []; $ret['entry'][] = [];
} }
Logger::log("End of poco", Logger::DEBUG); Logger::info("End of poco");
if ($format === 'xml') { if ($format === 'xml') {
header('Content-type: text/xml'); header('Content-type: text/xml');

View File

@ -25,6 +25,7 @@ use Friendica\Core\Protocol;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Protocol\Feed;
use Friendica\Protocol\OStatus; use Friendica\Protocol\OStatus;
use Friendica\Util\Strings; use Friendica\Util\Strings;
use Friendica\Util\Network; use Friendica\Util\Network;
@ -146,11 +147,11 @@ function pubsub_post(App $a)
Logger::log('Import item for ' . $nick . ' from ' . $contact['nick'] . ' (' . $contact['id'] . ')'); Logger::log('Import item for ' . $nick . ' from ' . $contact['nick'] . ' (' . $contact['id'] . ')');
$feedhub = ''; $feedhub = '';
consume_feed($xml, $importer, $contact, $feedhub); Feed::consume($xml, $importer, $contact, $feedhub);
// do it a second time for DFRN so that any children find their parents. // do it a second time for DFRN so that any children find their parents.
if ($contact['network'] === Protocol::DFRN) { if ($contact['network'] === Protocol::DFRN) {
consume_feed($xml, $importer, $contact, $feedhub); Feed::consume($xml, $importer, $contact, $feedhub);
} }
hub_post_return(); hub_post_return();

View File

@ -24,7 +24,6 @@ use Friendica\Core\Logger;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\PushSubscriber; use Friendica\Model\PushSubscriber;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
function post_var($name) { function post_var($name) {
@ -126,7 +125,7 @@ function pubsubhubbub_init(App $a) {
$hub_callback = rtrim($hub_callback, ' ?&#'); $hub_callback = rtrim($hub_callback, ' ?&#');
$separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&'; $separator = parse_url($hub_callback, PHP_URL_QUERY) === null ? '?' : '&';
$fetchResult = Network::fetchUrlFull($hub_callback . $separator . $params); $fetchResult = DI::httpRequest()->fetchFull($hub_callback . $separator . $params);
$body = $fetchResult->getBody(); $body = $fetchResult->getBody();
$ret = $fetchResult->getReturnCode(); $ret = $fetchResult->getReturnCode();

View File

@ -27,7 +27,6 @@ use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Util\Network;
use Friendica\Util\Strings; use Friendica\Util\Strings;
function redir_init(App $a) { function redir_init(App $a) {
@ -171,7 +170,7 @@ function redir_magic($a, $cid, $url)
} }
// Test for magic auth on the target system // Test for magic auth on the target system
$serverret = Network::curl($basepath . '/magic'); $serverret = DI::httpRequest()->get($basepath . '/magic');
if ($serverret->isSuccess()) { if ($serverret->isSuccess()) {
$separator = strpos($target_url, '?') ? '&' : '?'; $separator = strpos($target_url, '?') ? '&' : '?';
$target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url); $target_url .= $separator . 'zrl=' . urlencode($visitor) . '&addr=' . urlencode($contact_url);

View File

@ -28,7 +28,7 @@ use Friendica\Model\Contact;
function repair_ostatus_content(App $a) { function repair_ostatus_content(App $a) {
if (! local_user()) { if (! local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
DI::baseUrl()->redirect('ostatus_repair'); DI::baseUrl()->redirect('ostatus_repair');
// NOTREACHED // NOTREACHED
} }

View File

@ -31,7 +31,6 @@ use Friendica\Core\Worker;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\GContact;
use Friendica\Model\Group; use Friendica\Model\Group;
use Friendica\Model\Notify\Type; use Friendica\Model\Notify\Type;
use Friendica\Model\User; use Friendica\Model\User;
@ -63,7 +62,7 @@ function settings_post(App $a)
} }
if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) { if (count($a->user) && !empty($a->user['uid']) && $a->user['uid'] != local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -198,13 +197,10 @@ function settings_post(App $a)
unset($dcrpass); unset($dcrpass);
if (!$mbox) { if (!$mbox) {
$failed = true; $failed = true;
notice(DI::l10n()->t('Failed to connect with email account using the settings provided.') . EOL); notice(DI::l10n()->t('Failed to connect with email account using the settings provided.'));
} }
} }
} }
if (!$failed) {
info(DI::l10n()->t('Email settings updated.') . EOL);
}
} }
} }
@ -219,7 +215,6 @@ function settings_post(App $a)
DI::pConfig()->set(local_user(), 'feature', substr($k, 8), ((intval($v)) ? 1 : 0)); DI::pConfig()->set(local_user(), 'feature', substr($k, 8), ((intval($v)) ? 1 : 0));
} }
} }
info(DI::l10n()->t('Features updated') . EOL);
return; return;
} }
@ -231,7 +226,7 @@ function settings_post(App $a)
// was there an error // was there an error
if ($_FILES['importcontact-filename']['error'] > 0) { if ($_FILES['importcontact-filename']['error'] > 0) {
Logger::notice('Contact CSV file upload error'); Logger::notice('Contact CSV file upload error');
info(DI::l10n()->t('Contact CSV file upload error')); notice(DI::l10n()->t('Contact CSV file upload error'));
} else { } else {
$csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name'])); $csvArray = array_map('str_getcsv', file($_FILES['importcontact-filename']['tmp_name']));
// import contacts // import contacts
@ -424,10 +419,10 @@ function settings_post(App $a)
$hidewall = 1; $hidewall = 1;
if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) { if (!$str_contact_allow && !$str_group_allow && !$str_contact_deny && !$str_group_deny) {
if ($def_gid) { if ($def_gid) {
info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'). EOL); info(DI::l10n()->t('Private forum has no privacy permissions. Using default privacy group.'));
$str_group_allow = '<' . $def_gid . '>'; $str_group_allow = '<' . $def_gid . '>';
} else { } else {
notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.') . EOL); notice(DI::l10n()->t('Private forum has no privacy permissions and no default privacy group.'));
} }
} }
} }
@ -443,8 +438,8 @@ function settings_post(App $a)
$fields['openidserver'] = ''; $fields['openidserver'] = '';
} }
if (DBA::update('user', $fields, ['uid' => local_user()])) { if (!DBA::update('user', $fields, ['uid' => local_user()])) {
info(DI::l10n()->t('Settings updated.') . EOL); notice(DI::l10n()->t('Settings were not updated.'));
} }
// clear session language // clear session language
@ -475,9 +470,6 @@ function settings_post(App $a)
Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user()); Worker::add(PRIORITY_LOW, 'ProfileUpdate', local_user());
// Update the global contact for the user
GContact::updateForUser(local_user());
DI::baseUrl()->redirect('settings'); DI::baseUrl()->redirect('settings');
return; // NOTREACHED return; // NOTREACHED
} }
@ -489,12 +481,12 @@ function settings_content(App $a)
Nav::setSelected('settings'); Nav::setSelected('settings');
if (!local_user()) { if (!local_user()) {
//notice(DI::l10n()->t('Permission denied.') . EOL); //notice(DI::l10n()->t('Permission denied.'));
return Login::form(); return Login::form();
} }
if (!empty($_SESSION['submanage'])) { if (!empty($_SESSION['submanage'])) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -722,7 +714,7 @@ function settings_content(App $a)
$profile = DBA::selectFirst('profile', [], ['uid' => local_user()]); $profile = DBA::selectFirst('profile', [], ['uid' => local_user()]);
if (!DBA::isResult($profile)) { if (!DBA::isResult($profile)) {
notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.') . EOL); notice(DI::l10n()->t('Unable to find your profile. Please contact your admin.'));
return; return;
} }
@ -837,26 +829,6 @@ function settings_content(App $a)
$stpl = Renderer::getMarkupTemplate('settings/settings.tpl'); $stpl = Renderer::getMarkupTemplate('settings/settings.tpl');
// Private/public post links for the non-JS ACL form
$private_post = 1;
if (!empty($_REQUEST['public']) && !$_REQUEST['public']) {
$private_post = 0;
}
$query_str = DI::args()->getQueryString();
if (strpos($query_str, 'public=1') !== false) {
$query_str = str_replace(['?public=1', '&public=1'], ['', ''], $query_str);
}
// I think $a->query_string may never have ? in it, but I could be wrong
// It looks like it's from the index.php?q=[etc] rewrite that the web
// server does, which converts any ? to &, e.g. suggest&ignore=61 for suggest?ignore=61
if (strpos($query_str, '?') === false) {
$public_post_link = '?public=1';
} else {
$public_post_link = '&public=1';
}
/* Installed langs */ /* Installed langs */
$lang_choices = DI::l10n()->getAvailableLanguages(); $lang_choices = DI::l10n()->getAvailableLanguages();
@ -874,7 +846,7 @@ function settings_content(App $a)
'$password1'=> ['password', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).')], '$password1'=> ['password', DI::l10n()->t('New Password:'), '', DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).')],
'$password2'=> ['confirm', DI::l10n()->t('Confirm:'), '', DI::l10n()->t('Leave password fields blank unless changing')], '$password2'=> ['confirm', DI::l10n()->t('Confirm:'), '', DI::l10n()->t('Leave password fields blank unless changing')],
'$password3'=> ['opassword', DI::l10n()->t('Current Password:'), '', DI::l10n()->t('Your current password to confirm the changes')], '$password3'=> ['opassword', DI::l10n()->t('Current Password:'), '', DI::l10n()->t('Your current password to confirm the changes')],
'$password4'=> ['mpassword', DI::l10n()->t('Password:'), '', DI::l10n()->t('Your current password to confirm the changes')], '$password4'=> ['mpassword', DI::l10n()->t('Password:'), '', DI::l10n()->t('Your current password to confirm the changes of the email address')],
'$oid_enable' => (!DI::config()->get('system', 'no_openid')), '$oid_enable' => (!DI::config()->get('system', 'no_openid')),
'$openid' => $openid_field, '$openid' => $openid_field,
'$delete_openid' => ['delete_openid', DI::l10n()->t('Delete OpenID URL'), false, ''], '$delete_openid' => ['delete_openid', DI::l10n()->t('Delete OpenID URL'), false, ''],

View File

@ -34,7 +34,7 @@ function subthread_content(App $a)
$item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0); $item_id = (($a->argc > 1) ? Strings::escapeTags(trim($a->argv[1])) : 0);
if (!Item::performActivity($item_id, 'follow')) { if (!Item::performActivity($item_id, 'follow', local_user())) {
Logger::info('Following item failed', ['item' => $item_id]); Logger::info('Following item failed', ['item' => $item_id]);
throw new HTTPException\BadRequestException(); throw new HTTPException\BadRequestException();
} }

View File

@ -20,39 +20,18 @@
*/ */
use Friendica\App; use Friendica\App;
use Friendica\Content\ContactSelector;
use Friendica\Content\Widget; use Friendica\Content\Widget;
use Friendica\Core\Renderer; use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Model\GContact; use Friendica\Module\Contact as ModuleContact;
use Friendica\Util\Proxy as ProxyUtils; use Friendica\Network\HTTPException;
function suggest_init(App $a)
{
if (! local_user()) {
return;
}
}
function suggest_post(App $a)
{
if (!empty($_POST['ignore']) && !empty($_POST['confirm'])) {
DBA::insert('gcign', ['uid' => local_user(), 'gcid' => $_POST['ignore']]);
notice(DI::l10n()->t('Contact suggestion successfully ignored.'));
}
DI::baseUrl()->redirect('suggest');
}
function suggest_content(App $a) function suggest_content(App $a)
{ {
$o = ''; if (!local_user()) {
throw new HTTPException\ForbiddenException(DI::l10n()->t('Permission denied.'));
if (! local_user()) {
notice(DI::l10n()->t('Permission denied.') . EOL);
return;
} }
$_SESSION['return_path'] = DI::args()->getCommand(); $_SESSION['return_path'] = DI::args()->getCommand();
@ -60,80 +39,20 @@ function suggest_content(App $a)
DI::page()['aside'] .= Widget::findPeople(); DI::page()['aside'] .= Widget::findPeople();
DI::page()['aside'] .= Widget::follow(); DI::page()['aside'] .= Widget::follow();
$contacts = Contact\Relation::getSuggestions(local_user());
$r = GContact::suggestionQuery(local_user()); if (!DBA::isResult($contacts)) {
return DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.');
if (! DBA::isResult($r)) {
$o .= DI::l10n()->t('No suggestions available. If this is a new site, please try again in 24 hours.');
return $o;
} }
if (!empty($_GET['ignore'])) {
// <form> can't take arguments in its "action" parameter
// so add any arguments as hidden inputs
$query = explode_querystring(DI::args()->getQueryString());
$inputs = [];
foreach ($query['args'] as $arg) {
if (strpos($arg, 'confirm=') === false) {
$arg_parts = explode('=', $arg);
$inputs[] = ['name' => $arg_parts[0], 'value' => $arg_parts[1]];
}
}
return Renderer::replaceMacros(Renderer::getMarkupTemplate('confirm.tpl'), [
'$method' => 'post',
'$message' => DI::l10n()->t('Do you really want to delete this suggestion?'),
'$extra_inputs' => $inputs,
'$confirm' => DI::l10n()->t('Yes'),
'$confirm_url' => $query['base'],
'$confirm_name' => 'confirm',
'$cancel' => DI::l10n()->t('Cancel'),
]);
}
$id = 0;
$entries = []; $entries = [];
foreach ($contacts as $contact) {
foreach ($r as $rr) { $entries[] = ModuleContact::getContactTemplateVars($contact);
$connlnk = DI::baseUrl() . '/follow/?url=' . (($rr['connect']) ? $rr['connect'] : $rr['url']);
$ignlnk = DI::baseUrl() . '/suggest?ignore=' . $rr['id'];
$photo_menu = [
'profile' => [DI::l10n()->t("View Profile"), Contact::magicLink($rr["url"])],
'follow' => [DI::l10n()->t("Connect/Follow"), $connlnk],
'hide' => [DI::l10n()->t('Ignore/Hide'), $ignlnk]
];
$contact_details = Contact::getDetailsByURL($rr["url"], local_user(), $rr);
$entry = [
'url' => Contact::magicLink($rr['url']),
'itemurl' => (($contact_details['addr'] != "") ? $contact_details['addr'] : $rr['url']),
'img_hover' => $rr['url'],
'name' => $contact_details['name'],
'thumb' => ProxyUtils::proxifyUrl($contact_details['thumb'], false, ProxyUtils::SIZE_THUMB),
'details' => $contact_details['location'],
'tags' => $contact_details['keywords'],
'about' => $contact_details['about'],
'account_type' => Contact::getAccountType($contact_details),
'ignlnk' => $ignlnk,
'ignid' => $rr['id'],
'conntxt' => DI::l10n()->t('Connect'),
'connlnk' => $connlnk,
'photo_menu' => $photo_menu,
'ignore' => DI::l10n()->t('Ignore/Hide'),
'network' => ContactSelector::networkToName($rr['network'], $rr['url']),
'id' => ++$id,
];
$entries[] = $entry;
} }
$tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl'); $tpl = Renderer::getMarkupTemplate('viewcontact_template.tpl');
$o .= Renderer::replaceMacros($tpl,[ return Renderer::replaceMacros($tpl,[
'$title' => DI::l10n()->t('Friend Suggestions'), '$title' => DI::l10n()->t('Friend Suggestions'),
'$contacts' => $entries, '$contacts' => $entries,
]); ]);
return $o;
} }

View File

@ -44,7 +44,6 @@ function tagrm_post(App $a)
$item_id = $_POST['item'] ?? 0; $item_id = $_POST['item'] ?? 0;
update_tags($item_id, $tags); update_tags($item_id, $tags);
info(DI::l10n()->t('Tag(s) removed') . EOL);
DI::baseUrl()->redirect($_SESSION['photo_return']); DI::baseUrl()->redirect($_SESSION['photo_return']);
// NOTREACHED // NOTREACHED

View File

@ -29,7 +29,7 @@ use Friendica\DI;
function uimport_post(App $a) function uimport_post(App $a)
{ {
if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) { if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -42,7 +42,7 @@ function uimport_post(App $a)
function uimport_content(App $a) function uimport_content(App $a)
{ {
if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) { if ((DI::config()->get('config', 'register_policy') != \Friendica\Module\Register::OPEN) && !is_site_admin()) {
notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.') . EOL); notice(DI::l10n()->t('User imports on closed servers can only be done by an administrator.'));
return; return;
} }
@ -51,7 +51,7 @@ function uimport_content(App $a)
$r = q("select count(*) as total from user where register_date > UTC_TIMESTAMP - INTERVAL 1 day"); $r = q("select count(*) as total from user where register_date > UTC_TIMESTAMP - INTERVAL 1 day");
if ($r && $r[0]['total'] >= $max_dailies) { if ($r && $r[0]['total'] >= $max_dailies) {
Logger::log('max daily registrations exceeded.'); Logger::log('max daily registrations exceeded.');
notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.') . EOL); notice(DI::l10n()->t('This site has exceeded the number of allowed daily account registrations. Please try again tomorrow.'));
return; return;
} }
} }

View File

@ -79,7 +79,6 @@ function unfollow_post(App $a)
$return_path = $base_return_path . '/' . $contact['id']; $return_path = $base_return_path . '/' . $contact['id'];
} }
info(DI::l10n()->t('Contact unfollowed'));
DI::baseUrl()->redirect($return_path); DI::baseUrl()->redirect($return_path);
// NOTREACHED // NOTREACHED
} }
@ -146,7 +145,7 @@ function unfollow_content(App $a)
]); ]);
DI::page()['aside'] = ''; DI::page()['aside'] = '';
Profile::load($a, '', Contact::getDetailsByURL($contact['url'])); Profile::load($a, '', Contact::getByURL($contact['url'], false));
$o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => DI::l10n()->t('Status Messages and Posts')]); $o .= Renderer::replaceMacros(Renderer::getMarkupTemplate('section_title.tpl'), ['$title' => DI::l10n()->t('Status Messages and Posts')]);

View File

@ -126,7 +126,7 @@ function videos_content(App $a)
if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) { if (DI::config()->get('system', 'block_public') && !Session::isAuthenticated()) {
notice(DI::l10n()->t('Public access denied.') . EOL); notice(DI::l10n()->t('Public access denied.'));
return; return;
} }
@ -179,7 +179,7 @@ function videos_content(App $a)
} }
if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) { if ($a->data['user']['hidewall'] && (local_user() != $owner_uid) && !$remote_contact) {
notice(DI::l10n()->t('Access to this item is restricted.') . EOL); notice(DI::l10n()->t('Access to this item is restricted.'));
return; return;
} }

View File

@ -106,7 +106,7 @@ function wall_attach_post(App $a) {
if ($r_json) { if ($r_json) {
echo json_encode(['error' => $msg]); echo json_encode(['error' => $msg]);
} else { } else {
notice($msg . EOL); notice($msg);
} }
@unlink($src); @unlink($src);
exit(); exit();

View File

@ -99,7 +99,7 @@ function wall_upload_post(App $a, $desktopmode = true)
echo json_encode(['error' => DI::l10n()->t('Permission denied.')]); echo json_encode(['error' => DI::l10n()->t('Permission denied.')]);
exit(); exit();
} }
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
exit(); exit();
} }
@ -159,7 +159,7 @@ function wall_upload_post(App $a, $desktopmode = true)
echo json_encode(['error' => DI::l10n()->t('Invalid request.')]); echo json_encode(['error' => DI::l10n()->t('Invalid request.')]);
exit(); exit();
} }
notice(DI::l10n()->t('Invalid request.').EOL); notice(DI::l10n()->t('Invalid request.'));
exit(); exit();
} }

View File

@ -32,7 +32,7 @@ function wallmessage_post(App $a) {
$replyto = Profile::getMyURL(); $replyto = Profile::getMyURL();
if (!$replyto) { if (!$replyto) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -56,7 +56,7 @@ function wallmessage_post(App $a) {
$user = $r[0]; $user = $r[0];
if (! intval($user['unkmail'])) { if (! intval($user['unkmail'])) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
@ -73,19 +73,17 @@ function wallmessage_post(App $a) {
switch ($ret) { switch ($ret) {
case -1: case -1:
notice(DI::l10n()->t('No recipient selected.') . EOL); notice(DI::l10n()->t('No recipient selected.'));
break; break;
case -2: case -2:
notice(DI::l10n()->t('Unable to check your home location.') . EOL); notice(DI::l10n()->t('Unable to check your home location.'));
break; break;
case -3: case -3:
notice(DI::l10n()->t('Message could not be sent.') . EOL); notice(DI::l10n()->t('Message could not be sent.'));
break; break;
case -4: case -4:
notice(DI::l10n()->t('Message collection failure.') . EOL); notice(DI::l10n()->t('Message collection failure.'));
break; break;
default:
info(DI::l10n()->t('Message sent.') . EOL);
} }
DI::baseUrl()->redirect('profile/'.$user['nickname']); DI::baseUrl()->redirect('profile/'.$user['nickname']);
@ -95,14 +93,14 @@ function wallmessage_post(App $a) {
function wallmessage_content(App $a) { function wallmessage_content(App $a) {
if (!Profile::getMyURL()) { if (!Profile::getMyURL()) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }
$recipient = (($a->argc > 1) ? $a->argv[1] : ''); $recipient = (($a->argc > 1) ? $a->argv[1] : '');
if (!$recipient) { if (!$recipient) {
notice(DI::l10n()->t('No recipient.') . EOL); notice(DI::l10n()->t('No recipient.'));
return; return;
} }
@ -111,7 +109,7 @@ function wallmessage_content(App $a) {
); );
if (! DBA::isResult($r)) { if (! DBA::isResult($r)) {
notice(DI::l10n()->t('No recipient.') . EOL); notice(DI::l10n()->t('No recipient.'));
Logger::log('wallmessage: no recipient'); Logger::log('wallmessage: no recipient');
return; return;
} }
@ -119,7 +117,7 @@ function wallmessage_content(App $a) {
$user = $r[0]; $user = $r[0];
if (!intval($user['unkmail'])) { if (!intval($user['unkmail'])) {
notice(DI::l10n()->t('Permission denied.') . EOL); notice(DI::l10n()->t('Permission denied.'));
return; return;
} }

View File

@ -105,6 +105,9 @@ $HTTP["scheme"] == "https" {
"^\/([^\?]*)\?(.*)$" => "/index.php?pagename=$1&$2", "^\/([^\?]*)\?(.*)$" => "/index.php?pagename=$1&$2",
"^\/(.*)$" => "/index.php?pagename=$1" "^\/(.*)$" => "/index.php?pagename=$1"
) )
$HOST["url"] =~ "^/bin/" {
url.access.deny ( "" )
}
} }
else $HTTP["host"] !~ "(friendica.example.com|wordpress.example.com)" { else $HTTP["host"] !~ "(friendica.example.com|wordpress.example.com)" {
server.document-root = "/var/www/wordpress" server.document-root = "/var/www/wordpress"

View File

@ -141,4 +141,9 @@ server {
location ~ /\. { location ~ /\. {
deny all; deny all;
} }
# deny access to the CLI scripts
location ^~ /bin {
deny all;
}
} }

View File

@ -240,22 +240,6 @@ class App
} }
} }
/**
* Returns the current UserAgent as a String
*
* @return string the UserAgent as a String
* @throws HTTPException\InternalServerErrorException
*/
public function getUserAgent()
{
return
FRIENDICA_PLATFORM . " '" .
FRIENDICA_CODENAME . "' " .
FRIENDICA_VERSION . '-' .
DB_UPDATE_VERSION . '; ' .
$this->baseURL->get();
}
/** /**
* Returns the current theme name. May be overriden by the mobile theme name. * Returns the current theme name. May be overriden by the mobile theme name.
* *

View File

@ -47,7 +47,7 @@ class Arguments
*/ */
private $argc; private $argc;
public function __construct(string $queryString = '', string $command = '', array $argv = [Module::DEFAULT], int $argc = 1) public function __construct(string $queryString = '', string $command = '', array $argv = [], int $argc = 0)
{ {
$this->queryString = $queryString; $this->queryString = $queryString;
$this->command = $command; $this->command = $command;
@ -56,7 +56,7 @@ class Arguments
} }
/** /**
* @return string The whole query string of this call * @return string The whole query string of this call with url-encoded query parameters
*/ */
public function getQueryString() public function getQueryString()
{ {
@ -121,50 +121,27 @@ class Arguments
*/ */
public function determine(array $server, array $get) public function determine(array $server, array $get)
{ {
$queryString = ''; // removing leading / - maybe a nginx problem
$server['QUERY_STRING'] = ltrim($server['QUERY_STRING'] ?? '', '/');
if (!empty($server['QUERY_STRING']) && strpos($server['QUERY_STRING'], 'pagename=') === 0) { $queryParameters = [];
$queryString = urldecode(substr($server['QUERY_STRING'], 9)); parse_str($server['QUERY_STRING'], $queryParameters);
} elseif (!empty($server['QUERY_STRING']) && strpos($server['QUERY_STRING'], 'q=') === 0) {
$queryString = urldecode(substr($server['QUERY_STRING'], 2));
}
// eventually strip ZRL
$queryString = $this->stripZRLs($queryString);
// eventually strip OWT
$queryString = $this->stripQueryParam($queryString, 'owt');
// removing trailing / - maybe a nginx problem
$queryString = ltrim($queryString, '/');
if (!empty($get['pagename'])) { if (!empty($get['pagename'])) {
$command = trim($get['pagename'], '/\\'); $command = trim($get['pagename'], '/\\');
} elseif (!empty($queryParameters['pagename'])) {
$command = trim($queryParameters['pagename'], '/\\');
} elseif (!empty($get['q'])) { } elseif (!empty($get['q'])) {
// Legacy page name parameter, now conflicts with the search query parameter
$command = trim($get['q'], '/\\'); $command = trim($get['q'], '/\\');
} else { } else {
$command = Module::DEFAULT; $command = '';
} }
// Remove generated and one-time use parameters
// fix query_string unset($queryParameters['pagename']);
if (!empty($command)) { unset($queryParameters['zrl']);
$queryString = str_replace( unset($queryParameters['owt']);
$command . '&',
$command . '?',
$queryString
);
}
// unix style "homedir"
if (substr($command, 0, 1) === '~') {
$command = 'profile/' . substr($command, 1);
}
// Diaspora style profile url
if (substr($command, 0, 2) === 'u/') {
$command = 'profile/' . substr($command, 2);
}
/* /*
* Break the URL path into C style argc/argv style arguments for our * Break the URL path into C style argc/argv style arguments for our
@ -173,41 +150,17 @@ class Arguments
* [0] => 'module' * [0] => 'module'
* [1] => 'arg1' * [1] => 'arg1'
* [2] => 'arg2' * [2] => 'arg2'
*
*
* There will always be one argument. If provided a naked domain
* URL, $this->argv[0] is set to "home".
*/ */
if ($command) {
$argv = explode('/', $command);
} else {
$argv = [];
}
$argv = explode('/', $command);
$argc = count($argv); $argc = count($argv);
$queryString = $command . ($queryParameters ? '?' . http_build_query($queryParameters) : '');
return new Arguments($queryString, $command, $argv, $argc); return new Arguments($queryString, $command, $argv, $argc);
} }
}
/**
* Strip zrl parameter from a string.
*
* @param string $queryString The input string.
*
* @return string The zrl.
*/
public function stripZRLs(string $queryString)
{
return preg_replace('/[?&]zrl=(.*?)(&|$)/ism', '$2', $queryString);
}
/**
* Strip query parameter from a string.
*
* @param string $queryString The input string.
* @param string $param
*
* @return string The query parameter.
*/
public function stripQueryParam(string $queryString, string $param)
{
return preg_replace('/[?&]' . $param . '=(.*?)(&|$)/ism', '$2', $queryString);
}
}

View File

@ -207,7 +207,7 @@ class Authentication
// if it's an email address or doesn't resolve to a URL, fail. // if it's an email address or doesn't resolve to a URL, fail.
if ($noid || strpos($openid_url, '@') || !Network::isUrlValid($openid_url)) { if ($noid || strpos($openid_url, '@') || !Network::isUrlValid($openid_url)) {
notice($this->l10n->t('Login failed.') . EOL); notice($this->l10n->t('Login failed.'));
$this->baseUrl->redirect(); $this->baseUrl->redirect();
} }
@ -270,7 +270,7 @@ class Authentication
} }
} catch (Exception $e) { } catch (Exception $e) {
$this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]); $this->logger->warning('authenticate: failed login attempt', ['action' => 'login', 'username' => Strings::escapeTags($username), 'ip' => $_SERVER['REMOTE_ADDR']]);
info($this->l10n->t('Login failed. Please check your credentials.' . EOL)); notice($this->l10n->t('Login failed. Please check your credentials.'));
$this->baseUrl->redirect(); $this->baseUrl->redirect();
} }
@ -389,8 +389,6 @@ class Authentication
info($this->l10n->t('Welcome %s', $user_record['username'])); info($this->l10n->t('Welcome %s', $user_record['username']));
info($this->l10n->t('Please upload a profile photo.')); info($this->l10n->t('Please upload a profile photo.'));
$this->baseUrl->redirect('settings/profile/photo/new'); $this->baseUrl->redirect('settings/profile/photo/new');
} else {
info($this->l10n->t("Welcome back %s", $user_record['username']));
} }
} }

View File

@ -237,7 +237,7 @@ class Module
public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, array $server, array $post) public function run(Core\L10n $l10n, App\BaseURL $baseUrl, LoggerInterface $logger, array $server, array $post)
{ {
if ($this->printNotAllowedAddon) { if ($this->printNotAllowedAddon) {
info($l10n->t("You must be logged in to use addons. ")); notice($l10n->t("You must be logged in to use addons. "));
} }
/* The URL provided does not resolve to a valid module. /* The URL provided does not resolve to a valid module.

View File

@ -165,11 +165,10 @@ class Page implements ArrayAccess
* The path can be absolute or relative to the Friendica installation base folder. * The path can be absolute or relative to the Friendica installation base folder.
* *
* @param string $path * @param string $path
* * @param string $media
* @see Page::initHead() * @see Page::initHead()
*
*/ */
public function registerStylesheet($path) public function registerStylesheet($path, string $media = 'screen')
{ {
$path = Network::appendQueryParam($path, ['v' => FRIENDICA_VERSION]); $path = Network::appendQueryParam($path, ['v' => FRIENDICA_VERSION]);
@ -177,7 +176,7 @@ class Page implements ArrayAccess
$path = mb_substr($path, mb_strlen($this->basePath . DIRECTORY_SEPARATOR)); $path = mb_substr($path, mb_strlen($this->basePath . DIRECTORY_SEPARATOR));
} }
$this->stylesheets[] = trim($path, '/'); $this->stylesheets[trim($path, '/')] = $media;
} }
/** /**
@ -252,7 +251,7 @@ class Page implements ArrayAccess
'$shortcut_icon' => $shortcut_icon, '$shortcut_icon' => $shortcut_icon,
'$touch_icon' => $touch_icon, '$touch_icon' => $touch_icon,
'$block_public' => intval($config->get('system', 'block_public')), '$block_public' => intval($config->get('system', 'block_public')),
'$stylesheets' => array_unique($this->stylesheets), '$stylesheets' => $this->stylesheets,
]) . $this->page['htmlhead']; ]) . $this->page['htmlhead'];
} }

View File

@ -26,6 +26,8 @@ use FastRoute\DataGenerator\GroupCountBased;
use FastRoute\Dispatcher; use FastRoute\Dispatcher;
use FastRoute\RouteCollector; use FastRoute\RouteCollector;
use FastRoute\RouteParser\Std; use FastRoute\RouteParser\Std;
use Friendica\Core\Cache\Duration;
use Friendica\Core\Cache\ICache;
use Friendica\Core\Hook; use Friendica\Core\Hook;
use Friendica\Core\L10n; use Friendica\Core\L10n;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
@ -66,14 +68,24 @@ class Router
/** @var L10n */ /** @var L10n */
private $l10n; private $l10n;
/** @var ICache */
private $cache;
/** @var string */
private $baseRoutesFilepath;
/** /**
* @param array $server The $_SERVER variable * @param array $server The $_SERVER variable
* @param L10n $l10n * @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty
* @param RouteCollector|null $routeCollector Optional the loaded Route collector * @param L10n $l10n
* @param ICache $cache
* @param RouteCollector|null $routeCollector
*/ */
public function __construct(array $server, L10n $l10n, RouteCollector $routeCollector = null) public function __construct(array $server, string $baseRoutesFilepath, L10n $l10n, ICache $cache, RouteCollector $routeCollector = null)
{ {
$this->baseRoutesFilepath = $baseRoutesFilepath;
$this->l10n = $l10n; $this->l10n = $l10n;
$this->cache = $cache;
$httpMethod = $server['REQUEST_METHOD'] ?? self::GET; $httpMethod = $server['REQUEST_METHOD'] ?? self::GET;
$this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET; $this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET;
@ -84,6 +96,9 @@ class Router
} }
/** /**
* This will be called either automatically if a base routes file path was submitted,
* or can be called manually with a custom route array.
*
* @param array $routes The routes to add to the Router * @param array $routes The routes to add to the Router
* *
* @return self The router instance with the loaded routes * @return self The router instance with the loaded routes
@ -100,6 +115,9 @@ class Router
$this->routeCollector = $routeCollector; $this->routeCollector = $routeCollector;
// Add routes from addons
Hook::callAll('route_collection', $this->routeCollector);
return $this; return $this;
} }
@ -191,12 +209,9 @@ class Router
*/ */
public function getModuleClass($cmd) public function getModuleClass($cmd)
{ {
// Add routes from addons
Hook::callAll('route_collection', $this->routeCollector);
$cmd = '/' . ltrim($cmd, '/'); $cmd = '/' . ltrim($cmd, '/');
$dispatcher = new Dispatcher\GroupCountBased($this->routeCollector->getData()); $dispatcher = new Dispatcher\GroupCountBased($this->getCachedDispatchData());
$moduleClass = null; $moduleClass = null;
$this->parameters = []; $this->parameters = [];
@ -223,4 +238,51 @@ class Router
{ {
return $this->parameters; return $this->parameters;
} }
/**
* If a base routes file path has been provided, we can load routes from it if the cache misses.
*
* @return array
* @throws HTTPException\InternalServerErrorException
*/
private function getDispatchData()
{
$dispatchData = [];
if ($this->baseRoutesFilepath && file_exists($this->baseRoutesFilepath)) {
$dispatchData = require $this->baseRoutesFilepath;
if (!is_array($dispatchData)) {
throw new HTTPException\InternalServerErrorException('Invalid base routes file');
}
}
$this->loadRoutes($dispatchData);
return $this->routeCollector->getData();
}
/**
* We cache the dispatch data for speed, as computing the current routes (version 2020.09)
* takes about 850ms for each requests.
*
* The cached "routerDispatchData" lasts for a day, and must be cleared manually when there
* is any changes in the enabled addons list.
*
* @return array|mixed
* @throws HTTPException\InternalServerErrorException
*/
private function getCachedDispatchData()
{
$routerDispatchData = $this->cache->get('routerDispatchData');
if ($routerDispatchData) {
return $routerDispatchData;
}
$routerDispatchData = $this->getDispatchData();
$this->cache->set('routerDispatchData', $routerDispatchData, Duration::DAY);
return $routerDispatchData;
}
} }

View File

@ -140,7 +140,7 @@ abstract class BaseModule
return false; return false;
} }
$sec_hash = hash('whirlpool', $a->user['guid'] . $a->user['prvkey'] . session_id() . $x[0] . $typename); $sec_hash = hash('whirlpool', ($a->user['guid'] ?? '') . ($a->user['prvkey'] ?? '') . session_id() . $x[0] . $typename);
return ($sec_hash == $x[1]); return ($sec_hash == $x[1]);
} }
@ -171,4 +171,40 @@ abstract class BaseModule
throw new \Friendica\Network\HTTPException\ForbiddenException(); throw new \Friendica\Network\HTTPException\ForbiddenException();
} }
} }
protected static function getContactFilterTabs(string $baseUrl, string $current, bool $displayCommonTab)
{
$tabs = [
[
'label' => DI::l10n()->t('All contacts'),
'url' => $baseUrl . '/contacts',
'sel' => !$current || $current == 'all' ? 'active' : '',
],
[
'label' => DI::l10n()->t('Followers'),
'url' => $baseUrl . '/contacts/followers',
'sel' => $current == 'followers' ? 'active' : '',
],
[
'label' => DI::l10n()->t('Following'),
'url' => $baseUrl . '/contacts/following',
'sel' => $current == 'following' ? 'active' : '',
],
[
'label' => DI::l10n()->t('Mutual friends'),
'url' => $baseUrl . '/contacts/mutuals',
'sel' => $current == 'mutuals' ? 'active' : '',
],
];
if ($displayCommonTab) {
$tabs[] = [
'label' => DI::l10n()->t('Common'),
'url' => $baseUrl . '/contacts/common',
'sel' => $current == 'common' ? 'active' : '',
];
}
return $tabs;
}
} }

View File

@ -109,26 +109,22 @@ abstract class BaseRepository extends BaseFactory
*/ */
public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT) public function selectByBoundaries(array $condition = [], array $params = [], int $max_id = null, int $since_id = null, int $limit = self::LIMIT)
{ {
$condition = DBA::collapseCondition($condition); $totalCount = DBA::count(static::$table_name, $condition);
$boundCondition = $condition; $boundCondition = $condition;
if (isset($max_id)) { if (isset($max_id)) {
$boundCondition[0] .= " AND `id` < ?"; $boundCondition = DBA::mergeConditions($boundCondition, ['`id` < ?', $max_id]);
$boundCondition[] = $max_id;
} }
if (isset($since_id)) { if (isset($since_id)) {
$boundCondition[0] .= " AND `id` > ?"; $boundCondition = DBA::mergeConditions($boundCondition, ['`id` > ?', $since_id]);
$boundCondition[] = $since_id;
} }
$params['limit'] = $limit; $params['limit'] = $limit;
$models = $this->selectModels($boundCondition, $params); $models = $this->selectModels($boundCondition, $params);
$totalCount = DBA::count(static::$table_name, $condition);
return new static::$collection_class($models, $totalCount); return new static::$collection_class($models, $totalCount);
} }

View File

@ -55,6 +55,7 @@ Commands
update Update database schema update Update database schema
dumpsql Dump database schema dumpsql Dump database schema
toinnodb Convert all tables from MyISAM or InnoDB in the Antelope file format to InnoDB in the Barracuda file format toinnodb Convert all tables from MyISAM or InnoDB in the Antelope file format to InnoDB in the Barracuda file format
version Set the database to a given number
Options Options
-h|--help|-? Show help information -h|--help|-? Show help information
@ -86,8 +87,10 @@ HELP;
return 0; return 0;
} }
if (count($this->args) > 1) { if ((count($this->args) > 1) && ($this->getArgument(0) != 'version')) {
throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments'); throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
} elseif ((count($this->args) != 2) && ($this->getArgument(0) == 'version')) {
throw new \Asika\SimpleConsole\CommandArgsException('This command needs two arguments');
} }
if (!$this->dba->isConnected()) { if (!$this->dba->isConnected()) {
@ -115,6 +118,12 @@ HELP;
DBStructure::convertToInnoDB(); DBStructure::convertToInnoDB();
$output = ob_get_clean(); $output = ob_get_clean();
break; break;
case "version":
ob_start();
DBStructure::setDatabaseVersion($this->getArgument(1));
$output = ob_get_clean();
break;
default: default:
$output = 'Unknown command: ' . $this->getArgument(0); $output = 'Unknown command: ' . $this->getArgument(0);
} }

134
src/Console/Relay.php Normal file
View File

@ -0,0 +1,134 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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\Console;
use Asika\SimpleConsole\CommandArgsException;
use Friendica\Model\APContact;
use Friendica\Model\Contact;
use Friendica\Protocol\ActivityPub\Transmitter;
/**
* tool to control the list of ActivityPub relay servers from the CLI
*
* With this script you can access the relay servers of your node from
* the CLI.
*/
class Relay extends \Asika\SimpleConsole\Console
{
protected $helpOptions = ['h', 'help', '?'];
/**
* @var $dba Friendica\Database\Database
*/
private $dba;
protected function getHelp()
{
$help = <<<HELP
console relay - Manage ActivityPub relay configuration
Synopsis
bin/console relay [-h|--help|-?] [-v]
bin/console relay add <actor> [-h|--help|-?] [-v]
bin/console relay remove <actoor> [-h|--help|-?] [-v]
Description
bin/console relay
Lists all active relay servers
bin/console relay add <actor>
Add a relay actor in the format https://relayserver.tld/actor
bin/console relay remove <actor>
Remove a relay actor in the format https://relayserver.tld/actor
Options
-h|--help|-? Show help information
-v Show more debug information.
HELP;
return $help;
}
public function __construct(\Friendica\Database\Database $dba, array $argv = null)
{
parent::__construct($argv);
$this->dba = $dba;
}
protected function doExecute()
{
if ($this->getOption('v')) {
$this->out('Executable: ' . $this->executable);
$this->out('Class: ' . __CLASS__);
$this->out('Arguments: ' . var_export($this->args, true));
$this->out('Options: ' . var_export($this->options, true));
}
if (count($this->args) > 2) {
throw new CommandArgsException('Too many arguments');
}
if (count($this->args) == 1) {
throw new CommandArgsException('Too few arguments');
}
if (count($this->args) == 0) {
$contacts = $this->dba->select('apcontact', ['url'],
["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))",
'Application', 0, Contact::FOLLOWER, Contact::FRIEND]);
while ($contact = $this->dba->fetch($contacts)) {
$this->out($contact['url']);
}
$this->dba->close($contacts);
}
if (count($this->args) == 2) {
$mode = $this->getArgument(0);
$actor = $this->getArgument(1);
$apcontact = APContact::getByURL($actor);
if (empty($apcontact) || ($apcontact['type'] != 'Application')) {
$this->out($actor . ' is no relay actor');
return 1;
}
if ($mode == 'add') {
if (Transmitter::sendRelayFollow($actor)) {
$this->out('Successfully added ' . $actor);
} else {
$this->out($actor . " couldn't be added");
}
} elseif ($mode == 'remove') {
if (Transmitter::sendRelayUndoFollow($actor)) {
$this->out('Successfully removed ' . $actor);
} else {
$this->out($actor . " couldn't be removed");
}
} else {
throw new CommandArgsException($mode . ' is no valid command');
}
}
return 0;
}
}

View File

@ -48,14 +48,18 @@ class ServerBlock extends Console
$help = <<<HELP $help = <<<HELP
console serverblock - Manage blocked server domain patterns console serverblock - Manage blocked server domain patterns
Usage Usage
bin/console serverblock [-h|--help|-?] [-v] bin/console serverblock [-h|--help|-?] [-v]
bin/console serverblock add <pattern> <reason> [-h|--help|-?] [-v] bin/console serverblock add <pattern> <reason> [-h|--help|-?] [-v]
bin/console serverblock remove <pattern> [-h|--help|-?] [-v] bin/console serverblock remove <pattern> [-h|--help|-?] [-v]
bin/console serverblock export <filename>
bin/console serverblock import <filename>
Description Description
With this tool, you can list the current blocked server domain patterns With this tool, you can list the current blocked server domain patterns
or you can add / remove a blocked server domain pattern from the list. or you can add / remove a blocked server domain pattern from the list.
Using the export and import options you can share your server blocklist
with other node admins by CSV files.
Patterns are case-insensitive shell wildcard comprising the following special characters: Patterns are case-insensitive shell wildcard comprising the following special characters:
- * : Any number of characters - * : Any number of characters
- ? : Any single character - ? : Any single character
@ -87,12 +91,79 @@ HELP;
return $this->addBlockedServer($this->config); return $this->addBlockedServer($this->config);
case 'remove': case 'remove':
return $this->removeBlockedServer($this->config); return $this->removeBlockedServer($this->config);
case 'export':
return $this->exportBlockedServers($this->config);
case 'import':
return $this->importBlockedServers($this->config);
default: default:
throw new CommandArgsException('Unknown command.'); throw new CommandArgsException('Unknown command.');
break; break;
} }
} }
/**
* Exports the list of blocked domains including the reason for the
* block to a CSV file.
*
* @param IConfig $config
*/
private function exportBlockedServers(IConfig $config)
{
$filename = $this->getArgument(1);
$blocklist = $config->get('system', 'blocklist', []);
$fp = fopen($filename, 'w');
if (!$fp) {
throw new Exception(sprintf('The file "%s" could not be created.', $filename));
}
foreach ($blocklist as $domain) {
fputcsv($fp, $domain);
}
}
/**
* Imports a list of domains and a reason for the block from a CSV
* file, e.g. created with the export function.
*
* @param IConfig $config
*/
private function importBlockedServers(IConfig $config)
{
$filename = $this->getArgument(1);
$currBlockList = $config->get('system', 'blocklist', []);
$newBlockList = [];
if (($fp = fopen($filename, 'r')) !== false) {
while (($data = fgetcsv($fp, 1000, ',')) !== false) {
$domain = $data[0];
if (count($data) == 0) {
$reason = self::DEFAULT_REASON;
} else {
$reason = $data[1];
}
$data = [
'domain' => $domain,
'reason' => $reason
];
if (!in_array($data, $newBlockList)) {
$newBlockList[] = $data;
}
}
foreach ($currBlockList as $blocked) {
if (!in_array($blocked, $newBlockList)) {
$newBlockList[] = $blocked;
}
}
if ($config->set('system', 'blocklist', $newBlockList)) {
$this->out(sprintf("Entries from %s that were not blocked before are now blocked", $filename));
return 0;
} else {
$this->out(sprintf("Couldn't save '%s' as blocked server", $domain));
return 1;
}
} else {
throw new Exception(sprintf('The file "%s" could not be opened for importing', $filename));
}
}
/** /**
* Prints the whole list of blocked domains including the reason * Prints the whole list of blocked domains including the reason
* *
@ -127,9 +198,9 @@ HELP;
$update = false; $update = false;
$currBlocklist = $config->get('system', 'blocklist', []); $currBlockList = $config->get('system', 'blocklist', []);
$newBlockList = []; $newBlockList = [];
foreach ($currBlocklist as $blocked) { foreach ($currBlockList as $blocked) {
if ($blocked['domain'] === $domain) { if ($blocked['domain'] === $domain) {
$update = true; $update = true;
$newBlockList[] = [ $newBlockList[] = [
@ -178,9 +249,9 @@ HELP;
$found = false; $found = false;
$currBlocklist = $config->get('system', 'blocklist', []); $currBlockList = $config->get('system', 'blocklist', []);
$newBlockList = []; $newBlockList = [];
foreach ($currBlocklist as $blocked) { foreach ($currBlockList as $blocked) {
if ($blocked['domain'] === $domain) { if ($blocked['domain'] === $domain) {
$found = true; $found = true;
} else { } else {

View File

@ -106,7 +106,7 @@ HELP;
$isregisterd = false; $isregisterd = false;
foreach ($this->storageManager->listBackends() as $name => $class) { foreach ($this->storageManager->listBackends() as $name => $class) {
$issel = ' '; $issel = ' ';
if ($current::getName() == $name) { if ($current && $current::getName() == $name) {
$issel = '*'; $issel = '*';
$isregisterd = true; $isregisterd = true;
}; };

View File

@ -409,7 +409,7 @@ HELP;
case 'guid': case 'guid':
$user = UserModel::getByGuid($param, $fields); $user = UserModel::getByGuid($param, $fields);
break; break;
case 'email': case 'mail':
$user = UserModel::getByEmail($param, $fields); $user = UserModel::getByEmail($param, $fields);
break; break;
case 'nick': case 'nick':

View File

@ -76,14 +76,6 @@ class ContactSelector
$server_url = Strings::normaliseLink($contact['baseurl']); $server_url = Strings::normaliseLink($contact['baseurl']);
} }
if (empty($server_url)) {
// Fetch the server url from the gcontact table
$gcontact = DBA::selectFirst('gcontact', ['server_url'], ['nurl' => Strings::normaliseLink($profile)]);
if (!empty($gcontact) && !empty($gcontact['server_url'])) {
$server_url = Strings::normaliseLink($gcontact['server_url']);
}
}
if (empty($server_url)) { if (empty($server_url)) {
// Create the server url out of the profile url // Create the server url out of the profile url
$parts = parse_url($profile); $parts = parse_url($profile);

View File

@ -96,7 +96,6 @@ class Feature
DI::l10n()->t('General Features'), DI::l10n()->t('General Features'),
//array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')), //array('expire', DI::l10n()->t('Content Expiration'), DI::l10n()->t('Remove old posts/comments after a period of time')),
['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)], ['photo_location', DI::l10n()->t('Photo Location'), DI::l10n()->t("Photo metadata is normally stripped. This extracts the location \x28if present\x29 prior to stripping metadata and links it to a map."), false, DI::config()->get('feature_lock', 'photo_location', false)],
['export_calendar', DI::l10n()->t('Export Public Calendar'), DI::l10n()->t('Ability for visitors to download the public calendar'), false, DI::config()->get('feature_lock', 'export_calendar', false)],
['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)], ['trending_tags', DI::l10n()->t('Trending Tags'), DI::l10n()->t('Show a community page widget with a list of the most popular tags in recent public posts.'), false, DI::config()->get('feature_lock', 'trending_tags', false)],
], ],
@ -107,20 +106,6 @@ class Feature
['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)], ['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)],
], ],
// Network sidebar widgets
'widgets' => [
DI::l10n()->t('Network Sidebar'),
['archives', DI::l10n()->t('Archives'), DI::l10n()->t('Ability to select posts by date ranges'), false, DI::config()->get('feature_lock', 'archives', false)],
['networks', DI::l10n()->t('Protocol Filter'), DI::l10n()->t('Enable widget to display Network posts only from selected protocols'), false, DI::config()->get('feature_lock', 'networks', false)],
],
// Network tabs
'net_tabs' => [
DI::l10n()->t('Network Tabs'),
['new_tab', DI::l10n()->t('Network New Tab'), DI::l10n()->t("Enable tab to display only new Network posts \x28from the last 12 hours\x29"), false, DI::config()->get('feature_lock', 'new_tab', false)],
['link_tab', DI::l10n()->t('Network Shared Links Tab'), DI::l10n()->t('Enable tab to display only Network posts with links in them'), false, DI::config()->get('feature_lock', 'link_tab', false)],
],
// Item tools // Item tools
'tools' => [ 'tools' => [
DI::l10n()->t('Post/Comment Tools'), DI::l10n()->t('Post/Comment Tools'),

View File

@ -27,7 +27,6 @@ use Friendica\Core\Renderer;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact; use Friendica\Model\Contact;
use Friendica\Util\Proxy as ProxyUtils;
/** /**
* This class handles methods related to the forum functionality * This class handles methods related to the forum functionality
@ -72,7 +71,7 @@ class ForumManager
$forumlist = []; $forumlist = [];
$fields = ['id', 'url', 'name', 'micro', 'thumb']; $fields = ['id', 'url', 'name', 'micro', 'thumb', 'avatar'];
$condition = [$condition_str, Protocol::DFRN, Protocol::ACTIVITYPUB, $uid]; $condition = [$condition_str, Protocol::DFRN, Protocol::ACTIVITYPUB, $uid];
$contacts = DBA::select('contact', $fields, $condition, $params); $contacts = DBA::select('contact', $fields, $condition, $params);
if (!$contacts) { if (!$contacts) {
@ -131,7 +130,7 @@ class ForumManager
'name' => $contact['name'], 'name' => $contact['name'],
'cid' => $contact['id'], 'cid' => $contact['id'],
'selected' => $selected, 'selected' => $selected,
'micro' => DI::baseUrl()->remove(ProxyUtils::proxifyUrl($contact['micro'], false, ProxyUtils::SIZE_MICRO)), 'micro' => DI::baseUrl()->remove(Contact::getMicro($contact)),
'id' => ++$id, 'id' => ++$id,
]; ];
$entries[] = $entry; $entries[] = $entry;

View File

@ -130,7 +130,7 @@ class Item
// Checking for the alias that is used for OStatus // Checking for the alias that is used for OStatus
$pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism'; $pattern = '/[@!]\[url\=(.*?)\](.*?)\[\/url\]/ism';
if (preg_match($pattern, $tag, $matches)) { if (preg_match($pattern, $tag, $matches)) {
$data = Contact::getDetailsByURL($matches[1]); $data = Contact::getByURL($matches[1], false, ['alias', 'nick']);
if ($data['alias'] != '') { if ($data['alias'] != '') {
$newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]'; $newtag = '@[url=' . $data['alias'] . ']' . $data['nick'] . '[/url]';
@ -149,15 +149,8 @@ class Item
$name = $nameparts[0]; $name = $nameparts[0];
// Try to detect the contact in various ways // Try to detect the contact in various ways
if (strpos($name, 'http://')) { if (strpos($name, 'http://') || strpos($name, '@')) {
// At first we have to ensure that the contact exists $contact = Contact::getByURLForUser($name, $profile_uid);
Contact::getIdForURL($name);
// Now we should have something
$contact = Contact::getDetailsByURL($name, $profile_uid);
} elseif (strpos($name, '@')) {
// This function automatically probes when no entry was found
$contact = Contact::getDetailsByAddr($name, $profile_uid);
} else { } else {
$contact = false; $contact = false;
$fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv']; $fields = ['id', 'url', 'nick', 'name', 'alias', 'network', 'forum', 'prv'];

View File

@ -27,6 +27,7 @@ use Friendica\Core\Renderer;
use Friendica\Core\Session; use Friendica\Core\Session;
use Friendica\Database\DBA; use Friendica\Database\DBA;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Model\Profile; use Friendica\Model\Profile;
use Friendica\Model\User; use Friendica\Model\User;
@ -183,7 +184,7 @@ class Nav
// user info // user info
$contact = DBA::selectFirst('contact', ['micro'], ['uid' => $a->user['uid'], 'self' => true]); $contact = DBA::selectFirst('contact', ['micro'], ['uid' => $a->user['uid'], 'self' => true]);
$userinfo = [ $userinfo = [
'icon' => (DBA::isResult($contact) ? DI::baseUrl()->remove($contact['micro']) : 'images/person-48.jpg'), 'icon' => (DBA::isResult($contact) ? DI::baseUrl()->remove($contact['micro']) : Contact::DEFAULT_AVATAR_MICRO),
'name' => $a->user['username'], 'name' => $a->user['username'],
]; ];
} else { } else {

View File

@ -95,7 +95,7 @@ class OEmbed
if (!in_array($ext, $noexts)) { if (!in_array($ext, $noexts)) {
// try oembed autodiscovery // try oembed autodiscovery
$html_text = Network::fetchUrl($embedurl, false, 15, 'text/*'); $html_text = DI::httpRequest()->fetch($embedurl, false, 15, 'text/*');
if ($html_text) { if ($html_text) {
$dom = @DOMDocument::loadHTML($html_text); $dom = @DOMDocument::loadHTML($html_text);
if ($dom) { if ($dom) {
@ -103,14 +103,14 @@ class OEmbed
$entries = $xpath->query("//link[@type='application/json+oembed']"); $entries = $xpath->query("//link[@type='application/json+oembed']");
foreach ($entries as $e) { foreach ($entries as $e) {
$href = $e->getAttributeNode('href')->nodeValue; $href = $e->getAttributeNode('href')->nodeValue;
$json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth); $json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth);
break; break;
} }
$entries = $xpath->query("//link[@type='text/json+oembed']"); $entries = $xpath->query("//link[@type='text/json+oembed']");
foreach ($entries as $e) { foreach ($entries as $e) {
$href = $e->getAttributeNode('href')->nodeValue; $href = $e->getAttributeNode('href')->nodeValue;
$json_string = Network::fetchUrl($href . '&maxwidth=' . $a->videowidth); $json_string = DI::httpRequest()->fetch($href . '&maxwidth=' . $a->videowidth);
break; break;
} }
} }

View File

@ -40,7 +40,7 @@ class PageInfo
* @return string * @return string
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
*/ */
public static function appendToBody(string $body, bool $searchNakedUrls = false, bool $no_photos = false) public static function searchAndAppendToBody(string $body, bool $searchNakedUrls = false, bool $no_photos = false)
{ {
Logger::info('add_page_info_to_body: fetch page info for body', ['body' => $body]); Logger::info('add_page_info_to_body: fetch page info for body', ['body' => $body]);
@ -49,14 +49,34 @@ class PageInfo
return $body; return $body;
} }
$footer = self::getFooterFromUrl($url, $no_photos); $data = self::queryUrl($url);
if (!$footer) { if (!$data) {
return $body; return $body;
} }
$body = self::stripTrailingUrlFromBody($body, $url); return self::appendDataToBody($body, $data, $no_photos);
}
$body .= "\n" . $footer; /**
* @param string $body
* @param array $data
* @param bool $no_photos
* @return string
* @throws HTTPException\InternalServerErrorException
*/
public static function appendDataToBody(string $body, array $data, bool $no_photos = false)
{
// Only one [attachment] tag per body is allowed
$existingAttachmentPos = strpos($body, '[attachment');
if ($existingAttachmentPos !== false) {
$linkTitle = $data['title'] ?: $data['url'];
// Additional link attachments are prepended before the existing [attachment] tag
$body = substr_replace($body, "\n[bookmark=" . $data['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0);
} else {
$footer = PageInfo::getFooterFromData($data, $no_photos);
$body = self::stripTrailingUrlFromBody($body, $data['url']);
$body .= "\n" . $footer;
}
return $body; return $body;
} }
@ -114,14 +134,6 @@ class PageInfo
$text = "[attachment type='" . $data['type'] . "'"; $text = "[attachment type='" . $data['type'] . "'";
if (empty($data['text'])) {
$data['text'] = $data['title'];
}
if (empty($data['text'])) {
$data['text'] = $data['url'];
}
if (!empty($data['url'])) { if (!empty($data['url'])) {
$text .= " url='" . $data['url'] . "'"; $text .= " url='" . $data['url'] . "'";
} }
@ -130,6 +142,10 @@ class PageInfo
$text .= " title='" . $data['title'] . "'"; $text .= " title='" . $data['title'] . "'";
} }
if (empty($data['text'])) {
$data['text'] = '';
}
// Only embedd a picture link when it seems to be a valid picture ("width" is set) // Only embedd a picture link when it seems to be a valid picture ("width" is set)
if (!empty($data['images']) && !empty($data['images'][0]['width'])) { if (!empty($data['images']) && !empty($data['images'][0]['width'])) {
$preview = str_replace(['[', ']'], ['&#91;', '&#93;'], htmlentities($data['images'][0]['src'], ENT_QUOTES, 'UTF-8', false)); $preview = str_replace(['[', ']'], ['&#91;', '&#93;'], htmlentities($data['images'][0]['src'], ENT_QUOTES, 'UTF-8', false));
@ -140,6 +156,14 @@ class PageInfo
$text .= " image='" . $preview . "'"; $text .= " image='" . $preview . "'";
} else { } else {
$text .= " preview='" . $preview . "'"; $text .= " preview='" . $preview . "'";
if (empty($data['text'])) {
$data['text'] = $data['title'];
}
if (empty($data['text'])) {
$data['text'] = $data['url'];
}
} }
} }
@ -252,22 +276,35 @@ class PageInfo
/** /**
* Remove the provided URL from the body if it is at the end of it. * Remove the provided URL from the body if it is at the end of it.
* Keep the link label if it isn't the full URL. * Keep the link label if it isn't the full URL or a shortened version of it.
* *
* @param string $body * @param string $body
* @param string $url * @param string $url
* @return string|string[]|null * @return string
*/ */
protected static function stripTrailingUrlFromBody(string $body, string $url) protected static function stripTrailingUrlFromBody(string $body, string $url)
{ {
$quotedUrl = preg_quote($url, '#'); $quotedUrl = preg_quote($url, '#');
$body = preg_replace("#(?: $body = preg_replace_callback("#(?:
\[url]$quotedUrl\[/url]| \[url]$quotedUrl\[/url]|
\[url=$quotedUrl]$quotedUrl\[/url]| \[url=$quotedUrl]$quotedUrl\[/url]|
\[url=$quotedUrl]([^[]*?)\[/url]| \[url=$quotedUrl]([^[]*?)\[/url]|
$quotedUrl $quotedUrl
)$#isx", '$1', $body); )$#isx", function ($match) use ($url) {
// Stripping URLs with no label
if (!isset($match[1])) {
return '';
}
return $body; // Stripping link labels that include a shortened version of the URL
if (strpos($url, trim($match[1], '.…')) !== false) {
return '';
}
// Keep all other labels
return $match[1];
}, $body);
return rtrim($body);
} }
} }

View File

@ -128,7 +128,7 @@ class Pager
/** /**
* Sets the base query string from a full query string. * Sets the base query string from a full query string.
* *
* Strips the 'page' parameter, and remove the 'q=' string for some reason. * Strips the 'page' parameter
* *
* @param string $queryString * @param string $queryString
*/ */

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