Merge remote-tracking branch 'upstream/develop' into search

This commit is contained in:
Michael 2023-05-17 01:30:56 +00:00
commit 97456ff205
20 changed files with 377 additions and 180 deletions

View file

@ -129,20 +129,6 @@ class Update
DI::lock()->release('dbupdate', true);
}
if (!DBStructure::existsTable('config')) {
DBA::e(<<<EOF
CREATE TABLE IF NOT EXISTS `config` (
`id` int unsigned NOT NULL auto_increment COMMENT '',
`cat` varbinary(50) NOT NULL DEFAULT '' COMMENT 'The category of the entry',
`k` varbinary(50) NOT NULL DEFAULT '' COMMENT 'The key of the entry',
`v` mediumtext COMMENT '',
PRIMARY KEY(`id`),
UNIQUE INDEX `cat_k` (`cat`,`k`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='main configuration storage';
EOF
);
}
$build = DI::config()->get('system', 'build');
if (empty($build)) {

View file

@ -151,8 +151,8 @@ class Cron
// We are acquiring the two locks from the worker to avoid locking problems
if (DI::lock()->acquire(Worker::LOCK_PROCESS, 10)) {
if (DI::lock()->acquire(Worker::LOCK_WORKER, 10)) {
DBA::e("OPTIMIZE TABLE `workerqueue`");
DBA::e("OPTIMIZE TABLE `process`");
DBA::optimizeTable('workerqueue');
DBA::optimizeTable('process');
DI::lock()->release(Worker::LOCK_WORKER);
}
DI::lock()->release(Worker::LOCK_PROCESS);
@ -197,7 +197,7 @@ class Cron
// Optimizing this table only last seconds
if (DI::config()->get('system', 'optimize_tables')) {
Logger::info('Optimize start');
DBA::e("OPTIMIZE TABLE `post-delivery`");
DBA::optimizeTable('post-delivery');
Logger::info('Optimize end');
}
}

View file

@ -821,6 +821,27 @@ class DBA
return DI::dba()->processlist();
}
/**
* Optimizes tables
*
* @param string $table a given table
*
* @return bool True, if successfully optimized, otherwise false
* @throws \Exception
*/
public static function optimizeTable(string $table): bool
{
return DI::dba()->optimizeTable($table);
}
/**
* Kill sleeping database processes
*/
public static function deleteSleepingProcesses()
{
DI::dba()->deleteSleepingProcesses();
}
/**
* Fetch a database variable
*

View file

@ -57,6 +57,18 @@ class DBStructure
echo DI::l10n()->t('The database version had been set to %s.', $version);
}
/**
* Drops a specific table
*
* @param string $table the table name
*
* @return bool true if possible, otherwise false
*/
public static function dropTable(string $table): bool
{
return DBA::isResult(DBA::e('DROP TABLE ' . DBA::quoteIdentifier($table) . ';'));
}
/**
* Drop unused tables
*
@ -94,8 +106,7 @@ class DBStructure
$sql = 'DROP TABLE ' . DBA::quoteIdentifier($table) . ';';
echo $sql . "\n";
$result = DBA::e($sql);
if (!DBA::isResult($result)) {
if (!static::dropTable($table)) {
self::printUpdateError($sql);
}
} else {

View file

@ -1357,6 +1357,15 @@ class Database
}
$fields = $this->castFields($table, $fields);
$direct_fields = [];
foreach ($fields as $key => $value) {
if (is_numeric($key)) {
$direct_fields[] = $value;
unset($fields[$key]);
}
}
$table_string = DBA::buildTableString([$table]);
@ -1369,7 +1378,8 @@ class Database
}
$sql = "UPDATE " . $ignore . $table_string . " SET "
. implode(" = ?, ", array_map([DBA::class, 'quoteIdentifier'], array_keys($fields))) . " = ?"
. ((count($fields) > 0) ? implode(" = ?, ", array_map([DBA::class, 'quoteIdentifier'], array_keys($fields))) . " = ?" : "")
. ((count($direct_fields) > 0) ? ((count($fields) > 0) ? " , " : "") . implode(" , ", $direct_fields) : "")
. $condition_string;
// Combines the updated fields parameter values with the condition parameter values
@ -1758,6 +1768,37 @@ class Database
return (['list' => $statelist, 'amount' => $processes]);
}
/**
* Optimizes tables
*
* @param string $table a given table
*
* @return bool True, if successfully optimized, otherwise false
* @throws \Exception
*/
public function optimizeTable(string $table): bool
{
return $this->e("OPTIMIZE TABLE " . DBA::buildTableString([$table])) !== false;
}
/**
* Kill sleeping database processes
*
* @return void
*/
public function deleteSleepingProcesses()
{
$processes = $this->p("SHOW FULL PROCESSLIST");
while ($process = $this->fetch($processes)) {
if (($process['Command'] != 'Sleep') || ($process['Time'] < 300) || ($process['db'] != $this->databaseName())) {
continue;
}
$this->e("KILL ?", $process['Id']);
}
$this->close($processes);
}
/**
* Fetch a database variable
*

View file

@ -41,19 +41,21 @@ class DatabaseException extends Exception
* @param string $message The Database error message.
* @param int $code The Database error code.
* @param string $query The Database error query.
* @param Throwable $previous [optional] The previous throwable used for the exception chaining.
* @param Throwable|null $previous [optional] The previous throwable used for the exception chaining.
*/
public function __construct(string $message, int $code, string $query, Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
parent::__construct(sprintf('"%s" at "%s"', $message, $query) , $code, $previous);
$this->query = $query;
}
/**
* {@inheritDoc}
* Returns the query, which caused the exception
*
* @return string
*/
public function __toString()
public function getQuery(): string
{
return sprintf('Database error %d "%s" at "%s"', $this->message, $this->code, $this->query);
return $this->query;
}
}

View file

@ -88,7 +88,10 @@ final class DeliveryQueueItem extends \Friendica\BaseRepository
public function remove(Entity\DeliveryQueueItem $deliveryQueueItem): bool
{
return $this->db->delete(self::$table_name, ['uri-id' => $deliveryQueueItem->postUriId, 'gsid' => $deliveryQueueItem->targetServerId]);
return $this->db->delete(self::$table_name, [
'uri-id' => $deliveryQueueItem->postUriId,
'gsid' => $deliveryQueueItem->targetServerId
]);
}
public function removeFailedByServerId(int $gsid, int $failedThreshold): bool
@ -98,16 +101,17 @@ final class DeliveryQueueItem extends \Friendica\BaseRepository
public function incrementFailed(Entity\DeliveryQueueItem $deliveryQueueItem): bool
{
return $this->db->e("
UPDATE " . DBA::buildTableString([self::$table_name]) . "
SET `failed` = `failed` + 1
WHERE `uri-id` = ? AND `gsid` = ?",
$deliveryQueueItem->postUriId, $deliveryQueueItem->targetServerId
);
return $this->db->update(self::$table_name, [
"`failed` = `failed` + 1"
], [
"`uri-id` = ? AND `gsid` = ?",
$deliveryQueueItem->postUriId,
$deliveryQueueItem->targetServerId
]);
}
public function optimizeStorage(): bool
{
return $this->db->e("OPTIMIZE TABLE " . DBA::buildTableString([self::$table_name]));
return $this->db->optimizeTable(self::$table_name);
}
}

