Merge branch 'develop' into new_image_presentation

This commit is contained in:
Marek Bachmann 2022-12-04 19:06:23 +01:00
commit 7833da5196
54 changed files with 716 additions and 342 deletions

View file

@ -6,8 +6,12 @@ matrix:
PHP_VERSION: 7.4.33
- PHP_MAJOR_VERSION: 8.0
PHP_VERSION: 8.0.25
# - PHP_MAJOR_VERSION: 8.1
# PHP_VERSION: 8.1.12
- PHP_MAJOR_VERSION: 8.1
PHP_VERSION: 8.1.12
# This forces PHP Unit executions at the "opensocial" labeled location (because of much more power...)
labels:
location: opensocial
pipeline:
php-lint:

View file

@ -70,7 +70,8 @@
"npm-asset/moment": "^2.24",
"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",
"kornrunner/blurhash": "^1.2"
},
"repositories": [
{

46
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "2e082bac083ca61cc0c22f7055d690bf",
"content-hash": "f8e7baec685d20e6aee56978c275d64c",
"packages": [
{
"name": "asika/simple-console",
@ -1116,6 +1116,50 @@
],
"time": "2022-06-20T21:43:03+00:00"
},
{
"name": "kornrunner/blurhash",
"version": "v1.2.2",
"source": {
"type": "git",
"url": "https://github.com/kornrunner/php-blurhash.git",
"reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/kornrunner/php-blurhash/zipball/bc8a4596cb0a49874f0158696a382ab3933fefe4",
"reference": "bc8a4596cb0a49874f0158696a382ab3933fefe4",
"shasum": ""
},
"require": {
"php": "^7.3|^8.0"
},
"require-dev": {
"ext-gd": "*",
"ocramius/package-versions": "^1.4|^2.0",
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^9",
"vimeo/psalm": "^4.3"
},
"type": "library",
"autoload": {
"psr-4": {
"kornrunner\\Blurhash\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Boris Momčilović",
"email": "boris.momcilovic@gmail.com"
}
],
"description": "Pure PHP implementation of Blurhash",
"homepage": "https://github.com/kornrunner/php-blurhash",
"time": "2022-07-13T19:38:39+00:00"
},
{
"name": "league/html-to-markdown",
"version": "4.10.0",

View file

@ -1,6 +1,6 @@
-- ------------------------------------------
-- Friendica 2022.12-dev (Giant Rhubarb)
-- DB_UPDATE_VERSION 1496
-- DB_UPDATE_VERSION 1497
-- ------------------------------------------
@ -1088,6 +1088,7 @@ CREATE TABLE IF NOT EXISTS `photo` (
`height` smallint unsigned NOT NULL DEFAULT 0 COMMENT '',
`width` smallint unsigned NOT NULL DEFAULT 0 COMMENT '',
`datasize` int unsigned NOT NULL DEFAULT 0 COMMENT '',
`blurhash` varbinary(255) COMMENT 'BlurHash representation of the photo',
`data` mediumblob NOT NULL COMMENT '',
`scale` tinyint unsigned NOT NULL DEFAULT 0 COMMENT '',
`profile` boolean NOT NULL DEFAULT '0' COMMENT '',
@ -1313,6 +1314,7 @@ CREATE TABLE IF NOT EXISTS `post-media` (
`height` smallint unsigned COMMENT 'Height of the media',
`width` smallint unsigned COMMENT 'Width of the media',
`size` bigint unsigned COMMENT 'Media size',
`blurhash` varbinary(255) COMMENT 'BlurHash representation of the image',
`preview` varbinary(512) COMMENT 'Preview URL',
`preview-height` smallint unsigned COMMENT 'Height of the preview picture',
`preview-width` smallint unsigned COMMENT 'Width of the preview picture',
@ -2693,6 +2695,7 @@ CREATE VIEW `owner-view` AS SELECT
`user`.`language` AS `language`,
`user`.`register_date` AS `register_date`,
`user`.`login_date` AS `login_date`,
`user`.`last-activity` AS `last-activity`,
`user`.`default-location` AS `default-location`,
`user`.`allow_location` AS `allow_location`,
`user`.`theme` AS `theme`,

View file

@ -25,6 +25,7 @@ Fields
| height | | smallint unsigned | NO | | 0 | |
| width | | smallint unsigned | NO | | 0 | |
| datasize | | int unsigned | NO | | 0 | |
| blurhash | BlurHash representation of the photo | varbinary(255) | YES | | NULL | |
| data | | mediumblob | NO | | NULL | |
| scale | | tinyint unsigned | NO | | 0 | |
| profile | | boolean | NO | | 0 | |

View file

@ -17,6 +17,7 @@ Fields
| height | Height of the media | smallint unsigned | YES | | NULL | |
| width | Width of the media | smallint unsigned | YES | | NULL | |
| size | Media size | bigint unsigned | YES | | NULL | |
| blurhash | BlurHash representation of the image | varbinary(255) | YES | | NULL | |
| preview | Preview URL | varbinary(512) | YES | | NULL | |
| preview-height | Height of the preview picture | smallint unsigned | YES | | NULL | |
| preview-width | Width of the preview picture | smallint unsigned | YES | | NULL | |

View file

@ -370,7 +370,7 @@ HELP;
$contact['url'],
$contact['email'],
Temporal::getRelativeDate($contact['created']),
Temporal::getRelativeDate($contact['login_date']),
Temporal::getRelativeDate($contact['last-activity']),
Temporal::getRelativeDate($contact['last-item']),
]);
}
@ -396,7 +396,7 @@ HELP;
'nickname',
'email',
'register_date',
'login_date',
'last-activity',
'verified',
'blocked',
];

View file

@ -21,6 +21,8 @@
namespace Friendica\Content\Text;
use Friendica\Core\Logger;
use Friendica\Core\System;
use Friendica\DI;
use Friendica\Model\Contact;
@ -106,8 +108,18 @@ class Markdown
* So we'll use that to convert to HTML, then convert the HTML back to bbcode,
* and then clean up a few Diaspora specific constructs.
*/
public static function toBBCode($s)
public static function toBBCode($s): string
{
// @TODO Temporary until we find the source of the null value to finally set the correct type-hint
if (is_null($s)) {
Logger::warning('Received null value', ['callstack' => System::callstack()]);
return '';
}
if (!$s) {
return $s;
}
DI::profiler()->startRecording('rendering');
// The parser cannot handle paragraphs correctly

View file

@ -21,6 +21,8 @@
namespace Friendica\Core;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\DI;
use Friendica\Module\Response;
@ -438,14 +440,15 @@ class System
/**
* Fetch the load and number of processes
*
* @param bool $get_processes
* @return array
*/
public static function getLoadAvg(): array
public static function getLoadAvg(bool $get_processes = true): array
{
if (@is_readable('/proc/loadavg')) {
if ($get_processes && @is_readable('/proc/loadavg')) {
$content = @file_get_contents('/proc/loadavg');
if (empty($content)) {
$content = shell_exec('cat /proc/loadavg');
$content = shell_exec('uptime | sed "s/.*averages*: //" | sed "s/,//g"');
}
}
@ -659,4 +662,30 @@ class System
// Reaching this point means that the operating system is configured badly.
return "";
}
/**
* Fetch the system rules
*
* @return array
*/
public static function getRules(): array
{
$rules = [];
$id = 0;
if (DI::config()->get('system', 'tosdisplay')) {
$rulelist = DI::config()->get('system', 'tosrules') ?: DI::config()->get('system', 'tostext');
$html = BBCode::convert($rulelist, false, BBCode::EXTERNAL);
$msg = HTML::toPlaintext($html, 0, true);
foreach (explode("\n", $msg) as $line) {
$line = trim($line);
if ($line) {
$rules[] = ['id' => (string)++$id, 'text' => $line];
}
}
}
return $rules;
}
}

View file

@ -462,7 +462,7 @@ class Worker
return false;
}
$load = System::getLoadAvg();
$load = System::getLoadAvg($processes_cooldown != 0);
if (empty($load)) {
return false;
}
@ -508,7 +508,7 @@ class Worker
$sleeping = false;
while ($load = System::getLoadAvg()) {
while ($load = System::getLoadAvg($processes_cooldown != 0)) {
if (($load_cooldown > 0) && ($load['average1'] > $load_cooldown)) {
if (!$sleeping) {
Logger::notice('Load induced pre execution cooldown.', ['max' => $load_cooldown, 'load' => $load, 'called-by' => System::callstack(1)]);

View file

@ -94,7 +94,7 @@ class Attachment extends BaseFactory
*/
public function createFromPhoto(int $id): array
{
$photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type', 'width', 'height'], ['id' => $id]);
$photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type', 'width', 'height', 'blurhash'], ['id' => $id]);
if (empty($photo)) {
return [];
}
@ -104,6 +104,7 @@ class Attachment extends BaseFactory
'description' => $photo['title'],
'width' => $photo['width'],
'height' => $photo['height'],
'blurhash' => $photo['blurhash'],
];
$photoTypes = Images::supportedTypes();

View file

@ -74,6 +74,7 @@ class Card extends BaseFactory
$data['image'] = $attached['preview'];
$data['width'] = $attached['preview-width'];
$data['height'] = $attached['preview-height'];
$data['blurhash'] = $attached['blurhash'];
}
}

View file

@ -136,6 +136,18 @@ class Contact
return $contact;
}
/**
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
* @param array $params Array of several parameters
* @return array
* @throws \Exception
*/
public static function selectAccountToArray(array $fields = [], array $condition = [], array $params = []): array
{
return DBA::selectToArray('account-user-view', $fields, $condition, $params);
}
/**
* @param array $fields Array of selected fields, empty for all
* @param array $condition Array of fields for condition
@ -3154,6 +3166,8 @@ class Contact
return;
}
Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']);
self::clearFollowerFollowingEndpointCache($contact['uid']);
$cdata = self::getPublicAndUserContactID($contact['id'], $contact['uid']);
@ -3177,6 +3191,8 @@ class Contact
} else {
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
}
Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']);
}
/**

View file

@ -68,6 +68,26 @@ class Relation
DBA::insert('contact-relation', ['last-interaction' => $interaction_date, 'cid' => $target, 'relation-cid' => $actor], Database::INSERT_UPDATE);
}
/**
* Fetch the followers of a given user
*
* @param integer $uid User ID
* @return void
*/
public static function discoverByUser(int $uid)
{
$contact = Contact::selectFirst(['id', 'url', 'network'], ['uid' => $uid, 'self' => true]);
if (empty($contact)) {
Logger::warning('Self contact for user not found', ['uid' => $uid]);
return;
}
$followers = self::getContacts($uid, [Contact::FOLLOWER, Contact::FRIEND]);
$followings = self::getContacts($uid, [Contact::SHARING, Contact::FRIEND]);
self::updateFollowersFollowings($contact, $followers, $followings);
}
/**
* Fetches the followers of a given profile and adds them
*
@ -113,13 +133,27 @@ class Relation
$followings = [];
}
self::updateFollowersFollowings($contact, $followers, $followings);
}
/**
* Update followers and followings for the given contact
*
* @param array $contact
* @param array $followers
* @param array $followings
* @return void
*/
private static function updateFollowersFollowings(array $contact, array $followers, array $followings)
{
if (empty($followers) && empty($followings)) {
Contact::update(['last-discovery' => DateTimeFormat::utcNow()], ['id' => $contact['id']]);
Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $url, 'network' => $contact['network']]);
Logger::info('The contact does not offer discoverable data', ['id' => $contact['id'], 'url' => $contact['url'], 'network' => $contact['network']]);
return;
}
$target = $contact['id'];
$url = $contact['url'];
if (!empty($followers)) {
// Clear the follower list, since it will be recreated in the next step

View file

@ -2291,24 +2291,24 @@ class Item
public static function isRemoteSelf(array $contact, array &$datarray): bool
{
if (!$contact['remote_self']) {
if ($contact['remote_self'] != Contact::MIRROR_OWN_POST) {
return false;
}
// Prevent the forwarding of posts that are forwarded
if (!empty($datarray["extid"]) && ($datarray["extid"] == Protocol::DFRN)) {
if (!empty($datarray['extid']) && ($datarray['extid'] == Protocol::DFRN)) {
Logger::info('Already forwarded');
return false;
}
// Prevent to forward already forwarded posts
if ($datarray["app"] == DI::baseUrl()->getHostname()) {
if ($datarray['app'] == DI::baseUrl()->getHostname()) {
Logger::info('Already forwarded (second test)');
return false;
}
// Only forward posts
if ($datarray["verb"] != Activity::POST) {
if ($datarray['verb'] != Activity::POST) {
Logger::info('No post');
return false;
}
@ -2320,54 +2320,49 @@ class Item
$datarray2 = $datarray;
Logger::info('remote-self start', ['contact' => $contact['url'], 'remote_self'=> $contact['remote_self'], 'item' => $datarray]);
if ($contact['remote_self'] == Contact::MIRROR_OWN_POST) {
$self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'],
$self = DBA::selectFirst('contact', ['id', 'name', 'url', 'thumb'],
['uid' => $contact['uid'], 'self' => true]);
if (DBA::isResult($self)) {
$datarray['contact-id'] = $self["id"];
$datarray['owner-name'] = $self["name"];
$datarray['owner-link'] = $self["url"];
$datarray['owner-avatar'] = $self["thumb"];
$datarray['author-name'] = $datarray['owner-name'];
$datarray['author-link'] = $datarray['owner-link'];
$datarray['author-avatar'] = $datarray['owner-avatar'];
unset($datarray['edited']);
unset($datarray['network']);
unset($datarray['owner-id']);
unset($datarray['author-id']);
}
if ($contact['network'] != Protocol::FEED) {
$old_uri_id = $datarray["uri-id"] ?? 0;
$datarray["guid"] = System::createUUID();
unset($datarray["plink"]);
$datarray["uri"] = self::newURI($datarray["guid"]);
$datarray["uri-id"] = ItemURI::getIdByURI($datarray["uri"]);
$datarray["extid"] = Protocol::DFRN;
$urlpart = parse_url($datarray2['author-link']);
$datarray["app"] = $urlpart["host"];
if (!empty($old_uri_id)) {
Post\Media::copy($old_uri_id, $datarray["uri-id"]);
}
unset($datarray["parent-uri"]);
unset($datarray["thr-parent"]);
} else {
$datarray['private'] = self::PUBLIC;
}
if (!DBA::isResult($self)) {
Logger::error('Self contact not found', ['uid' => $contact['uid']]);
return false;
}
$datarray['contact-id'] = $self['id'];
$datarray['author-name'] = $datarray['owner-name'] = $self['name'];
$datarray['author-link'] = $datarray['owner-link'] = $self['url'];
$datarray['author-avatar'] = $datarray['owner-avatar'] = $self['thumb'];
unset($datarray['edited']);
unset($datarray['network']);
unset($datarray['owner-id']);
unset($datarray['author-id']);
if ($contact['network'] != Protocol::FEED) {
$old_uri_id = $datarray['uri-id'] ?? 0;
$datarray['guid'] = System::createUUID();
unset($datarray['plink']);
$datarray['uri'] = self::newURI($datarray['guid']);
$datarray['uri-id'] = ItemURI::getIdByURI($datarray['uri']);
$datarray['extid'] = Protocol::DFRN;
$urlpart = parse_url($datarray2['author-link']);
$datarray['app'] = $urlpart['host'];
if (!empty($old_uri_id)) {
Post\Media::copy($old_uri_id, $datarray['uri-id']);
}
unset($datarray['parent-uri']);
unset($datarray['thr-parent']);
// Store the original post
$result = self::insert($datarray2);
Logger::info('remote-self post original item', ['contact' => $contact['url'], 'result'=> $result, 'item' => $datarray2]);
} else {
Logger::info('No valid mirroring option', ['uid' => $contact['uid'], 'id' => $contact['id'], 'network' => $contact['network'], 'remote_self' => $contact['remote_self']]);
return false;
$datarray['private'] = self::PUBLIC;
$datarray['app'] = 'Feed';
$result = true;
}
return (bool)$result;

View file

@ -346,11 +346,14 @@ class Photo
* @param string $url Image URL
* @param int $uid User ID of the requesting person
* @param string $mimetype Image mime type. Is guessed by file name when empty.
* @param string $blurhash The blurhash that will be used to generate a picture when the original picture can't be fetched
* @param int $width Image width
* @param int $height Image height
*
* @return array
* @throws \Exception
*/
public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = ''): array
public static function createPhotoForExternalResource(string $url, int $uid = 0, string $mimetype = '', string $blurhash = null, int $width = null, int $height = null): array
{
if (empty($mimetype)) {
$mimetype = Images::guessTypeByExtension($url);
@ -364,6 +367,9 @@ class Photo
$photo['backend-ref'] = json_encode(['url' => $url, 'uid' => $uid]);
$photo['type'] = $mimetype;
$photo['cacheable'] = true;
$photo['blurhash'] = $blurhash;
$photo['width'] = $width;
$photo['height'] = $height;
return $photo;
}
@ -436,6 +442,7 @@ class Photo
'height' => $image->getHeight(),
'width' => $image->getWidth(),
'datasize' => strlen($image->asString()),
'blurhash' => $image->getBlurHash(),
'data' => $data,
'scale' => $scale,
'photo-type' => $type,
@ -1318,7 +1325,7 @@ class Photo
logger::warning('profile banner upload with scale 3 (960) failed');
}
logger::info('new profile banner upload ended');
logger::info('new profile banner upload ended', ['uid' => $uid, 'resource_id' => $resource_id, 'filename' => $filename]);
$condition = ["`photo-type` = ? AND `resource-id` != ? AND `uid` = ?", self::USER_BANNER, $resource_id, $uid];
self::update(['photo-type' => self::DEFAULT], $condition);

View file

@ -117,7 +117,7 @@ class Media
*/
private static function unsetEmptyFields(array $media): array
{
$fields = ['mimetype', 'height', 'width', 'size', 'preview', 'preview-height', 'preview-width', 'description'];
$fields = ['mimetype', 'height', 'width', 'size', 'preview', 'preview-height', 'preview-width', 'blurhash', 'description'];
foreach ($fields as $field) {
if (empty($media[$field])) {
unset($media[$field]);
@ -203,6 +203,7 @@ class Media
$media['size'] = $imagedata['size'];
$media['width'] = $imagedata[0];
$media['height'] = $imagedata[1];
$media['blurhash'] = $imagedata['blurhash'] ?? null;
} else {
Logger::notice('No image data', ['media' => $media]);
}
@ -232,6 +233,7 @@ class Media
$media['preview'] = $data['images'][0]['src'] ?? null;
$media['preview-height'] = $data['images'][0]['height'] ?? null;
$media['preview-width'] = $data['images'][0]['width'] ?? null;
$media['blurhash'] = $data['images'][0]['blurhash'] ?? null;
$media['description'] = $data['text'] ?? null;
$media['name'] = $data['title'] ?? null;
$media['author-url'] = $data['author_url'] ?? null;
@ -287,6 +289,7 @@ class Media
$media['preview'] = null;
$media['preview-height'] = null;
$media['preview-width'] = null;
$media['blurhash'] = null;
$media['description'] = $item['body'];
$media['name'] = $item['title'];
$media['author-url'] = $item['author-link'];
@ -328,6 +331,7 @@ class Media
$media['preview'] = null;
$media['preview-height'] = null;
$media['preview-width'] = null;
$media['blurhash'] = null;
$media['description'] = $contact['about'];
$media['name'] = $contact['name'];
$media['author-url'] = $contact['url'];
@ -357,6 +361,7 @@ class Media
$media['size'] = $photo['datasize'];
$media['width'] = $photo['width'];
$media['height'] = $photo['height'];
$media['blurhash'] = $photo['blurhash'];
}
if (!preg_match('|.*?/photo/(.*[a-fA-F0-9])\-(.*[0-9])\..*[\w]|', $media['preview'] ?? '', $matches)) {

View file

@ -682,6 +682,8 @@ class User
if ($user['last-activity'] != $current_day) {
User::update(['last-activity' => $current_day], $uid);
// Set the last actitivy for all identities of the user
DBA::update('user', ['last-activity' => $current_day], ['parent-uid' => $uid, 'account_removed' => false]);
}
}
@ -1729,8 +1731,8 @@ class User
'active_users_weekly' => 0,
];
$userStmt = DBA::select('owner-view', ['uid', 'login_date', 'last-item'],
["`verified` AND `login_date` > ? AND NOT `blocked`
$userStmt = DBA::select('owner-view', ['uid', 'last-activity', 'last-item'],
["`verified` AND `last-activity` > ? AND NOT `blocked`
AND NOT `account_removed` AND NOT `account_expired`",
DBA::NULL_DATETIME]);
if (!DBA::isResult($userStmt)) {
@ -1744,17 +1746,17 @@ class User
while ($user = DBA::fetch($userStmt)) {
$statistics['total_users']++;
if ((strtotime($user['login_date']) > $halfyear) || (strtotime($user['last-item']) > $halfyear)
if ((strtotime($user['last-activity']) > $halfyear) || (strtotime($user['last-item']) > $halfyear)
) {
$statistics['active_users_halfyear']++;
}
if ((strtotime($user['login_date']) > $month) || (strtotime($user['last-item']) > $month)
if ((strtotime($user['last-activity']) > $month) || (strtotime($user['last-item']) > $month)
) {
$statistics['active_users_monthly']++;
}
if ((strtotime($user['login_date']) > $week) || (strtotime($user['last-item']) > $week)
if ((strtotime($user['last-activity']) > $week) || (strtotime($user['last-item']) > $week)
) {
$statistics['active_users_weekly']++;
}

View file

@ -57,11 +57,13 @@ class Tos extends BaseAdmin
$displaytos = !empty($_POST['displaytos']);
$displayprivstatement = !empty($_POST['displayprivstatement']);
$tostext = (!empty($_POST['tostext']) ? strip_tags(trim($_POST['tostext'])) : '');
$tostext = (!empty($_POST['tostext']) ? strip_tags(trim($_POST['tostext'])) : '');
$tosrules = (!empty($_POST['tosrules']) ? strip_tags(trim($_POST['tosrules'])) : '');
$this->config->set('system', 'tosdisplay', $displaytos);
$this->config->set('system', 'tosprivstatement', $displayprivstatement);
$this->config->set('system', 'tostext', $tostext);
$this->config->set('system', 'tosrules', $tosrules);
$this->baseUrl->redirect('admin/tos');
}
@ -79,6 +81,7 @@ class Tos extends BaseAdmin
'$preview' => $this->t('Privacy Statement Preview'),
'$privtext' => $this->tos->privacy_complete,
'$tostext' => ['tostext', $this->t('The Terms of Service'), $this->config->get('system', 'tostext'), $this->t('Enter the Terms of Service for your node here. You can use BBCode. Headers of sections should be [h2] and below.')],
'$tosrules' => ['tosrules', $this->t('The rules'), $this->config->get('system', 'tosrules'), $this->t('Enter your system rules here. Each line represents one rule.')],
'$form_security_token' => self::getFormSecurityToken('admin_tos'),
'$submit' => $this->t('Save Settings'),
]);

View file

@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Module\BaseApi;
/**
@ -55,33 +56,60 @@ class Followers extends BaseApi
'limit' => 40, // Maximum number of results to return. Defaults to 40.
], $request);
$params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']];
if ($id == Contact::getPublicIdByUserId($uid)) {
$params = ['order' => ['pid' => true], 'limit' => $request['limit']];
$condition = ['cid' => $id, 'follows' => true];
$condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]];
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]);
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]);
$params['order'] = ['pid'];
}
$accounts = [];
foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) {
self::setBoundaries($follower['pid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid);
}
} else {
$params = ['order' => ['relation-cid' => true], 'limit' => $request['limit']];
$condition = ['cid' => $id, 'follows' => true];
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` < ?", $request['max_id']]);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]);
$params['order'] = ['relation-cid'];
}
$accounts = [];
$followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params);
while ($follower = DBA::fetch($followers)) {
self::setBoundaries($follower['relation-cid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid);
}
DBA::close($followers);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`relation-cid` > ?", $request['min_id']]);
$params['order'] = ['cid'];
}
$accounts = [];
$followers = DBA::select('contact-relation', ['relation-cid'], $condition, $params);
while ($follower = DBA::fetch($followers)) {
self::setBoundaries($follower['relation-cid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid);
}
DBA::close($followers);
if (!empty($request['min_id'])) {
$accounts = array_reverse($accounts);
}

View file

@ -24,6 +24,7 @@ namespace Friendica\Module\Api\Mastodon\Accounts;
use Friendica\Core\System;
use Friendica\Database\DBA;
use Friendica\DI;
use Friendica\Model\Contact;
use Friendica\Module\BaseApi;
/**
@ -55,33 +56,60 @@ class Following extends BaseApi
'limit' => 40, // Maximum number of results to return. Defaults to 40.
], $request);
$params = ['order' => ['cid' => true], 'limit' => $request['limit']];
if ($id == Contact::getPublicIdByUserId($uid)) {
$params = ['order' => ['pid' => true], 'limit' => $request['limit']];
$condition = ['relation-cid' => $id, 'follows' => true];
$condition = ['uid' => $uid, 'self' => false, 'rel' => [Contact::SHARING, Contact::FRIEND]];
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]);
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` < ?", $request['max_id']]);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`pid` > ?", $request['min_id']]);
$params['order'] = ['pid'];
}
$accounts = [];
foreach (Contact::selectAccountToArray(['pid'], $condition, $params) as $follower) {
self::setBoundaries($follower['pid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['pid'], $uid);
}
} else {
$params = ['order' => ['cid' => true], 'limit' => $request['limit']];
$condition = ['relation-cid' => $id, 'follows' => true];
if (!empty($request['max_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` < ?", $request['max_id']]);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]);
$params['order'] = ['cid'];
}
$accounts = [];
$followers = DBA::select('contact-relation', ['cid'], $condition, $params);
while ($follower = DBA::fetch($followers)) {
self::setBoundaries($follower['cid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid);
}
DBA::close($followers);
}
if (!empty($request['since_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['since_id']]);
}
if (!empty($request['min_id'])) {
$condition = DBA::mergeConditions($condition, ["`cid` > ?", $request['min_id']]);
$params['order'] = ['cid'];
}
$accounts = [];
$followers = DBA::select('contact-relation', ['cid'], $condition, $params);
while ($follower = DBA::fetch($followers)) {
self::setBoundaries($follower['cid']);
$accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid);
}
DBA::close($followers);
if (!empty($request['min_id'])) {
$accounts = array_reverse($accounts);
}

View file

@ -89,11 +89,11 @@ class UpdateCredentials extends BaseApi
Logger::info('Update profile and user', ['uid' => $uid, 'user' => $user, 'profile' => $profile]);
if (!empty($request['avatar'])) {
Photo::uploadAvatar(1, $request['avatar']);
Photo::uploadAvatar($uid, $request['avatar']);
}
if (!empty($request['header'])) {
Photo::uploadBanner(1, $request['header']);
Photo::uploadBanner($uid, $request['header']);
}
User::update($user, $uid);

View file

@ -59,6 +59,6 @@ class Instance extends BaseApi
*/
protected function rawContent(array $request = [])
{
System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database));
System::jsonExit(new InstanceEntity($this->config, $this->baseUrl, $this->database, System::getRules()));
}
}

View file

@ -38,21 +38,6 @@ class Rules extends BaseApi
*/
protected function rawContent(array $request = [])
{
$rules = [];
$id = 0;
if (DI::config()->get('system', 'tosdisplay')) {
$html = BBCode::convert(DI::config()->get('system', 'tostext'), false, BBCode::EXTERNAL);
$msg = HTML::toPlaintext($html, 0, true);
foreach (explode("\n", $msg) as $line) {
$line = trim($line);
if ($line) {
$rules[] = ['id' => (string)++$id, 'text' => $line];
}
}
}
System::jsonExit($rules);
System::jsonExit(System::getRules());
}
}

View file

@ -152,7 +152,7 @@ class API extends BaseModule
$share = intval($request['share'] ?? 0);
$isPreview = intval($request['preview'] ?? 0);
$start = DateTimeFormat::convert($strStartDateTime ?? DBA::NULL_DATETIME, $this->timezone);
$start = DateTimeFormat::convert($strStartDateTime ?? DBA::NULL_DATETIME, 'UTC', $this->timezone);
if (!$noFinish) {
$finish = DateTimeFormat::convert($strFinishDateTime ?? DBA::NULL_DATETIME, 'UTC', $this->timezone);
} else {
@ -170,12 +170,12 @@ class API extends BaseModule
$type = 'event';
$params = [
'summary' => $summary,
'description' => $desc,
'location' => $location,
'start' => $strStartDateTime,
'finish' => $strFinishDateTime,
'nofinish' => $noFinish,
'summary' => $summary,
'desc' => $desc,
'location' => $location,
'start' => $strStartDateTime,
'finish' => $strFinishDateTime,
'nofinish' => $noFinish,
];
$action = empty($eventId) ? 'new' : 'edit/' . $eventId;

View file

@ -234,13 +234,13 @@ class Form extends BaseModule
'start_text'
),
'$d_text' => $this->t('Description:'),
'$d_orig' => $d_orig,
'$l_text' => $this->t('Location:'),
'$l_orig' => $l_orig,
'$t_text' => $this->t('Title:') . ' <span class="required" title="' . $this->t('Required') . '">*</span>',
'$t_text' => $this->t('Title (BBCode not allowed)') . ' <span class="required" title="' . $this->t('Required') . '">*</span>',
'$t_orig' => $t_orig,
'$summary' => ['summary', $this->t('Title:'), $t_orig, '', '*'],
'$d_text' => $this->t('Description (BBCode allowed)'),
'$d_orig' => $d_orig,
'$l_text' => $this->t('Location (BBCode not allowed)'),
'$l_orig' => $l_orig,
'$summary' => ['summary', $this->t('Title (BBCode not allowed)'), $t_orig, '', '*'],
'$sh_text' => $this->t('Share this event'),
'$share' => ['share', $this->t('Share this event'), $share_checked, '', $share_disabled],
'$sh_checked' => $share_checked,

View file

@ -34,6 +34,7 @@ use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Profiler;
use Friendica\Util\Strings;
use Psr\Log\LoggerInterface;
/**
@ -82,12 +83,12 @@ class Get extends \Friendica\BaseModule
return [
'id' => $event['id'],
'title' => $event['summary'],
'title' => Strings::escapeHtml($event['summary']),
'start' => DateTimeFormat::local($event['start']),
'end' => DateTimeFormat::local($event['finish']),
'nofinish' => $event['nofinish'],
'desc' => $event['desc'],
'location' => $event['location'],
'desc' => Strings::escapeHtml($event['desc']),
'location' => Strings::escapeHtml($event['location']),
'item' => $item,
];
}, $events);

View file

@ -137,7 +137,7 @@ abstract class BaseUsers extends BaseModeration
$user['account_type'] = ($user['page_flags_raw'] == 0) ? $account_types[$user['account-type']] : '';
$user['register_date'] = Temporal::getRelativeDate($user['register_date']);
$user['login_date'] = Temporal::getRelativeDate($user['login_date']);
$user['login_date'] = Temporal::getRelativeDate($user['last-activity']);
$user['lastitem_date'] = Temporal::getRelativeDate($user['last-item']);
$user['is_admin'] = in_array($user['email'], $adminlist);
$user['is_deletable'] = !$user['account_removed'] && intval($user['uid']) != $this->session->getLocalUserId();

View file

@ -100,7 +100,7 @@ class Active extends BaseUsers
'name',
'email',
'register_date',
'login_date',
'last-activity',
'last-item',
'page-flags',
];

View file

@ -100,7 +100,7 @@ class Blocked extends BaseUsers
'name',
'email',
'register_date',
'login_date',
'last-activity',
'last-item',
'page-flags',
];

View file

@ -49,7 +49,7 @@ class Deleted extends BaseUsers
'name',
'email',
'register_date',
'login_date',
'last-activity',
'last-item',
'page-flags',
];

View file

@ -114,7 +114,7 @@ class Index extends BaseUsers
'name',
'email',
'register_date',
'login_date',
'last-activity',
'last-item',
'page-flags',
];

View file

@ -94,19 +94,9 @@ class NoScrape extends BaseModule
}
// We display the last activity (post or login), reduced to year and week number
$last_active = 0;
$condition = ['uid' => $owner['uid'], 'self' => true];
$contact = DBA::selectFirst('contact', ['last-item'], $condition);
if (DBA::isResult($contact)) {
$last_active = strtotime($contact['last-item']);
}
$condition = ['uid' => $owner['uid']];
$user = DBA::selectFirst('user', ['login_date'], $condition);
if (DBA::isResult($user)) {
if ($last_active < strtotime($user['login_date'])) {
$last_active = strtotime($user['login_date']);
}
$last_active = strtotime($owner['last-item']);
if ($last_active < strtotime($owner['last-activity'])) {
$last_active = strtotime($owner['last-activity']);
}
$json_info['last-activity'] = date('o-W', $last_active);

View file

@ -153,8 +153,12 @@ class Photo extends BaseModule
$stamp = microtime(true);
$imgdata = MPhoto::getImageDataForPhoto($photo);
if (empty($imgdata)) {
if (empty($imgdata) && empty($photo['blurhash'])) {
throw new HTTPException\NotFoundException();
} elseif (empty($imgdata) && !empty($photo['blurhash'])) {
$image = New Image('', 'image/png');
$image->getFromBlurHash($photo['blurhash'], $photo['width'], $photo['height']);
$imgdata = $image->asString();
}
// The mimetype for an external or system resource can only be known reliably after it had been fetched
@ -240,14 +244,18 @@ class Photo extends BaseModule
{
switch($type) {
case 'preview':
$media = DBA::selectFirst('post-media', ['preview', 'url', 'mimetype', 'type', 'uri-id'], ['id' => $id]);
$media = DBA::selectFirst('post-media', ['preview', 'url', 'preview-height', 'preview-width', 'height', 'width', 'mimetype', 'type', 'uri-id', 'blurhash'], ['id' => $id]);
if (empty($media)) {
return false;
}
$url = $media['preview'];
$url = $media['preview'];
$width = $media['preview-width'];
$height = $media['preview-height'];
if (empty($url) && ($media['type'] == Post\Media::IMAGE)) {
$url = $media['url'];
$url = $media['url'];
$width = $media['width'];
$height = $media['height'];
}
if (empty($url)) {
@ -258,9 +266,9 @@ class Photo extends BaseModule
return MPhoto::getPhoto($matches[1], $matches[2]);
}
return MPhoto::createPhotoForExternalResource($url, (int)DI::userSession()->getLocalUserId(), $media['mimetype'] ?? '');
return MPhoto::createPhotoForExternalResource($url, (int)DI::userSession()->getLocalUserId(), $media['mimetype'] ?? '', $media['blurhash'], $width, $height);
case 'media':
$media = DBA::selectFirst('post-media', ['url', 'mimetype', 'uri-id'], ['id' => $id, 'type' => Post\Media::IMAGE]);
$media = DBA::selectFirst('post-media', ['url', 'height', 'width', 'mimetype', 'uri-id', 'blurhash'], ['id' => $id, 'type' => Post\Media::IMAGE]);
if (empty($media)) {
return false;
}
@ -269,7 +277,7 @@ class Photo extends BaseModule
return MPhoto::getPhoto($matches[1], $matches[2]);
}
return MPhoto::createPhotoForExternalResource($media['url'], (int)DI::userSession()->getLocalUserId(), $media['mimetype']);
return MPhoto::createPhotoForExternalResource($media['url'], (int)DI::userSession()->getLocalUserId(), $media['mimetype'], $media['blurhash'], $media['width'], $media['height']);
case 'link':
$link = DBA::selectFirst('post-link', ['url', 'mimetype'], ['id' => $id]);
if (empty($link)) {

View file

@ -83,9 +83,22 @@ class Tos extends BaseModule
$tpl = Renderer::getMarkupTemplate('tos.tpl');
if ($this->config->get('system', 'tosdisplay')) {
$lines = $this->config->get('system', 'tosrules');
if (!empty($lines)) {
$rules = "[list=1]";
foreach (explode("\n", $lines) as $line) {
$rules .= "\n[*]" . $line;
}
$rules .= "\n[/list]\n";
} else {
$rules = '';
}
return Renderer::replaceMacros($tpl, [
'$title' => $this->t('Terms of Service'),
'$tostext' => BBCode::convert($this->config->get('system', 'tostext')),
'$rulestitle' => $this->t('Rules'),
'$rules' => BBCode::convert($rules),
'$displayprivstatement' => $this->config->get('system', 'tosprivstatement'),
'$privstatementtitle' => $this->t('Privacy Statement'),
'$privacy_operate' => $this->t('At the time of registration, and for providing communications between the user account and their contacts, the user has to provide a display name (pen name), an username (nickname) and a working email address. The names will be accessible on the profile page of the account by any visitor of the page, even if other profile details are not displayed. The email address will only be used to send the user notifications about interactions, but wont be visibly displayed. The listing of an account in the node\'s user directory or the global user directory is optional and can be controlled in the user settings, it is not necessary for communication.'),

View file

@ -46,6 +46,8 @@ class Attachment extends BaseDataTransferObject
protected $text_url;
/** @var string */
protected $description;
/** @var string */
protected $blurhash;
/** @var array */
protected $meta;
@ -68,6 +70,7 @@ class Attachment extends BaseDataTransferObject
$this->remote_url = $remote;
$this->text_url = $this->remote_url ?? $this->url;
$this->description = $attachment['description'];
$this->blurhash = $attachment['blurhash'];
if ($type === 'image') {
if ((int) $attachment['width'] > 0 && (int) $attachment['height'] > 0) {
$this->meta['original']['width'] = (int) $attachment['width'];

View file

@ -52,6 +52,8 @@ class Card extends BaseDataTransferObject
protected $height;
/** @var string */
protected $image;
/** @var string */
protected $blurhash;
/**
* Creates a card record from an attachment array.
@ -72,6 +74,7 @@ class Card extends BaseDataTransferObject
$this->width = $attachment['width'] ?? 0;
$this->height = $attachment['height'] ?? 0;
$this->image = $attachment['image'] ?? '';
$this->blurhash = $attachment['blurhash'] ?? '';
$this->history = $history;
}

View file

@ -75,11 +75,12 @@ class Instance extends BaseDataTransferObject
* @param IManageConfigValues $config
* @param BaseURL $baseUrl
* @param Database $database
* @param array $rules
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\NotFoundException
* @throws \ImagickException
*/
public function __construct(IManageConfigValues $config, BaseURL $baseUrl, Database $database)
public function __construct(IManageConfigValues $config, BaseURL $baseUrl, Database $database, array $rules = [])
{
$register_policy = intval($config->get('config', 'register_policy'));
@ -97,6 +98,7 @@ class Instance extends BaseDataTransferObject
$this->approval_required = ($register_policy == Register::APPROVE);
$this->invites_enabled = false;
$this->contact_account = [];
$this->rules = $rules;
$administrator = User::getFirstAdmin(['nickname']);
if ($administrator) {

View file

@ -26,13 +26,15 @@ use Friendica\DI;
use Friendica\Util\Images;
use Imagick;
use ImagickPixel;
use GDImage;
use kornrunner\Blurhash\Blurhash;
/**
* Class to handle images
*/
class Image
{
/** @var Imagick|resource */
/** @var GDImage|Imagick|resource */
private $image;
/*
@ -695,14 +697,13 @@ class Image
try {
/* Clean it */
$this->image = $this->image->deconstructImages();
$string = $this->image->getImagesBlob();
return $string;
return $this->image->getImagesBlob();
} catch (Exception $e) {
return false;
}
}
ob_start();
$stream = fopen('php://memory','r+');
// Enable interlacing
imageinterlace($this->image, true);
@ -710,18 +711,90 @@ class Image
switch ($this->getType()) {
case 'image/png':
$quality = DI::config()->get('system', 'png_quality');
imagepng($this->image, null, $quality);
imagepng($this->image, $stream, $quality);
break;
case 'image/jpeg':
case 'image/jpg':
$quality = DI::config()->get('system', 'jpeg_quality');
imagejpeg($this->image, null, $quality);
imagejpeg($this->image, $stream, $quality);
break;
}
$string = ob_get_contents();
ob_end_clean();
rewind($stream);
return stream_get_contents($stream);
}
return $string;
/**
* Create a blurhash out of a given image string
*
* @param string $img_str
* @return string
*/
public function getBlurHash(): string
{
if ($this->isImagick()) {
// Imagick is not supported
return '';
}
$width = $this->getWidth();
$height = $this->getHeight();
if (max($width, $height) > 90) {
$this->scaleDown(90);
$width = $this->getWidth();
$height = $this->getHeight();
}
$pixels = [];
for ($y = 0; $y < $height; ++$y) {
$row = [];
for ($x = 0; $x < $width; ++$x) {
$index = imagecolorat($this->image, $x, $y);
$colors = imagecolorsforindex($this->image, $index);
$row[] = [$colors['red'], $colors['green'], $colors['blue']];
}
$pixels[] = $row;
}
// The components define the amount of details (1 to 9).
$components_x = 9;
$components_y = 9;
return Blurhash::encode($pixels, $components_x, $components_y);
}
/**
* Create an image out of a blurhash
*
* @param string $blurhash
* @param integer $width
* @param integer $height
* @return void
*/
public function getFromBlurHash(string $blurhash, int $width, int $height)
{
if ($this->isImagick()) {
// Imagick is not supported
return;
}
$scaled = Images::getScalingDimensions($width, $height, 90);
$pixels = Blurhash::decode($blurhash, $scaled['width'], $scaled['height']);
$this->image = imagecreatetruecolor($scaled['width'], $scaled['height']);
for ($y = 0; $y < $scaled['height']; ++$y) {
for ($x = 0; $x < $scaled['width']; ++$x) {
[$r, $g, $b] = $pixels[$y][$x];
imagesetpixel($this->image, $x, $y, imagecolorallocate($this->image, $r, $g, $b));
}
}
$this->width = imagesx($this->image);
$this->height = imagesy($this->image);
$this->valid = true;
$this->scaleUp(min($width, $height));
}
}

View file

@ -943,7 +943,7 @@ class Feed
if ((time() - strtotime($owner['last-item'])) < 15*60) {
$result = DI::cache()->get($cachekey);
if (!$nocache && !is_null($result)) {
Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]);
Logger::info('Cached feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner['nickname'], 'filter' => $filter, 'created' => $previous_created]);
return $result['feed'];
}
}
@ -997,7 +997,7 @@ class Feed
$msg = ['feed' => $feeddata, 'last_update' => $last_update];
DI::cache()->set($cachekey, $msg, Duration::QUARTER_HOUR);
Logger::info('Feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner_nick, 'filter' => $filter, 'created' => $previous_created]);
Logger::info('Feed duration', ['seconds' => number_format(microtime(true) - $stamp, 3), 'nick' => $owner['nickname'], 'filter' => $filter, 'created' => $previous_created]);
return $feeddata;
}

View file

@ -356,7 +356,7 @@ class Authentication
$this->dba->update('user', ['last-activity' => DateTimeFormat::utcNow('Y-m-d'), 'login_date' => DateTimeFormat::utcNow()], ['uid' => $user_record['uid']]);
// Set the login date for all identities of the user
$this->dba->update('user', ['login_date' => DateTimeFormat::utcNow()],
$this->dba->update('user', ['last-activity' => DateTimeFormat::utcNow('Y-m-d'), 'login_date' => DateTimeFormat::utcNow()],
['parent-uid' => $user_record['uid'], 'account_removed' => false]);
// Regularly update suggestions

View file

@ -25,6 +25,7 @@ use Friendica\Core\Logger;
use Friendica\DI;
use Friendica\Model\Photo;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Object\Image;
/**
* Image utilities
@ -244,7 +245,10 @@ class Images
}
if ($data) {
$data['size'] = $filesize;
$image = new Image($img_str);
$data['blurhash'] = $image->getBlurHash();
$data['size'] = $filesize;
}
return is_array($data) ? $data : [];

View file

@ -567,6 +567,7 @@ class ParseUrl
$image['width'] = $photodata[0];
$image['height'] = $photodata[1];
$image['contenttype'] = $photodata['mime'];
$image['blurhash'] = $photodata['blurhash'] ?? null;
unset($image['url']);
ksort($image);
} else {

View file

@ -23,6 +23,7 @@ namespace Friendica\Util;
use Friendica\Content\ContactSelector;
use Friendica\Core\Logger;
use Friendica\Core\System;
use ParagonIE\ConstantTime\Base64;
/**
@ -480,7 +481,7 @@ class Strings
$blocks = [];
$text = preg_replace_callback($regex,
$return = preg_replace_callback($regex,
function ($matches) use ($executionId, &$blocks) {
$return = '«block-' . $executionId . '-' . count($blocks) . '»';
@ -491,7 +492,11 @@ class Strings
$text
);
$text = $callback($text) ?? '';
if (is_null($return)) {
Logger::warning('Received null value from preg_replace_callback', ['text' => $text, 'regex' => $regex, 'blocks' => $blocks, 'executionId' => $executionId, 'callstack' => System::callstack(10)]);
}
$text = $callback($return ?? $text) ?? '';
// Restore code blocks
$text = preg_replace_callback('/«block-' . $executionId . '-([0-9]+)»/iU',

View file

@ -0,0 +1,36 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Worker;
use Friendica\Model\Contact;
class ContactDiscoveryForUser
{
/**
* Discover contact relations
* @param string $url
*/
public static function execute(int $uid)
{
Contact\Relation::discoverByUser($uid);
}
}

View file

@ -45,7 +45,7 @@ class PollContacts
if (!empty($abandon_days)) {
$condition = DBA::mergeConditions($condition,
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed` AND `login_date` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]);
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed` AND `last-activity` > ?)", 0, DateTimeFormat::utc('now - ' . $abandon_days . ' days')]);
} else {
$condition = DBA::mergeConditions($condition,
["`uid` != ? AND `uid` IN (SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`)", 0]);

View file

@ -55,7 +55,7 @@
use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1496);
define('DB_UPDATE_VERSION', 1497);
}
return [
@ -1129,6 +1129,7 @@ return [
"height" => ["type" => "smallint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"width" => ["type" => "smallint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"datasize" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"blurhash" => ["type" => "varbinary(255)", "comment" => "BlurHash representation of the photo"],
"data" => ["type" => "mediumblob", "not null" => "1", "comment" => ""],
"scale" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
"profile" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
@ -1340,6 +1341,7 @@ return [
"height" => ["type" => "smallint unsigned", "comment" => "Height of the media"],
"width" => ["type" => "smallint unsigned", "comment" => "Width of the media"],
"size" => ["type" => "bigint unsigned", "comment" => "Media size"],
"blurhash" => ["type" => "varbinary(255)", "comment" => "BlurHash representation of the image"],
"preview" => ["type" => "varbinary(512)", "comment" => "Preview URL"],
"preview-height" => ["type" => "smallint unsigned", "comment" => "Height of the preview picture"],
"preview-width" => ["type" => "smallint unsigned", "comment" => "Width of the preview picture"],

View file

@ -889,6 +889,7 @@
"language" => ["user", "language"],
"register_date" => ["user", "register_date"],
"login_date" => ["user", "login_date"],
"last-activity" => ["user", "last-activity"],
"default-location" => ["user", "default-location"],
"allow_location" => ["user", "allow_location"],
"theme" => ["user", "theme"],

View file

@ -318,6 +318,8 @@ return [
'/proofs' => [Module\Api\Mastodon\Proofs::class, [R::GET ]], // Dummy, not supported
],
'/about[/more]' => [Module\Friendica::class, [R::GET]],
'/admin' => [
'[/]' => [Module\Admin\Summary::class, [R::GET]],

View file

@ -1127,3 +1127,9 @@ function update_1491()
DBA::update('contact', ['remote_self' => Contact::MIRROR_OWN_POST], ['remote_self' => Contact::MIRROR_FORWARDED]);
return Update::SUCCESS;
}
function update_1497()
{
DBA::e("UPDATE `user` SET `last-activity` = DATE(`login_date`) WHERE `last-activity` IS NULL");
return Update::SUCCESS;
}

View file

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: 2022.12-dev\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-01 18:01-0500\n"
"POT-Creation-Date: 2022-12-04 06:41-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -1262,7 +1262,7 @@ msgid "Public post"
msgstr ""
#: src/Content/Conversation.php:396 src/Content/Widget/VCard.php:113
#: src/Model/Profile.php:465 src/Module/Admin/Logs/View.php:93
#: src/Model/Profile.php:469 src/Module/Admin/Logs/View.php:93
#: src/Module/Post/Edit.php:177
msgid "Message"
msgstr ""
@ -1516,7 +1516,7 @@ msgstr ""
msgid "show more"
msgstr ""
#: src/Content/Item.php:294 src/Model/Item.php:2919
#: src/Content/Item.php:294 src/Model/Item.php:2914
msgid "event"
msgstr ""
@ -1525,7 +1525,7 @@ msgstr ""
msgid "status"
msgstr ""
#: src/Content/Item.php:303 src/Model/Item.php:2921
#: src/Content/Item.php:303 src/Model/Item.php:2916
#: src/Module/Post/Tag/Add.php:123
msgid "photo"
msgstr ""
@ -1539,31 +1539,31 @@ msgstr ""
msgid "Follow Thread"
msgstr ""
#: src/Content/Item.php:387 src/Model/Contact.php:1197
#: src/Content/Item.php:387 src/Model/Contact.php:1209
msgid "View Status"
msgstr ""
#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1135
#: src/Model/Contact.php:1189 src/Model/Contact.php:1198
#: src/Content/Item.php:388 src/Content/Item.php:406 src/Model/Contact.php:1147
#: src/Model/Contact.php:1201 src/Model/Contact.php:1210
#: src/Module/Directory.php:157 src/Module/Settings/Profile/Index.php:234
msgid "View Profile"
msgstr ""
#: src/Content/Item.php:389 src/Model/Contact.php:1199
#: src/Content/Item.php:389 src/Model/Contact.php:1211
msgid "View Photos"
msgstr ""
#: src/Content/Item.php:390 src/Model/Contact.php:1190
#: src/Model/Contact.php:1200
#: src/Content/Item.php:390 src/Model/Contact.php:1202
#: src/Model/Contact.php:1212
msgid "Network Posts"
msgstr ""
#: src/Content/Item.php:391 src/Model/Contact.php:1191
#: src/Model/Contact.php:1201
#: src/Content/Item.php:391 src/Model/Contact.php:1203
#: src/Model/Contact.php:1213
msgid "View Contact"
msgstr ""
#: src/Content/Item.php:392 src/Model/Contact.php:1202
#: src/Content/Item.php:392 src/Model/Contact.php:1214
msgid "Send PM"
msgstr ""
@ -1588,7 +1588,7 @@ msgid "Languages"
msgstr ""
#: src/Content/Item.php:403 src/Content/Widget.php:80
#: src/Model/Contact.php:1192 src/Model/Contact.php:1203
#: src/Model/Contact.php:1204 src/Model/Contact.php:1215
#: src/Module/Contact/Follow.php:167 view/theme/vier/theme.php:196
msgid "Connect/Follow"
msgstr ""
@ -1774,9 +1774,9 @@ msgstr ""
msgid "Information about this friendica instance"
msgstr ""
#: src/Content/Nav.php:265 src/Module/Admin/Tos.php:76
#: src/Content/Nav.php:265 src/Module/Admin/Tos.php:78
#: src/Module/BaseAdmin.php:95 src/Module/Register.php:176
#: src/Module/Tos.php:87
#: src/Module/Tos.php:98
msgid "Terms of Service"
msgstr ""
@ -1920,8 +1920,8 @@ msgid ""
"<a href=\"%1$s\" target=\"_blank\" rel=\"noopener noreferrer\">%2$s</a> %3$s"
msgstr ""
#: src/Content/Text/BBCode.php:1245 src/Model/Item.php:3541
#: src/Model/Item.php:3547 src/Model/Item.php:3548
#: src/Content/Text/BBCode.php:1245 src/Model/Item.php:3536
#: src/Model/Item.php:3542 src/Model/Item.php:3543
msgid "Link to source"
msgstr ""
@ -1954,7 +1954,7 @@ msgid "The end"
msgstr ""
#: src/Content/Text/HTML.php:882 src/Content/Widget/VCard.php:109
#: src/Model/Profile.php:459 src/Module/Contact/Profile.php:427
#: src/Model/Profile.php:463 src/Module/Contact/Profile.php:427
msgid "Follow"
msgstr ""
@ -2081,7 +2081,7 @@ msgstr ""
msgid "Organisations"
msgstr ""
#: src/Content/Widget.php:523 src/Model/Contact.php:1629
#: src/Content/Widget.php:523 src/Model/Contact.php:1641
msgid "News"
msgstr ""
@ -2150,21 +2150,20 @@ msgid "Matrix:"
msgstr ""
#: src/Content/Widget/VCard.php:104 src/Model/Event.php:82
#: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:961
#: src/Model/Profile.php:373 src/Module/Calendar/Event/Form.php:239
#: src/Module/Contact/Profile.php:369 src/Module/Directory.php:147
#: src/Module/Notifications/Introductions.php:187
#: src/Model/Event.php:109 src/Model/Event.php:471 src/Model/Event.php:958
#: src/Model/Profile.php:373 src/Module/Contact/Profile.php:369
#: src/Module/Directory.php:147 src/Module/Notifications/Introductions.php:187
#: src/Module/Profile/Profile.php:186
msgid "Location:"
msgstr ""
#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:472
#: src/Content/Widget/VCard.php:107 src/Model/Profile.php:476
#: src/Module/Notifications/Introductions.php:201
msgid "Network:"
msgstr ""
#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1193
#: src/Model/Contact.php:1204 src/Model/Profile.php:461
#: src/Content/Widget/VCard.php:111 src/Model/Contact.php:1205
#: src/Model/Contact.php:1216 src/Model/Profile.php:465
#: src/Module/Contact/Profile.php:419
msgid "Unfollow"
msgstr ""
@ -2861,77 +2860,77 @@ msgstr ""
msgid "Legacy module file not found: %s"
msgstr ""
#: src/Model/Contact.php:1210 src/Module/Moderation/Users/Pending.php:102
#: src/Model/Contact.php:1222 src/Module/Moderation/Users/Pending.php:102
#: src/Module/Notifications/Introductions.php:132
#: src/Module/Notifications/Introductions.php:204
msgid "Approve"
msgstr ""
#: src/Model/Contact.php:1625
#: src/Model/Contact.php:1637
msgid "Organisation"
msgstr ""
#: src/Model/Contact.php:1633
#: src/Model/Contact.php:1645
msgid "Forum"
msgstr ""
#: src/Model/Contact.php:2819
#: src/Model/Contact.php:2831
msgid "Disallowed profile URL."
msgstr ""
#: src/Model/Contact.php:2824 src/Module/Friendica.php:82
#: src/Model/Contact.php:2836 src/Module/Friendica.php:82
msgid "Blocked domain"
msgstr ""
#: src/Model/Contact.php:2829
#: src/Model/Contact.php:2841
msgid "Connect URL missing."
msgstr ""
#: src/Model/Contact.php:2838
#: src/Model/Contact.php:2850
msgid ""
"The contact could not be added. Please check the relevant network "
"credentials in your Settings -> Social Networks page."
msgstr ""
#: src/Model/Contact.php:2880
#: src/Model/Contact.php:2892
msgid "The profile address specified does not provide adequate information."
msgstr ""
#: src/Model/Contact.php:2882
#: src/Model/Contact.php:2894
msgid "No compatible communication protocols or feeds were discovered."
msgstr ""
#: src/Model/Contact.php:2885
#: src/Model/Contact.php:2897
msgid "An author or name was not found."
msgstr ""
#: src/Model/Contact.php:2888
#: src/Model/Contact.php:2900
msgid "No browser URL could be matched to this address."
msgstr ""
#: src/Model/Contact.php:2891
#: src/Model/Contact.php:2903
msgid ""
"Unable to match @-style Identity Address with a known protocol or email "
"contact."
msgstr ""
#: src/Model/Contact.php:2892
#: src/Model/Contact.php:2904
msgid "Use mailto: in front of address to force email check."
msgstr ""
#: src/Model/Contact.php:2898
#: src/Model/Contact.php:2910
msgid ""
"The profile address specified belongs to a network which has been disabled "
"on this site."
msgstr ""
#: src/Model/Contact.php:2903
#: src/Model/Contact.php:2915
msgid ""
"Limited profile. This person will be unable to receive direct/personal "
"notifications from you."
msgstr ""
#: src/Model/Contact.php:2962
#: src/Model/Contact.php:2974
msgid "Unable to retrieve contact information."
msgstr ""
@ -2940,12 +2939,12 @@ msgid "l F d, Y \\@ g:i A \\G\\M\\TP (e)"
msgstr ""
#: src/Model/Event.php:75 src/Model/Event.php:92 src/Model/Event.php:469
#: src/Model/Event.php:943
#: src/Model/Event.php:940
msgid "Starts:"
msgstr ""
#: src/Model/Event.php:78 src/Model/Event.php:98 src/Model/Event.php:470
#: src/Model/Event.php:947
#: src/Model/Event.php:944
msgid "Finishes:"
msgstr ""
@ -3005,32 +3004,32 @@ msgstr ""
msgid "Delete event"
msgstr ""
#: src/Model/Event.php:899 src/Module/Debug/Localtime.php:38
#: src/Model/Event.php:896 src/Module/Debug/Localtime.php:38
msgid "l F d, Y \\@ g:i A"
msgstr ""
#: src/Model/Event.php:900
#: src/Model/Event.php:897
msgid "D g:i A"
msgstr ""
#: src/Model/Event.php:901
#: src/Model/Event.php:898
msgid "g:i A"
msgstr ""
#: src/Model/Event.php:962 src/Model/Event.php:964
#: src/Model/Event.php:959 src/Model/Event.php:961
msgid "Show map"
msgstr ""
#: src/Model/Event.php:963
#: src/Model/Event.php:960
msgid "Hide map"
msgstr ""
#: src/Model/Event.php:1056
#: src/Model/Event.php:1053
#, php-format
msgid "%s's birthday"
msgstr ""
#: src/Model/Event.php:1057
#: src/Model/Event.php:1054
#, php-format
msgid "Happy Birthday %s"
msgstr ""
@ -3084,61 +3083,61 @@ msgstr ""
msgid "Detected languages in this post:\\n%s"
msgstr ""
#: src/Model/Item.php:2923
#: src/Model/Item.php:2918
msgid "activity"
msgstr ""
#: src/Model/Item.php:2925
#: src/Model/Item.php:2920
msgid "comment"
msgstr ""
#: src/Model/Item.php:2928
#: src/Model/Item.php:2923
msgid "post"
msgstr ""
#: src/Model/Item.php:3069
#: src/Model/Item.php:3064
#, php-format
msgid "Content warning: %s"
msgstr ""
#: src/Model/Item.php:3453
#: src/Model/Item.php:3448
msgid "bytes"
msgstr ""
#: src/Model/Item.php:3484
#: src/Model/Item.php:3479
#, php-format
msgid "%2$s (%3$d%%, %1$d vote)"
msgid_plural "%2$s (%3$d%%, %1$d votes)"
msgstr[0] ""
msgstr[1] ""
#: src/Model/Item.php:3486
#: src/Model/Item.php:3481
#, php-format
msgid "%2$s (%1$d vote)"
msgid_plural "%2$s (%1$d votes)"
msgstr[0] ""
msgstr[1] ""
#: src/Model/Item.php:3491
#: src/Model/Item.php:3486
#, php-format
msgid "%d voter. Poll end: %s"
msgid_plural "%d voters. Poll end: %s"
msgstr[0] ""
msgstr[1] ""
#: src/Model/Item.php:3493
#: src/Model/Item.php:3488
#, php-format
msgid "%d voter."
msgid_plural "%d voters."
msgstr[0] ""
msgstr[1] ""
#: src/Model/Item.php:3495
#: src/Model/Item.php:3490
#, php-format
msgid "Poll end: %s"
msgstr ""
#: src/Model/Item.php:3529 src/Model/Item.php:3530
#: src/Model/Item.php:3524 src/Model/Item.php:3525
msgid "View on separate page"
msgstr ""
@ -3169,133 +3168,133 @@ msgstr ""
msgid "About:"
msgstr ""
#: src/Model/Profile.php:463
#: src/Model/Profile.php:467
msgid "Atom feed"
msgstr ""
#: src/Model/Profile.php:470
#: src/Model/Profile.php:474
msgid "This website has been verified to belong to the same person."
msgstr ""
#: src/Model/Profile.php:507
#: src/Model/Profile.php:511
msgid "F d"
msgstr ""
#: src/Model/Profile.php:571 src/Model/Profile.php:660
#: src/Model/Profile.php:575 src/Model/Profile.php:664
msgid "[today]"
msgstr ""
#: src/Model/Profile.php:580
#: src/Model/Profile.php:584
msgid "Birthday Reminders"
msgstr ""
#: src/Model/Profile.php:581
#: src/Model/Profile.php:585
msgid "Birthdays this week:"
msgstr ""
#: src/Model/Profile.php:609
#: src/Model/Profile.php:613
msgid "g A l F d"
msgstr ""
#: src/Model/Profile.php:647
#: src/Model/Profile.php:651
msgid "[No description]"
msgstr ""
#: src/Model/Profile.php:673
#: src/Model/Profile.php:677
msgid "Event Reminders"
msgstr ""
#: src/Model/Profile.php:674
#: src/Model/Profile.php:678
msgid "Upcoming events the next 7 days:"
msgstr ""
#: src/Model/Profile.php:869
#: src/Model/Profile.php:873
#, php-format
msgid "OpenWebAuth: %1$s welcomes %2$s"
msgstr ""
#: src/Model/Profile.php:1009
#: src/Model/Profile.php:1013
msgid "Hometown:"
msgstr ""
#: src/Model/Profile.php:1010
#: src/Model/Profile.php:1014
msgid "Marital Status:"
msgstr ""
#: src/Model/Profile.php:1011
#: src/Model/Profile.php:1015
msgid "With:"
msgstr ""
#: src/Model/Profile.php:1012
#: src/Model/Profile.php:1016
msgid "Since:"
msgstr ""
#: src/Model/Profile.php:1013
#: src/Model/Profile.php:1017
msgid "Sexual Preference:"
msgstr ""
#: src/Model/Profile.php:1014
#: src/Model/Profile.php:1018
msgid "Political Views:"
msgstr ""
#: src/Model/Profile.php:1015
#: src/Model/Profile.php:1019
msgid "Religious Views:"
msgstr ""
#: src/Model/Profile.php:1016
#: src/Model/Profile.php:1020
msgid "Likes:"
msgstr ""
#: src/Model/Profile.php:1017
#: src/Model/Profile.php:1021
msgid "Dislikes:"
msgstr ""
#: src/Model/Profile.php:1018
#: src/Model/Profile.php:1022
msgid "Title/Description:"
msgstr ""
#: src/Model/Profile.php:1019 src/Module/Admin/Summary.php:217
#: src/Model/Profile.php:1023 src/Module/Admin/Summary.php:217
#: src/Module/Moderation/Summary.php:77
msgid "Summary"
msgstr ""
#: src/Model/Profile.php:1020
#: src/Model/Profile.php:1024
msgid "Musical interests"
msgstr ""
#: src/Model/Profile.php:1021
#: src/Model/Profile.php:1025
msgid "Books, literature"
msgstr ""
#: src/Model/Profile.php:1022
#: src/Model/Profile.php:1026
msgid "Television"
msgstr ""
#: src/Model/Profile.php:1023
#: src/Model/Profile.php:1027
msgid "Film/dance/culture/entertainment"
msgstr ""
#: src/Model/Profile.php:1024
#: src/Model/Profile.php:1028
msgid "Hobbies/Interests"
msgstr ""
#: src/Model/Profile.php:1025
#: src/Model/Profile.php:1029
msgid "Love/romance"
msgstr ""
#: src/Model/Profile.php:1026
#: src/Model/Profile.php:1030
msgid "Work/employment"
msgstr ""
#: src/Model/Profile.php:1027
#: src/Model/Profile.php:1031
msgid "School/education"
msgstr ""
#: src/Model/Profile.php:1028
#: src/Model/Profile.php:1032
msgid "Contact information and Social Networks"
msgstr ""
#: src/Model/User.php:212 src/Model/User.php:1100
#: src/Model/User.php:212 src/Model/User.php:1102
msgid "SERIOUS ERROR: Generation of security keys failed."
msgstr ""
@ -3307,134 +3306,134 @@ msgstr ""
msgid "Not enough information to authenticate"
msgstr ""
#: src/Model/User.php:750
#: src/Model/User.php:752
msgid "Password can't be empty"
msgstr ""
#: src/Model/User.php:792
#: src/Model/User.php:794
msgid "Empty passwords are not allowed."
msgstr ""
#: src/Model/User.php:796
#: src/Model/User.php:798
msgid ""
"The new password has been exposed in a public data dump, please choose "
"another."
msgstr ""
#: src/Model/User.php:800
#: src/Model/User.php:802
msgid "The password length is limited to 72 characters."
msgstr ""
#: src/Model/User.php:804
#: src/Model/User.php:806
msgid ""
"The password can't contain accentuated letters, white spaces or colons (:)"
msgstr ""
#: src/Model/User.php:983
#: src/Model/User.php:985
msgid "Passwords do not match. Password unchanged."
msgstr ""
#: src/Model/User.php:990
#: src/Model/User.php:992
msgid "An invitation is required."
msgstr ""
#: src/Model/User.php:994
#: src/Model/User.php:996
msgid "Invitation could not be verified."
msgstr ""
#: src/Model/User.php:1002
#: src/Model/User.php:1004
msgid "Invalid OpenID url"
msgstr ""
#: src/Model/User.php:1015 src/Security/Authentication.php:241
#: src/Model/User.php:1017 src/Security/Authentication.php:241
msgid ""
"We encountered a problem while logging in with the OpenID you provided. "
"Please check the correct spelling of the ID."
msgstr ""
#: src/Model/User.php:1015 src/Security/Authentication.php:241
#: src/Model/User.php:1017 src/Security/Authentication.php:241
msgid "The error message was:"
msgstr ""
#: src/Model/User.php:1021
#: src/Model/User.php:1023
msgid "Please enter the required information."
msgstr ""
#: src/Model/User.php:1035
#: src/Model/User.php:1037
#, php-format
msgid ""
"system.username_min_length (%s) and system.username_max_length (%s) are "
"excluding each other, swapping values."
msgstr ""
#: src/Model/User.php:1042
#: src/Model/User.php:1044
#, php-format
msgid "Username should be at least %s character."
msgid_plural "Username should be at least %s characters."
msgstr[0] ""
msgstr[1] ""
#: src/Model/User.php:1046
#: src/Model/User.php:1048
#, php-format
msgid "Username should be at most %s character."
msgid_plural "Username should be at most %s characters."
msgstr[0] ""
msgstr[1] ""
#: src/Model/User.php:1054
#: src/Model/User.php:1056
msgid "That doesn't appear to be your full (First Last) name."
msgstr ""
#: src/Model/User.php:1059
#: src/Model/User.php:1061
msgid "Your email domain is not among those allowed on this site."
msgstr ""
#: src/Model/User.php:1063
#: src/Model/User.php:1065
msgid "Not a valid email address."
msgstr ""
#: src/Model/User.php:1066
#: src/Model/User.php:1068
msgid "The nickname was blocked from registration by the nodes admin."
msgstr ""
#: src/Model/User.php:1070 src/Model/User.php:1076
#: src/Model/User.php:1072 src/Model/User.php:1078
msgid "Cannot use that email."
msgstr ""
#: src/Model/User.php:1082
#: src/Model/User.php:1084
msgid "Your nickname can only contain a-z, 0-9 and _."
msgstr ""
#: src/Model/User.php:1090 src/Model/User.php:1147
#: src/Model/User.php:1092 src/Model/User.php:1149
msgid "Nickname is already registered. Please choose another."
msgstr ""
#: src/Model/User.php:1134 src/Model/User.php:1138
#: src/Model/User.php:1136 src/Model/User.php:1140
msgid "An error occurred during registration. Please try again."
msgstr ""
#: src/Model/User.php:1161
#: src/Model/User.php:1163
msgid "An error occurred creating your default profile. Please try again."
msgstr ""
#: src/Model/User.php:1168
#: src/Model/User.php:1170
msgid "An error occurred creating your self contact. Please try again."
msgstr ""
#: src/Model/User.php:1173
#: src/Model/User.php:1175
msgid "Friends"
msgstr ""
#: src/Model/User.php:1177
#: src/Model/User.php:1179
msgid ""
"An error occurred creating your default contact group. Please try again."
msgstr ""
#: src/Model/User.php:1216
#: src/Model/User.php:1218
msgid "Profile Photos"
msgstr ""
#: src/Model/User.php:1409
#: src/Model/User.php:1411
#, php-format
msgid ""
"\n"
@ -3442,7 +3441,7 @@ msgid ""
"\t\t\tthe administrator of %2$s has set up an account for you."
msgstr ""
#: src/Model/User.php:1412
#: src/Model/User.php:1414
#, php-format
msgid ""
"\n"
@ -3480,12 +3479,12 @@ msgid ""
"\t\tThank you and welcome to %4$s."
msgstr ""
#: src/Model/User.php:1445 src/Model/User.php:1552
#: src/Model/User.php:1447 src/Model/User.php:1554
#, php-format
msgid "Registration details for %s"
msgstr ""
#: src/Model/User.php:1465
#: src/Model/User.php:1467
#, php-format
msgid ""
"\n"
@ -3501,12 +3500,12 @@ msgid ""
"\t\t"
msgstr ""
#: src/Model/User.php:1484
#: src/Model/User.php:1486
#, php-format
msgid "Registration at %s"
msgstr ""
#: src/Model/User.php:1508
#: src/Model/User.php:1510
#, php-format
msgid ""
"\n"
@ -3515,7 +3514,7 @@ msgid ""
"\t\t\t"
msgstr ""
#: src/Model/User.php:1516
#: src/Model/User.php:1518
#, php-format
msgid ""
"\n"
@ -3583,7 +3582,7 @@ msgstr ""
#: src/Module/Admin/Logs/View.php:84 src/Module/Admin/Queue.php:72
#: src/Module/Admin/Site.php:437 src/Module/Admin/Storage.php:138
#: src/Module/Admin/Summary.php:216 src/Module/Admin/Themes/Details.php:90
#: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:75
#: src/Module/Admin/Themes/Index.php:111 src/Module/Admin/Tos.php:77
#: src/Module/Moderation/Users/Create.php:61
#: src/Module/Moderation/Users/Pending.php:96
msgid "Administration"
@ -3620,7 +3619,7 @@ msgstr ""
#: src/Module/Admin/Addons/Index.php:69 src/Module/Admin/Features.php:87
#: src/Module/Admin/Logs/Settings.php:81 src/Module/Admin/Site.php:440
#: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:83
#: src/Module/Admin/Themes/Index.php:113 src/Module/Admin/Tos.php:86
#: src/Module/Settings/Account.php:560 src/Module/Settings/Addons.php:81
#: src/Module/Settings/Connectors.php:159
#: src/Module/Settings/Connectors.php:244
@ -5154,21 +5153,21 @@ msgstr ""
msgid "[Unsupported]"
msgstr ""
#: src/Module/Admin/Tos.php:77
#: src/Module/Admin/Tos.php:79
msgid "Display Terms of Service"
msgstr ""
#: src/Module/Admin/Tos.php:77
#: src/Module/Admin/Tos.php:79
msgid ""
"Enable the Terms of Service page. If this is enabled a link to the terms "
"will be added to the registration form and the general information page."
msgstr ""
#: src/Module/Admin/Tos.php:78
#: src/Module/Admin/Tos.php:80
msgid "Display Privacy Statement"
msgstr ""
#: src/Module/Admin/Tos.php:78
#: src/Module/Admin/Tos.php:80
#, php-format
msgid ""
"Show some informations regarding the needed information to operate the node "
@ -5176,20 +5175,28 @@ msgid ""
"\">EU-GDPR</a>."
msgstr ""
#: src/Module/Admin/Tos.php:79
#: src/Module/Admin/Tos.php:81
msgid "Privacy Statement Preview"
msgstr ""
#: src/Module/Admin/Tos.php:81
#: src/Module/Admin/Tos.php:83
msgid "The Terms of Service"
msgstr ""
#: src/Module/Admin/Tos.php:81
#: src/Module/Admin/Tos.php:83
msgid ""
"Enter the Terms of Service for your node here. You can use BBCode. Headers "
"of sections should be [h2] and below."
msgstr ""
#: src/Module/Admin/Tos.php:84
msgid "The rules"
msgstr ""
#: src/Module/Admin/Tos.php:84
msgid "Enter your system rules here. Each line represents one rule."
msgstr ""
#: src/Module/Api/ApiResponse.php:279
#, php-format
msgid "API endpoint %s %s is not implemented"
@ -5484,7 +5491,7 @@ msgid "Event Starts:"
msgstr ""
#: src/Module/Calendar/Event/Form.php:209
#: src/Module/Calendar/Event/Form.php:241 src/Module/Debug/Probe.php:59
#: src/Module/Calendar/Event/Form.php:237 src/Module/Debug/Probe.php:59
#: src/Module/Install.php:207 src/Module/Install.php:240
#: src/Module/Install.php:245 src/Module/Install.php:264
#: src/Module/Install.php:275 src/Module/Install.php:280
@ -5515,14 +5522,17 @@ msgstr ""
msgid "Event Finishes:"
msgstr ""
#: src/Module/Calendar/Event/Form.php:237 src/Module/Profile/Profile.php:164
#: src/Module/Settings/Profile/Index.php:247
msgid "Description:"
#: src/Module/Calendar/Event/Form.php:237
#: src/Module/Calendar/Event/Form.php:243
msgid "Title (BBCode not allowed)"
msgstr ""
#: src/Module/Calendar/Event/Form.php:239
msgid "Description (BBCode allowed)"
msgstr ""
#: src/Module/Calendar/Event/Form.php:241
#: src/Module/Calendar/Event/Form.php:243
msgid "Title:"
msgid "Location (BBCode not allowed)"
msgstr ""
#: src/Module/Calendar/Event/Form.php:244
@ -8231,6 +8241,10 @@ msgid_plural "%d years old"
msgstr[0] ""
msgstr[1] ""
#: src/Module/Profile/Profile.php:164 src/Module/Settings/Profile/Index.php:247
msgid "Description:"
msgstr ""
#: src/Module/Profile/Profile.php:226
msgid "Forums:"
msgstr ""
@ -10238,7 +10252,7 @@ msgstr ""
msgid "Exception thrown in %s:%d"
msgstr ""
#: src/Module/Tos.php:57 src/Module/Tos.php:91
#: src/Module/Tos.php:57 src/Module/Tos.php:104
msgid ""
"At the time of registration, and for providing communications between the "
"user account and their contacts, the user has to provide a display name (pen "
@ -10251,14 +10265,14 @@ msgid ""
"settings, it is not necessary for communication."
msgstr ""
#: src/Module/Tos.php:58 src/Module/Tos.php:92
#: src/Module/Tos.php:58 src/Module/Tos.php:105
msgid ""
"This data is required for communication and is passed on to the nodes of the "
"communication partners and is stored there. Users can enter additional "
"private data that may be transmitted to the communication partners accounts."
msgstr ""
#: src/Module/Tos.php:59 src/Module/Tos.php:93
#: src/Module/Tos.php:59 src/Module/Tos.php:106
#, php-format
msgid ""
"At any point in time a logged in user can export their account data from the "
@ -10269,10 +10283,14 @@ msgid ""
"communication partners."
msgstr ""
#: src/Module/Tos.php:62 src/Module/Tos.php:90
#: src/Module/Tos.php:62 src/Module/Tos.php:103
msgid "Privacy Statement"
msgstr ""
#: src/Module/Tos.php:100
msgid "Rules"
msgstr ""
#: src/Module/Update/Display.php:45
msgid "Parameter uri_id is missing."
msgstr ""

View file

@ -6,6 +6,7 @@
{{include file="field_checkbox.tpl" field=$displaytos}}
{{include file="field_checkbox.tpl" field=$displayprivstatement}}
{{include file="field_textarea.tpl" field=$tostext}}
{{include file="field_textarea.tpl" field=$tosrules}}
<div class="submit"><input type="submit" name="page_tos" value="{{$submit}}" /></div>
</form>
<h2>{{$preview}}</h2>

View file

@ -2,6 +2,11 @@
{{$tostext nofilter}}
{{if $rules}}
<h2>{{$rulestitle}}</h2>
{{$rules nofilter}}
{{/if}}
{{if $displayprivstatement}}
<h2>{{$privstatementtitle nofilter}}</h2>
<p>{{$privacy_operate nofilter}}</p>

View file

@ -89,7 +89,7 @@
{{* The contact description (e.g. Name, Network, kind of connection and so on *}}
<div class="contact-entry-desc">
<div class="contact-entry-name" id="contact-entry-name-{{$contact.id}}">
<h4 class="media-heading"><a href="{{if $contact.photo_menu.edit}}{{$contact.photo_menu.edit.1}}{{else}}{{$contact.url}}{{/if}}">{{$contact.name}}</a>
<h4 class="media-heading"><a href="{{if !empty($contact.photo_menu.edit)}}{{$contact.photo_menu.edit.1}}{{else}}{{$contact.url}}{{/if}}">{{$contact.name}}</a>
{{if $contact.account_type}} <small class="contact-entry-details" id="contact-entry-accounttype-{{$contact.id}}">({{$contact.account_type}})</small>{{/if}}
{{if $contact.account_type == 'Forum'}}<i class="fa fa-comments-o" aria-hidden="true"></i>{{/if}}
{{* @todo this needs some changing in core because $contact.account_type contains a translated string which may notbe the same in every language *}}