Merge remote-tracking branch 'upstream/develop' into api
This commit is contained in:
commit
b2d7c4ec43
83 changed files with 1458 additions and 936 deletions
|
@ -69,8 +69,6 @@ if (!DI::mode()->has(App\Mode::MAINTENANCEDISABLED)) {
|
|||
return;
|
||||
}
|
||||
|
||||
DI::baseUrl()->saveByURL(DI::config()->get('system', 'url'));
|
||||
|
||||
$spawn = array_key_exists('s', $options) || array_key_exists('spawn', $options);
|
||||
|
||||
if ($spawn) {
|
||||
|
|
14
composer.lock
generated
14
composer.lock
generated
|
@ -666,6 +666,7 @@
|
|||
"x509",
|
||||
"x690"
|
||||
],
|
||||
"abandoned": true,
|
||||
"time": "2021-12-11T12:41:06+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1244,16 +1245,16 @@
|
|||
},
|
||||
{
|
||||
"name": "level-2/dice",
|
||||
"version": "4.0.3",
|
||||
"version": "4.0.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Level-2/Dice.git",
|
||||
"reference": "3e9a8548398c01e2527110c916a93f6efa17ac9c"
|
||||
"reference": "e04c98d96bcc932a917b2b7e7944887e4839056a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Level-2/Dice/zipball/3e9a8548398c01e2527110c916a93f6efa17ac9c",
|
||||
"reference": "3e9a8548398c01e2527110c916a93f6efa17ac9c",
|
||||
"url": "https://api.github.com/repos/Level-2/Dice/zipball/e04c98d96bcc932a917b2b7e7944887e4839056a",
|
||||
"reference": "e04c98d96bcc932a917b2b7e7944887e4839056a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1263,6 +1264,9 @@
|
|||
"phpunit/phpunit": "^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"patches_applied": []
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Dice\\": "./"
|
||||
|
@ -1286,7 +1290,7 @@
|
|||
"di",
|
||||
"ioc"
|
||||
],
|
||||
"time": "2021-04-20T14:06:06+00:00"
|
||||
"time": "2022-03-28T21:20:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "lightopenid/lightopenid",
|
||||
|
|
|
@ -36,9 +36,11 @@ return [
|
|||
'sitename' => 'Friendica Social Network',
|
||||
'register_policy' => \Friendica\Module\Register::OPEN,
|
||||
'register_text' => '',
|
||||
'hostname' => 'friendica.local',
|
||||
],
|
||||
'system' => [
|
||||
'default_timezone' => 'UTC',
|
||||
'language' => 'en',
|
||||
'url' => 'https://friendica.local',
|
||||
],
|
||||
];
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
-- ------------------------------------------
|
||||
-- Friendica 2023.03-dev (Giant Rhubarb)
|
||||
-- DB_UPDATE_VERSION 1511
|
||||
-- DB_UPDATE_VERSION 1512
|
||||
-- ------------------------------------------
|
||||
|
||||
|
||||
|
|
|
@ -40,5 +40,7 @@ return [
|
|||
'language' => 'en',
|
||||
'basepath' => '/vagrant',
|
||||
'ssl_policy' => \Friendica\App\BaseURL::SSL_POLICY_SELFSIGN,
|
||||
'url' => 'https://192.168.56.10',
|
||||
'urlpath' => '',
|
||||
],
|
||||
];
|
||||
|
|
20
src/App.php
20
src/App.php
|
@ -540,25 +540,6 @@ class App
|
|||
return Core\Theme::getStylesheetPath($this->getCurrentTheme());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base url for use in cmdline programs which don't have
|
||||
* $_SERVER variables
|
||||
*/
|
||||
public function checkURL()
|
||||
{
|
||||
$url = $this->config->get('system', 'url');
|
||||
|
||||
// if the url isn't set or the stored url is radically different
|
||||
// than the currently visited url, store the current value accordingly.
|
||||
// "Radically different" ignores common variations such as http vs https
|
||||
// and www.example.com vs example.com.
|
||||
// We will only change the url to an ip address if there is no existing setting
|
||||
|
||||
if (empty($url) || (!Util\Strings::compareLink($url, $this->baseURL->get())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $this->baseURL->getHostname()))) {
|
||||
$this->config->set('system', 'url', $this->baseURL->get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Frontend App script
|
||||
*
|
||||
|
@ -657,7 +638,6 @@ class App
|
|||
if ($this->mode->isInstall() && $moduleName !== 'install') {
|
||||
$this->baseURL->redirect('install');
|
||||
} else {
|
||||
$this->checkURL();
|
||||
Core\Update::check($this->getBasePath(), false);
|
||||
Core\Addon::loadAddons();
|
||||
Core\Hook::loadHooks();
|
||||
|
|
|
@ -257,109 +257,18 @@ class BaseURL
|
|||
*/
|
||||
public function __construct(IManageConfigValues $config, array $server)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->server = $server;
|
||||
|
||||
$this->determineSchema();
|
||||
$this->checkConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the current config during loading
|
||||
*/
|
||||
public function checkConfig()
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->server = $server;
|
||||
$this->hostname = $this->config->get('config', 'hostname');
|
||||
$this->urlPath = $this->config->get('system', 'urlpath');
|
||||
$this->sslPolicy = $this->config->get('system', 'ssl_policy');
|
||||
$this->urlPath = $this->config->get('system', 'urlpath') ?? '';
|
||||
$this->sslPolicy = $this->config->get('system', 'ssl_policy') ?? static::DEFAULT_SSL_SCHEME;
|
||||
$this->url = $this->config->get('system', 'url');
|
||||
|
||||
if (empty($this->hostname)) {
|
||||
$this->determineHostname();
|
||||
|
||||
if (!empty($this->hostname)) {
|
||||
$this->config->set('config', 'hostname', $this->hostname);
|
||||
}
|
||||
if (empty($this->hostname) || empty($this->url)) {
|
||||
throw new \Exception('Invalid config - Missing system.url or config.hostname');
|
||||
}
|
||||
|
||||
if (!isset($this->urlPath)) {
|
||||
$this->determineURLPath();
|
||||
$this->config->set('system', 'urlpath', $this->urlPath);
|
||||
}
|
||||
|
||||
if (!isset($this->sslPolicy)) {
|
||||
if ($this->scheme == 'https') {
|
||||
$this->sslPolicy = self::SSL_POLICY_FULL;
|
||||
} else {
|
||||
$this->sslPolicy = self::DEFAULT_SSL_SCHEME;
|
||||
}
|
||||
$this->config->set('system', 'ssl_policy', $this->sslPolicy);
|
||||
}
|
||||
|
||||
if (empty($this->url)) {
|
||||
$this->determineBaseUrl();
|
||||
|
||||
if (!empty($this->url)) {
|
||||
$this->config->set('system', 'url', $this->url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the hostname of this node if not set already
|
||||
*/
|
||||
private function determineHostname()
|
||||
{
|
||||
$this->hostname = '';
|
||||
|
||||
if (!empty($this->server['SERVER_NAME'])) {
|
||||
$this->hostname = $this->server['SERVER_NAME'];
|
||||
|
||||
if (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] != 80 && $this->server['SERVER_PORT'] != 443) {
|
||||
$this->hostname .= ':' . $this->server['SERVER_PORT'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out if we are running at the top of a domain or in a sub-directory
|
||||
*/
|
||||
private function determineURLPath()
|
||||
{
|
||||
$this->urlPath = '';
|
||||
|
||||
/*
|
||||
* The automatic path detection in this function is currently deactivated,
|
||||
* see issue https://github.com/friendica/friendica/issues/6679
|
||||
*
|
||||
* The problem is that the function seems to be confused with some url.
|
||||
* These then confuses the detection which changes the url path.
|
||||
*/
|
||||
|
||||
/* Relative script path to the web server root
|
||||
* Not all of those $_SERVER properties can be present, so we do by inverse priority order
|
||||
*/
|
||||
$relative_script_path =
|
||||
($this->server['REDIRECT_URL'] ?? '') ?:
|
||||
($this->server['REDIRECT_URI'] ?? '') ?:
|
||||
($this->server['REDIRECT_SCRIPT_URL'] ?? '') ?:
|
||||
($this->server['SCRIPT_URL'] ?? '') ?:
|
||||
$this->server['REQUEST_URI'] ?? '';
|
||||
|
||||
/* $relative_script_path gives /relative/path/to/friendica/module/parameter
|
||||
* QUERY_STRING gives pagename=module/parameter
|
||||
*
|
||||
* To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING
|
||||
*/
|
||||
if (!empty($relative_script_path)) {
|
||||
// Module
|
||||
if (!empty($this->server['QUERY_STRING'])) {
|
||||
$this->urlPath = trim(dirname($relative_script_path, substr_count(trim($this->server['QUERY_STRING'], '/'), '/') + 1), '/');
|
||||
} else {
|
||||
// Root page
|
||||
$this->urlPath = trim($relative_script_path, '/');
|
||||
}
|
||||
}
|
||||
$this->determineSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -74,7 +74,7 @@ class Request
|
|||
public function __construct(IManageConfigValues $config, array $server = [])
|
||||
{
|
||||
$this->remoteAddress = $this->determineRemoteAddress($config, $server);
|
||||
$this->requestId = $server[static::DEFAULT_REQUEST_ID_HEADER] ?? System::createGUID(8);
|
||||
$this->requestId = $server[static::DEFAULT_REQUEST_ID_HEADER] ?? System::createGUID(8, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -316,7 +316,7 @@ class OEmbed
|
|||
if ($stopoembed == true) {
|
||||
return preg_replace("/\[embed\](.+?)\[\/embed\]/is", "<!-- oembed $1 --><i>" . DI::l10n()->t('Embedding disabled') . " : $1</i><!-- /oembed $1 -->", $text);
|
||||
}
|
||||
return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", ['self', 'replaceCallback'], $text);
|
||||
return preg_replace_callback("/\[embed\](.+?)\[\/embed\]/is", [self::class, 'replaceCallback'], $text);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -218,8 +218,8 @@ class Smilies
|
|||
return $text;
|
||||
}
|
||||
|
||||
$text = preg_replace_callback('/<(pre)>(.*?)<\/pre>/ism', 'self::encode', $text);
|
||||
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', 'self::encode', $text);
|
||||
$text = preg_replace_callback('/<(pre)>(.*?)<\/pre>/ism', [self::class, 'encode'], $text);
|
||||
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', [self::class, 'encode'], $text);
|
||||
|
||||
if ($no_images) {
|
||||
$cleaned = ['texts' => [], 'icons' => []];
|
||||
|
@ -233,11 +233,11 @@ class Smilies
|
|||
$smilies = $cleaned;
|
||||
}
|
||||
|
||||
$text = preg_replace_callback('/<(3+)/', 'self::heartReplaceCallback', $text);
|
||||
$text = preg_replace_callback('/<(3+)/', [self::class, 'heartReplaceCallback'], $text);
|
||||
$text = self::strOrigReplace($smilies['texts'], $smilies['icons'], $text);
|
||||
|
||||
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', 'self::decode', $text);
|
||||
$text = preg_replace_callback('/<(pre)>(.*?)<\/pre>/ism', 'self::decode', $text);
|
||||
$text = preg_replace_callback('/<(code)>(.*?)<\/code>/ism', [self::class, 'decode'], $text);
|
||||
$text = preg_replace_callback('/<(pre)>(.*?)<\/pre>/ism', [self::class, 'decode'], $text);
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
|
|
@ -1415,8 +1415,8 @@ class BBCode
|
|||
public static function cleanPictureLinks(string $text): string
|
||||
{
|
||||
DI::profiler()->startRecording('rendering');
|
||||
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $text);
|
||||
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::cleanPictureLinksCallback', $return);
|
||||
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img=(.*)\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'cleanPictureLinksCallback'], $text);
|
||||
$return = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'cleanPictureLinksCallback'], $return);
|
||||
DI::profiler()->stopRecording();
|
||||
return $return;
|
||||
}
|
||||
|
@ -1450,7 +1450,7 @@ class BBCode
|
|||
{
|
||||
DI::profiler()->startRecording('rendering');
|
||||
$regexp = "/([@!])\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
|
||||
$body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body);
|
||||
$body = preg_replace_callback($regexp, [self::class, 'mentionCallback'], $body);
|
||||
DI::profiler()->stopRecording();
|
||||
return $body;
|
||||
}
|
||||
|
@ -2002,12 +2002,12 @@ class BBCode
|
|||
|
||||
if (!$for_plaintext) {
|
||||
if (in_array($simple_html, [self::OSTATUS, self::MASTODON_API, self::TWITTER_API, self::ACTIVITYPUB])) {
|
||||
$text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", 'self::convertUrlForActivityPubCallback', $text);
|
||||
$text = preg_replace_callback("/\[url\](.*?)\[\/url\]/ism", [self::class, 'convertUrlForActivityPubCallback'], $text);
|
||||
$text = preg_replace_callback("/\[url\=(.*?)\](.*?)\[\/url\]/ism", [self::class, 'convertUrlForActivityPubCallback'], $text);
|
||||
}
|
||||
} else {
|
||||
$text = preg_replace("(\[url\](.*?)\[\/url\])ism", " $1 ", $text);
|
||||
$text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", 'self::removePictureLinksCallback', $text);
|
||||
$text = preg_replace_callback("&\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]&Usi", [self::class, 'removePictureLinksCallback'], $text);
|
||||
}
|
||||
|
||||
// Bookmarks in red - will be converted to bookmarks in friendica
|
||||
|
@ -2017,7 +2017,7 @@ class BBCode
|
|||
"[bookmark=$1]$2[/bookmark]", $text);
|
||||
|
||||
if (in_array($simple_html, [self::OSTATUS, self::TWITTER])) {
|
||||
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", "self::expandLinksCallback", $text);
|
||||
$text = preg_replace_callback("/([^#@!])\[url\=([^\]]*)\](.*?)\[\/url\]/ism", [self::class, 'expandLinksCallback'], $text);
|
||||
//$text = preg_replace("/[^#@!]\[url\=([^\]]*)\](.*?)\[\/url\]/ism", ' $2 [url]$1[/url]', $text);
|
||||
$text = preg_replace("/\[bookmark\=([^\]]*)\](.*?)\[\/bookmark\]/ism", ' $2 [url]$1[/url]', $text);
|
||||
}
|
||||
|
@ -2327,7 +2327,7 @@ class BBCode
|
|||
$url_search_string = "^\[\]";
|
||||
$text = preg_replace_callback(
|
||||
"/([@!])\[(.*?)\]\(([$url_search_string]*?)\)/ism",
|
||||
['self', 'bbCodeMention2DiasporaCallback'],
|
||||
[self::class, 'bbCodeMention2DiasporaCallback'],
|
||||
$text
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1032,7 +1032,7 @@ class HTML
|
|||
// the quotes, e.g.:
|
||||
//
|
||||
// concat("'foo'", '"', "bar")
|
||||
return 'concat(' . implode(', \'"\', ', array_map(['self', 'xpathQuote'], explode('"', $value))) . ')';
|
||||
return 'concat(' . implode(', \'"\', ', array_map([self::class, 'xpathQuote'], explode('"', $value))) . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -144,7 +144,7 @@ class TagCloud
|
|||
$x ++;
|
||||
}
|
||||
|
||||
usort($tags, 'self::tagsSort');
|
||||
usort($tags, [self::class, 'tagsSort']);
|
||||
$range = max(0.01, $max - $min) * 1.0001;
|
||||
|
||||
for ($x = 0; $x < count($tags); $x ++) {
|
||||
|
|
|
@ -214,6 +214,17 @@ class ConfigFileTransformer
|
|||
case "NULL":
|
||||
return "null";
|
||||
case "object":
|
||||
if (method_exists($value, '__toString')) {
|
||||
return sprintf('\'%s\'', $value);
|
||||
} elseif ($value instanceof \Serializable) {
|
||||
try {
|
||||
return $value->serialize();
|
||||
} catch (\Exception $e) {
|
||||
throw new \InvalidArgumentException(sprintf('Cannot serialize %s.', gettype($value)), $e);
|
||||
}
|
||||
} else {
|
||||
throw new \InvalidArgumentException(sprintf('%s is an object without stringify.', gettype($value)));
|
||||
}
|
||||
case "resource":
|
||||
case "resource (closed)":
|
||||
throw new \InvalidArgumentException(sprintf('%s in configs are not supported yet.', gettype($value)));
|
||||
|
|
29
src/Core/Hooks/Capabilities/IAmAStrategy.php
Normal file
29
src/Core/Hooks/Capabilities/IAmAStrategy.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Capabilities;
|
||||
|
||||
/**
|
||||
* All classes, implementing this interface are valid Strategies for Hook calls
|
||||
*/
|
||||
interface IAmAStrategy
|
||||
{
|
||||
}
|
81
src/Core/Hooks/Capabilities/ICanManageInstances.php
Normal file
81
src/Core/Hooks/Capabilities/ICanManageInstances.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Capabilities;
|
||||
|
||||
use Friendica\Core\Hooks\Exceptions\HookInstanceException;
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
|
||||
/**
|
||||
* Managing special instance and decorator treatments for classes
|
||||
*/
|
||||
interface ICanManageInstances
|
||||
{
|
||||
/**
|
||||
* Register a class(strategy) for a given interface with a unique name.
|
||||
*
|
||||
* @see https://refactoring.guru/design-patterns/strategy
|
||||
*
|
||||
* @param string $interface The interface, which the given class implements
|
||||
* @param string $name An arbitrary identifier for the given class, which will be used for factories, dependency injections etc.
|
||||
* @param string $class The fully-qualified given class name
|
||||
* @param ?array $arguments Additional arguments, which can be passed to the constructor
|
||||
*
|
||||
* @return $this This interface for chain-calls
|
||||
*
|
||||
* @throws HookRegisterArgumentException in case the given class for the interface isn't valid or already set
|
||||
*/
|
||||
public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): self;
|
||||
|
||||
/**
|
||||
* Register a new decorator for a given class or interface
|
||||
* @see https://refactoring.guru/design-patterns/decorator
|
||||
*
|
||||
* @note Decorator attach new behaviors to classes without changing them or without letting them know about it.
|
||||
*
|
||||
* @param string $class The fully-qualified class or interface name, which gets decorated by a class
|
||||
* @param string $decoratorClass The fully-qualified name of the class which mimics the given class or interface and adds new functionality
|
||||
* @param array $arguments Additional arguments, which can be passed to the constructor of "decoratorClass"
|
||||
*
|
||||
* @return $this This interface for chain-calls
|
||||
*
|
||||
* @throws HookRegisterArgumentException in case the given class for the class or interface isn't valid
|
||||
*/
|
||||
public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): self;
|
||||
|
||||
/**
|
||||
* Returns a new instance of a given class for the corresponding name
|
||||
*
|
||||
* The instance will be build based on the registered strategy and the (unique) name
|
||||
*
|
||||
* In case, there are registered decorators for this class as well, all decorators of the list will be wrapped
|
||||
* around the instance before returning it
|
||||
*
|
||||
* @param string $class The fully-qualified name of the given class or interface which will get returned
|
||||
* @param string $name An arbitrary identifier to find a concrete instance strategy.
|
||||
* @param array $arguments Additional arguments, which can be passed to the constructor of "$class" at runtime
|
||||
*
|
||||
* @return object The concrete instance of the type "$class"
|
||||
*
|
||||
* @throws HookInstanceException In case the class cannot get created
|
||||
*/
|
||||
public function getInstance(string $class, string $name, array $arguments = []): object;
|
||||
}
|
30
src/Core/Hooks/Exceptions/HookInstanceException.php
Normal file
30
src/Core/Hooks/Exceptions/HookInstanceException.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Exceptions;
|
||||
|
||||
class HookInstanceException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = "", \Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
}
|
30
src/Core/Hooks/Exceptions/HookRegisterArgumentException.php
Normal file
30
src/Core/Hooks/Exceptions/HookRegisterArgumentException.php
Normal file
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Exceptions;
|
||||
|
||||
class HookRegisterArgumentException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = "", \Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
}
|
104
src/Core/Hooks/Model/InstanceManager.php
Normal file
104
src/Core/Hooks/Model/InstanceManager.php
Normal file
|
@ -0,0 +1,104 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Hooks\Model;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Hooks\Exceptions\HookInstanceException;
|
||||
use Friendica\Core\Hooks\Exceptions\HookRegisterArgumentException;
|
||||
|
||||
/** {@inheritDoc} */
|
||||
class InstanceManager implements ICanManageInstances
|
||||
{
|
||||
protected $instance = [];
|
||||
protected $instanceArguments = [];
|
||||
protected $decorator = [];
|
||||
|
||||
/** @var Dice */
|
||||
protected $dice;
|
||||
|
||||
public function __construct(Dice $dice)
|
||||
{
|
||||
$this->dice = $dice;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function registerStrategy(string $interface, string $name, string $class, array $arguments = null): ICanManageInstances
|
||||
{
|
||||
if (!is_a($class, $interface, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s is not a valid class for the interface %s', $class, $interface));
|
||||
}
|
||||
|
||||
if (!is_a($class, IAmAStrategy::class, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s does not inherit from the marker interface %s', $class, IAmAStrategy::class));
|
||||
}
|
||||
|
||||
if (!empty($this->instance[$interface][$name])) {
|
||||
throw new HookRegisterArgumentException(sprintf('A class with the name %s is already set for the interface %s', $name, $interface));
|
||||
}
|
||||
|
||||
$this->instance[$interface][$name] = $class;
|
||||
$this->instanceArguments[$interface][$name] = $arguments;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function registerDecorator(string $class, string $decoratorClass, array $arguments = []): ICanManageInstances
|
||||
{
|
||||
if (!is_a($decoratorClass, $class, true)) {
|
||||
throw new HookRegisterArgumentException(sprintf('%s is not a valid substitution for the given class or interface %s', $decoratorClass, $class));
|
||||
}
|
||||
|
||||
$this->decorator[$class][] = [
|
||||
'class' => $decoratorClass,
|
||||
'arguments' => $arguments,
|
||||
];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public function getInstance(string $class, string $name, array $arguments = []): object
|
||||
{
|
||||
if (empty($this->instance[$class][$name])) {
|
||||
throw new HookInstanceException(sprintf('The class with the name %s isn\'t registered for the class or interface %s', $name, $class));
|
||||
}
|
||||
|
||||
$instance = $this->dice->create($this->instance[$class][$name], array_merge($this->instanceArguments[$class][$name] ?? [], $arguments));
|
||||
|
||||
foreach ($this->decorator[$class] ?? [] as $decorator) {
|
||||
$this->dice = $this->dice->addRule($class, [
|
||||
'instanceOf' => $decorator['class'],
|
||||
'constructParams' => empty($decorator['arguments']) ? null : $decorator['arguments'],
|
||||
/// @todo maybe support call structures for hooks as well in a later stage - could make factory calls easier
|
||||
'call' => null,
|
||||
'substitutions' => [$class => $instance],
|
||||
]);
|
||||
|
||||
$instance = $this->dice->create($class);
|
||||
}
|
||||
|
||||
return $instance;
|
||||
}
|
||||
}
|
32
src/Core/Logger/Exception/LoggerInvalidException.php
Normal file
32
src/Core/Logger/Exception/LoggerInvalidException.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (C) 2010-2023, the Friendica project
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Friendica\Core\Logger\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
class LoggerInvalidException extends \RuntimeException
|
||||
{
|
||||
public function __construct($message = "", Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, 500, $previous);
|
||||
}
|
||||
}
|
|
@ -22,16 +22,11 @@
|
|||
namespace Friendica\Core\Logger\Factory;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Util\FileSystem;
|
||||
use Friendica\Core\Logger\Type\ProfilerLogger;
|
||||
use Friendica\Core\Logger\Type\StreamLogger;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use Psr\Log\NullLogger;
|
||||
|
@ -44,115 +39,55 @@ class Logger
|
|||
const DEV_CHANNEL = 'dev';
|
||||
|
||||
/** @var string The log-channel (app, worker, ...) */
|
||||
private $channel;
|
||||
protected $channel;
|
||||
/** @var ICanManageInstances */
|
||||
protected $instanceManager;
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
public function __construct(string $channel, bool $includeAddon = true)
|
||||
public function __construct(string $channel, ICanManageInstances $instanceManager, IManageConfigValues $config, string $logfile = null)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->channel = $channel;
|
||||
$this->instanceManager = $instanceManager;
|
||||
$this->config = $config;
|
||||
|
||||
/// @fixme clean solution = Making Addon & Hook dynamic and load them inside the constructor, so there's no custom load logic necessary anymore
|
||||
if ($includeAddon) {
|
||||
Core\Addon::loadAddons();
|
||||
Core\Hook::loadHooks();
|
||||
$this->instanceManager
|
||||
->registerStrategy(LoggerInterface::class, 'syslog', SyslogLogger::class)
|
||||
->registerStrategy(LoggerInterface::class, 'stream', StreamLogger::class, isset($logfile) ? [$logfile] : null);
|
||||
|
||||
if ($this->config->get('system', 'profiling') ?? false) {
|
||||
$this->instanceManager->registerDecorator(LoggerInterface::class, ProfilerLogger::class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new PSR-3 compliant logger instances
|
||||
*
|
||||
* @param Database $database The Friendica Database instance
|
||||
* @param IManageConfigValues $config The config
|
||||
* @param Profiler $profiler The profiler of the app
|
||||
* @param FileSystem $fileSystem FileSystem utils
|
||||
* @param string|null $minLevel (optional) Override minimum Loglevel to log
|
||||
* @param string|null $loglevel (optional) A given loglevel in case the loglevel in the config isn't applicable
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
*/
|
||||
public function create(Database $database, IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, IHaveCallIntrospections $introspection, ?string $minLevel = null): LoggerInterface
|
||||
public function create(string $loglevel = null): LoggerInterface
|
||||
{
|
||||
if (empty($config->get('system', 'debugging', false))) {
|
||||
$logger = new NullLogger();
|
||||
$database->setLogger($logger);
|
||||
if (empty($this->config->get('system', 'debugging') ?? false)) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$loglevel = $loglevel ?? static::mapLegacyConfigDebugLevel($this->config->get('system', 'loglevel'));
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
|
||||
try {
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [$this->channel, $loglevel]);
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create(LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
return $logger;
|
||||
} catch (\Throwable $e) {
|
||||
// No logger ...
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$minLevel = $minLevel ?? $config->get('system', 'loglevel');
|
||||
$loglevel = self::mapLegacyConfigDebugLevel((string)$minLevel);
|
||||
|
||||
$name = $config->get('system', 'logger_config', 'stream');
|
||||
|
||||
switch ($name) {
|
||||
case 'syslog':
|
||||
try {
|
||||
$logger = new SyslogLogger($this->channel, $introspection, $loglevel, $config->get('system', 'syslog_flags', SyslogLogger::DEFAULT_FLAGS), $config->get('system', 'syslog_facility', SyslogLogger::DEFAULT_FACILITY));
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
} catch (\Throwable $e) {
|
||||
// No logger ...
|
||||
$logger = new NullLogger();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'stream':
|
||||
default:
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'channel' => $this->channel,
|
||||
'introspection' => $introspection,
|
||||
'loglevel' => $loglevel,
|
||||
'logger' => null,
|
||||
];
|
||||
try {
|
||||
Core\Hook::callAll('logger_instance', $data);
|
||||
} catch (InternalServerErrorException $exception) {
|
||||
$data['logger'] = null;
|
||||
}
|
||||
|
||||
if (($data['logger'] ?? null) instanceof LoggerInterface) {
|
||||
$logger = $data['logger'];
|
||||
}
|
||||
|
||||
if (empty($logger)) {
|
||||
$stream = $config->get('system', 'logfile');
|
||||
// just add a stream in case it's either writable or not file
|
||||
if (!is_file($stream) || is_writable($stream)) {
|
||||
try {
|
||||
$logger = new StreamLogger($this->channel, $stream, $introspection, $fileSystem, $loglevel);
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
} catch (\Throwable $t) {
|
||||
// No logger ...
|
||||
$logger = new NullLogger();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
$logger = new SyslogLogger($this->channel, $introspection, $loglevel);
|
||||
} catch (LogLevelException $exception) {
|
||||
// If there's a wrong config value for loglevel, try again with standard
|
||||
$logger = $this->create($database, $config, $profiler, $fileSystem, $introspection, LogLevel::NOTICE);
|
||||
$logger->warning('Invalid loglevel set in config.', ['loglevel' => $loglevel]);
|
||||
} catch (\Throwable $e) {
|
||||
// No logger ...
|
||||
$logger = new NullLogger();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$profiling = $config->get('system', 'profiling', false);
|
||||
|
||||
// In case profiling is enabled, wrap the ProfilerLogger around the current logger
|
||||
if (isset($profiling) && $profiling !== false) {
|
||||
$logger = new ProfilerLogger($logger, $profiler);
|
||||
}
|
||||
|
||||
$database->setLogger($logger);
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -163,63 +98,24 @@ class Logger
|
|||
*
|
||||
* It should never get filled during normal usage of Friendica
|
||||
*
|
||||
* @param IManageConfigValues $config The config
|
||||
* @param Profiler $profiler The profiler of the app
|
||||
* @param FileSystem $fileSystem FileSystem utils
|
||||
*
|
||||
* @return LoggerInterface The PSR-3 compliant logger instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function createDev(IManageConfigValues $config, Profiler $profiler, FileSystem $fileSystem, IHaveCallIntrospections $introspection)
|
||||
public function createDev()
|
||||
{
|
||||
$debugging = $config->get('system', 'debugging');
|
||||
$stream = $config->get('system', 'dlogfile');
|
||||
$developerIp = $config->get('system', 'dlogip');
|
||||
$debugging = $this->config->get('system', 'debugging');
|
||||
$stream = $this->config->get('system', 'dlogfile');
|
||||
$developerIp = $this->config->get('system', 'dlogip');
|
||||
|
||||
if ((!isset($developerIp) || !$debugging) &&
|
||||
(!is_file($stream) || is_writable($stream))) {
|
||||
return new NullLogger();
|
||||
}
|
||||
|
||||
$name = $config->get('system', 'logger_config', 'stream');
|
||||
$name = $this->config->get('system', 'logger_config') ?? 'stream';
|
||||
|
||||
switch ($name) {
|
||||
|
||||
case 'syslog':
|
||||
$logger = new SyslogLogger(self::DEV_CHANNEL, $introspection, LogLevel::DEBUG);
|
||||
break;
|
||||
|
||||
case 'stream':
|
||||
default:
|
||||
$data = [
|
||||
'name' => $name,
|
||||
'channel' => self::DEV_CHANNEL,
|
||||
'introspection' => $introspection,
|
||||
'loglevel' => LogLevel::DEBUG,
|
||||
'logger' => null,
|
||||
];
|
||||
try {
|
||||
Core\Hook::callAll('logger_instance', $data);
|
||||
} catch (InternalServerErrorException $exception) {
|
||||
$data['logger'] = null;
|
||||
}
|
||||
|
||||
if (($data['logger'] ?? null) instanceof LoggerInterface) {
|
||||
return $data['logger'];
|
||||
}
|
||||
|
||||
$logger = new StreamLogger(self::DEV_CHANNEL, $stream, $introspection, $fileSystem, LogLevel::DEBUG);
|
||||
break;
|
||||
}
|
||||
|
||||
$profiling = $config->get('system', 'profiling', false);
|
||||
|
||||
// In case profiling is enabled, wrap the ProfilerLogger around the current logger
|
||||
if (isset($profiling) && $profiling !== false) {
|
||||
$logger = new ProfilerLogger($logger, $profiler);
|
||||
}
|
||||
|
||||
return $logger;
|
||||
/** @var LoggerInterface */
|
||||
return $this->instanceManager->getInstance(LoggerInterface::class, $name, [self::DEV_CHANNEL, LogLevel::DEBUG, $stream]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Util\Introspection;
|
||||
use Friendica\Util\Strings;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
|
@ -46,7 +46,7 @@ abstract class AbstractLogger implements LoggerInterface
|
|||
|
||||
/**
|
||||
* The Introspection for the current call
|
||||
* @var Introspection
|
||||
* @var IHaveCallIntrospections
|
||||
*/
|
||||
protected $introspection;
|
||||
|
||||
|
@ -69,11 +69,11 @@ abstract class AbstractLogger implements LoggerInterface
|
|||
|
||||
/**
|
||||
* @param string $channel The output channel
|
||||
* @param Introspection $introspection The introspection of the current call
|
||||
* @param IHaveCallIntrospections $introspection The introspection of the current call
|
||||
*
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function __construct(string $channel, Introspection $introspection)
|
||||
public function __construct(string $channel, IHaveCallIntrospections $introspection)
|
||||
{
|
||||
$this->channel = $channel;
|
||||
$this->introspection = $introspection;
|
||||
|
|
|
@ -21,18 +21,20 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerArgumentException;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\FileSystem;
|
||||
use Friendica\Core\Logger\Util\Introspection;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* A Logger instance for logging into a stream (file, stdout, stderr)
|
||||
*/
|
||||
class StreamLogger extends AbstractLogger
|
||||
class StreamLogger extends AbstractLogger implements IAmAStrategy
|
||||
{
|
||||
/**
|
||||
* The minimum loglevel at which this logger will be triggered
|
||||
|
@ -80,16 +82,20 @@ class StreamLogger extends AbstractLogger
|
|||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @param string|resource $stream The stream to write with this logger (either a file or a stream, i.e. stdout)
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
*
|
||||
* @throws LoggerArgumentException
|
||||
* @throws LogLevelException
|
||||
*/
|
||||
public function __construct($channel, $stream, Introspection $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, FileSystem $fileSystem, string $level = LogLevel::DEBUG)
|
||||
{
|
||||
$this->fileSystem = $fileSystem;
|
||||
|
||||
$stream = $this->logfile ?? $config->get('system', 'logfile');
|
||||
if ((file_exists($stream) && !is_writable($stream)) || is_writable(basename($stream))) {
|
||||
throw new LoggerArgumentException(sprintf('%s is not a valid logfile', $stream));
|
||||
}
|
||||
|
||||
parent::__construct($channel, $introspection);
|
||||
|
||||
if (is_resource($stream)) {
|
||||
|
|
|
@ -21,16 +21,18 @@
|
|||
|
||||
namespace Friendica\Core\Logger\Type;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
use Friendica\Core\Logger\Capabilities\IHaveCallIntrospections;
|
||||
use Friendica\Core\Logger\Exception\LoggerException;
|
||||
use Friendica\Core\Logger\Exception\LogLevelException;
|
||||
use Friendica\Core\Logger\Util\Introspection;
|
||||
use Psr\Log\LogLevel;
|
||||
|
||||
/**
|
||||
* A Logger instance for syslogging (fast, but simple)
|
||||
* @see http://php.net/manual/en/function.syslog.php
|
||||
*/
|
||||
class SyslogLogger extends AbstractLogger
|
||||
class SyslogLogger extends AbstractLogger implements IAmAStrategy
|
||||
{
|
||||
const IDENT = 'Friendica';
|
||||
|
||||
|
@ -100,17 +102,16 @@ class SyslogLogger extends AbstractLogger
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
* @param string $level The minimum loglevel at which this logger will be triggered
|
||||
* @param int $logOpts Indicates what logging options will be used when generating a log message
|
||||
* @param int $logFacility Used to specify what type of program is logging the message
|
||||
*
|
||||
* @throws LogLevelException
|
||||
* @throws LoggerException
|
||||
*/
|
||||
public function __construct($channel, Introspection $introspection, string $level = LogLevel::NOTICE, int $logOpts = self::DEFAULT_FLAGS, int $logFacility = self::DEFAULT_FACILITY )
|
||||
public function __construct(string $channel, IManageConfigValues $config, IHaveCallIntrospections $introspection, string $level = LogLevel::NOTICE)
|
||||
{
|
||||
parent::__construct($channel, $introspection);
|
||||
$this->logOpts = $logOpts;
|
||||
$this->logFacility = $logFacility;
|
||||
|
||||
$this->logOpts = $config->get('system', 'syslog_flags') ?? static::DEFAULT_FLAGS;
|
||||
$this->logFacility = $config->get('system', 'syslog_facility') ?? static::DEFAULT_FACILITY;
|
||||
$this->logLevel = $this->mapLevelToPriority($level);
|
||||
$this->introspection->addClasses([self::class]);
|
||||
}
|
||||
|
|
|
@ -527,7 +527,7 @@ class DBA
|
|||
public static function buildTableString(array $tables): string
|
||||
{
|
||||
// Quote each entry
|
||||
return implode(',', array_map(['self', 'quoteIdentifier'], $tables));
|
||||
return implode(',', array_map([self::class, 'quoteIdentifier'], $tables));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -717,7 +717,7 @@ class DBA
|
|||
{
|
||||
$groupby_string = '';
|
||||
if (!empty($params['group_by'])) {
|
||||
$groupby_string = " GROUP BY " . implode(', ', array_map(['self', 'quoteIdentifier'], $params['group_by']));
|
||||
$groupby_string = " GROUP BY " . implode(', ', array_map([self::class, 'quoteIdentifier'], $params['group_by']));
|
||||
}
|
||||
|
||||
$order_string = '';
|
||||
|
|
|
@ -783,7 +783,7 @@ class DBStructure
|
|||
}
|
||||
|
||||
if (!DBA::exists('verb', ['id' => 0])) {
|
||||
DBA::insert('verb', ['name' => '']);
|
||||
DBA::insert('verb', ['name' => ''], Database::INSERT_IGNORE);
|
||||
$lastid = DBA::lastInsertId();
|
||||
if ($lastid != 0) {
|
||||
DBA::update('verb', ['id' => 0], ['id' => $lastid]);
|
||||
|
@ -819,7 +819,7 @@ class DBStructure
|
|||
}
|
||||
|
||||
if (self::existsTable('contact') && !DBA::exists('contact', ['id' => 0])) {
|
||||
DBA::insert('contact', ['nurl' => '']);
|
||||
DBA::insert('contact', ['nurl' => ''], Database::INSERT_IGNORE);
|
||||
$lastid = DBA::lastInsertId();
|
||||
if ($lastid != 0) {
|
||||
DBA::update('contact', ['id' => 0], ['id' => $lastid]);
|
||||
|
@ -834,7 +834,7 @@ class DBStructure
|
|||
}
|
||||
|
||||
if (self::existsTable('tag') && !DBA::exists('tag', ['id' => 0])) {
|
||||
DBA::insert('tag', ['name' => '']);
|
||||
DBA::insert('tag', ['name' => ''], Database::INSERT_IGNORE);
|
||||
$lastid = DBA::lastInsertId();
|
||||
if ($lastid != 0) {
|
||||
DBA::update('tag', ['id' => 0], ['id' => $lastid]);
|
||||
|
@ -850,7 +850,7 @@ class DBStructure
|
|||
|
||||
if (self::existsTable('permissionset')) {
|
||||
if (!DBA::exists('permissionset', ['id' => 0])) {
|
||||
DBA::insert('permissionset', ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => '']);
|
||||
DBA::insert('permissionset', ['allow_cid' => '', 'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''], Database::INSERT_IGNORE);
|
||||
$lastid = DBA::lastInsertId();
|
||||
if ($lastid != 0) {
|
||||
DBA::update('permissionset', ['id' => 0], ['id' => $lastid]);
|
||||
|
@ -878,7 +878,7 @@ class DBStructure
|
|||
}
|
||||
$fields = ['id' => $set['psid'], 'uid' => $set['uid'], 'allow_cid' => $permission,
|
||||
'allow_gid' => '', 'deny_cid' => '', 'deny_gid' => ''];
|
||||
DBA::insert('permissionset', $fields);
|
||||
DBA::insert('permissionset', $fields, Database::INSERT_IGNORE);
|
||||
}
|
||||
DBA::close($sets);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ use PDO;
|
|||
use PDOException;
|
||||
use PDOStatement;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
/**
|
||||
* This class is for the low level database stuff that does driver specific things.
|
||||
|
@ -81,16 +80,14 @@ class Database
|
|||
/** @var ViewDefinition */
|
||||
protected $viewDefinition;
|
||||
|
||||
public function __construct(IManageConfigValues $config, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition)
|
||||
public function __construct(IManageConfigValues $config, Profiler $profiler, DbaDefinition $dbaDefinition, ViewDefinition $viewDefinition, LoggerInterface $logger)
|
||||
{
|
||||
// We are storing these values for being able to perform a reconnect
|
||||
$this->config = $config;
|
||||
$this->profiler = $profiler;
|
||||
$this->config = $config;
|
||||
$this->profiler = $profiler;
|
||||
$this->dbaDefinition = $dbaDefinition;
|
||||
$this->viewDefinition = $viewDefinition;
|
||||
|
||||
// Temporary NullLogger until we can fetch the logger class from the config
|
||||
$this->logger = new NullLogger();
|
||||
$this->logger = $logger;
|
||||
|
||||
$this->connect();
|
||||
}
|
||||
|
@ -196,21 +193,6 @@ class Database
|
|||
$this->testmode = $test;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logger for DBA
|
||||
*
|
||||
* @note this is necessary because if we want to load the logger configuration
|
||||
* from the DB, but there's an error, we would print out an exception.
|
||||
* So the logger gets updated after the logger configuration can be retrieved
|
||||
* from the database
|
||||
*
|
||||
* @param LoggerInterface $logger
|
||||
*/
|
||||
public function setLogger(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the profiler for DBA
|
||||
*
|
||||
|
|
|
@ -87,6 +87,10 @@ class DbaDefinition
|
|||
$data[$field] = mb_substr($data[$field], 0, $result[1]);
|
||||
} elseif (is_string($data[$field]) && preg_match("/binary\((\d*)\)/", $definition[$table]['fields'][$field]['type'], $result)) {
|
||||
$data[$field] = substr($data[$field], 0, $result[1]);
|
||||
} elseif (is_numeric($data[$field]) && $definition[$table]['fields'][$field]['type'] === 'int') {
|
||||
$data[$field] = min(max((int)$data[$field], -2147483648), 2147483647);
|
||||
} elseif (is_numeric($data[$field]) && $definition[$table]['fields'][$field]['type'] === 'int unsigned') {
|
||||
$data[$field] = min(max((int)$data[$field], 0), 4294967295);
|
||||
}
|
||||
$fields[$field] = $data[$field];
|
||||
}
|
||||
|
|
|
@ -243,15 +243,13 @@ class Contact
|
|||
* @throws \Exception
|
||||
* @todo Let's get rid of boolean type of $old_fields
|
||||
*/
|
||||
public static function update(array $fields, array $condition, $old_fields = [])
|
||||
public static function update(array $fields, array $condition, $old_fields = []): bool
|
||||
{
|
||||
$fields = DI::dbaDefinition()->truncateFieldsForTable('contact', $fields);
|
||||
$ret = DBA::update('contact', $fields, $condition, $old_fields);
|
||||
|
||||
// Apply changes to the "user-contact" table on dedicated fields
|
||||
Contact\User::updateByContactUpdate($fields, $condition);
|
||||
|
||||
return $ret;
|
||||
$fields = DI::dbaDefinition()->truncateFieldsForTable('contact', $fields);
|
||||
return DBA::update('contact', $fields, $condition, $old_fields);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2970,7 +2968,7 @@ class Contact
|
|||
}
|
||||
|
||||
// check if we already have a contact
|
||||
$condition = ['uid' => $uid, 'nurl' => Strings::normaliseLink($ret['url'])];
|
||||
$condition = ['uid' => $uid, 'nurl' => Strings::normaliseLink($ret['url']), 'deleted' => false];
|
||||
$contact = DBA::selectFirst('contact', ['id', 'rel', 'url', 'pending', 'hub-verify'], $condition);
|
||||
|
||||
$protocol = self::getProtocol($ret['url'], $ret['network']);
|
||||
|
@ -3293,7 +3291,7 @@ class Contact
|
|||
if ($contact['rel'] == self::SHARING || in_array($contact['network'], [Protocol::FEED, Protocol::MAIL])) {
|
||||
self::remove($contact['id']);
|
||||
} else {
|
||||
self::update(['rel' => self::FOLLOWER], ['id' => $contact['id']]);
|
||||
self::update(['rel' => self::FOLLOWER, 'pending' => false], ['id' => $contact['id']]);
|
||||
}
|
||||
|
||||
Worker::add(Worker::PRIORITY_LOW, 'ContactDiscoveryForUser', $contact['uid']);
|
||||
|
|
|
@ -198,7 +198,7 @@ class Event
|
|||
|
||||
public static function sortByDate(array $event_list): array
|
||||
{
|
||||
usort($event_list, ['self', 'compareDatesCallback']);
|
||||
usort($event_list, [self::class, 'compareDatesCallback']);
|
||||
return $event_list;
|
||||
}
|
||||
|
||||
|
|
|
@ -455,22 +455,26 @@ class GServer
|
|||
* Set failed server status
|
||||
*
|
||||
* @param string $url
|
||||
* @return void
|
||||
*/
|
||||
public static function setFailureByUrl(string $url)
|
||||
{
|
||||
$gserver = DBA::selectFirst('gserver', [], ['nurl' => Strings::normaliseLink($url)]);
|
||||
$nurl = Strings::normaliseLink($url);
|
||||
|
||||
$gserver = DBA::selectFirst('gserver', [], ['nurl' => $nurl]);
|
||||
if (DBA::isResult($gserver)) {
|
||||
$next_update = self::getNextUpdateDate(false, $gserver['created'], $gserver['last_contact']);
|
||||
self::update(['url' => $url, 'failed' => true, 'blocked' => Network::isUrlBlocked($url), 'last_failure' => DateTimeFormat::utcNow(),
|
||||
'next_contact' => $next_update, 'network' => Protocol::PHANTOM, 'detection-method' => null],
|
||||
['nurl' => Strings::normaliseLink($url)]);
|
||||
['nurl' => $nurl]);
|
||||
Logger::info('Set failed status for existing server', ['url' => $url]);
|
||||
if (self::isDefunct($gserver)) {
|
||||
self::archiveContacts($gserver['id']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
self::insert(['url' => $url, 'nurl' => Strings::normaliseLink($url),
|
||||
|
||||
self::insert(['url' => $url, 'nurl' => $nurl,
|
||||
'network' => Protocol::PHANTOM, 'created' => DateTimeFormat::utcNow(),
|
||||
'failed' => true, 'last_failure' => DateTimeFormat::utcNow()]);
|
||||
Logger::info('Set failed status for new server', ['url' => $url]);
|
||||
|
@ -556,7 +560,7 @@ class GServer
|
|||
// If the URL missmatches, then we mark the old entry as failure
|
||||
if (!Strings::compareLink($url, $original_url)) {
|
||||
self::setFailureByUrl($original_url);
|
||||
if (!self::getID($url, true)) {
|
||||
if (!self::getID($url, true) && !Network::isUrlBlocked($url)) {
|
||||
self::detect($url, $network, $only_nodeinfo);
|
||||
}
|
||||
return false;
|
||||
|
@ -577,7 +581,7 @@ class GServer
|
|||
(((parse_url($url, PHP_URL_HOST) != parse_url($valid_url, PHP_URL_HOST)) || (parse_url($url, PHP_URL_PATH) != parse_url($valid_url, PHP_URL_PATH))) && empty(parse_url($valid_url, PHP_URL_PATH)))) {
|
||||
Logger::debug('Found redirect. Mark old entry as failure', ['old' => $url, 'new' => $valid_url]);
|
||||
self::setFailureByUrl($url);
|
||||
if (!self::getID($valid_url, true)) {
|
||||
if (!self::getID($valid_url, true) && !Network::isUrlBlocked($valid_url)) {
|
||||
self::detect($valid_url, $network, $only_nodeinfo);
|
||||
}
|
||||
return false;
|
||||
|
@ -591,7 +595,7 @@ class GServer
|
|||
$valid_url = (string)Uri::fromParts($parts);
|
||||
|
||||
self::setFailureByUrl($url);
|
||||
if (!self::getID($valid_url, true)) {
|
||||
if (!self::getID($valid_url, true) && !Network::isUrlBlocked($valid_url)) {
|
||||
self::detect($valid_url, $network, $only_nodeinfo);
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -55,17 +55,17 @@ class Nodeinfo
|
|||
|
||||
$userStats = User::getStatistics();
|
||||
|
||||
$config->set('nodeinfo', 'total_users', $userStats['total_users']);
|
||||
$config->set('nodeinfo', 'active_users_halfyear', $userStats['active_users_halfyear']);
|
||||
$config->set('nodeinfo', 'active_users_monthly', $userStats['active_users_monthly']);
|
||||
$config->set('nodeinfo', 'active_users_weekly', $userStats['active_users_weekly']);
|
||||
DI::keyValue()->set('nodeinfo_total_users', $userStats['total_users']);
|
||||
DI::keyValue()->set('nodeinfo_active_users_halfyear', $userStats['active_users_halfyear']);
|
||||
DI::keyValue()->set('nodeinfo_active_users_monthly', $userStats['active_users_monthly']);
|
||||
DI::keyValue()->set('nodeinfo_active_users_weekly', $userStats['active_users_weekly']);
|
||||
|
||||
$logger->info('user statistics', $userStats);
|
||||
|
||||
$posts = DBA::count('post-thread', ["`uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE NOT `deleted` AND `origin`)"]);
|
||||
$comments = DBA::count('post', ["NOT `deleted` AND `gravity` = ? AND `uri-id` IN (SELECT `uri-id` FROM `post-user` WHERE `origin`)", Item::GRAVITY_COMMENT]);
|
||||
$config->set('nodeinfo', 'local_posts', $posts);
|
||||
$config->set('nodeinfo', 'local_comments', $comments);
|
||||
DI::keyValue()->set('nodeinfo_local_posts', $posts);
|
||||
DI::keyValue()->set('nodeinfo_local_comments', $comments);
|
||||
|
||||
$logger->info('User actitivy', ['posts' => $posts, 'comments' => $comments]);
|
||||
}
|
||||
|
@ -83,14 +83,14 @@ class Nodeinfo
|
|||
$usage->users = new \stdClass;
|
||||
|
||||
if (!empty($config->get('system', 'nodeinfo'))) {
|
||||
$usage->users->total = intval($config->get('nodeinfo', 'total_users'));
|
||||
$usage->users->activeHalfyear = intval($config->get('nodeinfo', 'active_users_halfyear'));
|
||||
$usage->users->activeMonth = intval($config->get('nodeinfo', 'active_users_monthly'));
|
||||
$usage->localPosts = intval($config->get('nodeinfo', 'local_posts'));
|
||||
$usage->localComments = intval($config->get('nodeinfo', 'local_comments'));
|
||||
$usage->users->total = intval(DI::keyValue()->get('nodeinfo_total_users'));
|
||||
$usage->users->activeHalfyear = intval(DI::keyValue()->get('nodeinfo_active_users_halfyear'));
|
||||
$usage->users->activeMonth = intval(DI::keyValue()->get('nodeinfo_active_users_monthly'));
|
||||
$usage->localPosts = intval(DI::keyValue()->get('nodeinfo_local_posts'));
|
||||
$usage->localComments = intval(DI::keyValue()->get('nodeinfo_local_comments'));
|
||||
|
||||
if ($version2) {
|
||||
$usage->users->activeWeek = intval($config->get('nodeinfo', 'active_users_weekly'));
|
||||
$usage->users->activeWeek = intval(DI::keyValue()->get('nodeinfo_active_users_weekly'));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -757,7 +757,7 @@ class User
|
|||
}
|
||||
|
||||
/**
|
||||
* Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).
|
||||
* Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces and accentuated letters.
|
||||
*
|
||||
* Password length is limited to 72 characters if the current default password hashing algorithm is Blowfish.
|
||||
* From the manual: "Using the PASSWORD_BCRYPT as the algorithm, will result in the password parameter being
|
||||
|
@ -770,13 +770,13 @@ class User
|
|||
*/
|
||||
public static function getPasswordRegExp(string $delimiter = null): string
|
||||
{
|
||||
$allowed_characters = '!"#$%&\'()*+,-./;<=>?@[\]^_`{|}~';
|
||||
$allowed_characters = ':!"#$%&\'()*+,-./;<=>?@[\]^_`{|}~';
|
||||
|
||||
if ($delimiter) {
|
||||
$allowed_characters = preg_quote($allowed_characters, $delimiter);
|
||||
}
|
||||
|
||||
return '^[a-zA-Z0-9' . $allowed_characters . ']' . (PASSWORD_DEFAULT !== PASSWORD_BCRYPT ? '{1,72}' : '+') . '$';
|
||||
return '^[a-zA-Z0-9' . $allowed_characters . ']' . (PASSWORD_DEFAULT === PASSWORD_BCRYPT ? '{1,72}' : '+') . '$';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -804,7 +804,7 @@ class User
|
|||
}
|
||||
|
||||
if (!preg_match('/' . self::getPasswordRegExp('/') . '/', $password)) {
|
||||
throw new Exception(DI::l10n()->t('The password can\'t contain accentuated letters, white spaces or colons (:)'));
|
||||
throw new Exception(DI::l10n()->t("The password can't contain white spaces nor accentuated letters"));
|
||||
}
|
||||
|
||||
return self::updatePasswordHashed($uid, self::hashPassword($password));
|
||||
|
|
|
@ -197,7 +197,7 @@ class Profile extends BaseModule
|
|||
Contact\User::setIgnored($contact['id'], DI::userSession()->getLocalUserId(), true);
|
||||
$message = $this->t('Contact has been ignored');
|
||||
}
|
||||
|
||||
|
||||
// @TODO: add $this->localRelationship->save($localRelationship);
|
||||
DI::sysmsg()->addInfo($message);
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ class Profile extends BaseModule
|
|||
Contact\User::setCollapsed($contact['id'], DI::userSession()->getLocalUserId(), true);
|
||||
$message = $this->t('Contact has been collapsed');
|
||||
}
|
||||
|
||||
|
||||
// @TODO: add $this->localRelationship->save($localRelationship);
|
||||
DI::sysmsg()->addInfo($message);
|
||||
}
|
||||
|
@ -239,9 +239,6 @@ class Profile extends BaseModule
|
|||
'$baseurl' => $this->baseUrl->get(true),
|
||||
]);
|
||||
|
||||
$contact['blocked'] = Contact\User::isBlocked($contact['id'], DI::userSession()->getLocalUserId());
|
||||
$contact['readonly'] = Contact\User::isIgnored($contact['id'], DI::userSession()->getLocalUserId());
|
||||
|
||||
switch ($localRelationship->rel) {
|
||||
case Contact::FRIEND: $relation_text = $this->t('You are mutual friends with %s', $contact['name']); break;
|
||||
case Contact::FOLLOWER: $relation_text = $this->t('You are sharing with %s', $contact['name']); break;
|
||||
|
@ -361,16 +358,13 @@ class Profile extends BaseModule
|
|||
'$last_update' => $last_update,
|
||||
'$udnow' => $this->t('Update now'),
|
||||
'$contact_id' => $contact['id'],
|
||||
'$block_text' => ($contact['blocked'] ? $this->t('Unblock') : $this->t('Block')),
|
||||
'$ignore_text' => ($contact['readonly'] ? $this->t('Unignore') : $this->t('Ignore')),
|
||||
'$insecure' => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
|
||||
'$info' => $localRelationship->info,
|
||||
'$cinfo' => ['info', '', $localRelationship->info, ''],
|
||||
'$blocked' => ($contact['blocked'] ? $this->t('Currently blocked') : ''),
|
||||
'$ignored' => ($contact['readonly'] ? $this->t('Currently ignored') : ''),
|
||||
'$collapsed' => (Contact\User::isCollapsed($contact['id'], DI::userSession()->getLocalUserId()) ? $this->t('Currently collapsed') : ''),
|
||||
'$pending' => $localRelationship->pending ? $this->t('Awaiting connection acknowledge') : '',
|
||||
'$blocked' => $localRelationship->blocked ? $this->t('Currently blocked') : '',
|
||||
'$ignored' => $localRelationship->ignored ? $this->t('Currently ignored') : '',
|
||||
'$collapsed' => $localRelationship->collapsed ? $this->t('Currently collapsed') : '',
|
||||
'$archived' => ($contact['archive'] ? $this->t('Currently archived') : ''),
|
||||
'$pending' => ($contact['pending'] ? $this->t('Awaiting connection acknowledge') : ''),
|
||||
'$insecure' => (in_array($contact['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::MAIL, Protocol::DIASPORA]) ? '' : $insecure),
|
||||
'$cinfo' => ['info', '', $localRelationship->info, ''],
|
||||
'$hidden' => ['hidden', $this->t('Hide this contact from others'), $localRelationship->hidden, $this->t('Replies/likes to your public posts <strong>may</strong> still be visible')],
|
||||
'$notify_new_posts' => ['notify_new_posts', $this->t('Notification for new posts'), ($localRelationship->notifyNewPosts), $this->t('Send a notification of every new post of this contact')],
|
||||
'$fetch_further_information' => $fetch_further_information,
|
||||
|
|
|
@ -36,7 +36,7 @@ class Maintenance extends BaseModule
|
|||
{
|
||||
protected function content(array $request = []): string
|
||||
{
|
||||
$reason = DI::config()->get('system', 'maintenance_reason');
|
||||
$reason = DI::config()->get('system', 'maintenance_reason') ?? '';
|
||||
|
||||
if ((substr(Strings::normaliseLink($reason), 0, 7) === 'http://') ||
|
||||
(substr(Strings::normaliseLink($reason), 0, 8) === 'https://')) {
|
||||
|
|
|
@ -61,7 +61,10 @@ class Token extends BaseApi
|
|||
}
|
||||
|
||||
if (empty($request['client_id']) && substr($authorization, 0, 6) == 'Basic ') {
|
||||
$datapair = explode(':', base64_decode(trim(substr($authorization, 6))));
|
||||
// Per RFC2617, usernames can't contain a colon but password can,
|
||||
// so we cut on the first colon to obtain the username and the password
|
||||
// @see https://www.rfc-editor.org/rfc/rfc2617#section-2
|
||||
$datapair = explode(':', base64_decode(trim(substr($authorization, 6))), 2);
|
||||
if (count($datapair) == 2) {
|
||||
$request['client_id'] = $datapair[0];
|
||||
$request['client_secret'] = $datapair[1];
|
||||
|
|
|
@ -98,7 +98,7 @@ class PasswordTooLong extends \Friendica\BaseModule
|
|||
'$return_url' => $request['return_url'] ?? '',
|
||||
|
||||
'$password_current' => ['password_current', $this->l10n->t('Current Password:'), '', $this->l10n->t('Your current password to confirm the changes'), 'required', 'autocomplete="off"'],
|
||||
'$password' => ['password', $this->l10n->t('New Password:'), '', $this->l10n->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).') . ' ' . $this->l10n->t('Password length is limited to 72 characters.'), 'required', 'autocomplete="off"', User::getPasswordRegExp()],
|
||||
'$password' => ['password', $this->l10n->t('New Password:'), '', $this->l10n->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces and accentuated letters.') . ' ' . $this->l10n->t('Password length is limited to 72 characters.'), 'required', 'autocomplete="off"', User::getPasswordRegExp()],
|
||||
'$password_confirm' => ['password_confirm', $this->l10n->t('Confirm:'), '', '', 'required', 'autocomplete="off"'],
|
||||
]);
|
||||
|
||||
|
|
|
@ -549,7 +549,7 @@ class Account extends BaseSettings
|
|||
|
||||
$notify_type = DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'notify_type');
|
||||
|
||||
$passwordRules = DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces, accentuated letters and colon (:).')
|
||||
$passwordRules = DI::l10n()->t('Allowed characters are a-z, A-Z, 0-9 and special characters except white spaces and accentuated letters.')
|
||||
. (PASSWORD_DEFAULT === PASSWORD_BCRYPT ? ' ' . DI::l10n()->t('Password length is limited to 72 characters.') : '');
|
||||
|
||||
$tpl = Renderer::getMarkupTemplate('settings/account.tpl');
|
||||
|
|
|
@ -25,6 +25,7 @@ use Friendica\App;
|
|||
use Friendica\BaseModule;
|
||||
use Friendica\Core\Addon;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\KeyValueStorage\Capabilities\IManageKeyValuePairs;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Network\HTTPException\NotFoundException;
|
||||
|
@ -35,13 +36,15 @@ class Statistics extends BaseModule
|
|||
{
|
||||
/** @var IManageConfigValues */
|
||||
protected $config;
|
||||
|
||||
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, IManageConfigValues $config, Response $response, array $server, array $parameters = [])
|
||||
/** @var IManageKeyValuePairs */
|
||||
protected $keyValue;
|
||||
|
||||
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, IManageConfigValues $config, IManageKeyValuePairs $keyValue, Response $response, array $server, array $parameters = [])
|
||||
{
|
||||
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
$this->config = $config;
|
||||
$this->keyValue = $keyValue;
|
||||
if (!$this->config->get("system", "nodeinfo")) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
@ -72,10 +75,10 @@ class Statistics extends BaseModule
|
|||
'network' => App::PLATFORM,
|
||||
'version' => App::VERSION . '-' . DB_UPDATE_VERSION,
|
||||
'registrations_open' => $registration_open,
|
||||
'total_users' => $this->config->get('nodeinfo', 'total_users'),
|
||||
'active_users_halfyear' => $this->config->get('nodeinfo', 'active_users_halfyear'),
|
||||
'active_users_monthly' => $this->config->get('nodeinfo', 'active_users_monthly'),
|
||||
'local_posts' => $this->config->get('nodeinfo', 'local_posts'),
|
||||
'total_users' => $this->keyValue->get('nodeinfo_total_users'),
|
||||
'active_users_halfyear' => $this->keyValue->get('nodeinfo_active_users_halfyear'),
|
||||
'active_users_monthly' => $this->keyValue->get('nodeinfo_active_users_monthly'),
|
||||
'local_posts' => $this->keyValue->get('nodeinfo_local_posts'),
|
||||
'services' => $services,
|
||||
], $services);
|
||||
|
||||
|
|
|
@ -614,7 +614,7 @@ class Notify extends BaseRepository
|
|||
|
||||
$emailBuilder->setHeader('Message-ID', $message_id);
|
||||
$log_msg = "No previous notification found for this parent:\n" .
|
||||
" parent: ${params['parent']}\n" . " uid : ${params['uid']}\n";
|
||||
" parent: {$params['parent']}\n" . " uid : {$params['uid']}\n";
|
||||
$this->logger->info($log_msg);
|
||||
} else {
|
||||
// If not, just "follow" the thread.
|
||||
|
|
|
@ -69,6 +69,10 @@ class HttpClient implements ICanSendHttpRequests
|
|||
$this->logger->debug('Request start.', ['url' => $url, 'method' => $method]);
|
||||
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
if (empty($host)) {
|
||||
throw new \InvalidArgumentException('Unable to retrieve the host in URL: ' . $url);
|
||||
}
|
||||
|
||||
if(!filter_var($host, FILTER_VALIDATE_IP) && !@dns_get_record($host . '.', DNS_A + DNS_AAAA) && !gethostbyname($host)) {
|
||||
$this->logger->debug('URL cannot be resolved.', ['url' => $url, 'callstack' => System::callstack(20)]);
|
||||
$this->profiler->stopRecording();
|
||||
|
|
|
@ -54,6 +54,8 @@ class Card extends BaseDataTransferObject
|
|||
protected $image;
|
||||
/** @var string */
|
||||
protected $blurhash;
|
||||
/** @var array */
|
||||
protected $history;
|
||||
|
||||
/**
|
||||
* Creates a card record from an attachment array.
|
||||
|
|
|
@ -25,7 +25,6 @@ use Friendica\BaseDataTransferObject;
|
|||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Protocol;
|
||||
use Friendica\Database\Database;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
|
||||
/**
|
||||
|
@ -45,9 +44,9 @@ class Stats extends BaseDataTransferObject
|
|||
public function __construct(IManageConfigValues $config, Database $database)
|
||||
{
|
||||
if (!empty($config->get('system', 'nodeinfo'))) {
|
||||
$this->user_count = intval($config->get('nodeinfo', 'total_users'));
|
||||
$this->status_count = $config->get('nodeinfo', 'local_posts') + $config->get('nodeinfo', 'local_comments');
|
||||
$this->domain_count = $database->count('gserver', ["`network` in (?, ?) AND NOT `failed`", Protocol::DFRN, Protocol::ACTIVITYPUB]);
|
||||
$this->user_count = intval(DI::keyValue()->get('nodeinfo_total_users'));
|
||||
$this->status_count = DI::keyValue()->get('nodeinfo_local_posts') + DI::keyValue()->get('nodeinfo_local_comments');
|
||||
$this->domain_count = $database->count('gserver', ["`network` in (?, ?) AND NOT `failed` AND NOT `blocked`", Protocol::DFRN, Protocol::ACTIVITYPUB]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1661,7 +1661,7 @@ class Transmitter
|
|||
*
|
||||
* } elseif (($type == 'Article') && empty($data['summary'])) {
|
||||
* $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
|
||||
* $summary = preg_replace_callback($regexp, ['self', 'mentionAddrCallback'], $body);
|
||||
* $summary = preg_replace_callback($regexp, [self::class, 'mentionAddrCallback'], $body);
|
||||
* $data['summary'] = BBCode::toPlaintext(Plaintext::shorten(self::removePictures($summary), 1000));
|
||||
* }
|
||||
*/
|
||||
|
|
|
@ -646,7 +646,7 @@ class ParseUrl
|
|||
$arr_tags = str_getcsv($string);
|
||||
if (count($arr_tags)) {
|
||||
// add the # sign to every tag
|
||||
array_walk($arr_tags, ['self', 'arrAddHashes']);
|
||||
array_walk($arr_tags, [self::class, 'arrAddHashes']);
|
||||
|
||||
return $arr_tags;
|
||||
}
|
||||
|
|
|
@ -141,7 +141,7 @@ class Proxy
|
|||
{
|
||||
$html = str_replace(Strings::normaliseLink(DI::baseUrl()) . '/', DI::baseUrl() . '/', $html);
|
||||
|
||||
return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', 'self::replaceUrl', $html);
|
||||
return preg_replace_callback('/(<img [^>]*src *= *["\'])([^"\']+)(["\'][^>]*>)/siU', [self::class, 'replaceUrl'], $html);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -57,7 +57,7 @@ class UpdateServerPeers
|
|||
$total = 0;
|
||||
$added = 0;
|
||||
foreach ($peers as $peer) {
|
||||
if (Network::isUrlBlocked('http://' . $peer)) {
|
||||
if (Network::isUrlBlocked('https://' . $peer)) {
|
||||
// Ignore blocked systems as soon as possible in the loop to avoid being slowed down by tar pits
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
use Friendica\Database\DBA;
|
||||
|
||||
if (!defined('DB_UPDATE_VERSION')) {
|
||||
define('DB_UPDATE_VERSION', 1511);
|
||||
define('DB_UPDATE_VERSION', 1512);
|
||||
}
|
||||
|
||||
return [
|
||||
|
|
|
@ -37,6 +37,8 @@ use Dice\Dice;
|
|||
use Friendica\App;
|
||||
use Friendica\Core\Cache;
|
||||
use Friendica\Core\Config;
|
||||
use Friendica\Core\Hooks\Capabilities\ICanManageInstances;
|
||||
use Friendica\Core\Hooks\Model\InstanceManager;
|
||||
use Friendica\Core\PConfig;
|
||||
use Friendica\Core\L10n;
|
||||
use Friendica\Core\Lock;
|
||||
|
@ -76,6 +78,12 @@ return [
|
|||
$_SERVER
|
||||
]
|
||||
],
|
||||
ICanManageInstances::class => [
|
||||
'instanceOf' => InstanceManager::class,
|
||||
'constructParams' => [
|
||||
[Dice::INSTANCE => Dice::SELF],
|
||||
],
|
||||
],
|
||||
Config\Util\ConfigFileManager::class => [
|
||||
'instanceOf' => Config\Factory\Config::class,
|
||||
'call' => [
|
||||
|
|
|
@ -30,6 +30,7 @@ use Friendica\Database\Definition\ViewDefinition;
|
|||
use Friendica\Test\DatabaseTestTrait;
|
||||
use Friendica\Test\Util\Database\StaticDatabase;
|
||||
use Friendica\Util\Profiler;
|
||||
use Psr\Log\NullLogger;
|
||||
|
||||
trait CreateDatabaseTrait
|
||||
{
|
||||
|
@ -45,7 +46,7 @@ trait CreateDatabaseTrait
|
|||
],
|
||||
]));
|
||||
|
||||
$database = new StaticDatabase($config, new Profiler($config), (new DbaDefinition($this->root->url()))->load(), (new ViewDefinition($this->root->url()))->load());
|
||||
$database = new StaticDatabase($config, new Profiler($config), (new DbaDefinition($this->root->url()))->load(), (new ViewDefinition($this->root->url()))->load(), new NullLogger());
|
||||
$database->setTestmode(true);
|
||||
|
||||
return $database;
|
||||
|
|
60
tests/Util/Hooks/InstanceMocks/FakeInstance.php
Normal file
60
tests/Util/Hooks/InstanceMocks/FakeInstance.php
Normal file
|
@ -0,0 +1,60 @@
|
|||
<?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\Util\Hooks\InstanceMocks;
|
||||
|
||||
use Friendica\Core\Hooks\Capabilities\IAmAStrategy;
|
||||
|
||||
class FakeInstance implements IAmADecoratedInterface, IAmAStrategy
|
||||
{
|
||||
protected $aText = null;
|
||||
protected $cBool = null;
|
||||
protected $bText = null;
|
||||
|
||||
public function __construct(string $aText = null, bool $cBool = null, string $bText = null)
|
||||
{
|
||||
$this->aText = $aText;
|
||||
$this->cBool = $cBool;
|
||||
$this->bText = $bText;
|
||||
}
|
||||
|
||||
public function createSomething(string $aText, bool $cBool, string $bText): string
|
||||
{
|
||||
$this->aText = $aText;
|
||||
$this->cBool = $cBool;
|
||||
$this->bText = $bText;
|
||||
}
|
||||
|
||||
public function getAText(): ?string
|
||||
{
|
||||
return $this->aText;
|
||||
}
|
||||
|
||||
public function getBText(): ?string
|
||||
{
|
||||
return $this->bText;
|
||||
}
|
||||
|
||||
public function getCBool(): ?bool
|
||||
{
|
||||
return $this->cBool;
|
||||
}
|
||||
}
|
59
tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php
Normal file
59
tests/Util/Hooks/InstanceMocks/FakeInstanceDecorator.php
Normal file
|
@ -0,0 +1,59 @@
|
|||
<?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\Util\Hooks\InstanceMocks;
|
||||
|
||||
class FakeInstanceDecorator implements IAmADecoratedInterface
|
||||
{
|
||||
public static $countInstance = 0;
|
||||
|
||||
/** @var IAmADecoratedInterface */
|
||||
protected $orig;
|
||||
protected $prefix = '';
|
||||
|
||||
public function __construct(IAmADecoratedInterface $orig, string $prefix = '')
|
||||
{
|
||||
$this->orig = $orig;
|
||||
$this->prefix = $prefix;
|
||||
|
||||
self::$countInstance++;
|
||||
}
|
||||
|
||||
public function createSomething(string $aText, bool $cBool, string $bText): string
|
||||
{
|
||||
return $this->orig->createSomething($aText, $cBool, $bText);
|
||||
}
|
||||
|
||||
public function getAText(): ?string
|
||||
{
|
||||
return $this->prefix . $this->orig->getAText();
|
||||
}
|
||||
|
||||
public function getBText(): ?string
|
||||
{
|
||||
return $this->prefix . $this->orig->getBText();
|
||||
}
|
||||
|
||||
public function getCBool(): ?bool
|
||||
{
|
||||
return $this->prefix . $this->orig->getCBool();
|
||||
}
|
||||
}
|
33
tests/Util/Hooks/InstanceMocks/IAmADecoratedInterface.php
Normal file
33
tests/Util/Hooks/InstanceMocks/IAmADecoratedInterface.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?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\Util\Hooks\InstanceMocks;
|
||||
|
||||
interface IAmADecoratedInterface
|
||||
{
|
||||
public function createSomething(string $aText, bool $cBool, string $bText): string;
|
||||
|
||||
public function getAText(): ?string;
|
||||
|
||||
public function getBText(): ?string;
|
||||
|
||||
public function getCBool(): ?bool;
|
||||
}
|
35
tests/Util/SerializableObjectDouble.php
Normal file
35
tests/Util/SerializableObjectDouble.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?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\Util;
|
||||
|
||||
class SerializableObjectDouble implements \Serializable
|
||||
{
|
||||
public function serialize()
|
||||
{
|
||||
return '\'serialized\'';
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
{
|
||||
return '\'unserialized\'';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
use Friendica\Test\Util\SerializableObjectDouble;
|
||||
use ParagonIE\HiddenString\HiddenString;
|
||||
|
||||
return [
|
||||
'object' => [
|
||||
'toString' => new HiddenString('test'),
|
||||
'serializable' => new SerializableObjectDouble(),
|
||||
],
|
||||
];
|
|
@ -25,7 +25,6 @@ use Dice\Dice;
|
|||
use Friendica\App;
|
||||
use Friendica\Core\Cache\Capability\ICanCache;
|
||||
use Friendica\Core\Cache\Capability\ICanCacheInMemory;
|
||||
use Friendica\Core\Config\Model\Config;
|
||||
use Friendica\Core\Config\ValueObject\Cache;
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Lock\Capability\ICanLock;
|
||||
|
@ -33,7 +32,6 @@ use Friendica\Database\Database;
|
|||
use Friendica\Test\Util\VFSTrait;
|
||||
use Friendica\Util\BasePath;
|
||||
use Friendica\Core\Config\Util\ConfigFileManager;
|
||||
use Friendica\Util\Profiler;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
|
@ -53,7 +51,18 @@ class DependencyCheckTest extends TestCase
|
|||
$this->setUpVfsDir();
|
||||
|
||||
$this->dice = (new Dice())
|
||||
->addRules(include __DIR__ . '/../../static/dependencies.config.php');
|
||||
->addRules(include __DIR__ . '/../../static/dependencies.config.php')
|
||||
->addRule(BasePath::class, [
|
||||
'constructParams' => [
|
||||
$this->root->url(),
|
||||
[],
|
||||
],
|
||||
])
|
||||
->addRule(LoggerInterface::class, ['constructParams' => ['test']]);
|
||||
|
||||
/** @var IManageConfigValues $config */
|
||||
$config = $this->dice->create(IManageConfigValues::class);
|
||||
$config->set('system', 'logfile', $this->root->url() . '/logs/friendica.log');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,7 +151,7 @@ class DependencyCheckTest extends TestCase
|
|||
public function testLogger()
|
||||
{
|
||||
/** @var LoggerInterface $logger */
|
||||
$logger = $this->dice->create(LoggerInterface::class, ['test']);
|
||||
$logger = $this->dice->create(LoggerInterface::class, [['$channel' => 'test']]);
|
||||
|
||||
self::assertInstanceOf(LoggerInterface::class, $logger);
|
||||
}
|
||||
|
@ -154,7 +163,7 @@ class DependencyCheckTest extends TestCase
|
|||
$config->set('system', 'dlogfile', $this->root->url() . '/friendica.log');
|
||||
|
||||
/** @var LoggerInterface $logger */
|
||||
$logger = $this->dice->create('$devLogger', ['dev']);
|
||||
$logger = $this->dice->create('$devLogger', [['$channel' => 'dev']]);
|
||||
|
||||
self::assertInstanceOf(LoggerInterface::class, $logger);
|
||||
}
|
||||
|
@ -164,6 +173,7 @@ class DependencyCheckTest extends TestCase
|
|||
/** @var ICanCache $cache */
|
||||
$cache = $this->dice->create(ICanCache::class);
|
||||
|
||||
|
||||
self::assertInstanceOf(ICanCache::class, $cache);
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ class DatabaseCacheTest extends CacheTest
|
|||
$dbaDefinition = (new DbaDefinition($configCache->get('system', 'basepath')))->load();
|
||||
$viewDefinition = (new ViewDefinition($configCache->get('system', 'basepath')))->load();
|
||||
|
||||
$dba = new StaticDatabase($config, $profiler, $dbaDefinition, $viewDefinition);
|
||||
$dba = new StaticDatabase($config, $profiler, $dbaDefinition, $viewDefinition, new NullLogger());
|
||||
|
||||
$this->cache = new Cache\Type\DatabaseCache('database', $dba);
|
||||
return $this->cache;
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace Friendica\Test\src\Core\Config\Util;
|
|||
|
||||
use Friendica\Core\Config\Util\ConfigFileTransformer;
|
||||
use Friendica\Test\MockedTest;
|
||||
use Friendica\Test\Util\SerializableObjectDouble;
|
||||
use ParagonIE\HiddenString\HiddenString;
|
||||
use function PHPUnit\Framework\assertEquals;
|
||||
|
||||
class ConfigFileTransformerTest extends MockedTest
|
||||
{
|
||||
|
@ -45,9 +48,34 @@ class ConfigFileTransformerTest extends MockedTest
|
|||
'configFile' => (dirname(__DIR__, 4) . '/datasets/config/transformer/object.node.config.php'),
|
||||
'assertException' => true,
|
||||
],
|
||||
'ressource_invalid' => [
|
||||
'resource' => [
|
||||
'configFile' => (dirname(__DIR__, 4) . '/datasets/config/transformer/ressource.node.config.php'),
|
||||
'assertException' => true,
|
||||
'assertException' => false,
|
||||
'assertion' => <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'ressource' => [
|
||||
'ressources_not_allowed' => '',
|
||||
],
|
||||
];
|
||||
|
||||
EOF,
|
||||
],
|
||||
'object_valid' => [
|
||||
'configFile' => (dirname(__DIR__, 4) . '/datasets/config/transformer/object_valid.node.config.php'),
|
||||
'assertException' => false,
|
||||
'assertion' => <<<EOF
|
||||
<?php
|
||||
|
||||
return [
|
||||
'object' => [
|
||||
'toString' => 'test',
|
||||
'serializable' => 'serialized',
|
||||
],
|
||||
];
|
||||
|
||||
EOF,
|
||||
],
|
||||
'test_types' => [
|
||||
'configFile' => (dirname(__DIR__, 4) . '/datasets/config/transformer/types.node.config.php'),
|
||||
|
@ -63,7 +91,7 @@ class ConfigFileTransformerTest extends MockedTest
|
|||
*
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testConfigFile(string $configFile, bool $assertException = false)
|
||||
public function testConfigFile(string $configFile, bool $assertException = false, $assertion = null)
|
||||
{
|
||||
$dataArray = include $configFile;
|
||||
|
||||
|
@ -73,6 +101,10 @@ class ConfigFileTransformerTest extends MockedTest
|
|||
|
||||
$newConfig = ConfigFileTransformer::encode($dataArray);
|
||||
|
||||
self::assertEquals(file_get_contents($configFile), $newConfig);
|
||||
if (empty($assertion)) {
|
||||
self::assertEquals(file_get_contents($configFile), $newConfig);
|
||||
} else {
|
||||
self::assertEquals($assertion, $newConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
258
tests/src/Core/Hooks/Model/InstanceManagerTest.php
Normal file
258
tests/src/Core/Hooks/Model/InstanceManagerTest.php
Normal file
|
@ -0,0 +1,258 @@
|
|||
<?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\Core\Hooks\Model;
|
||||
|
||||
use Dice\Dice;
|
||||
use Friendica\Core\Hooks\Model\InstanceManager;
|
||||
use Friendica\Test\MockedTest;
|
||||
use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstance;
|
||||
use Friendica\Test\Util\Hooks\InstanceMocks\FakeInstanceDecorator;
|
||||
use Friendica\Test\Util\Hooks\InstanceMocks\IAmADecoratedInterface;
|
||||
|
||||
class InstanceManagerTest extends MockedTest
|
||||
{
|
||||
public function testEqualButNotSameInstance()
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class);
|
||||
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake');
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake');
|
||||
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
FakeInstanceDecorator::$countInstance = 0;
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function dataTests(): array
|
||||
{
|
||||
return [
|
||||
'only_a' => [
|
||||
'aString' => 'test',
|
||||
],
|
||||
'a_b' => [
|
||||
'aString' => 'test',
|
||||
'cBool' => false,
|
||||
'bString' => 'test23',
|
||||
|
||||
],
|
||||
'a_c' => [
|
||||
'aString' => 'test',
|
||||
'cBool' => false,
|
||||
'bString' => null,
|
||||
],
|
||||
'a_b_c' => [
|
||||
'aString' => 'test',
|
||||
'cBool' => false,
|
||||
'bString' => 'test23',
|
||||
],
|
||||
'null' => [],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testInstanceWithConstructorAnonymArgs(string $aString = null, bool $cBool = null, string $bString = null)
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$args = [];
|
||||
|
||||
if (isset($aString)) {
|
||||
$args[] = $aString;
|
||||
}
|
||||
if (isset($bString)) {
|
||||
$args[] = $bString;
|
||||
}
|
||||
if (isset($cBool)) {
|
||||
$args[] = $cBool;
|
||||
}
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args);
|
||||
|
||||
/** @var IAmADecoratedInterface $getInstanceA */
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake');
|
||||
/** @var IAmADecoratedInterface $getInstanceB */
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake');
|
||||
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
self::assertEquals($aString, $getInstanceA->getAText());
|
||||
self::assertEquals($aString, $getInstanceB->getAText());
|
||||
self::assertEquals($bString, $getInstanceA->getBText());
|
||||
self::assertEquals($bString, $getInstanceB->getBText());
|
||||
self::assertEquals($cBool, $getInstanceA->getCBool());
|
||||
self::assertEquals($cBool, $getInstanceB->getCBool());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testInstanceConstructorAndGetInstanceWithNamedArgs(string $aString = null, bool $cBool = null, string $bString = null)
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$args = [];
|
||||
|
||||
if (isset($aString)) {
|
||||
$args[] = $aString;
|
||||
}
|
||||
if (isset($cBool)) {
|
||||
$args[] = $cBool;
|
||||
}
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args);
|
||||
|
||||
/** @var IAmADecoratedInterface $getInstanceA */
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]);
|
||||
/** @var IAmADecoratedInterface $getInstanceB */
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]);
|
||||
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
self::assertEquals($aString, $getInstanceA->getAText());
|
||||
self::assertEquals($aString, $getInstanceB->getAText());
|
||||
self::assertEquals($bString, $getInstanceA->getBText());
|
||||
self::assertEquals($bString, $getInstanceB->getBText());
|
||||
self::assertEquals($cBool, $getInstanceA->getCBool());
|
||||
self::assertEquals($cBool, $getInstanceB->getCBool());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testInstanceWithTwoStrategies(string $aString = null, bool $cBool = null, string $bString = null)
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$args = [];
|
||||
|
||||
if (isset($aString)) {
|
||||
$args[] = $aString;
|
||||
}
|
||||
if (isset($cBool)) {
|
||||
$args[] = $cBool;
|
||||
}
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args);
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args);
|
||||
|
||||
/** @var IAmADecoratedInterface $getInstanceA */
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]);
|
||||
/** @var IAmADecoratedInterface $getInstanceB */
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]);
|
||||
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
self::assertEquals($aString, $getInstanceA->getAText());
|
||||
self::assertEquals($aString, $getInstanceB->getAText());
|
||||
self::assertEquals($bString, $getInstanceA->getBText());
|
||||
self::assertEquals($bString, $getInstanceB->getBText());
|
||||
self::assertEquals($cBool, $getInstanceA->getCBool());
|
||||
self::assertEquals($cBool, $getInstanceB->getCBool());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testDecorator(string $aString = null, bool $cBool = null, string $bString = null)
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$args = [];
|
||||
|
||||
if (isset($aString)) {
|
||||
$args[] = $aString;
|
||||
}
|
||||
if (isset($cBool)) {
|
||||
$args[] = $cBool;
|
||||
}
|
||||
|
||||
$prefix = 'prefix1';
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args);
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args);
|
||||
$instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class, [$prefix]);
|
||||
|
||||
/** @var IAmADecoratedInterface $getInstanceA */
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]);
|
||||
/** @var IAmADecoratedInterface $getInstanceB */
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]);
|
||||
|
||||
self::assertEquals(2, FakeInstanceDecorator::$countInstance);
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
self::assertEquals($prefix . $aString, $getInstanceA->getAText());
|
||||
self::assertEquals($prefix . $aString, $getInstanceB->getAText());
|
||||
self::assertEquals($prefix . $bString, $getInstanceA->getBText());
|
||||
self::assertEquals($prefix . $bString, $getInstanceB->getBText());
|
||||
self::assertEquals($prefix . $cBool, $getInstanceA->getCBool());
|
||||
self::assertEquals($prefix . $cBool, $getInstanceB->getCBool());
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataTests
|
||||
*/
|
||||
public function testTwoDecoratorWithPrefix(string $aString = null, bool $cBool = null, string $bString = null)
|
||||
{
|
||||
$instance = new InstanceManager(new Dice());
|
||||
|
||||
$args = [];
|
||||
|
||||
if (isset($aString)) {
|
||||
$args[] = $aString;
|
||||
}
|
||||
if (isset($cBool)) {
|
||||
$args[] = $cBool;
|
||||
}
|
||||
|
||||
$prefix = 'prefix1';
|
||||
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake', FakeInstance::class, $args);
|
||||
$instance->registerStrategy(IAmADecoratedInterface::class, 'fake23', FakeInstance::class, $args);
|
||||
$instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class, [$prefix]);
|
||||
$instance->registerDecorator(IAmADecoratedInterface::class, FakeInstanceDecorator::class);
|
||||
|
||||
/** @var IAmADecoratedInterface $getInstanceA */
|
||||
$getInstanceA = $instance->getInstance(IAmADecoratedInterface::class, 'fake', [$bString]);
|
||||
/** @var IAmADecoratedInterface $getInstanceB */
|
||||
$getInstanceB = $instance->getInstance(IAmADecoratedInterface::class, 'fake23', [$bString]);
|
||||
|
||||
self::assertEquals(4, FakeInstanceDecorator::$countInstance);
|
||||
self::assertEquals($getInstanceA, $getInstanceB);
|
||||
self::assertNotSame($getInstanceA, $getInstanceB);
|
||||
self::assertEquals($prefix . $aString, $getInstanceA->getAText());
|
||||
self::assertEquals($prefix . $aString, $getInstanceB->getAText());
|
||||
self::assertEquals($prefix . $bString, $getInstanceA->getBText());
|
||||
self::assertEquals($prefix . $bString, $getInstanceB->getBText());
|
||||
self::assertEquals($prefix . $cBool, $getInstanceA->getCBool());
|
||||
self::assertEquals($prefix . $cBool, $getInstanceB->getCBool());
|
||||
}
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Test\src\Core\Logger;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Test\MockedTest;
|
||||
use Friendica\Core\Logger\Util\Introspection;
|
||||
use Mockery\MockInterface;
|
||||
|
@ -41,6 +42,10 @@ abstract class AbstractLoggerTest extends MockedTest
|
|||
* @var Introspection|MockInterface
|
||||
*/
|
||||
protected $introspection;
|
||||
/**
|
||||
* @var IManageConfigValues|MockInterface
|
||||
*/
|
||||
protected $config;
|
||||
|
||||
/**
|
||||
* Returns the content of the current logger instance
|
||||
|
@ -68,6 +73,8 @@ abstract class AbstractLoggerTest extends MockedTest
|
|||
'line' => self::LINE,
|
||||
'function' => self::FUNC
|
||||
]);
|
||||
|
||||
$this->config = \Mockery::mock(IManageConfigValues::class);
|
||||
}
|
||||
|
||||
public function assertLogline($string)
|
||||
|
|
|
@ -62,7 +62,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
$this->logfile = vfsStream::newFile('friendica.log')
|
||||
->at($this->root);
|
||||
|
||||
$logger = new StreamLogger('test', $this->logfile->url(), $this->introspection, $this->fileSystem, $level);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($this->logfile->url())->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, $level);
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
@ -75,43 +77,6 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
return $this->logfile->getContent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if a stream is working
|
||||
*/
|
||||
public function testStream()
|
||||
{
|
||||
$logfile = vfsStream::newFile('friendica.log')
|
||||
->at($this->root);
|
||||
|
||||
$filehandler = fopen($logfile->url(), 'ab');
|
||||
|
||||
$logger = new \Friendica\Core\Logger\Type\StreamLogger('test', $filehandler, $this->introspection, $this->fileSystem);
|
||||
$logger->emergency('working');
|
||||
|
||||
$text = $logfile->getContent();
|
||||
|
||||
self::assertLogline($text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the close statement is working
|
||||
*/
|
||||
public function testClose()
|
||||
{
|
||||
$logfile = vfsStream::newFile('friendica.log')
|
||||
->at($this->root);
|
||||
|
||||
$logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem);
|
||||
$logger->emergency('working');
|
||||
$logger->close();
|
||||
// close doesn't affect
|
||||
$logger->emergency('working too');
|
||||
|
||||
$text = $logfile->getContent();
|
||||
|
||||
self::assertLoglineNums(2, $text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when a file isn't set
|
||||
*/
|
||||
|
@ -120,7 +85,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
$this->expectException(LoggerArgumentException::class);
|
||||
$this->expectExceptionMessage("Missing stream URL.");
|
||||
|
||||
$logger = new StreamLogger('test', '', $this->introspection, $this->fileSystem);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('')->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem);
|
||||
|
||||
$logger->emergency('not working');
|
||||
}
|
||||
|
@ -130,13 +97,14 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
*/
|
||||
public function testWrongUrl()
|
||||
{
|
||||
$this->expectException(LoggerException::class);
|
||||
$this->expectExceptionMessage("Cannot create stream.");
|
||||
$this->expectException(LoggerArgumentException::class);
|
||||
|
||||
$logfile = vfsStream::newFile('friendica.log')
|
||||
->at($this->root)->chmod(0);
|
||||
|
||||
$logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem);
|
||||
|
||||
$logger->emergency('not working');
|
||||
}
|
||||
|
@ -164,7 +132,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
$this->expectException(LogLevelException::class);
|
||||
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
|
||||
|
||||
$logger = new StreamLogger('test', 'file.text', $this->introspection, $this->fileSystem, 'NOPE');
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('file.text')->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem, 'NOPE');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,7 +148,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
$logfile = vfsStream::newFile('friendica.log')
|
||||
->at($this->root);
|
||||
|
||||
$logger = new StreamLogger('test', $logfile->url(), $this->introspection, $this->fileSystem);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn($logfile->url())->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem);
|
||||
|
||||
$logger->log('NOPE', 'a test');
|
||||
}
|
||||
|
@ -191,7 +163,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
$this->expectException(LoggerArgumentException::class);
|
||||
$this->expectExceptionMessage("A stream must either be a resource or a string.");
|
||||
|
||||
$logger = new StreamLogger('test', null, $this->introspection, $this->fileSystem);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn(null)->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -207,7 +181,9 @@ class StreamLoggerTest extends AbstractLoggerTest
|
|||
|
||||
chdir($this->root->getChild('logs')->url());
|
||||
|
||||
$logger = new StreamLogger('test', '../friendica.log' , $this->introspection, $this->fileSystem);
|
||||
$this->config->shouldReceive('get')->with('system', 'logfile')->andReturn('../friendica.log')->once();
|
||||
|
||||
$logger = new StreamLogger('test', $this->config, $this->introspection, $this->fileSystem);
|
||||
|
||||
$logger->info('Test');
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ class SyslogLoggerTest extends AbstractLoggerTest
|
|||
parent::setUp();
|
||||
|
||||
$this->introspection->shouldReceive('addClasses')->with([SyslogLogger::class]);
|
||||
$this->config->shouldReceive('get')->with('system', 'syslog_flags')->andReturn(SyslogLogger::DEFAULT_FLAGS)
|
||||
->once();
|
||||
$this->config->shouldReceive('get')->with('system', 'syslog_facility')
|
||||
->andReturn(SyslogLogger::DEFAULT_FACILITY)->once();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,7 +58,7 @@ class SyslogLoggerTest extends AbstractLoggerTest
|
|||
*/
|
||||
protected function getInstance($level = LogLevel::DEBUG)
|
||||
{
|
||||
$this->logger = new SyslogLoggerWrapper('test', $this->introspection, $level);
|
||||
$this->logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, $level);
|
||||
|
||||
return $this->logger;
|
||||
}
|
||||
|
@ -68,7 +72,7 @@ class SyslogLoggerTest extends AbstractLoggerTest
|
|||
$this->expectException(LogLevelException::class);
|
||||
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
|
||||
|
||||
$logger = new SyslogLoggerWrapper('test', $this->introspection, 'NOPE');
|
||||
$logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection, 'NOPE');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -79,7 +83,7 @@ class SyslogLoggerTest extends AbstractLoggerTest
|
|||
$this->expectException(LogLevelException::class);
|
||||
$this->expectExceptionMessageMatches("/The level \".*\" is not valid./");
|
||||
|
||||
$logger = new SyslogLoggerWrapper('test', $this->introspection);
|
||||
$logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection);
|
||||
|
||||
$logger->log('NOPE', 'a test');
|
||||
}
|
||||
|
@ -90,7 +94,7 @@ class SyslogLoggerTest extends AbstractLoggerTest
|
|||
*/
|
||||
public function testClose()
|
||||
{
|
||||
$logger = new SyslogLoggerWrapper('test', $this->introspection);
|
||||
$logger = new SyslogLoggerWrapper('test', $this->config, $this->introspection);
|
||||
$logger->emergency('test');
|
||||
$logger->close();
|
||||
// Reopened itself
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
namespace Friendica\Test\src\Core\Logger;
|
||||
|
||||
use Friendica\Core\Config\Capability\IManageConfigValues;
|
||||
use Friendica\Core\Logger\Type\SyslogLogger;
|
||||
use Friendica\Core\Logger\Util\Introspection;
|
||||
use Psr\Log\LogLevel;
|
||||
|
@ -32,9 +33,9 @@ class SyslogLoggerWrapper extends SyslogLogger
|
|||
{
|
||||
private $content;
|
||||
|
||||
public function __construct($channel, Introspection $introspection, $level = LogLevel::NOTICE, $logOpts = LOG_PID, $logFacility = LOG_USER)
|
||||
public function __construct($channel, IManageConfigValues $config, Introspection $introspection, $level = LogLevel::NOTICE)
|
||||
{
|
||||
parent::__construct($channel, $introspection, $level, $logOpts, $logFacility);
|
||||
parent::__construct($channel, $config, $introspection, $level);
|
||||
|
||||
$this->content = '';
|
||||
}
|
||||
|
|
|
@ -200,47 +200,6 @@ class BaseURLTest extends MockedTest
|
|||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the default config determination
|
||||
* @dataProvider dataDefault
|
||||
*/
|
||||
public function testCheck($server, $input, $assert)
|
||||
{
|
||||
$configMock = \Mockery::mock(IManageConfigValues::class);
|
||||
$configMock->shouldReceive('get')->with('config', 'hostname')->andReturn($input['hostname']);
|
||||
$configMock->shouldReceive('get')->with('system', 'urlpath')->andReturn($input['urlPath']);
|
||||
$configMock->shouldReceive('get')->with('system', 'ssl_policy')->andReturn($input['sslPolicy']);
|
||||
$configMock->shouldReceive('get')->with('system', 'url')->andReturn($input['url']);
|
||||
|
||||
// If we don't have an urlPath as an input, we assert it, we will save it to the DB for the next time
|
||||
if (!isset($input['urlPath']) && isset($assert['urlPath'])) {
|
||||
$configMock->shouldReceive('set')->with('system', 'urlpath', $assert['urlPath'])->once();
|
||||
}
|
||||
|
||||
// If we don't have the ssl_policy as an input, we assert it, we will save it to the DB for the next time
|
||||
if (!isset($input['sslPolicy']) && isset($assert['sslPolicy'])) {
|
||||
$configMock->shouldReceive('set')->with('system', 'ssl_policy', $assert['sslPolicy'])->once();
|
||||
}
|
||||
|
||||
// If we don't have the hostname as an input, we assert it, we will save it to the DB for the next time
|
||||
if (empty($input['hostname']) && !empty($assert['hostname'])) {
|
||||
$configMock->shouldReceive('set')->with('config', 'hostname', $assert['hostname'])->once();
|
||||
}
|
||||
|
||||
// If we don't have an URL at first, but we assert it, we will save it to the DB for the next time
|
||||
if (empty($input['url']) && !empty($assert['url'])) {
|
||||
$configMock->shouldReceive('set')->with('system', 'url', $assert['url'])->once();
|
||||
}
|
||||
|
||||
$baseUrl = new BaseURL($configMock, $server);
|
||||
|
||||
self::assertEquals($assert['hostname'], $baseUrl->getHostname());
|
||||
self::assertEquals($assert['urlPath'], $baseUrl->getUrlPath());
|
||||
self::assertEquals($assert['sslPolicy'], $baseUrl->getSSLPolicy());
|
||||
self::assertEquals($assert['scheme'], $baseUrl->getScheme());
|
||||
self::assertEquals($assert['url'], $baseUrl->get());
|
||||
}
|
||||
|
||||
public function dataSave()
|
||||
{
|
||||
return [
|
||||
|
|
17
update.php
17
update.php
|
@ -1227,3 +1227,20 @@ function update_1510()
|
|||
}
|
||||
return Update::SUCCESS;
|
||||
}
|
||||
|
||||
function update_1512()
|
||||
{
|
||||
DI::keyValue()->set('nodeinfo_total_users', DI::config()->get('nodeinfo', 'total_users'));
|
||||
DI::keyValue()->set('nodeinfo_active_users_halfyear', DI::config()->get('nodeinfo', 'active_users_halfyear'));
|
||||
DI::keyValue()->set('nodeinfo_active_users_monthly', DI::config()->get('nodeinfo', 'active_users_monthly'));
|
||||
DI::keyValue()->set('nodeinfo_active_users_weekly', DI::config()->get('nodeinfo', 'active_users_weekly'));
|
||||
DI::keyValue()->set('nodeinfo_local_posts', DI::config()->get('nodeinfo', 'local_posts'));
|
||||
DI::keyValue()->set('nodeinfo_local_comments', DI::config()->get('nodeinfo', 'local_comments'));
|
||||
|
||||
DI::config()->delete('nodeinfo', 'total_users');
|
||||
DI::config()->delete('nodeinfo', 'active_users_halfyear');
|
||||
DI::config()->delete('nodeinfo', 'active_users_monthly');
|
||||
DI::config()->delete('nodeinfo', 'active_users_weekly');
|
||||
DI::config()->delete('nodeinfo', 'local_posts');
|
||||
DI::config()->delete('nodeinfo', 'local_comments');
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,4 @@
|
|||
|
||||
<div id="contact-edit-wrapper">
|
||||
|
||||
{{* Insert Tab-Nav *}}
|
||||
|
@ -12,7 +13,7 @@
|
|||
|
||||
{{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
|
||||
<div id="contact-edit-actions">
|
||||
<a class="btn" rel="#contact-actions-menu" href="#" id="contact-edit-actions-button">{{$contact_action_button}}</a>
|
||||
<button class="btn" id="contact-edit-actions-button">{{$contact_action_button}}</button>
|
||||
|
||||
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup">
|
||||
{{if $contact_actions.follow}}<li role="menuitem"><a href="{{$contact_actions.follow.url}}" title="{{$contact_actions.follow.title}}">{{$contact_actions.follow.label}}</a></li>{{/if}}
|
||||
|
@ -23,7 +24,6 @@
|
|||
<li class="divider"></li>
|
||||
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
|
||||
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
|
||||
<li role="menuitem"><a href="#" title="{{$contact_actions.collapse.title}}" onclick="window.location.href='{{$contact_actions.collapse.url}}'; return false;">{{$contact_actions.collapse.label}}</a></li>
|
||||
{{if $contact_actions.revoke_follow.url}}<li role="menuitem"><a href="{{$contact_actions.revoke_follow.url}}" title="{{$contact_actions.revoke_follow.title}}">{{$contact_actions.revoke_follow.label}}</a></li>{{/if}}
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -34,9 +34,9 @@
|
|||
|
||||
{{if $poll_enabled}}
|
||||
<li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
|
||||
{{if $poll_interval}}
|
||||
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval nofilter}}
|
||||
{{/if}}
|
||||
{{if $poll_interval}}
|
||||
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval nofilter}}
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
|
@ -45,13 +45,12 @@
|
|||
{{if $blocked && !$pending}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
|
||||
{{if $pending}}<li><div id="pending-message">{{$pending}}</div></li>{{/if}}
|
||||
{{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
|
||||
{{if $collapsed}}<li><div id="collapse-message">{{$collapsed}}</div></li>{{/if}}
|
||||
{{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
|
||||
</ul>
|
||||
</div> {{* End of contact-edit-status-wrapper *}}
|
||||
|
||||
{{* Some information about the contact from the profile *}}
|
||||
<dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$url}}">{{$profileurl}}</a></dd></dl>
|
||||
<dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$profileurl}}">{{$profileurl}}</a></dd></dl>
|
||||
{{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location nofilter}}</dd></dl>{{/if}}
|
||||
{{if $xmpp}}<dl><dt>{{$xmpp_label}}</dt><dd>{{$xmpp}}</dd></dl>{{/if}}
|
||||
{{if $matrix}}<dl><dt>{{$matrix_label}}</dt><dd>{{$matrix}}</dd></dl>{{/if}}
|
||||
|
@ -63,40 +62,39 @@
|
|||
|
||||
<hr />
|
||||
|
||||
{{if $contact_settings_label}}
|
||||
{{if $contact_settings_label}}
|
||||
<h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
|
||||
<div id="contact-edit-settings">
|
||||
<input type="hidden" name="contact_id" value="{{$contact_id}}">
|
||||
|
||||
<div id="contact-edit-end"></div>
|
||||
{{include file="field_checkbox.tpl" field=$notify_new_posts}}
|
||||
{{if $fetch_further_information}}
|
||||
{{include file="field_select.tpl" field=$fetch_further_information}}
|
||||
{{if $fetch_further_information.2 == 2 || $fetch_further_information.2 == 3}} {{include file="field_textarea.tpl" field=$ffi_keyword_denylist}} {{/if}}
|
||||
{{/if}}
|
||||
{{if $allow_remote_self}}
|
||||
{{include file="field_select.tpl" field=$remote_self}}
|
||||
{{/if}}
|
||||
<div id="contact-edit-end"></div>
|
||||
|
||||
{{include file="field_checkbox.tpl" field=$hidden}}
|
||||
{{include file="field_checkbox.tpl" field=$notify_new_posts}}
|
||||
|
||||
<div id="contact-edit-info-wrapper">
|
||||
<h4>{{$lbl_info1}}</h4>
|
||||
<textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
|
||||
</div>
|
||||
<div id="contact-edit-info-end"></div>
|
||||
{{if $fetch_further_information}}
|
||||
{{include file="field_select.tpl" field=$fetch_further_information}}
|
||||
{{if $fetch_further_information.2 == 2 || $fetch_further_information.2 == 3}} {{include file="field_textarea.tpl" field=$ffi_keyword_denylist}} {{/if}}
|
||||
{{/if}}
|
||||
|
||||
{{if $reason}}
|
||||
{{if $allow_remote_self}}
|
||||
{{include file="field_select.tpl" field=$remote_self}}
|
||||
{{/if}}
|
||||
|
||||
{{include file="field_checkbox.tpl" field=$hidden}}
|
||||
|
||||
{{include file="field_textarea.tpl" field=$cinfo}}
|
||||
|
||||
{{if $reason}}
|
||||
<div id="contact-info-wrapper">
|
||||
<h4>{{$lbl_info2}}</h4>
|
||||
<p>{{$reason}}</p>
|
||||
</div>
|
||||
<div id="contact-info-end"></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit}}" />
|
||||
{{/if}}
|
||||
</div>
|
||||
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit}}" />
|
||||
{{/if}}
|
||||
|
||||
<div class="contact-edit-submit-end clearfix"></div>
|
||||
|
||||
</form>{{* End of the form *}}
|
||||
|
|
|
@ -69,8 +69,8 @@
|
|||
<img role="presentation" id="profile-rotator" src="images/rotator.gif" alt="{{$l10n.wait}}" title="{{$l10n.wait}}" style="display: none;" />
|
||||
</span>
|
||||
<span role="presentation" id="character-counter" class="grey text-info"></span>
|
||||
<button type="button" class="btn btn-defaul btn-sm" onclick="preview_comment({{$id}});" id="comment-edit-preview-link-{{$id}}" tabindex="5"><i class="fa fa-eye"></i> {{$l10n.preview}}</button>
|
||||
<button type="submit" class="btn btn-primary btn-sm" id="comment-edit-submit-{{$id}}" name="submit" tabindex="4"><i class="fa fa-envelope"></i> {{$l10n.submit}}</button>
|
||||
<button type="button" class="btn btn-defaul" onclick="preview_comment({{$id}});" id="comment-edit-preview-link-{{$id}}" tabindex="5"><i class="fa fa-eye"></i> {{$l10n.preview}}</button>
|
||||
<button type="submit" class="btn btn-primary" id="comment-edit-submit-{{$id}}" name="submit" tabindex="4"><i class="fa fa-envelope"></i> {{$l10n.submit}}</button>
|
||||
</p>
|
||||
|
||||
<div id="comment-edit-preview-{{$id}}" class="comment-edit-preview" style="display:none;"></div>
|
||||
|
|
|
@ -367,6 +367,10 @@ btn-eventnav:hover {
|
|||
aside .badge {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.forum-widget-entry .badge,
|
||||
.sidebar-group-li .badge {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* disabled elements */
|
||||
.community-content-wrapper > h3,
|
||||
|
@ -807,13 +811,15 @@ nav.navbar .nav > li > button:focus {
|
|||
}
|
||||
#offcanvasUsermenu a {
|
||||
display: block;
|
||||
margin: -10px -15px;
|
||||
padding: 10px 15px;
|
||||
}
|
||||
#offcanvasUsermenu li.nav-sitename {
|
||||
font-weight: bold;
|
||||
}
|
||||
#topbar-first .dropdown.account li#nav-sitename {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
font-weight: bold;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
@ -822,6 +828,10 @@ nav.navbar .nav > li > button:focus {
|
|||
background-color: $nav_bg;
|
||||
}
|
||||
/* Nav Search */
|
||||
.menu-popup {
|
||||
max-height: calc(100vh - 55px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
#topbar-first #search-box .navbar-form {
|
||||
margin: 0px;
|
||||
padding: 12px 12px;
|
||||
|
@ -963,13 +973,15 @@ ul li .intro-wrapper button.intro-action-link {
|
|||
background-color: $nav_bg;
|
||||
border: none;
|
||||
}
|
||||
.dropdown-menu .divider {
|
||||
margin: 5px 0;
|
||||
}
|
||||
.nav-pills .dropdown-menu li.divider,
|
||||
.nav-tabs .dropdown-menu li.divider,
|
||||
.account .dropdown-menu li.divider,
|
||||
.contact-photo-wrapper .dropdown-menu li.divider {
|
||||
background-color: $menu_background_hover_color;
|
||||
border-bottom: none;
|
||||
margin: 9px 1px !important;
|
||||
}
|
||||
.nav-pills .dropdown-menu li > a,
|
||||
.nav-tabs .dropdown-menu li > a,
|
||||
|
@ -987,8 +999,6 @@ ul li .intro-wrapper button.intro-action-link {
|
|||
.contact-photo-wrapper .dropdown-menu li .btn-link {
|
||||
color: $nav_icon_color;
|
||||
font-weight: 400;
|
||||
font-size: 13px;
|
||||
padding: 4px 15px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
@ -1044,7 +1054,6 @@ aside .widget,
|
|||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
font-size: 13px;
|
||||
overflow: auto;
|
||||
}
|
||||
aside .widget h3,
|
||||
|
@ -1052,7 +1061,7 @@ aside .widget h3,
|
|||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
padding-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
aside .widget ul,
|
||||
|
@ -1065,10 +1074,11 @@ aside .widget ul,
|
|||
list-style: none;
|
||||
}
|
||||
|
||||
aside .widget li .label {
|
||||
float: left;
|
||||
}
|
||||
aside .widget li,
|
||||
.nav-container .widget li {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
padding-left: 20px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
@ -1081,6 +1091,12 @@ aside .widget li.selected,
|
|||
border-left: 3px solid $link_color !important;
|
||||
padding-left: 17px;
|
||||
}
|
||||
.side-link-link,
|
||||
aside .widget li a {
|
||||
display: block;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
aside .widget li a,
|
||||
aside .widget li a:hover {
|
||||
color: $font_color_darker;
|
||||
|
@ -1300,8 +1316,12 @@ div#sidebar-group-list {
|
|||
}
|
||||
|
||||
.group-edit-tool {
|
||||
padding-top: 0;
|
||||
color: $font_color_darker;
|
||||
}
|
||||
.sidebar-widget-header .group-edit-tool {
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.faded-icon {
|
||||
color: $font_color_darker;
|
||||
|
@ -1316,9 +1336,12 @@ div#sidebar-group-list {
|
|||
margin-left: 20px;
|
||||
}
|
||||
|
||||
aside #group-sidebar .sidebar-group-li:hover .group-edit-tool.faded-icon,
|
||||
aside #saved-search-list .saved-search-li:hover .savedsearchdrop.faded-icon,
|
||||
aside .widget:hover .widget-action.faded-icon {
|
||||
aside .widget-action {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
aside #group-sidebar .sidebar-group-li .group-edit-tool.faded-icon:hover,
|
||||
aside #saved-search-list .saved-search-li .savedsearchdrop.faded-icon:hover,
|
||||
aside .widget.widget-action.faded-icon:hover {
|
||||
opacity: 0.8;
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
|
@ -1328,7 +1351,7 @@ aside .widget .widget-action.faded-icon:hover {
|
|||
opacity: 1;
|
||||
}
|
||||
aside #group-sidebar li .group-checkbox {
|
||||
margin: 0;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
aside #group-sidebar li .group-edit-tool {
|
||||
padding-right: 10px;
|
||||
|
@ -1762,9 +1785,6 @@ blockquote.shared_content {
|
|||
}
|
||||
|
||||
/* wall items contact info */
|
||||
.media .media-body {
|
||||
font-size: 13px;
|
||||
}
|
||||
.media .media-body h4.media-heading {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
@ -1930,6 +1950,9 @@ code > .hl-main {
|
|||
margin: 10px 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.wall-item-tags:empty {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.wall-item-tags a {
|
||||
color: $font_color_darker;
|
||||
|
@ -1953,14 +1976,16 @@ code > .hl-main {
|
|||
margin: 0;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.wall-item-actions .btn,
|
||||
.wall-item-actions a,
|
||||
.wall-item-actions button {
|
||||
font-size: 13px;
|
||||
color: $font_color_darker;
|
||||
background-color: transparent;
|
||||
}
|
||||
.wall-item-actions .active {
|
||||
font-weight: bold;
|
||||
color: $link_color;
|
||||
box-shadow: none;
|
||||
}
|
||||
.wall-item-actions-left {
|
||||
display: table-cell;
|
||||
|
@ -1970,17 +1995,31 @@ code > .hl-main {
|
|||
display: flex;
|
||||
}
|
||||
.wall-item-actions .checkbox {
|
||||
margin: 0;
|
||||
margin-left: 20px;
|
||||
margin: 0 0 0 15px;
|
||||
}
|
||||
@media screen and (max-width: 767px) {
|
||||
.wall-item-actions .btn,
|
||||
.wall-item-actions a,
|
||||
.wall-item-actions button {
|
||||
padding-right: 12px;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.wall-item-actions .checkbox {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.wall-item-actions .like-rotator {
|
||||
padding-top: 8px;
|
||||
}
|
||||
}
|
||||
.wall-item-actions button:hover {
|
||||
color: $font_color_darker;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.wall-item-actions .separator {
|
||||
margin: 0 0.3em;
|
||||
}
|
||||
|
||||
.wall-item-responses {
|
||||
margin-top: .3em;
|
||||
}
|
||||
.wall-item-responses > div > p {
|
||||
margin: 0;
|
||||
}
|
||||
|
@ -2174,6 +2213,10 @@ the section element. Only after we have
|
|||
moved it to the nav through js */
|
||||
display: none !important;
|
||||
}
|
||||
.tabbar-wrapper__link {
|
||||
padding-right: 10px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
#tabmenu,
|
||||
.tabbar-wrapper,
|
||||
.tabbar,
|
||||
|
@ -2190,7 +2233,6 @@ ul.tabs {
|
|||
list-style: none;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
ul.tabs li {
|
||||
|
@ -2203,8 +2245,9 @@ ul.tabs li {
|
|||
transition: all 0.15s ease;
|
||||
}
|
||||
ul.tabs li a {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
display: block;
|
||||
padding-top: 11px;
|
||||
padding-bottom: 11px;
|
||||
}
|
||||
ul.tabs li:hover,
|
||||
ul.tabs li.active {
|
||||
|
@ -2214,7 +2257,7 @@ ul.tabs li.active {
|
|||
padding-top: 0;
|
||||
}
|
||||
#dropdownMenuTools-xs {
|
||||
padding: 9px 10px;
|
||||
padding: 9px 15px;
|
||||
}
|
||||
ul.tabbar ul.tabs-extended:hover li.dropdown {
|
||||
border-bottom: 0;
|
||||
|
@ -2236,6 +2279,12 @@ ul.dropdown-menu li:hover {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
/* Dropdown Menu */
|
||||
.dropdown-menu li .btn-link,
|
||||
.dropdown-menu li a,
|
||||
.tabs .dropdown-menu li a {
|
||||
padding: 6px 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.dropdown-menu li a,
|
||||
.dropdown-menu li .btn-link {
|
||||
color: $font_color_darker;
|
||||
|
@ -2493,20 +2542,18 @@ ul.viewcontact_wrapper > li {
|
|||
}
|
||||
.contact-wrapper .contact-actions {
|
||||
display: flex;
|
||||
position: relative;
|
||||
margin: -8px -8px 0 0;
|
||||
}
|
||||
.contact-wrapper .contact-action-link,
|
||||
.contact-wrapper .contact-action-link:hover,
|
||||
.textcomplete-item .contact-wrapper .contact-action-link {
|
||||
padding: 0 5px;
|
||||
color: $font_color_darker;
|
||||
border: 0;
|
||||
}
|
||||
.contact-wrapper .contact-action-link {
|
||||
opacity: 0.1;
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
ul li:hover .contact-wrapper .contact-action-link {
|
||||
opacity: 0.8;
|
||||
background-color: transparent;
|
||||
opacity: 0.3;
|
||||
transition: all 0.25s ease-in-out;
|
||||
}
|
||||
ul li:hover .contact-wrapper .contact-action-link:hover {
|
||||
|
@ -2912,6 +2959,10 @@ ul li:hover .contact-wrapper .contact-action-link:hover {
|
|||
.section-subtitle-wrapper {
|
||||
padding: 1px 10px;
|
||||
}
|
||||
.accordion-toggle {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
details.profile-jot-net[open] summary:before,
|
||||
.panel .section-subtitle-wrapper .accordion-toggle:before {
|
||||
font-family: ForkAwesome;
|
||||
|
@ -2976,6 +3027,12 @@ details.profile-jot-net[open] summary:before {
|
|||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.section-subtitle-wrapper > h2 .accordion-toggle {
|
||||
margin-top: -10px;
|
||||
margin-bottom: -10px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.section-subtitle-wrapper > h3 {
|
||||
font-size: 16px;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{* The dropdown to change the callendar view *}}
|
||||
<ul class="nav nav-pills">
|
||||
<li class="dropdown pull-right">
|
||||
<button class="btn btn-link btn-sm dropdown-toggle" type="button" id="event-calendar-views" data-toggle="dropdown" aria-expanded="false">
|
||||
<button class="btn btn-link dropdown-toggle" type="button" id="event-calendar-views" data-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-angle-down" aria-hidden="true"></i> {{$view}}
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="event-calendar-views">
|
||||
|
|
|
@ -54,9 +54,9 @@
|
|||
|
||||
<p class="comment-edit-submit-wrapper">
|
||||
{{if $preview}}
|
||||
<button type="button" class="btn btn-defaul btn-sm comment-edit-preview" onclick="preview_comment({{$id}});" id="comment-edit-preview-link-{{$id}}"><i class="fa fa-eye"></i> {{$preview}}</button>
|
||||
<button type="button" class="btn btn-defaul comment-edit-preview" onclick="preview_comment({{$id}});" id="comment-edit-preview-link-{{$id}}"><i class="fa fa-eye"></i> {{$preview}}</button>
|
||||
{{/if}}
|
||||
<button type="submit" class="btn btn-primary btn-sm comment-edit-submit" id="comment-edit-submit-{{$id}}" name="submit" data-loading-text="{{$loading}}"><i class="fa fa-envelope"></i> {{$submit}}</button>
|
||||
<button type="submit" class="btn btn-primary comment-edit-submit" id="comment-edit-submit-{{$id}}" name="submit" data-loading-text="{{$loading}}"><i class="fa fa-envelope"></i> {{$submit}}</button>
|
||||
</p>
|
||||
|
||||
<div class="comment-edit-end clear"></div>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<ul class="tabs flex-nav" role="menu">
|
||||
{{foreach $tabs as $tab}}
|
||||
<li id="{{$tab.id}}" role="presentation" {{if $tab.sel}} class="{{$tab.sel}}" {{/if}}>
|
||||
<a role="menuitem" href="{{$tab.url}}" {{if $tab.accesskey}}accesskey="{{$tab.accesskey}}" {{/if}}
|
||||
<a role="menuitem" class="tabbar-wrapper__link" href="{{$tab.url}}" {{if $tab.accesskey}}accesskey="{{$tab.accesskey}}" {{/if}}
|
||||
{{if $tab.title}} title="{{$tab.title}}" {{/if}}>
|
||||
{{$tab.label}}
|
||||
</a>
|
||||
|
@ -37,7 +37,7 @@
|
|||
{{foreach $tabs as $tab}}
|
||||
{{if $tab.sel}}
|
||||
<li id="{{$tab.id}}-xs" role="presentation" {{if $tab.sel}} class="{{$tab.sel}}" {{/if}}>
|
||||
<a role="menuitem" href="{{$tab.url}}" {{if $tab.title}} title="{{$tab.title}}" {{/if}}>
|
||||
<a role="menuitem" class="tabbar-wrapper__link" href="{{$tab.url}}" {{if $tab.title}} title="{{$tab.title}}" {{/if}}>
|
||||
{{$tab.label}}
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</div>
|
||||
|
||||
{{* For very small displays we use a dropdown menu for contact relating actions *}}
|
||||
<button type="button" class="btn btn-link dropdown-toggle visible-xs" id="contact-photo-menu-button-{{$contact.id}}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button type="button" class="btn btn-default dropdown-toggle visible-xs" id="contact-photo-menu-button-{{$contact.id}}" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{* use a smaller picture on very small displays (e.g. mobiles) *}}
|
||||
<div class="contact-photo-image-wrapper visible-xs">
|
||||
<img class="contact-photo-xs media-object" src="{{$contact.thumb}}" {{$contact.sparkle}} alt="{{$contact.name}}" />
|
||||
|
@ -44,29 +44,29 @@
|
|||
<div class="media-body">
|
||||
{{if $contact.photo_menu}}
|
||||
{{* The contact actions like private mail, delete contact, edit contact and so on *}}
|
||||
<div class="contact-actions pull-right nav-pills preferences hidden-xs">
|
||||
<div class="btn-group contact-actions pull-right nav-pills preferences hidden-xs" role="group">
|
||||
{{if $contact.photo_menu.pm}}
|
||||
<button type="button" class="contact-action-link btn-link" onclick="addToModal('{{$contact.photo_menu.pm.1}}'); return false;" data-toggle="tooltip" title="{{$contact.photo_menu.pm.0}}">
|
||||
<button type="button" class="contact-action-link btn btn-default" onclick="addToModal('{{$contact.photo_menu.pm.1}}'); return false;" data-toggle="tooltip" title="{{$contact.photo_menu.pm.0}}">
|
||||
<i class="fa fa-envelope" aria-hidden="true"></i>
|
||||
</button>
|
||||
{{/if}}
|
||||
{{if $contact.photo_menu.network}}
|
||||
<a class="contact-action-link btn-link" href="{{$contact.photo_menu.network.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.network.0}}">
|
||||
<a class="contact-action-link btn btn-default" href="{{$contact.photo_menu.network.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.network.0}}">
|
||||
<i class="fa fa-cloud" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $contact.photo_menu.follow}}
|
||||
<a class="contact-action-link btn-link" href="{{$contact.photo_menu.follow.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.follow.0}}">
|
||||
<a class="contact-action-link btn btn-default" href="{{$contact.photo_menu.follow.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.follow.0}}">
|
||||
<i class="fa fa-user-plus" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $contact.photo_menu.unfollow}}
|
||||
<a class="contact-action-link btn-link" href="{{$contact.photo_menu.unfollow.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.unfollow.0}}">
|
||||
<a class="contact-action-link btn btn-default" href="{{$contact.photo_menu.unfollow.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.unfollow.0}}">
|
||||
<i class="fa fa-user-times" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $contact.photo_menu.hide}}
|
||||
<a class="contact-action-link btn-link" href="{{$contact.photo_menu.hide.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.hide.0}}">
|
||||
<a class="contact-action-link btn btn-default" href="{{$contact.photo_menu.hide.1}}" data-toggle="tooltip" title="{{$contact.photo_menu.hide.0}}">
|
||||
<i class="fa fa-times" aria-hidden="true"></i>
|
||||
</a>
|
||||
{{/if}}
|
||||
|
@ -76,7 +76,7 @@
|
|||
{{* The button to add or remove contacts from a contact group - group edit page *}}
|
||||
{{if $contact.change_member}}
|
||||
<div class="contact-group-actions pull-right nav-pills preferences">
|
||||
<button type="button" class="contact-action-link contact-group-link btn-link" onclick="groupChangeMember({{$contact.change_member.gid}},{{$contact.change_member.cid}},'{{$contact.change_member.sec_token}}'); return true;" data-toggle="tooltip" title="{{$contact.change_member.title}}">
|
||||
<button type="button" class="contact-action-link btn contact-group-link btn-default" onclick="groupChangeMember({{$contact.change_member.gid}},{{$contact.change_member.cid}},'{{$contact.change_member.sec_token}}'); return true;" data-toggle="tooltip" title="{{$contact.change_member.title}}">
|
||||
{{if $contact.label == "members"}}
|
||||
<i class="fa fa-times-circle" aria-hidden="true"></i>
|
||||
{{elseif $contact.label == "contacts"}}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
|
||||
<ul id="contact-edit-actions" class="nav nav-pills preferences">
|
||||
<li class="dropdown pull-right">
|
||||
<button type="button" class="btn btn-link btn-sm dropdown-toggle" id="contact-edit-actions-button" data-toggle="dropdown" aria-expanded="false">
|
||||
<button type="button" class="btn btn-link dropdown-toggle" id="contact-edit-actions-button" data-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-angle-down" aria-hidden="true"></i> {{$contact_action_button}}
|
||||
</button>
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
{{* We put the contact batch actions in a dropdown menu *}}
|
||||
<ul class="nav nav-pills preferences">
|
||||
<li class="dropdown pull-right">
|
||||
<button type="button" class="btn btn-link btn-sm dropdown-toggle" id="BatchActionDropdownMenuTools" data-toggle="dropdown" aria-expanded="false">
|
||||
<button type="button" class="btn btn-link dropdown-toggle" id="BatchActionDropdownMenuTools" data-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-angle-down"></i> {{$h_batch_actions}}
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="BatchActionDropdownMenuTools">
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<h3>{{$title}}</h3>
|
||||
</span>
|
||||
<div class="widget" id="group-sidebar">
|
||||
<div id="sidebar-group-header">
|
||||
<div id="sidebar-group-header" class="sidebar-widget-header">
|
||||
<span class="fakelink" onclick="openCloseWidget('group-sidebar', 'group-sidebar-inflated');">
|
||||
<h3>{{$title}}</h3>
|
||||
</span>
|
||||
|
@ -22,7 +22,7 @@
|
|||
</form>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div id="sidebar-group-list">
|
||||
<div id="sidebar-group-list" class="sidebar-widget-list">
|
||||
{{* The list of available groups *}}
|
||||
<ul role="menu" id="sidebar-group-ul">
|
||||
{{foreach $groups as $group}}
|
||||
|
|
|
@ -42,12 +42,12 @@
|
|||
{{if $follow_link || $unfollow_link}}
|
||||
<div id="dfrn-request-link-button">
|
||||
{{if $unfollow_link}}
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary btn-sm" href="{{$unfollow_link}}">
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary" href="{{$unfollow_link}}">
|
||||
<span class=""><i class="fa fa-user-times"></i></span>
|
||||
<span class="">{{$unfollow}}</span>
|
||||
</a>
|
||||
{{else}}
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary btn-sm" href="{{$follow_link}}">
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary" href="{{$follow_link}}">
|
||||
<span class=""><i class="fa fa-user-plus"></i></span>
|
||||
<span class="">{{$follow}}</span>
|
||||
</a>
|
||||
|
@ -56,7 +56,7 @@
|
|||
{{/if}}
|
||||
{{if $subscribe_feed_link}}
|
||||
<div id="subscribe-feed-link-button">
|
||||
<a id="subscribe-feed-link" class="btn btn-labeled btn-primary btn-sm" href="{{$subscribe_feed_link}}">
|
||||
<a id="subscribe-feed-link" class="btn btn-labeled btn-primary" href="{{$subscribe_feed_link}}">
|
||||
<span class=""><i class="fa fa-rss"></i></span>
|
||||
<span class="">{{$subscribe_feed}}</span>
|
||||
</a>
|
||||
|
@ -64,7 +64,7 @@
|
|||
{{/if}}
|
||||
{{if $wallmessage_link}}
|
||||
<div id="wallmessage-link-botton">
|
||||
<button type="button" id="wallmessage-link" class="btn btn-labeled btn-primary btn-sm" onclick="openWallMessage('{{$wallmessage_link}}')">
|
||||
<button type="button" id="wallmessage-link" class="btn btn-labeled btn-primary" onclick="openWallMessage('{{$wallmessage_link}}')">
|
||||
<span class=""><i class="fa fa-envelope"></i></span>
|
||||
<span class="">{{$wallmessage}}</span>
|
||||
</button>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<div id="profile-edit-links">
|
||||
<ul class="nav nav-pills preferences">
|
||||
<li class="dropdown pull-right">
|
||||
<button type="button" class="btn btn-link btn-sm dropdown-toggle" id="profile-edit-links-dropdown" data-toggle="dropdown" aria-expanded="false">
|
||||
<button type="button" class="btn btn-link dropdown-toggle" id="profile-edit-links-dropdown" data-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fa fa-angle-down" aria-hidden="true"></i> {{$profile_action}}
|
||||
</button>
|
||||
<ul class="dropdown-menu pull-right" role="menu" aria-labelledby="profile-edit-links-dropdown">
|
||||
|
|
|
@ -436,29 +436,25 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
</span>
|
||||
</span>
|
||||
|
||||
<div class="btn-toolbar visible-xs" role="toolbar">
|
||||
<div class="btn-toolbar btn-group visible-xs" role="group">
|
||||
{{* Buttons for like and dislike *}}
|
||||
{{if $item.vote}}
|
||||
<div class="btn-group" role="group">
|
||||
{{if $item.vote.like}}
|
||||
<button type="button" class="btn btn-sm button-likes{{if $item.responses.like.self}} active" aria-pressed="true{{/if}}" id="like-{{$item.id}}" title="{{$item.vote.like.0}}" onclick="doActivityItemAction({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-thumbs-up" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn button-likes{{if $item.responses.like.self}} active" aria-pressed="true{{/if}}" id="like-{{$item.id}}" title="{{$item.vote.like.0}}" onclick="doActivityItemAction({{$item.id}}, 'like'{{if $item.responses.like.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-thumbs-up" aria-hidden="true"></i></button>
|
||||
{{/if}}
|
||||
{{if $item.vote.dislike}}
|
||||
<button type="button" class="btn btn-sm button-likes{{if $item.responses.dislike.self}} active" aria-pressed="true{{/if}}" id="dislike-{{$item.id}}" title="{{$item.vote.dislike.0}}" onclick="doActivityItemAction({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-thumbs-down" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn button-likes{{if $item.responses.dislike.self}} active" aria-pressed="true{{/if}}" id="dislike-{{$item.id}}" title="{{$item.vote.dislike.0}}" onclick="doActivityItemAction({{$item.id}}, 'dislike'{{if $item.responses.dislike.self}}, true{{/if}});" data-toggle="button"><i class="fa fa-thumbs-down" aria-hidden="true"></i></button>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{* Button to open the comment text field *}}
|
||||
{{if $item.comment_html}}
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-sm button-comments" id="comment-{{$item.id}}" title="{{$item.switchcomment}}" {{if $item.thread_level != 1}}onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" {{else}} onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});"{{/if}}><i class="fa fa-commenting" aria-hidden="true"></i></button>
|
||||
</div>
|
||||
<button type="button" class="btn button-comments" id="comment-{{$item.id}}" title="{{$item.switchcomment}}" {{if $item.thread_level != 1}}onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});" {{else}} onclick="openClose('item-comments-{{$item.id}}'); commentExpand({{$item.id}});"{{/if}}><i class="fa fa-commenting" aria-hidden="true"></i></button>
|
||||
{{/if}}
|
||||
|
||||
{{if $item.vote.announce OR $item.vote.share}}
|
||||
<div class="share-links btn-group{{if $item.thread_level > 1}} dropup{{/if}}">
|
||||
<button type="button" class="btn btn-sm dropdown-toggle{{if $item.responses.announce.self}} active{{/if}}" data-toggle="dropdown" id="shareMenuOptions-{{$item.id}}" aria-haspopup="true" aria-expanded="false" title="{{$item.menu}}">
|
||||
<div class="share-links btn-group{{if $item.thread_level > 1}} dropup{{/if}}" role="group">
|
||||
<button type="button" class="btn dropdown-toggle{{if $item.responses.announce.self}} active{{/if}}" data-toggle="dropdown" id="shareMenuOptions-{{$item.id}}" aria-haspopup="true" aria-expanded="false" title="{{$item.menu}}">
|
||||
<i class="fa fa-share" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-left" role="menu" aria-labelledby="shareMenuOptions-{{$item.id}}">
|
||||
|
@ -487,28 +483,26 @@ as the value of $top_child_total (this is done at the end of this file)
|
|||
{{/if}}
|
||||
|
||||
{{if $item.browsershare}}
|
||||
<button type="button" class="btn btn-sm button-browser-share" onclick="navigator.share({url: '{{$item.plink.orig}}'})" title="{{$item.browsershare.1}}"><i class="fa fa-share-alt"></i></button>
|
||||
<button type="button" class="btn button-browser-share" onclick="navigator.share({url: '{{$item.plink.orig}}'})" title="{{$item.browsershare.1}}"><i class="fa fa-share-alt"></i></button>
|
||||
{{/if}}
|
||||
|
||||
{{* Put additional actions in a dropdown menu *}}
|
||||
<div class="btn-group" role="group">
|
||||
<img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
|
||||
</div>
|
||||
<img id="like-rotator-{{$item.id}}" class="like-rotator" src="images/rotator.gif" alt="{{$item.wait}}" title="{{$item.wait}}" style="display: none;" />
|
||||
</div>
|
||||
|
||||
<div class="wall-item-actions-right visible-xs">
|
||||
{{* Event attendance buttons *}}
|
||||
{{if $item.isevent}}
|
||||
<div class="btn-group" role="group">
|
||||
<button type="button" class="btn btn-sm btn-default button-event{{if $item.responses.attendyes.self}} active" aria-pressed="true{{/if}}" id="attendyes-{{$item.id}}" title="{{$item.attend.0}}" onclick="doActivityItemAction({{$item.id}}, 'attendyes'{{if $item.responses.attendyes.self}}, true{{/if}});"><i class="fa fa-check" aria-hidden="true"><span class="sr-only">{{$item.attend.0}}</span></i></button>
|
||||
<button type="button" class="btn btn-sm btn-default button-event{{if $item.responses.attendno.self}} active" aria-pressed="true{{/if}}" id="attendno-{{$item.id}}" title="{{$item.attend.1}}" onclick="doActivityItemAction({{$item.id}}, 'attendno'{{if $item.responses.attendno.self}}, true{{/if}});"><i class="fa fa-times" aria-hidden="true"><span class="sr-only">{{$item.attend.1}}</span></i></button>
|
||||
<button type="button" class="btn btn-sm btn-default button-event{{if $item.responses.attendmaybe.self}} active" aria-pressed="true{{/if}}" id="attendmaybe-{{$item.id}}" title="{{$item.attend.2}}" onclick="doActivityItemAction({{$item.id}}, 'attendmaybe'{{if $item.responses.attendmaybe.self}}, true{{/if}});"><i class="fa fa-question" aria-hidden="true"><span class="sr-only">{{$item.attend.2}}</span></i></button>
|
||||
<button type="button" class="btn btn-default button-event{{if $item.responses.attendyes.self}} active" aria-pressed="true{{/if}}" id="attendyes-{{$item.id}}" title="{{$item.attend.0}}" onclick="doActivityItemAction({{$item.id}}, 'attendyes'{{if $item.responses.attendyes.self}}, true{{/if}});"><i class="fa fa-check" aria-hidden="true"><span class="sr-only">{{$item.attend.0}}</span></i></button>
|
||||
<button type="button" class="btn btn-default button-event{{if $item.responses.attendno.self}} active" aria-pressed="true{{/if}}" id="attendno-{{$item.id}}" title="{{$item.attend.1}}" onclick="doActivityItemAction({{$item.id}}, 'attendno'{{if $item.responses.attendno.self}}, true{{/if}});"><i class="fa fa-times" aria-hidden="true"><span class="sr-only">{{$item.attend.1}}</span></i></button>
|
||||
<button type="button" class="btn btn-default button-event{{if $item.responses.attendmaybe.self}} active" aria-pressed="true{{/if}}" id="attendmaybe-{{$item.id}}" title="{{$item.attend.2}}" onclick="doActivityItemAction({{$item.id}}, 'attendmaybe'{{if $item.responses.attendmaybe.self}}, true{{/if}});"><i class="fa fa-question" aria-hidden="true"><span class="sr-only">{{$item.attend.2}}</span></i></button>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
{{if $item.edpost || $item.tagger || $item.filer || $item.pin || $item.star || $item.follow_thread || $item.ignore || ($item.drop && $item.drop.dropping)}}
|
||||
<div class="more-links btn-group{{if $item.thread_level > 1}} dropup{{/if}}">
|
||||
<button type="button" class="btn btn-sm dropdown-toggle" data-toggle="dropdown" id="dropdownMenuOptions-{{$item.id}}" aria-haspopup="true" aria-expanded="false" title="{{$item.menu}}"><i class="fa fa-ellipsis-h" aria-hidden="true"></i></button>
|
||||
<button type="button" class="btn dropdown-toggle" data-toggle="dropdown" id="dropdownMenuOptions-{{$item.id}}" aria-haspopup="true" aria-expanded="false" title="{{$item.menu}}"><i class="fa fa-ellipsis-h" aria-hidden="true"></i></button>
|
||||
<ul class="dropdown-menu dropdown-menu-right" role="menu" aria-labelledby="dropdownMenuOptions-{{$item.id}}">
|
||||
{{if $item.edpost}} {{* edit the posting *}}
|
||||
<li role="menuitem">
|
||||
|
|
|
@ -11,14 +11,14 @@
|
|||
</form>
|
||||
|
||||
{{* Directory links *}}
|
||||
<div class="side-link" id="side-directory-link"><a href="directory">{{$nv.local_directory}}</a></div>
|
||||
<div class="side-link" id="side-directory-link"><a href="{{$nv.global_dir}}" target="extlink">{{$nv.directory}}</a></div>
|
||||
<div class="side-link" id="side-directory-link"><a href="directory" class="side-link-link">{{$nv.local_directory}}</a></div>
|
||||
<div class="side-link" id="side-directory-link"><a href="{{$nv.global_dir}}" class="side-link-link" target="extlink">{{$nv.directory}}</a></div>
|
||||
{{* Additional links *}}
|
||||
<div class="side-link" id="side-match-link"><a href="contact/match">{{$nv.similar}}</a></div>
|
||||
<div class="side-link" id="side-suggest-link"><a href="contact/suggestions">{{$nv.suggest}}</a></div>
|
||||
<div class="side-link" id="side-random-profile-link"><a href="randprof" target="extlink">{{$nv.random}}</a></div>
|
||||
<div class="side-link" id="side-match-link"><a href="contact/match" class="side-link-link">{{$nv.similar}}</a></div>
|
||||
<div class="side-link" id="side-suggest-link"><a href="contact/suggestions" class="side-link-link">{{$nv.suggest}}</a></div>
|
||||
<div class="side-link" id="side-random-profile-link"><a href="randprof" class="side-link-link" target="extlink">{{$nv.random}}</a></div>
|
||||
|
||||
{{if $nv.inv}}
|
||||
<div class="side-link" id="side-invite-link"><button type="button" class="btn-link" onclick="addToModal('invite'); return false;">{{$nv.inv}}</button></div>
|
||||
<div class="side-link" id="side-invite-link"><button type="button" class="btn-link side-link-link" onclick="addToModal('invite'); return false;">{{$nv.inv}}</button></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -36,13 +36,13 @@
|
|||
<div id="profile-extra-links">
|
||||
<div id="dfrn-request-link-button">
|
||||
{{if $follow_link}}
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary btn-sm" href="{{$follow_link}}"">
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary" href="{{$follow_link}}"">
|
||||
<span class=""><i class="fa fa-user-plus"></i></span>
|
||||
<span class="">{{$follow}}</span>
|
||||
</a>
|
||||
{{/if}}
|
||||
{{if $unfollow_link}}
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary btn-sm" href="{{$unfollow_link}}">
|
||||
<a id="dfrn-request-link" class="btn btn-labeled btn-primary" href="{{$unfollow_link}}">
|
||||
<span class=""><i class="fa fa-user-times"></i></span>
|
||||
<span class="">{{$unfollow}}</span>
|
||||
</a>
|
||||
|
@ -50,7 +50,7 @@
|
|||
</div>
|
||||
{{if $wallmessage_link}}
|
||||
<div id="wallmessage-link-botton">
|
||||
<button type="button" id="wallmessage-link" class="btn btn-labeled btn-primary btn-sm" onclick="openWallMessage('{{$wallmessage_link}}')">
|
||||
<button type="button" id="wallmessage-link" class="btn btn-labeled btn-primary" onclick="openWallMessage('{{$wallmessage_link}}')">
|
||||
<span class=""><i class="fa fa-envelope"></i></span>
|
||||
<span class="">{{$wallmessage}}</span>
|
||||
</button>
|
||||
|
|
|
@ -1,103 +0,0 @@
|
|||
|
||||
<div id="contact-edit-wrapper">
|
||||
|
||||
{{* Insert Tab-Nav *}}
|
||||
{{$tab_str nofilter}}
|
||||
|
||||
|
||||
<div id="contact-edit-nav-wrapper">
|
||||
<form action="contact/{{$contact_id}}" method="post">
|
||||
<div id="contact-edit-links">
|
||||
<div id="contact-edit-status-wrapper">
|
||||
<span id="contact-edit-contact-status">{{$contact_status}}</span>
|
||||
|
||||
{{* This is the Action menu where contact related actions like 'ignore', 'hide' can be performed *}}
|
||||
<div id="contact-edit-actions">
|
||||
<a class="btn" id="contact-edit-actions-button">{{$contact_action_button}}</a>
|
||||
|
||||
<ul role="menu" aria-haspopup="true" id="contact-actions-menu" class="menu-popup">
|
||||
{{if $contact_actions.follow}}<li role="menuitem"><a href="{{$contact_actions.follow.url}}" title="{{$contact_actions.follow.title}}">{{$contact_actions.follow.label}}</a></li>{{/if}}
|
||||
{{if $contact_actions.unfollow}}<li role="menuitem"><a href="{{$contact_actions.unfollow.url}}" title="{{$contact_actions.unfollow.title}}">{{$contact_actions.unfollow.label}}</a></li>{{/if}}
|
||||
{{if $lblsuggest}}<li role="menuitem"><a href="{{$contact_actions.suggest.url}}" title="{{$contact_actions.suggest.title}}">{{$contact_actions.suggest.label}}</a></li>{{/if}}
|
||||
{{if $poll_enabled}}<li role="menuitem"><a href="{{$contact_actions.update.url}}" title="{{$contact_actions.update.title}}">{{$contact_actions.update.label}}</a></li>{{/if}}
|
||||
{{if $contact_actions.updateprofile}}<li role="menuitem"><a href="{{$contact_actions.updateprofile.url}}" title="{{$contact_actions.updateprofile.title}}">{{$contact_actions.updateprofile.label}}</a></li>{{/if}}
|
||||
<li class="divider"></li>
|
||||
<li role="menuitem"><a href="#" title="{{$contact_actions.block.title}}" onclick="window.location.href='{{$contact_actions.block.url}}'; return false;">{{$contact_actions.block.label}}</a></li>
|
||||
<li role="menuitem"><a href="#" title="{{$contact_actions.ignore.title}}" onclick="window.location.href='{{$contact_actions.ignore.url}}'; return false;">{{$contact_actions.ignore.label}}</a></li>
|
||||
{{if $contact_actions.revoke_follow.url}}<li role="menuitem"><a href="{{$contact_actions.revoke_follow.url}}" title="{{$contact_actions.revoke_follow.title}}">{{$contact_actions.revoke_follow.label}}</a></li>{{/if}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{* Block with status information about the contact *}}
|
||||
<ul>
|
||||
{{if $relation_text}}<li><div id="contact-edit-rel">{{$relation_text}}</div></li>{{/if}}
|
||||
|
||||
{{if $poll_enabled}}
|
||||
<li><div id="contact-edit-last-update-text">{{$lastupdtext}} <span id="contact-edit-last-updated">{{$last_update}}</span></div>
|
||||
{{if $poll_interval}}
|
||||
<span id="contact-edit-poll-text">{{$updpub}}</span> {{$poll_interval nofilter}}
|
||||
{{/if}}
|
||||
</li>
|
||||
{{/if}}
|
||||
|
||||
{{if $lost_contact}}<li><div id="lost-contact-message">{{$lost_contact}}</div></li>{{/if}}
|
||||
{{if $insecure}}<li><div id="insecure-message">{{$insecure}}</div></li> {{/if}}
|
||||
{{if $blocked && !$pending}}<li><div id="block-message">{{$blocked}}</div></li>{{/if}}
|
||||
{{if $pending}}<li><div id="pending-message">{{$pending}}</div></li>{{/if}}
|
||||
{{if $ignored}}<li><div id="ignore-message">{{$ignored}}</div></li>{{/if}}
|
||||
{{if $archived}}<li><div id="archive-message">{{$archived}}</div></li>{{/if}}
|
||||
</ul>
|
||||
</div> {{* End of contact-edit-status-wrapper *}}
|
||||
|
||||
{{* Some information about the contact from the profile *}}
|
||||
<dl><dt>{{$profileurllabel}}</dt><dd><a target="blank" href="{{$profileurl}}">{{$profileurl}}</a></dd></dl>
|
||||
{{if $location}}<dl><dt>{{$location_label}}</dt><dd>{{$location nofilter}}</dd></dl>{{/if}}
|
||||
{{if $xmpp}}<dl><dt>{{$xmpp_label}}</dt><dd>{{$xmpp}}</dd></dl>{{/if}}
|
||||
{{if $matrix}}<dl><dt>{{$matrix_label}}</dt><dd>{{$matrix}}</dd></dl>{{/if}}
|
||||
{{if $keywords}}<dl><dt>{{$keywords_label}}</dt><dd>{{$keywords}}</dd></dl>{{/if}}
|
||||
{{if $about}}<dl><dt>{{$about_label}}</dt><dd>{{$about nofilter}}</dd></dl>{{/if}}
|
||||
</div>{{* End of contact-edit-links *}}
|
||||
|
||||
<div id="contact-edit-links-end"></div>
|
||||
|
||||
<hr />
|
||||
|
||||
{{if $contact_settings_label}}
|
||||
<h4 id="contact-edit-settings-label" class="fakelink" onclick="openClose('contact-edit-settings')">{{$contact_settings_label}}</h4>
|
||||
<div id="contact-edit-settings">
|
||||
<input type="hidden" name="contact_id" value="{{$contact_id}}">
|
||||
|
||||
<div id="contact-edit-end"></div>
|
||||
{{include file="field_checkbox.tpl" field=$notify_new_posts}}
|
||||
{{if $fetch_further_information}}
|
||||
{{include file="field_select.tpl" field=$fetch_further_information}}
|
||||
{{if $fetch_further_information.2 == 2 || $fetch_further_information.2 == 3}} {{include file="field_textarea.tpl" field=$ffi_keyword_denylist}} {{/if}}
|
||||
{{/if}}
|
||||
{{if $allow_remote_self}}
|
||||
{{include file="field_select.tpl" field=$remote_self}}
|
||||
{{/if}}
|
||||
|
||||
{{include file="field_checkbox.tpl" field=$hidden}}
|
||||
|
||||
<div id="contact-edit-info-wrapper">
|
||||
<h4>{{$lbl_info1}}</h4>
|
||||
<textarea id="contact-edit-info" rows="8" cols="60" name="info">{{$info}}</textarea>
|
||||
</div>
|
||||
<div id="contact-edit-info-end"></div>
|
||||
|
||||
{{if $reason}}
|
||||
<div id="contact-info-wrapper">
|
||||
<h4>{{$lbl_info2}}</h4>
|
||||
<p>{{$reason}}</p>
|
||||
</div>
|
||||
<div id="contact-info-end"></div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<input class="contact-edit-submit" type="submit" name="submit" value="{{$submit}}" />
|
||||
{{/if}}
|
||||
|
||||
<div class="contact-edit-submit-end clearfix"></div>
|
||||
|
||||
</form>{{* End of the form *}}
|
||||
</div>{{* End of contact-edit-nav-wrapper *}}
|
||||
</div>
|
Loading…
Reference in a new issue