View file

@ -2682,6 +2682,8 @@ class Contact
return true;
}
$has_local_data = self::hasLocalData($id, $contact);
$uid = $contact['uid'];
unset($contact['uid']);
@ -2702,9 +2704,7 @@ class Contact
$updated = DateTimeFormat::utcNow();
$has_local_data = self::hasLocalData($id, $contact);
if (!Probe::isProbable($ret['network'])) {
if (!Probe::isProbable($ret['network']) && !Probe::isProbable($contact['network'])) {
// Periodical checks are only done on federated contacts
$failed_next_update = null;
$success_next_update = null;

View file

@ -78,7 +78,7 @@ class Delivery
*/
public static function incrementFailed(int $uri_id, string $inbox)
{
return DBA::e('UPDATE `post-delivery` SET `failed` = `failed` + 1 WHERE `uri-id` = ? AND `inbox-id` = ?', $uri_id, ItemURI::getIdByURI($inbox));
return DBA::update('post-delivery', ["`failed` = `failed` + 1"], ['uri-id' => $uri_id, 'inbox-id' => ItemURI::getIdByURI($inbox)]);
}
public static function selectForInbox(string $inbox)

View file

@ -82,27 +82,27 @@ class DeliveryData
*/
public static function incrementQueueDone(int $uri_id, int $protocol = 0)
{
$sql = '';
$increments = ["`queue_done` = `queue_done` + 1"];
switch ($protocol) {
case self::ACTIVITYPUB:
$sql = ", `activitypub` = `activitypub` + 1";
$increments[] = "`activitypub` = `activitypub` + 1";
break;
case self::DFRN:
$sql = ", `dfrn` = `dfrn` + 1";
$increments[] = "`dfrn` = `dfrn` + 1";
break;
case self::LEGACY_DFRN:
$sql = ", `legacy_dfrn` = `legacy_dfrn` + 1";
$increments[] = "`legacy_dfrn` = `legacy_dfrn` + 1";
break;
case self::DIASPORA:
$sql = ", `diaspora` = `diaspora` + 1";
$increments[] = "`diaspora` = `diaspora` + 1";
break;
case self::OSTATUS:
$sql = ", `ostatus` = `ostatus` + 1";
$increments[] = "`ostatus` = `ostatus` + 1";
break;
}
return DBA::e('UPDATE `post-delivery-data` SET `queue_done` = `queue_done` + 1' . $sql . ' WHERE `uri-id` = ?', $uri_id);
return DBA::update('post-delivery-data', $increments, ['uri-id' => $uri_id]);
}
/**
@ -116,7 +116,7 @@ class DeliveryData
*/
public static function incrementQueueFailed(int $uri_id)
{
return DBA::e('UPDATE `post-delivery-data` SET `queue_failed` = `queue_failed` + 1 WHERE `uri-id` = ?', $uri_id);
return DBA::update('post-delivery-data', ["`queue_failed` = `queue_failed` + 1"], ['uri-id' => $uri_id]);
}
/**
@ -129,7 +129,7 @@ class DeliveryData
*/
public static function incrementQueueCount(int $uri_id, int $increment = 1)
{
return DBA::e('UPDATE `post-delivery-data` SET `queue_count` = `queue_count` + ? WHERE `uri-id` = ?', $increment, $uri_id);
return DBA::update('post-delivery-data', ["`queue_count` = `queue_count` + $increment"], ['uri-id' => $uri_id]);
}
/**

View file

@ -21,6 +21,7 @@
namespace Friendica\Module;
use Exception;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Core\L10n;
@ -30,7 +31,6 @@ use Friendica\Database\Database;
use Friendica\Model\Contact;
use Friendica\Model\User;
use Friendica\Network\HTTPClient\Capability\ICanSendHttpRequests;
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
use Friendica\Util\HTTPSignature;
use Friendica\Util\Profiler;
@ -65,120 +65,102 @@ class Magic extends BaseModule
protected function rawContent(array $request = [])
{
$this->logger->info('magic module: invoked');
if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
$this->logger->debug('Got a HEAD request');
System::exit();
}
$this->logger->debug('args', ['request' => $_REQUEST]);
$this->logger->debug('Invoked', ['request' => $request]);
$addr = $request['addr'] ?? '';
$dest = $request['dest'] ?? '';
$bdest = $request['bdest'] ?? '';
$owa = intval($request['owa'] ?? 0);
$cid = 0;
// bdest is preferred as it is hex-encoded and can survive url rewrite and argument parsing
if (!empty($bdest)) {
$dest = hex2bin($bdest);
$this->logger->info('bdest detected. ', ['dest' => $dest]);
$this->logger->debug('bdest detected', ['dest' => $dest]);
}
if (!empty($addr)) {
$cid = Contact::getIdForURL($addr);
} elseif (!empty($dest)) {
$cid = Contact::getIdForURL($dest);
if ($addr ?: $dest) {
$contact = Contact::getByURL($addr ?: $dest);
}
$this->logger->info('Contact ID: ', ['cid' => $cid]);
$contact = false;
if (!$cid) {
$this->logger->info('No contact record found', $_REQUEST);
if (empty($contact)) {
if (!$owa) {
// @TODO Finding a more elegant possibility to redirect to either internal or external URL
$this->logger->info('No contact record found, no oWA, redirecting to destination.', ['request' => $request, 'server' => $_SERVER, 'dest' => $dest]);
$this->app->redirect($dest);
}
} else {
$contact = $this->dba->selectFirst('contact', ['id', 'nurl', 'url'], ['id' => $cid]);
// Redirect if the contact is already authenticated on this site.
if ($this->app->getContactId() && strpos($contact['nurl'], Strings::normaliseLink($this->baseUrl)) !== false) {
$this->logger->info('Contact is already authenticated');
$this->logger->info('Contact is already authenticated, redirecting to destination.', ['dest' => $dest]);
System::externalRedirect($dest);
}
$this->logger->info('Contact URL: ', ['url' => $contact['url']]);
$this->logger->debug('Contact found', ['url' => $contact['url']]);
}
if (!$this->userSession->getLocalUserId() || !$owa) {
$this->logger->notice('Not logged in or not OWA, redirecting to destination.', ['uid' => $this->userSession->getLocalUserId(), 'owa' => $owa, 'dest' => $dest]);
$this->app->redirect($dest);
}
// OpenWebAuth
if ($this->userSession->getLocalUserId() && $owa) {
$this->logger->info('Checking OWA now');
$user = User::getById($this->userSession->getLocalUserId());
$owner = User::getOwnerDataById($this->userSession->getLocalUserId());
$basepath = false;
if (!empty($contact)) {
$this->logger->info('Contact found - trying friendica style basepath extraction');
// Extract the basepath
// NOTE: we need another solution because this does only work
// for friendica contacts :-/ . We should have the basepath
// of a contact also in the contact table.
$contact_url = $contact['url'];
if (!(strpos($contact_url, '/profile/') === false)) {
$exp = explode('/profile/', $contact['url']);
$basepath = $exp[0];
$this->logger->info('Basepath: ', ['basepath' => $basepath]);
} else {
$this->logger->info('Not possible to extract basepath in friendica style');
}
}
if (!$basepath) {
// For the rest of the OpenWebAuth-enabled Fediverse
$parsed = parse_url($dest);
$this->logger->info('Parsed URL: ', ['parsed URL' => $parsed]);
if (!$parsed) {
$gserver = $this->dba->selectFirst('gserver', ['url'], ['id' => $contact['gsid']]);
if (empty($gserver)) {
$this->logger->notice('Server not found, redirecting to destination.', ['gsid' => $contact['gsid'], 'dest' => $dest]);
System::externalRedirect($dest);
}
$basepath = $parsed['scheme'] . '://' . $parsed['host'] . (isset($parsed['port']) ? ':' . $parsed['port'] : '');
}
$accept_headers = ['application/x-dfrn+json', 'application/x-zot+json'];
$basepath = $gserver['url'];
$header = [
'Accept' => $accept_headers,
'Accept' => ['application/x-dfrn+json', 'application/x-zot+json'],
'X-Open-Web-Auth' => [Strings::getRandomHex()],
];
// Create a header that is signed with the local users private key.
$header = HTTPSignature::createSig(
$header,
$user['prvkey'],
'acct:' . $user['nickname'] . '@' . $this->baseUrl->getHost() . ($this->baseUrl->getPath() ? '/' . $this->baseUrl->getPath() : '')
$owner['prvkey'],
'acct:' . $owner['addr']
);
$this->logger->info('Headers: ', ['headers' => $header]);
$this->logger->info('Fetch from remote system', ['basepath' => $basepath, 'headers' => $header]);
// Try to get an authentication token from the other instance.
$curlResult = $this->httpClient->get($basepath . '/owa', HttpClientAccept::DEFAULT, [HttpClientOptions::HEADERS => $header, HttpClientOptions::ACCEPT_CONTENT => $accept_headers]);
try {
$curlResult = $this->httpClient->request('get', $basepath . '/owa', [HttpClientOptions::HEADERS => $header]);
} catch (Exception $exception) {
$this->logger->notice('URL is invalid, redirecting to destination.', ['url' => $basepath, 'error' => $exception, 'dest' => $dest]);
System::externalRedirect($dest);
}
if (!$curlResult->isSuccess()) {
$this->logger->notice('OWA request failed, redirecting to destination.', ['returncode' => $curlResult->getReturnCode(), 'dest' => $dest]);
System::externalRedirect($dest);
}
if ($curlResult->isSuccess()) {
$j = json_decode($curlResult->getBody(), true);
$this->logger->info('Curl result body: ', ['body' => $j]);
if (empty($j) || !$j['success']) {
$this->logger->notice('Invalid JSON, redirecting to destination.', ['json' => $j, 'dest' => $dest]);
$this->app->redirect($dest);
}
if ($j['success']) {
$token = '';
if ($j['encrypted_token']) {
// The token is encrypted. If the local user is really the one the other instance
// thinks he/she is, the token can be decrypted with the local users public key.
openssl_private_decrypt(Strings::base64UrlDecode($j['encrypted_token']), $token, $user['prvkey']);
// thinks they is, the token can be decrypted with the local users public key.
$token = '';
openssl_private_decrypt(Strings::base64UrlDecode($j['encrypted_token']), $token, $owner['prvkey']);
} else {
$token = $j['token'];
}
$args = (strpbrk($dest, '?&') ? '&' : '?') . 'owt=' . $token;
$this->logger->info('Redirecting', ['path' => $dest . $args]);
$this->logger->debug('Redirecting', ['path' => $dest . $args]);
System::externalRedirect($dest . $args);
}
}
System::externalRedirect($dest);
}
// @TODO Finding a more elegant possibility to redirect to either internal or external URL
$this->app->redirect($dest);
}
}

View file

@ -134,6 +134,6 @@ class Notify extends BaseEntity
*/
public static function formatMessage(string $name, string $message): string
{
return str_replace('{0}', '<span class="contactname">' . strip_tags(BBCode::convert($name)) . '</span>', $message);
return str_replace('{0}', '<span class="contactname">' . strip_tags(BBCode::convert($name)) . '</span>', htmlspecialchars($message));
}
}

View file

@ -312,7 +312,7 @@ class Queue
// Optimizing this table only last seconds
if (DI::config()->get('system', 'optimize_tables')) {
Logger::info('Optimize start');
DBA::e("OPTIMIZE TABLE `inbox-entry`");
DBA::optimizeTable('inbox-entry');
Logger::info('Optimize end');
}
}

View file

@ -478,7 +478,7 @@ class Feed
$attachments = [];
$enclosures = $xpath->query("enclosure|' . $atomns . ':link[@rel='enclosure']", $entry);
$enclosures = $xpath->query("enclosure|$atomns:link[@rel='enclosure']", $entry);
if (!empty($enclosures)) {
foreach ($enclosures as $enclosure) {
$href = '';

View file

@ -163,15 +163,6 @@ class Cron
{
Logger::info('Looking for sleeping processes');
$processes = DBA::p("SHOW FULL PROCESSLIST");
while ($process = DBA::fetch($processes)) {
if (($process['Command'] != 'Sleep') || ($process['Time'] < 300) || ($process['db'] != DBA::databaseName())) {
continue;
}
DBA::e("KILL ?", $process['Id']);
Logger::notice('Killed sleeping process', ['id' => $process['Id']]);
}
DBA::close($processes);
DBA::deleteSleepingProcesses();
}
}

View file

@ -40,36 +40,36 @@ class OptimizeTables
Logger::info('Optimize start');
DBA::e("OPTIMIZE TABLE `cache`");
DBA::e("OPTIMIZE TABLE `locks`");
DBA::e("OPTIMIZE TABLE `oembed`");
DBA::e("OPTIMIZE TABLE `parsed_url`");
DBA::e("OPTIMIZE TABLE `session`");
DBA::optimizeTable('cache');
DBA::optimizeTable('locks');
DBA::optimizeTable('oembed');
DBA::optimizeTable('parsed_url');
DBA::optimizeTable('session');
if (DI::config()->get('system', 'optimize_all_tables')) {
DBA::e("OPTIMIZE TABLE `apcontact`");
DBA::e("OPTIMIZE TABLE `contact`");
DBA::e("OPTIMIZE TABLE `contact-relation`");
DBA::e("OPTIMIZE TABLE `conversation`");
DBA::e("OPTIMIZE TABLE `diaspora-contact`");
DBA::e("OPTIMIZE TABLE `diaspora-interaction`");
DBA::e("OPTIMIZE TABLE `fcontact`");
DBA::e("OPTIMIZE TABLE `gserver`");
DBA::e("OPTIMIZE TABLE `gserver-tag`");
DBA::e("OPTIMIZE TABLE `inbox-status`");
DBA::e("OPTIMIZE TABLE `item-uri`");
DBA::e("OPTIMIZE TABLE `notification`");
DBA::e("OPTIMIZE TABLE `notify`");
DBA::e("OPTIMIZE TABLE `photo`");
DBA::e("OPTIMIZE TABLE `post`");
DBA::e("OPTIMIZE TABLE `post-content`");
DBA::e("OPTIMIZE TABLE `post-delivery-data`");
DBA::e("OPTIMIZE TABLE `post-link`");
DBA::e("OPTIMIZE TABLE `post-thread`");
DBA::e("OPTIMIZE TABLE `post-thread-user`");
DBA::e("OPTIMIZE TABLE `post-user`");
DBA::e("OPTIMIZE TABLE `storage`");
DBA::e("OPTIMIZE TABLE `tag`");
DBA::optimizeTable('apcontact');
DBA::optimizeTable('contact');
DBA::optimizeTable('contact-relation');
DBA::optimizeTable('conversation');
DBA::optimizeTable('diaspora-contact');
DBA::optimizeTable('diaspora-interaction');
DBA::optimizeTable('fcontact');
DBA::optimizeTable('gserver');
DBA::optimizeTable('gserver-tag');
DBA::optimizeTable('inbox-status');
DBA::optimizeTable('item-uri');
DBA::optimizeTable('notification');
DBA::optimizeTable('notify');
DBA::optimizeTable('photo');
DBA::optimizeTable('post');
DBA::optimizeTable('post-content');
DBA::optimizeTable('post-delivery-data');
DBA::optimizeTable('post-link');
DBA::optimizeTable('post-thread');
DBA::optimizeTable('post-thread-user');
DBA::optimizeTable('post-user');
DBA::optimizeTable('storage');
DBA::optimizeTable('tag');
}
Logger::info('Optimize end');

View file

@ -35,6 +35,15 @@ return [
'workerqueue',
'mail',
'post-delivery-data',
'gserver' => [
[
'url' => 'https://friendica.local',
'nurl' => 'http://friendica.local',
'register_policy' => 0,
'registered-users' => 0,
'network' => 'unkn',
],
],
// Base test config to avoid notice messages
'user' => [
[

View file

@ -0,0 +1,103 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Test\src\Database;
use Friendica\Core\Config\Util\ConfigFileManager;
use Friendica\Core\Config\ValueObject\Cache;
use Friendica\Test\FixtureTest;
use Friendica\Test\Util\CreateDatabaseTrait;
class DatabaseTest extends FixtureTest
{
use CreateDatabaseTrait;
protected function setUp(): void
{
$this->setUpVfsDir();
parent::setUp();
$this->configCache = new Cache();
$this->configFileManager = new ConfigFileManager($this->root->url(), $this->root->url() . '/config/', $this->root->url() . '/static/');
}
/**
* Test, if directly updating a field is possible
*/
public function testUpdateIncrease()
{
$db = $this->getDbInstance();
self::assertTrue($db->insert('config', ['cat' => 'test', 'k' => 'inc', 'v' => 0]));
self::assertTrue($db->update('config', ["`v` = `v` + 1"], ['cat' => 'test', 'k' => 'inc']));
self::assertEquals(1, $db->selectFirst('config', ['v'], ['cat' => 'test', 'k' => 'inc'])['v']);
}
/**
* Test if combining directly field updates with normal updates is working
*/
public function testUpdateWithField()
{
$db = $this->getDbInstance();
self::assertEquals('https://friendica.local', $db->selectFirst('gserver', ['url'], ['nurl' => 'http://friendica.local'])['url']);
self::assertTrue($db->update('gserver', ['active-week-users' => 0], ['nurl' => 'http://friendica.local']));
self::assertTrue($db->update('gserver', [
'site_name' => 'test', "`registered-users` = `registered-users` + 1",
'info' => 'another test',
"`active-week-users` = `active-week-users` + 2"
], [
'nurl' => 'http://friendica.local'
]));
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
self::assertEquals(2, $db->selectFirst('gserver', ['active-week-users'], ['nurl' => 'http://friendica.local'])['active-week-users']);
self::assertTrue($db->update('gserver', [
'site_name' => 'test', "`registered-users` = `registered-users` + 1",
'info' => 'another test'
], [
'nurl' => 'http://friendica.local'
]));
self::assertEquals(2, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
self::assertTrue($db->update('gserver', [
'site_name' => 'test', "`registered-users` = `registered-users` - 1",
'info' => 'another test'
], [
'nurl' => 'http://friendica.local'
]));
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
}
public function testUpdateWithArray()
{
$db = $this->getDbInstance();
self::assertTrue($db->update('gserver', ['active-week-users' => 0, 'registered-users' => 0], ['nurl' => 'http://friendica.local']));
$fields = ["`registered-users` = `registered-users` + 1"];
$fields[] = "`active-week-users` = `active-week-users` + 2";
self::assertTrue($db->update('gserver', $fields, ['nurl' => 'http://friendica.local']));
self::assertEquals(2, $db->selectFirst('gserver', ['active-week-users'], ['nurl' => 'http://friendica.local'])['active-week-users']);
self::assertEquals(1, $db->selectFirst('gserver', ['registered-users'], ['nurl' => 'http://friendica.local'])['registered-users']);
}
}

View file

@ -0,0 +1,47 @@
<?php
/**
* @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
namespace Friendica\Test\src\Navigation\Notifications\Entity;
use Friendica\Navigation\Notifications\Entity\Notify;
use Friendica\Test\FixtureTest;
class NotifyTest extends FixtureTest
{
public function dataFormatNotify(): array
{
return [
'xss-notify' => [
'name' => 'Whiskers',
'message' => '{0} commented in the thread "If my username causes a pop up in a piece of software, that softwar…" from <script>alert("Tek");</script>',
'assertion' => '<span class="contactname">Whiskers</span> commented in the thread &quot;If my username causes a pop up in a piece of software, that softwar…&quot; from &lt;script&gt;alert(&quot;Tek&quot;);&lt;/script&gt;',
],
];
}
/**
* @dataProvider dataFormatNotify
*/
public function testFormatNotify(string $name, string $message, string $assertion)
{
self::assertEquals($assertion, Notify::formatMessage($name, $message));
}
}

View file

@ -1,4 +1,4 @@
<div class="notif-item {{if !$item_seen}}unseen{{/if}}" {{if $item_seen}}aria-hidden="true"{{/if}}>
<a href="{{$notification.link}}"><img src="{{$notification.image}}" aria-hidden="true" class="notif-image">{{$notification.text nofilter}} <span class="notif-when">{{$notification.ago}}</span></a>
<a href="{{$notification.link}}"><img src="{{$notification.image}}" aria-hidden="true" class="notif-image">{{$notification.text}} <span class="notif-when">{{$notification.ago}}</span></a>
</div>