1
1
Fork 0

Merge branch 'develop' into item-activities

This commit is contained in:
Michael Vogel 2018-07-08 06:35:50 +02:00 committed by GitHub
commit ff5ee74ecf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 2256 additions and 748 deletions

View file

@ -9,12 +9,15 @@ php:
services:
- mysql
- redis-server
- memcached
env:
- USER=travis DB=test
- MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=travis MYSQL_PASSWORD= MYSQL_DATABASE=test
install:
- composer install
before_script:
- mysql -e 'CREATE DATABASE IF NOT EXISTS test;'
# In order to avoid bin/worker.php warnings
- touch .htconfig.php
- mysql -utravis test < database.sql
- echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini

View file

@ -499,36 +499,6 @@ if (!defined("SIGTERM")) {
if (!defined('CURLE_OPERATION_TIMEDOUT')) {
define('CURLE_OPERATION_TIMEDOUT', CURLE_OPERATION_TIMEOUTED);
}
/**
* Reverse the effect of magic_quotes_gpc if it is enabled.
* Please disable magic_quotes_gpc so we don't have to do this.
* See http://php.net/manual/en/security.magicquotes.disabling.php
*/
function startup()
{
error_reporting(E_ERROR | E_WARNING | E_PARSE);
set_time_limit(0);
// This has to be quite large to deal with embedded private photos
ini_set('pcre.backtrack_limit', 500000);
if (get_magic_quotes_gpc()) {
$process = [&$_GET, &$_POST, &$_COOKIE, &$_REQUEST];
while (list($key, $val) = each($process)) {
foreach ($val as $k => $v) {
unset($process[$key][$k]);
if (is_array($v)) {
$process[$key][stripslashes($k)] = $v;
$process[] = &$process[$key][stripslashes($k)];
} else {
$process[$key][stripslashes($k)] = stripslashes($v);
}
}
}
unset($process);
}
}
/**
* @brief Retrieve the App structure

View file

@ -591,8 +591,10 @@ CREATE TABLE IF NOT EXISTS `locks` (
`id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID',
`name` varchar(128) NOT NULL DEFAULT '' COMMENT '',
`locked` boolean NOT NULL DEFAULT '0' COMMENT '',
`expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of lock expiration',
`pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process ID',
PRIMARY KEY(`id`)
PRIMARY KEY(`id`),
INDEX `name_expires` (`name`,`expires`)
) DEFAULT COLLATE utf8mb4_general_ci COMMENT='';
--

View file

@ -23,17 +23,19 @@ $db_data = 'mysqldatabasename';
// Use environment variables for mysql if they are set beforehand
if (!empty(getenv('MYSQL_HOST'))
&& !empty(getenv('MYSQL_PORT'))
&& (!empty(getenv('MYSQL_USERNAME')) || !empty(getenv('MYSQL_USER')))
&& !empty(getenv('MYSQL_PASSWORD'))
&& !getenv('MYSQL_PASSWORD') === false
&& !empty(getenv('MYSQL_DATABASE'))) {
$db_host = getenv('MYSQL_HOST') . ':' . getenv('MYSQL_PORT');
$db_host = getenv('MYSQL_HOST');
if (!empty(getenv('MYSQL_PORT'))) {
$db_host .= ':' . getenv('MYSQL_PORT');
}
if (!empty(getenv('MYSQL_USERNAME'))) {
$db_user = getenv('MYSQL_USERNAME');
} elseif (!empty(getenv('MYSQL_USER'))) {
} else {
$db_user = getenv('MYSQL_USER');
}
$db_pass = getenv('MYSQL_PASSWORD');
$db_pass = (string) getenv('MYSQL_PASSWORD');
$db_data = getenv('MYSQL_DATABASE');
}

View file

@ -16,6 +16,7 @@ use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Core\NotificationsManager;
use Friendica\Core\PConfig;
use Friendica\Core\Protocol;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBM;
@ -90,11 +91,15 @@ function api_source()
}
// Support for known clients that doesn't send a source name
if (strpos($_SERVER['HTTP_USER_AGENT'], "Twidere") !== false) {
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
if(strpos($_SERVER['HTTP_USER_AGENT'], "Twidere") !== false) {
return "Twidere";
}
logger("Unrecognized user-agent ".$_SERVER['HTTP_USER_AGENT'], LOGGER_DEBUG);
} else {
logger("Empty user-agent", LOGGER_DEBUG);
}
return "api";
}
@ -193,8 +198,8 @@ function api_login(App $a)
throw new UnauthorizedException("This API requires login");
}
$user = $_SERVER['PHP_AUTH_USER'];
$password = $_SERVER['PHP_AUTH_PW'];
$user = defaults($_SERVER, 'PHP_AUTH_USER', '');
$password = defaults($_SERVER, 'PHP_AUTH_PW', '');
// allow "user@server" login (but ignore 'server' part)
$at = strstr($user, "@", true);
@ -258,7 +263,7 @@ function api_check_method($method)
if ($method == "*") {
return true;
}
return (strpos($method, $_SERVER['REQUEST_METHOD']) !== false);
return (stripos($method, defaults($_SERVER, 'REQUEST_METHOD', 'GET')) !== false);
}
/**
@ -298,7 +303,7 @@ function api_call(App $a)
//unset($_SERVER['PHP_AUTH_USER']);
/// @TODO should be "true ==[=] $info['auth']", if you miss only one = character, you assign a variable (only with ==). Let's make all this even.
if ($info['auth'] === true && api_user() === false) {
if (!empty($info['auth']) && api_user() === false) {
api_login($a);
}
@ -475,7 +480,7 @@ function api_rss_extra(App $a, $arr, $user_info)
'base' => System::baseUrl(),
'updated' => api_date(null),
'atom_updated' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
'language' => $user_info['language'],
'language' => $user_info['lang'],
'logo' => System::baseUrl() . "/images/friendica-32.png",
];
@ -571,6 +576,7 @@ function api_get_user(App $a, $contact_id = null)
}
}
// $called_api is the API path exploded on / and is expected to have at least 2 elements
if (is_null($user) && ($a->argc > (count($called_api) - 1)) && (count($called_api) > 0)) {
$argid = count($called_api);
list($user, $null) = explode(".", $a->argv[$argid]);
@ -773,13 +779,13 @@ function api_get_user(App $a, $contact_id = null)
$link_color = PConfig::get($ret['uid'], 'frio', 'link_color');
$bgcolor = PConfig::get($ret['uid'], 'frio', 'background_color');
}
if (!$nav_bg) {
if (empty($nav_bg)) {
$nav_bg = "#708fa0";
}
if (!$link_color) {
if (empty($link_color)) {
$link_color = "#6fdbe8";
}
if (!$bgcolor) {
if (empty($bgcolor)) {
$bgcolor = "#ededed";
}
@ -801,12 +807,12 @@ function api_get_user(App $a, $contact_id = null)
*/
function api_item_get_user(App $a, $item)
{
$status_user = api_get_user($a, $item["author-id"]);
$status_user = api_get_user($a, defaults($item, 'author-id', null));
$status_user["protected"] = $item["private"];
$status_user["protected"] = defaults($item, 'private', 0);
if ($item['thr-parent'] == $item['uri']) {
$owner_user = api_get_user($a, $item["owner-id"]);
if (defaults($item, 'thr-parent', '') == defaults($item, 'uri', '')) {
$owner_user = api_get_user($a, defaults($item, 'author-id', null));
} else {
$owner_user = $status_user;
}
@ -880,7 +886,6 @@ function api_create_xml(array $data, $root_element)
{
$childname = key($data);
$data2 = array_pop($data);
$key = key($data2);
$namespaces = ["" => "http://api.twitter.com",
"statusnet" => "http://status.net/schema/api/1/",
@ -893,8 +898,8 @@ function api_create_xml(array $data, $root_element)
}
if (is_array($data2)) {
$key = key($data2);
api_walk_recursive($data2, "api_reformat_xml");
}
if ($key == "0") {
$data4 = [];
@ -906,6 +911,7 @@ function api_create_xml(array $data, $root_element)
$data2 = $data4;
}
}
$data3 = [$root_element => $data2];
@ -1299,19 +1305,19 @@ function api_status_show($type)
'in_reply_to_screen_name' => $in_reply_to['screen_name'],
'user' => $user_info,
$geo => null,
'coordinates' => "",
'place' => "",
'contributors' => "",
'coordinates' => '',
'place' => '',
'contributors' => '',
'is_quote_status' => false,
'retweet_count' => 0,
'favorite_count' => 0,
'favorited' => $lastwall['starred'] ? true : false,
'retweeted' => false,
'possibly_sensitive' => false,
'lang' => "",
'lang' => '',
'statusnet_html' => $converted["html"],
'statusnet_conversation_id' => $lastwall['parent'],
'external_url' => System::baseUrl() . "/display/" . $lastwall['guid'],
'external_url' => System::baseUrl() . '/display/' . $lastwall['guid'],
];
if (count($converted["attachments"]) > 0) {
@ -1477,7 +1483,7 @@ function api_users_lookup($type)
{
$users = [];
if (x($_REQUEST['user_id'])) {
if (!empty($_REQUEST['user_id'])) {
foreach (explode(',', $_REQUEST['user_id']) as $id) {
if (!empty($id)) {
$users[] = api_get_user(get_app(), $id);
@ -1801,20 +1807,20 @@ function api_statuses_show($type)
}
// params
$id = intval($a->argv[3]);
$id = intval(defaults($a->argv, 3, 0));
if ($id == 0) {
$id = intval($_REQUEST["id"]);
$id = intval(defaults($_REQUEST, 'id', 0));
}
// Hotot workaround
if ($id == 0) {
$id = intval($a->argv[4]);
$id = intval(defaults($a->argv, 4, 0));
}
logger('API: api_statuses_show: ' . $id);
$conversation = (x($_REQUEST, 'conversation') ? 1 : 0);
$conversation = !empty($_REQUEST['conversation']);
// try to fetch the item for the local user - or the public item, if there is no local one
$uri_item = Item::selectFirst(['uri'], ['id' => $id]);
@ -1874,24 +1880,24 @@ function api_conversation_show($type)
}
// params
$id = intval($a->argv[3]);
$count = (x($_REQUEST, 'count') ? $_REQUEST['count'] : 20);
$page = (x($_REQUEST, 'page') ? $_REQUEST['page'] - 1 : 0);
$id = intval(defaults($a->argv , 3 , 0));
$since_id = intval(defaults($_REQUEST, 'since_id', 0));
$max_id = intval(defaults($_REQUEST, 'max_id' , 0));
$count = intval(defaults($_REQUEST, 'count' , 20));
$page = intval(defaults($_REQUEST, 'page' , 1)) - 1;
if ($page < 0) {
$page = 0;
}
$since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0);
$max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0);
$start = $page*$count;
$start = $page * $count;
if ($id == 0) {
$id = intval($_REQUEST["id"]);
$id = intval(defaults($_REQUEST, 'id', 0));
}
// Hotot workaround
if ($id == 0) {
$id = intval($a->argv[4]);
$id = intval(defaults($a->argv, 4, 0));
}
logger('API: api_conversation_show: '.$id);
@ -1954,15 +1960,15 @@ function api_statuses_repeat($type)
api_get_user($a);
// params
$id = intval($a->argv[3]);
$id = intval(defaults($a->argv, 3, 0));
if ($id == 0) {
$id = intval($_REQUEST["id"]);
$id = intval(defaults($_REQUEST, 'id', 0));
}
// Hotot workaround
if ($id == 0) {
$id = intval($a->argv[4]);
$id = intval(defaults($a->argv, 4, 0));
}
logger('API: api_statuses_repeat: '.$id);
@ -2020,15 +2026,15 @@ function api_statuses_destroy($type)
api_get_user($a);
// params
$id = intval($a->argv[3]);
$id = intval(defaults($a->argv, 3, 0));
if ($id == 0) {
$id = intval($_REQUEST["id"]);
$id = intval(defaults($_REQUEST, 'id', 0));
}
// Hotot workaround
if ($id == 0) {
$id = intval($a->argv[4]);
$id = intval(defaults($a->argv, 4, 0));
}
logger('API: api_statuses_destroy: '.$id);
@ -2205,7 +2211,7 @@ function api_favorites_create_destroy($type)
// for versioned api.
/// @TODO We need a better global soluton
$action_argv_id = 2;
if ($a->argv[1] == "1.1") {
if (count($a->argv) > 1 && $a->argv[1] == "1.1") {
$action_argv_id = 3;
}
@ -2214,10 +2220,9 @@ function api_favorites_create_destroy($type)
}
$action = str_replace("." . $type, "", $a->argv[$action_argv_id]);
if ($a->argc == $action_argv_id + 2) {
$itemid = intval($a->argv[$action_argv_id + 1]);
$itemid = intval(defaults($a->argv, $action_argv_id + 1, 0));
} else {
/// @TODO use x() to check if _REQUEST contains 'id'
$itemid = intval($_REQUEST['id']);
$itemid = intval(defaults($_REQUEST, 'id', 0));
}
$item = Item::selectFirstForUser(api_user(), [], ['id' => $itemid, 'uid' => api_user()]);
@ -2344,21 +2349,29 @@ function api_format_messages($item, $recipient, $sender)
'sender_id' => $sender['id'] ,
'text' => "",
'recipient_id' => $recipient['id'],
'created_at' => api_date($item['created']),
'created_at' => api_date(defaults($item, 'created', DateTimeFormat::utcNow())),
'sender_screen_name' => $sender['screen_name'],
'recipient_screen_name' => $recipient['screen_name'],
'sender' => $sender,
'recipient' => $recipient,
'title' => "",
'friendica_seen' => $item['seen'],
'friendica_parent_uri' => $item['parent-uri'],
'friendica_seen' => defaults($item, 'seen', 0),
'friendica_parent_uri' => defaults($item, 'parent-uri', ''),
];
// "uid" and "self" are only needed for some internal stuff, so remove it from here
unset($ret["sender"]["uid"]);
unset($ret["sender"]["self"]);
unset($ret["recipient"]["uid"]);
unset($ret["recipient"]["self"]);
if (isset($ret['sender']['uid'])) {
unset($ret['sender']['uid']);
}
if (isset($ret['sender']['self'])) {
unset($ret['sender']['self']);
}
if (isset($ret['recipient']['uid'])) {
unset($ret['recipient']['uid']);
}
if (isset($ret['recipient']['self'])) {
unset($ret['recipient']['self']);
}
//don't send title to regular StatusNET requests to avoid confusing these apps
if (x($_GET, 'getText')) {
@ -2405,8 +2418,8 @@ function api_convert_item($item)
$statustext = trim($statustitle."\n\n".$statusbody);
}
if (($item["network"] == NETWORK_FEED) && (strlen($statustext)> 1000)) {
$statustext = substr($statustext, 0, 1000)."... \n".$item["plink"];
if ((defaults($item, 'network', Protocol::PHANTOM) == Protocol::FEED) && (strlen($statustext)> 1000)) {
$statustext = substr($statustext, 0, 1000) . "... \n" . defaults($item, 'plink', '');
}
$statushtml = BBCode::convert(api_clean_attachments($body), false);
@ -2440,7 +2453,7 @@ function api_convert_item($item)
}
// feeds without body should contain the link
if (($item['network'] == NETWORK_FEED) && (strlen($item['body']) == 0)) {
if ((defaults($item, 'network', Protocol::PHANTOM) == Protocol::FEED) && (strlen($item['body']) == 0)) {
$statushtml .= BBCode::convert($item['plink']);
}
@ -2482,7 +2495,7 @@ function api_get_attachments(&$body)
}
}
if (strstr($_SERVER['HTTP_USER_AGENT'], "AndStatus")) {
if (strstr(defaults($_SERVER, 'HTTP_USER_AGENT', ''), "AndStatus")) {
foreach ($images[0] as $orig) {
$body = str_replace($orig, "", $body);
}
@ -3324,18 +3337,15 @@ function api_statusnet_config($type)
{
$a = get_app();
$name = $a->config['sitename'];
$name = Config::get('config', 'sitename');
$server = $a->get_hostname();
$logo = System::baseUrl() . '/images/friendica-64.png';
$email = $a->config['admin_email'];
$closed = (($a->config['register_policy'] == REGISTER_CLOSED) ? 'true' : 'false');
$private = ((Config::get('system', 'block_public')) ? 'true' : 'false');
$textlimit = (string) (($a->config['max_import_size']) ? $a->config['max_import_size'] : 200000);
if ($a->config['api_import_size']) {
$textlimit = (string) $a->config['api_import_size'];
}
$ssl = ((Config::get('system', 'have_ssl')) ? 'true' : 'false');
$sslserver = (($ssl === 'true') ? str_replace('http:', 'https:', System::baseUrl()) : '');
$email = Config::get('config', 'admin_email');
$closed = Config::get('config', 'register_policy') == REGISTER_CLOSED ? 'true' : 'false';
$private = Config::get('system', 'block_public') ? 'true' : 'false';
$textlimit = (string) Config::get('config', 'api_import_size', Config::get('config', 'max_import_size', 200000));
$ssl = Config::get('system', 'have_ssl') ? 'true' : 'false';
$sslserver = Config::get('system', 'have_ssl') ? str_replace('http:', 'https:', System::baseUrl()) : '';
$config = [
'site' => ['name' => $name,'server' => $server, 'theme' => 'default', 'path' => '',
@ -3457,34 +3467,40 @@ api_register_func('api/followers/ids', 'api_followers_ids', true);
*/
function api_direct_messages_new($type)
{
$a = get_app();
if (api_user() === false) {
throw new ForbiddenException();
}
if (!x($_POST, "text") || (!x($_POST, "screen_name") && !x($_POST, "user_id"))) {
if (empty($_POST["text"]) || empty($_POST["screen_name"]) && empty($_POST["user_id"])) {
return;
}
$sender = api_get_user($a);
if ($_POST['screen_name']) {
$recipient = null;
if (!empty($_POST['screen_name'])) {
$r = q(
"SELECT `id`, `nurl`, `network` FROM `contact` WHERE `uid`=%d AND `nick`='%s'",
intval(api_user()),
dbesc($_POST['screen_name'])
);
if (DBM::is_result($r)) {
// Selecting the id by priority, friendica first
api_best_nickname($r);
$recipient = api_get_user($a, $r[0]['nurl']);
}
} else {
$recipient = api_get_user($a, $_POST['user_id']);
}
if (empty($recipient)) {
throw new NotFoundException('Recipient not found');
}
$replyto = '';
$sub = '';
if (x($_REQUEST, 'replyto')) {
@ -3622,17 +3638,17 @@ function api_direct_messages_box($type, $box, $verbose)
throw new ForbiddenException();
}
// params
$count = (x($_GET, 'count') ? $_GET['count'] : 20);
$page = (x($_REQUEST, 'page') ? $_REQUEST['page'] -1 : 0);
$count = defaults($_GET, 'count', 20);
$page = defaults($_REQUEST, 'page', 1) - 1;
if ($page < 0) {
$page = 0;
}
$since_id = (x($_REQUEST, 'since_id') ? $_REQUEST['since_id'] : 0);
$max_id = (x($_REQUEST, 'max_id') ? $_REQUEST['max_id'] : 0);
$since_id = defaults($_REQUEST, 'since_id', 0);
$max_id = defaults($_REQUEST, 'max_id', 0);
$user_id = (x($_REQUEST, 'user_id') ? $_REQUEST['user_id'] : "");
$screen_name = (x($_REQUEST, 'screen_name') ? $_REQUEST['screen_name'] : "");
$user_id = defaults($_REQUEST, 'user_id', '');
$screen_name = defaults($_REQUEST, 'screen_name', '');
// caller user info
unset($_REQUEST["user_id"]);
@ -3656,7 +3672,7 @@ function api_direct_messages_box($type, $box, $verbose)
if ($box=="sentbox") {
$sql_extra = "`mail`.`from-url`='" . dbesc($profile_url) . "'";
} elseif ($box == "conversation") {
$sql_extra = "`mail`.`parent-uri`='" . dbesc($_GET["uri"]) . "'";
$sql_extra = "`mail`.`parent-uri`='" . dbesc(defaults($_GET, 'uri', '')) . "'";
} elseif ($box == "all") {
$sql_extra = "true";
} elseif ($box == "inbox") {
@ -5577,9 +5593,11 @@ function api_friendica_notification($type)
if ($type == "xml") {
$xmlnotes = [];
if (!empty($notes)) {
foreach ($notes as $note) {
$xmlnotes[] = ["@attributes" => $note];
}
}
$notes = $xmlnotes;
}

View file

@ -1015,7 +1015,7 @@ class dba {
$commands = [];
// Create a key for the loop prevention
$key = $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions);
$key = $table . ':' . json_encode($conditions);
// We quit when this key already exists in the callstack.
if (isset($callstack[$key])) {
@ -1042,7 +1042,7 @@ class dba {
$rel_def = array_values(self::$relation[$table])[0];
// Create a key for preventing double queries
$qkey = $field . '-' . $table . ':' . implode(':', array_keys($conditions)) . ':' . implode(':', $conditions);
$qkey = $field . '-' . $table . ':' . json_encode($conditions);
// When the search field is the relation field, we don't need to fetch the rows
// This is useful when the leading record is already deleted in the frontend but the rest is done in the backend
@ -1109,7 +1109,7 @@ class dba {
// Split the SQL queries in chunks of 100 values
// We do the $i stuff here to make the code better readable
$i = $counter[$key_table][$key_condition];
$i = isset($counter[$key_table][$key_condition]) ? $counter[$key_table][$key_condition] : 0;
if (isset($compacted[$key_table][$key_condition][$i]) && count($compacted[$key_table][$key_condition][$i]) > 100) {
++$i;
}

View file

@ -41,7 +41,7 @@ function new_cookie($time, $user = [])
if ($user) {
$value = json_encode(["uid" => $user["uid"],
"hash" => cookie_hash($user),
"ip" => $_SERVER['REMOTE_ADDR']]);
"ip" => defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0')]);
} else {
$value = "";
}
@ -70,7 +70,7 @@ function authenticate_success($user_record, $login_initial = false, $interactive
$_SESSION['page_flags'] = $user_record['page-flags'];
$_SESSION['my_url'] = System::baseUrl() . '/profile/' . $user_record['nickname'];
$_SESSION['my_address'] = $user_record['nickname'] . '@' . substr(System::baseUrl(), strpos(System::baseUrl(), '://') + 3);
$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
$_SESSION['addr'] = defaults($_SERVER, 'REMOTE_ADDR', '0.0.0.0');
$a->user = $user_record;

View file

@ -1156,7 +1156,7 @@ function put_item_in_cache(&$item, $update = false)
$rendered_html = defaults($item, 'rendered-html', '');
if ($rendered_hash == ''
|| $item["rendered-html"] == ""
|| $rendered_html == ""
|| $rendered_hash != hash("md5", $item["body"])
|| Config::get("system", "ignore_cache")
) {
@ -1176,7 +1176,7 @@ function put_item_in_cache(&$item, $update = false)
$update = true;
}
if ($update && ($item["id"] > 0)) {
if ($update && !empty($item["id"])) {
Item::update(['rendered-html' => $item["rendered-html"], 'rendered-hash' => $item["rendered-hash"]],
['id' => $item["id"]]);
}

View file

@ -975,6 +975,7 @@ function admin_page_site_post(App $a)
$allowed_sites = ((x($_POST,'allowed_sites')) ? notags(trim($_POST['allowed_sites'])) : '');
$allowed_email = ((x($_POST,'allowed_email')) ? notags(trim($_POST['allowed_email'])) : '');
$forbidden_nicknames = ((x($_POST,'forbidden_nicknames')) ? strtolower(notags(trim($_POST['forbidden_nicknames']))) : '');
$no_oembed_rich_content = x($_POST,'no_oembed_rich_content');
$allowed_oembed = ((x($_POST,'allowed_oembed')) ? notags(trim($_POST['allowed_oembed'])) : '');
$block_public = ((x($_POST,'block_public')) ? True : False);
@ -1143,6 +1144,7 @@ function admin_page_site_post(App $a)
Config::set('config', 'register_text', $register_text);
Config::set('system', 'allowed_sites', $allowed_sites);
Config::set('system', 'allowed_email', $allowed_email);
Config::set('system', 'forbidden_nicknames', $forbidden_nicknames);
Config::set('system', 'no_oembed_rich_content', $no_oembed_rich_content);
Config::set('system', 'allowed_oembed', $allowed_oembed);
Config::set('system', 'block_public', $block_public);
@ -1349,6 +1351,8 @@ function admin_page_site(App $a)
if ($optimize_max_tablesize <= 0) {
$optimize_max_tablesize = -1;
}
// Default list of forbidden names, classic role names from RFC 2142
$default_forbidden_nicknames = 'info, marketing, sales, support, abuse, noc, security, postmaster, hostmaster, usenet, news, webmaster, www, uucp, ftp, root, sysop';
$t = get_markup_template('admin/site.tpl');
return replace_macros($t, [
@ -1388,6 +1392,7 @@ function admin_page_site(App $a)
'$register_policy' => ['register_policy', L10n::t("Register policy"), $a->config['register_policy'], "", $register_choices],
'$daily_registrations' => ['max_daily_registrations', L10n::t("Maximum Daily Registrations"), Config::get('system', 'max_daily_registrations'), L10n::t("If registration is permitted above, this sets the maximum number of new user registrations to accept per day. If register is set to closed, this setting has no effect.")],
'$register_text' => ['register_text', L10n::t("Register text"), $a->config['register_text'], L10n::t("Will be displayed prominently on the registration page. You can use BBCode here.")],
'$forbidden_nicknames' => ['forbidden_nicknames', L10n::t('Forbidden Nicknames'), Config::get('system', 'forbidden_nicknames', $default_forbidden_nicknames), L10n::t('Comma separated list of nicknames that are forbidden from registration. Preset is a list of role names according RFC 2142.')],
'$abandon_days' => ['abandon_days', L10n::t('Accounts abandoned after x days'), Config::get('system','account_abandon_days'), L10n::t('Will not waste system resources polling external sites for abandonded accounts. Enter 0 for no time limit.')],
'$allowed_sites' => ['allowed_sites', L10n::t("Allowed friend domains"), Config::get('system','allowed_sites'), L10n::t("Comma separated list of domains which are allowed to establish friendships with this site. Wildcards are accepted. Empty to allow any domains")],
'$allowed_email' => ['allowed_email', L10n::t("Allowed email domains"), Config::get('system','allowed_email'), L10n::t("Comma separated list of domains which are allowed in email addresses for registrations to this site. Wildcards are accepted. Empty to allow any domains")],

View file

@ -178,6 +178,8 @@ function item_post(App $a) {
return;
}
$categories = '';
if ($orig_post) {
$str_group_allow = $orig_post['allow_gid'];
$str_contact_allow = $orig_post['allow_cid'];
@ -223,13 +225,13 @@ function item_post(App $a) {
$str_contact_deny = perms2str($_REQUEST['contact_deny']);
}
$title = notags(trim($_REQUEST['title']));
$location = notags(trim($_REQUEST['location']));
$coord = notags(trim($_REQUEST['coord']));
$verb = notags(trim($_REQUEST['verb']));
$emailcc = notags(trim($_REQUEST['emailcc']));
$body = escape_tags(trim($_REQUEST['body']));
$network = notags(trim(defaults($_REQUEST, 'network', NETWORK_DFRN)));
$title = notags(trim(defaults($_REQUEST, 'title' , '')));
$location = notags(trim(defaults($_REQUEST, 'location', '')));
$coord = notags(trim(defaults($_REQUEST, 'coord' , '')));
$verb = notags(trim(defaults($_REQUEST, 'verb' , '')));
$emailcc = notags(trim(defaults($_REQUEST, 'emailcc' , '')));
$body = escape_tags(trim(defaults($_REQUEST, 'body' , '')));
$network = notags(trim(defaults($_REQUEST, 'network' , NETWORK_DFRN)));
$guid = get_guid(32);
$postopts = defaults($_REQUEST, 'postopts', '');
@ -279,15 +281,15 @@ function item_post(App $a) {
}
}
if (strlen($categories)) {
if (!empty($categories)) {
// get the "fileas" tags for this post
$filedas = file_tag_file_to_list($categories, 'file');
}
// save old and new categories, so we can determine what needs to be deleted from pconfig
$categories_old = $categories;
$categories = file_tag_list_to_file(trim($_REQUEST['category']), 'category');
$categories = file_tag_list_to_file(trim(defaults($_REQUEST, 'category', '')), 'category');
$categories_new = $categories;
if (strlen($filedas)) {
if (!empty($filedas)) {
// append the fileas stuff to the new categories list
$categories .= file_tag_list_to_file($filedas, 'file');
}

View file

@ -125,30 +125,38 @@ function wall_upload_post(App $a, $desktopmode = true)
$filetype = $_FILES['userfile']['type'];
} elseif (x($_FILES, 'media')) {
if (!empty($_FILES['media']['tmp_name'])) {
if (is_array($_FILES['media']['tmp_name'])) {
$src = $_FILES['media']['tmp_name'][0];
} else {
$src = $_FILES['media']['tmp_name'];
}
}
if (!empty($_FILES['media']['name'])) {
if (is_array($_FILES['media']['name'])) {
$filename = basename($_FILES['media']['name'][0]);
} else {
$filename = basename($_FILES['media']['name']);
}
}
if (!empty($_FILES['media']['size'])) {
if (is_array($_FILES['media']['size'])) {
$filesize = intval($_FILES['media']['size'][0]);
} else {
$filesize = intval($_FILES['media']['size']);
}
}
if (!empty($_FILES['media']['type'])) {
if (is_array($_FILES['media']['type'])) {
$filetype = $_FILES['media']['type'][0];
} else {
$filetype = $_FILES['media']['type'];
}
}
}
if ($src == "") {
if ($r_json) {

View file

@ -1,5 +1,7 @@
<?xml version="1.0"?>
<phpunit bootstrap="tests/bootstrap.php">
<phpunit
bootstrap="tests/bootstrap.php"
verbose="true">
<testsuites>
<testsuite>
<directory>tests/</directory>

View file

@ -181,7 +181,10 @@ class App
$this->process_id = uniqid('log', true);
startup();
set_time_limit(0);
// This has to be quite large to deal with embedded private photos
ini_set('pcre.backtrack_limit', 500000);
$this->scheme = 'http';
@ -290,7 +293,7 @@ class App
$this->is_tablet = $mobile_detect->isTablet();
// Friendica-Client
$this->is_friendica_app = ($_SERVER['HTTP_USER_AGENT'] == 'Apache-HttpClient/UNAVAILABLE (java 1.4)');
$this->is_friendica_app = isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == 'Apache-HttpClient/UNAVAILABLE (java 1.4)';
// Register template engines
$this->register_template_engine('Friendica\Render\FriendicaSmartyEngine');
@ -863,7 +866,7 @@ class App
return;
}
array_unshift($args, ((x($this->config, 'php_path')) && (strlen($this->config['php_path'])) ? $this->config['php_path'] : 'php'));
array_unshift($args, $this->getConfigValue('config', 'php_path', 'php'));
for ($x = 0; $x < count($args); $x ++) {
$args[$x] = escapeshellarg($args[$x]);
@ -875,7 +878,7 @@ class App
return;
}
if (Config::get('system', 'proc_windows')) {
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
$resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->get_basepath());
} else {
$resource = proc_open($cmdline . ' &', [], $foo, $this->get_basepath());

View file

@ -4,8 +4,7 @@
*/
namespace Friendica\Core;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Core\Cache\CacheDriverFactory;
/**
* @brief Class for storing data for a short time
@ -24,31 +23,13 @@ class Cache extends \Friendica\BaseObject
/**
* @var Cache\ICacheDriver
*/
static $driver = null;
private static $driver = null;
public static function init()
{
switch(Config::get('system', 'cache_driver', 'database')) {
case 'memcache':
$memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
$memcache_port = Config::get('system', 'memcache_port', 11211);
$driver_name = Config::get('system', 'cache_driver', 'database');
self::$driver = new Cache\MemcacheCacheDriver($memcache_host, $memcache_port);
break;
case 'memcached':
$memcached_hosts = Config::get('system', 'memcached_hosts', [['127.0.0.1', 11211]]);
self::$driver = new Cache\MemcachedCacheDriver($memcached_hosts);
break;
case 'redis':
$redis_host = Config::get('system', 'redis_host', '127.0.0.1');
$redis_port = Config::get('system', 'redis_port', 6379);
self::$driver = new Cache\RedisCacheDriver($redis_host, $redis_port);
break;
default:
self::$driver = new Cache\DatabaseCacheDriver();
}
self::$driver = CacheDriverFactory::create($driver_name);
}
/**

View file

@ -0,0 +1,24 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\BaseObject;
/**
* Abstract class for common used functions
*
* Class AbstractCacheDriver
*
* @package Friendica\Core\Cache
*/
abstract class AbstractCacheDriver extends BaseObject
{
/**
* @param string $key The original key
* @return string The cache key used for the cache
*/
protected function getCacheKey($key) {
// We fetch with the hostname as key to avoid problems with other applications
return self::getApp()->get_hostname() . ":" . $key;
}
}

View file

@ -0,0 +1,83 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\Core\Cache;
/**
* Implementation of the IMemoryCacheDriver mainly for testing purpose
*
* Class ArrayCache
*
* @package Friendica\Core\Cache
*/
class ArrayCache extends AbstractCacheDriver implements IMemoryCacheDriver
{
use TraitCompareDelete;
/** @var array Array with the cached data */
protected $cachedData = array();
/**
* (@inheritdoc)
*/
public function get($key)
{
if (isset($this->cachedData[$key])) {
return $this->cachedData[$key];
}
return null;
}
/**
* (@inheritdoc)
*/
public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$this->cachedData[$key] = $value;
return true;
}
/**
* (@inheritdoc)
*/
public function delete($key)
{
unset($this->cachedData[$key]);
return true;
}
/**
* (@inheritdoc)
*/
public function clear($outdated = true)
{
$this->cachedData = [];
return true;
}
/**
* (@inheritdoc)
*/
public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
{
if (isset($this->cachedData[$key])) {
return false;
} else {
return $this->set($key, $value, $ttl);
}
}
/**
* (@inheritdoc)
*/
public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES)
{
if ($this->get($key) === $oldValue) {
return $this->set($key, $newValue);
} else {
return false;
}
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\Core\Config;
/**
* Class CacheDriverFactory
*
* @package Friendica\Core\Cache
*
* A basic class to generate a CacheDriver
*/
class CacheDriverFactory
{
/**
* This method creates a CacheDriver for the given cache driver name
*
* @param string $driver The name of the cache driver
* @return ICacheDriver The instance of the CacheDriver
* @throws \Exception The exception if something went wrong during the CacheDriver creation
*/
public static function create($driver) {
switch ($driver) {
case 'memcache':
$memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
$memcache_port = Config::get('system', 'memcache_port', 11211);
return new MemcacheCacheDriver($memcache_host, $memcache_port);
break;
case 'memcached':
$memcached_hosts = Config::get('system', 'memcached_hosts', [['127.0.0.1', 11211]]);
return new MemcachedCacheDriver($memcached_hosts);
break;
case 'redis':
$redis_host = Config::get('system', 'redis_host', '127.0.0.1');
$redis_port = Config::get('system', 'redis_port', 6379);
return new RedisCacheDriver($redis_host, $redis_port);
break;
default:
return new DatabaseCacheDriver();
}
}
}

View file

@ -12,7 +12,7 @@ use Friendica\Util\DateTimeFormat;
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class DatabaseCacheDriver implements ICacheDriver
class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver
{
public function get($key)
{
@ -33,11 +33,11 @@ class DatabaseCacheDriver implements ICacheDriver
return null;
}
public function set($key, $value, $duration = Cache::MONTH)
public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$fields = [
'v' => serialize($value),
'expires' => DateTimeFormat::utc('now + ' . $duration . ' seconds'),
'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'),
'updated' => DateTimeFormat::utcNow()
];
@ -49,8 +49,12 @@ class DatabaseCacheDriver implements ICacheDriver
return dba::delete('cache', ['k' => $key]);
}
public function clear()
public function clear($outdated = true)
{
if ($outdated) {
return dba::delete('cache', ['`expires` < NOW()']);
} else {
return dba::delete('cache', ['`k` IS NOT NULL ']);
}
}
}

View file

@ -25,17 +25,16 @@ interface ICacheDriver
*
* @param string $key The cache key
* @param mixed $value The value to store
* @param integer $duration The cache lifespan, must be one of the Cache constants
* @param integer $ttl The cache lifespan, must be one of the Cache constants
*
* @return bool
*/
public function set($key, $value, $duration = Cache::MONTH);
public function set($key, $value, $ttl = Cache::FIVE_MINUTES);
/**
* Delete a key from the cache
*
* @param string $key
* @param string $key The cache key
*
* @return bool
*/
@ -43,8 +42,9 @@ interface ICacheDriver
/**
* Remove outdated data from the cache
* @param boolean $outdated just remove outdated values
*
* @return bool
*/
public function clear();
public function clear($outdated = true);
}

View file

@ -0,0 +1,45 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\Core\Cache;
/**
* This interface defines methods for Memory-Caches only
*
* Interface IMemoryCacheDriver
*
* @package Friendica\Core\Cache
*/
interface IMemoryCacheDriver extends ICacheDriver
{
/**
* Sets a value if it's not already stored
*
* @param string $key The cache key
* @param mixed $value The old value we know from the cache
* @param int $ttl The cache lifespan, must be one of the Cache constants
* @return bool
*/
public function add($key, $value, $ttl = Cache::FIVE_MINUTES);
/**
* Compares if the old value is set and sets the new value
*
* @param string $key The cache key
* @param mixed $oldValue The old value we know from the cache
* @param mixed $newValue The new value we want to set
* @param int $ttl The cache lifespan, must be one of the Cache constants
*
* @return bool
*/
public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES);
/**
* Compares if the old value is set and removes it
*
* @param string $key The cache key
* @param mixed $value The old value we know and want to delete
* @return bool
*/
public function compareDelete($key, $value);
}

View file

@ -2,7 +2,6 @@
namespace Friendica\Core\Cache;
use Friendica\BaseObject;
use Friendica\Core\Cache;
/**
@ -10,10 +9,13 @@ use Friendica\Core\Cache;
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class MemcacheCacheDriver extends BaseObject implements ICacheDriver
class MemcacheCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver
{
use TraitCompareSet;
use TraitCompareDelete;
/**
* @var Memcache
* @var \Memcache
*/
private $memcache;
@ -30,12 +32,16 @@ class MemcacheCacheDriver extends BaseObject implements ICacheDriver
}
}
/**
* (@inheritdoc)
*/
public function get($key)
{
$return = null;
$cachekey = $this->getCacheKey($key);
// We fetch with the hostname as key to avoid problems with other applications
$cached = $this->memcache->get(self::getApp()->get_hostname() . ':' . $key);
$cached = $this->memcache->get($cachekey);
// @see http://php.net/manual/en/memcache.get.php#84275
if (is_bool($cached) || is_double($cached) || is_long($cached)) {
@ -54,24 +60,57 @@ class MemcacheCacheDriver extends BaseObject implements ICacheDriver
return $return;
}
public function set($key, $value, $duration = Cache::MONTH)
/**
* (@inheritdoc)
*/
public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
// We store with the hostname as key to avoid problems with other applications
if ($ttl > 0) {
return $this->memcache->set(
self::getApp()->get_hostname() . ":" . $key,
$cachekey,
serialize($value),
MEMCACHE_COMPRESSED,
time() + $duration
time() + $ttl
);
} else {
return $this->memcache->set(
$cachekey,
serialize($value),
MEMCACHE_COMPRESSED
);
}
}
/**
* (@inheritdoc)
*/
public function delete($key)
{
return $this->memcache->delete($key);
$cachekey = $this->getCacheKey($key);
return $this->memcache->delete($cachekey);
}
public function clear()
/**
* (@inheritdoc)
*/
public function clear($outdated = true)
{
if ($outdated) {
return true;
} else {
return $this->memcache->flush();
}
}
/**
* (@inheritdoc)
*/
public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl);
}
}

View file

@ -2,7 +2,6 @@
namespace Friendica\Core\Cache;
use Friendica\BaseObject;
use Friendica\Core\Cache;
/**
@ -10,10 +9,13 @@ use Friendica\Core\Cache;
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
class MemcachedCacheDriver extends BaseObject implements ICacheDriver
class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver
{
use TraitCompareSet;
use TraitCompareDelete;
/**
* @var Memcached
* @var \Memcached
*/
private $memcached;
@ -35,9 +37,10 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver
public function get($key)
{
$return = null;
$cachekey = $this->getCacheKey($key);
// We fetch with the hostname as key to avoid problems with other applications
$value = $this->memcached->get(self::getApp()->get_hostname() . ':' . $key);
$value = $this->memcached->get($cachekey);
if ($this->memcached->getResultCode() === \Memcached::RES_SUCCESS) {
$return = $value;
@ -46,25 +49,52 @@ class MemcachedCacheDriver extends BaseObject implements ICacheDriver
return $return;
}
public function set($key, $value, $duration = Cache::MONTH)
public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
// We store with the hostname as key to avoid problems with other applications
if ($ttl > 0) {
return $this->memcached->set(
self::getApp()->get_hostname() . ':' . $key,
$cachekey,
$value,
time() + $duration
$ttl
);
} else {
return $this->memcached->set(
$cachekey,
$value
);
}
}
public function delete($key)
{
$return = $this->memcached->delete(self::getApp()->get_hostname() . ':' . $key);
return $return;
$cachekey = $this->getCacheKey($key);
return $this->memcached->delete($cachekey);
}
public function clear()
public function clear($outdated = true)
{
if ($outdated) {
return true;
} else {
return $this->memcached->flush();
}
}
/**
* @brief Sets a value if it's not already stored
*
* @param string $key The cache key
* @param mixed $value The old value we know from the cache
* @param int $ttl The cache lifespan, must be one of the Cache constants
* @return bool
*/
public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
return $this->memcached->add($cachekey, $value, $ttl);
}
}

View file

@ -2,7 +2,6 @@
namespace Friendica\Core\Cache;
use Friendica\BaseObject;
use Friendica\Core\Cache;
/**
@ -11,10 +10,10 @@ use Friendica\Core\Cache;
* @author Hypolite Petovan <mrpetovan@gmail.com>
* @author Roland Haeder <roland@mxchange.org>
*/
class RedisCacheDriver extends BaseObject implements ICacheDriver
class RedisCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver
{
/**
* @var Redis
* @var \Redis
*/
private $redis;
@ -34,16 +33,14 @@ class RedisCacheDriver extends BaseObject implements ICacheDriver
public function get($key)
{
$return = null;
$cachekey = $this->getCacheKey($key);
// We fetch with the hostname as key to avoid problems with other applications
$cached = $this->redis->get(self::getApp()->get_hostname() . ':' . $key);
// @see http://php.net/manual/en/redis.get.php#84275
if (is_bool($cached) || is_double($cached) || is_long($cached)) {
return $return;
$cached = $this->redis->get($cachekey);
if ($cached === false && !$this->redis->exists($cachekey)) {
return null;
}
$value = @unserialize($cached);
$value = json_decode($cached);
// Only return a value if the serialized value is valid.
// We also check if the db entry is a serialized
@ -55,23 +52,94 @@ class RedisCacheDriver extends BaseObject implements ICacheDriver
return $return;
}
public function set($key, $value, $duration = Cache::MONTH)
public function set($key, $value, $ttl = Cache::FIVE_MINUTES)
{
// We store with the hostname as key to avoid problems with other applications
return $this->redis->set(
self::getApp()->get_hostname() . ":" . $key,
serialize($value),
time() + $duration
$cachekey = $this->getCacheKey($key);
$cached = json_encode($value);
if ($ttl > 0) {
return $this->redis->setex(
$cachekey,
$ttl,
$cached
);
} else {
return $this->redis->set(
$cachekey,
$cached
);
}
}
public function delete($key)
{
return $this->redis->delete($key);
$cachekey = $this->getCacheKey($key);
return ($this->redis->delete($cachekey) > 0);
}
public function clear()
public function clear($outdated = true)
{
if ($outdated) {
return true;
} else {
return $this->redis->flushAll();
}
}
/**
* (@inheritdoc)
*/
public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
$cached = json_encode($value);
return $this->redis->setnx($cachekey, $cached);
}
/**
* (@inheritdoc)
*/
public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES)
{
$cachekey = $this->getCacheKey($key);
$newCached = json_encode($newValue);
$this->redis->watch($cachekey);
// If the old value isn't what we expected, somebody else changed the key meanwhile
if ($this->get($key) === $oldValue) {
if ($ttl > 0) {
$result = $this->redis->multi()
->setex($cachekey, $ttl, $newCached)
->exec();
} else {
$result = $this->redis->multi()
->set($cachekey, $newValue)
->exec();
}
return $result !== false;
}
$this->redis->unwatch();
return false;
}
/**
* (@inheritdoc)
*/
public function compareDelete($key, $value)
{
$cachekey = $this->getCacheKey($key);
$this->redis->watch($cachekey);
// If the old value isn't what we expected, somebody else changed the key meanwhile
if ($this->get($key) === $value) {
$result = $this->redis->multi()
->del($cachekey)
->exec();
return $result !== false;
}
$this->redis->unwatch();
return false;
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\Core\Cache;
/**
* Trait TraitCompareSetDelete
*
* This Trait is to compensate non native "exclusive" sets/deletes in caches
*
* @package Friendica\Core\Cache
*/
trait TraitCompareDelete
{
abstract public function get($key);
abstract public function set($key, $value, $ttl = Cache::FIVE_MINUTES);
abstract public function delete($key);
abstract public function add($key, $value, $ttl = Cache::FIVE_MINUTES);
/**
* NonNative - Compares if the old value is set and removes it
*
* @param string $key The cache key
* @param mixed $value The old value we know and want to delete
* @return bool
*/
public function compareDelete($key, $value) {
if ($this->add($key . "_lock", true)) {
if ($this->get($key) === $value) {
$this->delete($key);
$this->delete($key . "_lock");
return true;
} else {
$this->delete($key . "_lock");
return false;
}
} else {
return false;
}
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Friendica\Core\Cache;
use Friendica\Core\Cache;
/**
* Trait TraitCompareSetDelete
*
* This Trait is to compensate non native "exclusive" sets/deletes in caches
*
* @package Friendica\Core\Cache
*/
trait TraitCompareSet
{
abstract public function get($key);
abstract public function set($key, $value, $ttl = Cache::FIVE_MINUTES);
abstract public function delete($key);
abstract public function add($key, $value, $ttl = Cache::FIVE_MINUTES);
/**
* NonNative - Compares if the old value is set and sets the new value
*
* @param string $key The cache key
* @param mixed $oldValue The old value we know from the cache
* @param mixed $newValue The new value we want to set
* @param int $ttl The cache lifespan, must be one of the Cache constants
*
* @return bool
*/
public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES) {
if ($this->add($key . "_lock", true)) {
if ($this->get($key) === $oldValue) {
$this->set($key, $newValue, $ttl);
$this->delete($key . "_lock");
return true;
} else {
$this->delete($key . "_lock");
return false;
}
} else {
return false;
}
}
}

140
src/Core/Lock.php Normal file
View file

@ -0,0 +1,140 @@
<?php
namespace Friendica\Core;
/**
* @file src/Core/Lock.php
* @brief Functions for preventing parallel execution of functions
*/
use Friendica\Core\Cache\CacheDriverFactory;
use Friendica\Core\Cache\IMemoryCacheDriver;
/**
* @brief This class contain Functions for preventing parallel execution of functions
*/
class Lock
{
/**
* @var Lock\ILockDriver;
*/
static $driver = null;
public static function init()
{
$lock_driver = Config::get('system', 'lock_driver', 'default');
try {
switch ($lock_driver) {
case 'memcache':
case 'memcached':
case 'redis':
$cache_driver = CacheDriverFactory::create($lock_driver);
if ($cache_driver instanceof IMemoryCacheDriver) {
self::$driver = new Lock\CacheLockDriver($cache_driver);
}
break;
case 'database':
self::$driver = new Lock\DatabaseLockDriver();
break;
case 'semaphore':
self::$driver = new Lock\SemaphoreLockDriver();
break;
default:
self::useAutoDriver();
}
} catch (\Exception $exception) {
logger ('Driver \'' . $lock_driver . '\' failed - Fallback to \'useAutoDriver()\'');
self::useAutoDriver();
}
}
/**
* @brief This method tries to find the best - local - locking method for Friendica
*
* The following sequence will be tried:
* 1. Semaphore Locking
* 2. Cache Locking
* 3. Database Locking
*
*/
private static function useAutoDriver() {
// 1. Try to use Semaphores for - local - locking
if (function_exists('sem_get')) {
try {
self::$driver = new Lock\SemaphoreLockDriver();
return;
} catch (\Exception $exception) {
logger ('Using Semaphore driver for locking failed: ' . $exception->getMessage());
}
}
// 2. Try to use Cache Locking (don't use the DB-Cache Locking because it works different!)
$cache_driver = Config::get('system', 'cache_driver', 'database');
if ($cache_driver != 'database') {
try {
$lock_driver = CacheDriverFactory::create($cache_driver);
if ($lock_driver instanceof IMemoryCacheDriver) {
self::$driver = new Lock\CacheLockDriver($lock_driver);
}
return;
} catch (\Exception $exception) {
logger('Using Cache driver for locking failed: ' . $exception->getMessage());
}
}
// 3. Use Database Locking as a Fallback
self::$driver = new Lock\DatabaseLockDriver();
}
/**
* Returns the current cache driver
*
* @return Lock\ILockDriver;
*/
private static function getDriver()
{
if (self::$driver === null) {
self::init();
}
return self::$driver;
}
/**
* @brief Acquires a lock for a given name
*
* @param string $key Name of the lock
* @param integer $timeout Seconds until we give up
*
* @return boolean Was the lock successful?
*/
public static function acquire($key, $timeout = 120)
{
return self::getDriver()->acquireLock($key, $timeout);
}
/**
* @brief Releases a lock if it was set by us
*
* @param string $key Name of the lock
* @return void
*/
public static function release($key)
{
self::getDriver()->releaseLock($key);
}
/**
* @brief Releases all lock that were set by us
* @return void
*/
public static function releaseAll()
{
self::getDriver()->releaseAll();
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace Friendica\Core\Lock;
use Friendica\BaseObject;
/**
* Class AbstractLockDriver
*
* @package Friendica\Core\Lock
*
* Basic class for Locking with common functions (local acquired locks, releaseAll, ..)
*/
abstract class AbstractLockDriver extends BaseObject implements ILockDriver
{
/**
* @var array The local acquired locks
*/
protected $acquiredLocks = [];
/**
* Check if we've locally acquired a lock
*
* @param string key The Name of the lock
* @return bool Returns true if the lock is set
*/
protected function hasAcquiredLock($key) {
return isset($this->acquireLock[$key]) && $this->acquiredLocks[$key] === true;
}
/**
* Mark a locally acquired lock
*
* @param string $key The Name of the lock
*/
protected function markAcquire($key) {
$this->acquiredLocks[$key] = true;
}
/**
* Mark a release of a locally acquired lock
*
* @param string $key The Name of the lock
*/
protected function markRelease($key) {
unset($this->acquiredLocks[$key]);
}
/**
* Releases all lock that were set by us
*
* @return void
*/
public function releaseAll() {
foreach ($this->acquiredLocks as $acquiredLock => $hasLock) {
$this->releaseLock($acquiredLock);
}
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace Friendica\Core\Lock;
use Friendica\Core\Cache;
use Friendica\Core\Cache\IMemoryCacheDriver;
class CacheLockDriver extends AbstractLockDriver
{
/**
* @var \Friendica\Core\Cache\ICacheDriver;
*/
private $cache;
/**
* CacheLockDriver constructor.
*
* @param IMemoryCacheDriver $cache The CacheDriver for this type of lock
*/
public function __construct(IMemoryCacheDriver $cache)
{
$this->cache = $cache;
}
/**
* (@inheritdoc)
*/
public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES)
{
$got_lock = false;
$start = time();
$cachekey = self::getLockKey($key);
do {
$lock = $this->cache->get($cachekey);
// When we do want to lock something that was already locked by us.
if ((int)$lock == getmypid()) {
$got_lock = true;
}
// When we do want to lock something new
if (is_null($lock)) {
// At first initialize it with "0"
$this->cache->add($cachekey, 0);
// Now the value has to be "0" because otherwise the key was used by another process meanwhile
if ($this->cache->compareSet($cachekey, 0, getmypid(), $ttl)) {
$got_lock = true;
$this->markAcquire($key);
}
}
if (!$got_lock && ($timeout > 0)) {
usleep(rand(10000, 200000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function releaseLock($key)
{
$cachekey = self::getLockKey($key);
$this->cache->compareDelete($cachekey, getmypid());
$this->markRelease($key);
}
/**
* (@inheritdoc)
*/
public function isLocked($key)
{
$cachekey = self::getLockKey($key);
$lock = $this->cache->get($cachekey);
return isset($lock) && ($lock !== false);
}
/**
* @param string $key The original key
* @return string The cache key used for the cache
*/
private static function getLockKey($key) {
return "lock:" . $key;
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace Friendica\Core\Lock;
use dba;
use Friendica\Core\Cache;
use Friendica\Database\DBM;
use Friendica\Util\DateTimeFormat;
/**
* Locking driver that stores the locks in the database
*/
class DatabaseLockDriver extends AbstractLockDriver
{
/**
* (@inheritdoc)
*/
public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES)
{
$got_lock = false;
$start = time();
do {
dba::lock('locks');
$lock = dba::selectFirst('locks', ['locked', 'pid'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
if (DBM::is_result($lock)) {
if ($lock['locked']) {
// We want to lock something that was already locked by us? So we got the lock.
if ($lock['pid'] == getmypid()) {
$got_lock = true;
}
}
if (!$lock['locked']) {
dba::update('locks', ['locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]);
$got_lock = true;
}
} else {
dba::insert('locks', ['name' => $key, 'locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]);
$got_lock = true;
$this->markAcquire($key);
}
dba::unlock();
if (!$got_lock && ($timeout > 0)) {
usleep(rand(100000, 2000000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
/**
* (@inheritdoc)
*/
public function releaseLock($key)
{
dba::delete('locks', ['name' => $key, 'pid' => getmypid()]);
$this->markRelease($key);
return;
}
/**
* (@inheritdoc)
*/
public function releaseAll()
{
dba::delete('locks', ['pid' => getmypid()]);
$this->acquiredLocks = [];
}
/**
* (@inheritdoc)
*/
public function isLocked($key)
{
$lock = dba::selectFirst('locks', ['locked'], ['`name` = ? AND `expires` >= ?', $key, DateTimeFormat::utcNow()]);
if (DBM::is_result($lock)) {
return $lock['locked'] !== false;
} else {
return false;
}
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace Friendica\Core\Lock;
use Friendica\Core\Cache;
/**
* Lock Driver Interface
*
* @author Philipp Holzer <admin@philipp.info>
*/
interface ILockDriver
{
/**
* Checks, if a key is currently locked to a or my process
*
* @param string $key The name of the lock
* @return bool
*/
public function isLocked($key);
/**
*
* Acquires a lock for a given name
*
* @param string $key The Name of the lock
* @param integer $timeout Seconds until we give up
* @param integer $ttl Seconds The lock lifespan, must be one of the Cache constants
*
* @return boolean Was the lock successful?
*/
public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES);
/**
* Releases a lock if it was set by us
*
* @param string $key The Name of the lock
*
* @return void
*/
public function releaseLock($key);
/**
* Releases all lock that were set by us
*
* @return void
*/
public function releaseAll();
}

View file

@ -0,0 +1,72 @@
<?php
namespace Friendica\Core\Lock;
use Friendica\Core\Cache;
class SemaphoreLockDriver extends AbstractLockDriver
{
private static $semaphore = [];
public function __construct()
{
if (!function_exists('sem_get')) {
throw new \Exception('Semaphore lock not supported');
}
}
/**
* (@inheritdoc)
*/
private static function semaphoreKey($key)
{
$temp = get_temppath();
$file = $temp . '/' . $key . '.sem';
if (!file_exists($file)) {
file_put_contents($file, $key);
}
return ftok($file, 'f');
}
/**
* (@inheritdoc)
*/
public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES)
{
self::$semaphore[$key] = sem_get(self::semaphoreKey($key));
if (self::$semaphore[$key]) {
if (sem_acquire(self::$semaphore[$key], ($timeout == 0))) {
$this->markAcquire($key);
return true;
}
}
return false;
}
/**
* (@inheritdoc)
*/
public function releaseLock($key)
{
if (empty(self::$semaphore[$key])) {
return false;
} else {
$success = @sem_release(self::$semaphore[$key]);
unset(self::$semaphore[$key]);
$this->markRelease($key);
return $success;
}
}
/**
* (@inheritdoc)
*/
public function isLocked($key)
{
return isset(self::$semaphore[$key]);
}
}

View file

@ -72,6 +72,7 @@ class System extends BaseObject
}
} elseif (!in_array($func['function'], $ignore)) {
$callstack[] = $func['function'];
$func['class'] = '';
$previous = $func;
}
}

View file

@ -4,15 +4,11 @@
*/
namespace Friendica\Core;
use Friendica\Core\Addon;
use Friendica\Core\Config;
use Friendica\Core\System;
use dba;
use Friendica\Database\DBM;
use Friendica\Model\Process;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Lock;
use Friendica\Util\Network;
use dba;
require_once 'include/dba.php';
@ -108,16 +104,16 @@ class Worker
}
// If possible we will fetch new jobs for this worker
if (!$refetched && Lock::set('worker_process', 0)) {
if (!$refetched && Lock::acquire('worker_process', 0)) {
$stamp = (float)microtime(true);
$refetched = self::findWorkerProcesses($passing_slow);
self::$db_duration += (microtime(true) - $stamp);
Lock::remove('worker_process');
Lock::release('worker_process');
}
}
// To avoid the quitting of multiple workers only one worker at a time will execute the check
if (Lock::set('worker', 0)) {
if (Lock::acquire('worker', 0)) {
$stamp = (float)microtime(true);
// Count active workers and compare them with a maximum value that depends on the load
if (self::tooMuchWorkers()) {
@ -130,7 +126,7 @@ class Worker
logger('Memory limit reached, quitting.', LOGGER_DEBUG);
return;
}
Lock::remove('worker');
Lock::release('worker');
self::$db_duration += (microtime(true) - $stamp);
}
@ -883,7 +879,7 @@ class Worker
dba::close($r);
$stamp = (float)microtime(true);
if (!Lock::set('worker_process')) {
if (!Lock::acquire('worker_process')) {
return false;
}
self::$lock_duration = (microtime(true) - $stamp);
@ -892,7 +888,7 @@ class Worker
$found = self::findWorkerProcesses($passing_slow);
self::$db_duration += (microtime(true) - $stamp);
Lock::remove('worker_process');
Lock::release('worker_process');
if ($found) {
$r = dba::select('workerqueue', [], ['pid' => getmypid(), 'done' => false]);
@ -1097,13 +1093,13 @@ class Worker
}
// If there is a lock then we don't have to check for too much worker
if (!Lock::set('worker', 0)) {
if (!Lock::acquire('worker', 0)) {
return true;
}
// If there are already enough workers running, don't fork another one
$quit = self::tooMuchWorkers();
Lock::remove('worker');
Lock::release('worker');
if ($quit) {
return true;

View file

@ -4,10 +4,9 @@
*/
namespace Friendica\Database;
use dba;
use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Database\DBM;
use dba;
require_once 'boot.php';
require_once 'include/dba.php';
@ -413,7 +412,7 @@ class DBStructure
$field_definition = $database[$name]["fields"][$fieldname];
// Define the default collation if not given
if (!isset($parameters['Collation']) && !is_null($field_definition['Collation'])) {
if (!isset($parameters['Collation']) && !empty($field_definition['Collation'])) {
$parameters['Collation'] = 'utf8mb4_general_ci';
} else {
$parameters['Collation'] = null;
@ -537,26 +536,26 @@ class DBStructure
private static function FieldCommand($parameters, $create = true) {
$fieldstruct = $parameters["type"];
if (!is_null($parameters["Collation"])) {
if (!empty($parameters["Collation"])) {
$fieldstruct .= " COLLATE ".$parameters["Collation"];
}
if ($parameters["not null"]) {
if (!empty($parameters["not null"])) {
$fieldstruct .= " NOT NULL";
}
if (isset($parameters["default"])) {
if (!empty($parameters["default"])) {
if (strpos(strtolower($parameters["type"]),"int")!==false) {
$fieldstruct .= " DEFAULT ".$parameters["default"];
} else {
$fieldstruct .= " DEFAULT '".$parameters["default"]."'";
}
}
if ($parameters["extra"] != "") {
if (!empty($parameters["extra"])) {
$fieldstruct .= " ".$parameters["extra"];
}
if (!is_null($parameters["comment"])) {
if (!empty($parameters["comment"])) {
$fieldstruct .= " COMMENT '".dbesc($parameters["comment"])."'";
}
@ -580,7 +579,7 @@ class DBStructure
}
}
if (!is_null($structure["indexes"])) {
if (!empty($structure["indexes"])) {
foreach ($structure["indexes"] AS $indexname => $fieldnames) {
$sql_index = self::createIndex($indexname, $fieldnames, "");
if (!is_null($sql_index)) {
@ -589,11 +588,11 @@ class DBStructure
}
}
if (!is_null($structure["engine"])) {
if (!empty($structure["engine"])) {
$engine = " ENGINE=" . $structure["engine"];
}
if (!is_null($structure["comment"])) {
if (!empty($structure["comment"])) {
$comment = " COMMENT='" . dbesc($structure["comment"]) . "'";
}
@ -1299,9 +1298,11 @@ class DBStructure
"name" => ["type" => "varchar(128)", "not null" => "1", "default" => "", "comment" => ""],
"locked" => ["type" => "boolean", "not null" => "1", "default" => "0", "comment" => ""],
"pid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "comment" => "Process ID"],
"expires" => ["type" => "datetime", "not null" => "1", "default" => NULL_DATE, "comment" => "datetime of cache expiration"],
],
"indexes" => [
"PRIMARY" => ["id"],
"name_expires" => ["name", "expires"]
]
];
$database["mail"] = [

View file

@ -61,7 +61,7 @@ class Conversation
unset($old_conv['source']);
}
// Update structure data all the time but the source only when its from a better protocol.
if (($old_conv['protocol'] < $conversation['protocol']) && ($old_conv['protocol'] != 0)) {
if (isset($conversation['protocol']) && isset($conversation['source']) && ($old_conv['protocol'] < $conversation['protocol']) && ($old_conv['protocol'] != 0)) {
unset($conversation['protocol']);
unset($conversation['source']);
}

View file

@ -6,26 +6,22 @@
namespace Friendica\Model;
use dba;
use Friendica\BaseObject;
use Friendica\Content\Text;
use Friendica\Core\Addon;
use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Core\Lock;
use Friendica\Core\PConfig;
use Friendica\Core\System;
use Friendica\Core\Worker;
use Friendica\Database\DBM;
use Friendica\Model\Contact;
use Friendica\Model\Conversation;
use Friendica\Model\Group;
use Friendica\Model\Term;
use Friendica\Object\Image;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\OStatus;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\XML;
use Friendica\Util\Lock;
use dba;
use Text_LanguageDetect;
require_once 'boot.php';
@ -940,8 +936,10 @@ class Item extends BaseObject
// If item has attachments, drop them
foreach (explode(", ", $item['attach']) as $attach) {
preg_match("|attach/(\d+)|", $attach, $matches);
if (is_array($matches) && count($matches) > 1) {
dba::delete('attach', ['id' => $matches[1], 'uid' => $item['uid']]);
}
}
// Delete tags that had been attached to other items
self::deleteTagsFromItem($item);
@ -1773,7 +1771,7 @@ class Item extends BaseObject
}
// To avoid timing problems, we are using locks.
$locked = Lock::set('item_insert_content');
$locked = Lock::acquire('item_insert_content');
if (!$locked) {
logger("Couldn't acquire lock for URI " . $item['uri'] . " - proceeding anyway.");
}
@ -1793,7 +1791,7 @@ class Item extends BaseObject
logger('Could not insert content for URI ' . $item['uri'] . ' - trying asynchronously');
}
if ($locked) {
Lock::remove('item_insert_content');
Lock::release('item_insert_content');
}
}
@ -2208,7 +2206,7 @@ class Item extends BaseObject
Contact::unmarkForArchival($contact);
}
$update = (!$arr['private'] && (($arr["author-link"] === $arr["owner-link"]) || ($arr["parent-uri"] === $arr["uri"])));
$update = (!$arr['private'] && ((defaults($arr, 'author-link', '') === defaults($arr, 'owner-link', '')) || ($arr["parent-uri"] === $arr["uri"])));
// Is it a forum? Then we don't care about the rules from above
if (!$update && ($arr["network"] == NETWORK_DFRN) && ($arr["parent-uri"] === $arr["uri"])) {

View file

@ -304,6 +304,33 @@ class User
return dba::update('user', $fields, ['uid' => $uid]);
}
/**
* @brief Checks if a nickname is in the list of the forbidden nicknames
*
* Check if a nickname is forbidden from registration on the node by the
* admin. Forbidden nicknames (e.g. role namess) can be configured in the
* admin panel.
*
* @param string $nickname The nickname that should be checked
* @return boolean True is the nickname is blocked on the node
*/
public static function isNicknameBlocked($nickname)
{
$forbidden_nicknames = Config::get('system', 'forbidden_nicknames', '');
// if the config variable is empty return false
if (!x($forbidden_nicknames)) {
return false;
}
// check if the nickname is in the list of blocked nicknames
$forbidden = explode(',', $forbidden_nicknames);
$forbidden = array_map('trim', $forbidden);
if (in_array(strtolower($nickname), $forbidden)) {
return true;
}
// else return false
return false;
}
/**
* @brief Catch-all user creation function
*
@ -417,6 +444,9 @@ class User
if (!valid_email($email) || !Network::isEmailDomainValid($email)) {
throw new Exception(L10n::t('Not a valid email address.'));
}
if (self::isNicknameBlocked($nickname)) {
throw new Exception(L10n::t('The nickname was blocked from registration by the nodes admin.'));
}
if (Config::get('system', 'block_extended_register', false) && dba::exists('user', ['email' => $email])) {
throw new Exception(L10n::t('Cannot use that email.'));

View file

@ -109,6 +109,7 @@ class Probe
$redirects = 0;
logger("Probing for ".$host, LOGGER_DEBUG);
$xrd = null;
$ret = Network::curl($ssl_url, false, $redirects, ['timeout' => $xrd_timeout, 'accept_content' => 'application/xrd+xml']);
if ($ret['success']) {
@ -1510,6 +1511,7 @@ class Probe
return false;
}
$feed = $ret['body'];
$dummy1 = $dummy2 = $dummy3 = null;
$feed_data = Feed::import($feed, $dummy1, $dummy2, $dummy3, true);
if (!$feed_data) {

View file

@ -4,11 +4,15 @@
*/
namespace Friendica\Protocol;
use dba;
use DOMDocument;
use DOMXPath;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Cache;
use Friendica\Core\Config;
use Friendica\Core\L10n;
use Friendica\Core\Lock;
use Friendica\Core\System;
use Friendica\Database\DBM;
use Friendica\Model\Contact;
@ -19,12 +23,8 @@ use Friendica\Model\User;
use Friendica\Network\Probe;
use Friendica\Object\Image;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\Lock;
use Friendica\Util\Network;
use Friendica\Util\XML;
use dba;
use DOMDocument;
use DOMXPath;
require_once 'include/dba.php';
require_once 'include/items.php';
@ -515,9 +515,9 @@ class OStatus
logger("Item with uri ".$item["uri"]." is from a blocked contact.", LOGGER_DEBUG);
} else {
// We are having duplicated entries. Hopefully this solves it.
if (Lock::set('ostatus_process_item_insert')) {
if (Lock::acquire('ostatus_process_item_insert')) {
$ret = Item::insert($item);
Lock::remove('ostatus_process_item_insert');
Lock::release('ostatus_process_item_insert');
logger("Item with uri ".$item["uri"]." for user ".$importer["uid"].' stored. Return value: '.$ret);
} else {
$ret = Item::insert($item);

View file

@ -1,211 +0,0 @@
<?php
/**
* @file src/Util/Lock.php
*/
namespace Friendica\Util;
/**
* @file src/Util/Lock.php
* @brief Functions for preventing parallel execution of functions
*/
use Friendica\Core\Config;
use Friendica\Database\DBM;
use Memcache;
use dba;
require_once 'include/dba.php';
/**
* @brief This class contain Functions for preventing parallel execution of functions
*/
class Lock
{
private static $semaphore = [];
/**
* @brief Check for memcache and open a connection if configured
*
* @return object|boolean The memcache object - or "false" if not successful
*/
private static function connectMemcache()
{
if (!function_exists('memcache_connect')) {
return false;
}
if (!Config::get('system', 'memcache')) {
return false;
}
$memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
$memcache_port = Config::get('system', 'memcache_port', 11211);
$memcache = new Memcache;
if (!$memcache->connect($memcache_host, $memcache_port)) {
return false;
}
return $memcache;
}
/**
* @brief Creates a semaphore key
*
* @param string $fn_name Name of the lock
*
* @return ressource the semaphore key
*/
private static function semaphoreKey($fn_name)
{
$temp = get_temppath();
$file = $temp.'/'.$fn_name.'.sem';
if (!file_exists($file)) {
file_put_contents($file, $fn_name);
}
return ftok($file, 'f');
}
/**
* @brief Sets a lock for a given name
*
* @param string $fn_name Name of the lock
* @param integer $timeout Seconds until we give up
*
* @return boolean Was the lock successful?
*/
public static function set($fn_name, $timeout = 120)
{
$got_lock = false;
$start = time();
// The second parameter for "sem_acquire" doesn't exist before 5.6.1
if (function_exists('sem_get') && version_compare(PHP_VERSION, '5.6.1', '>=')) {
self::$semaphore[$fn_name] = sem_get(self::semaphoreKey($fn_name));
if (self::$semaphore[$fn_name]) {
return sem_acquire(self::$semaphore[$fn_name], ($timeout == 0));
}
}
$memcache = self::connectMemcache();
if (is_object($memcache)) {
$cachekey = get_app()->get_hostname().";lock:".$fn_name;
do {
// We only lock to be sure that nothing happens at exactly the same time
dba::lock('locks');
$lock = $memcache->get($cachekey);
if (!is_bool($lock)) {
$pid = (int)$lock;
// When the process id isn't used anymore, we can safely claim the lock for us.
// Or we do want to lock something that was already locked by us.
if (!posix_kill($pid, 0) || ($pid == getmypid())) {
$lock = false;
}
}
if (is_bool($lock)) {
$memcache->set($cachekey, getmypid(), MEMCACHE_COMPRESSED, 300);
$got_lock = true;
}
dba::unlock();
if (!$got_lock && ($timeout > 0)) {
usleep(rand(10000, 200000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
do {
dba::lock('locks');
$lock = dba::selectFirst('locks', ['locked', 'pid'], ['name' => $fn_name]);
if (DBM::is_result($lock)) {
if ($lock['locked']) {
// When the process id isn't used anymore, we can safely claim the lock for us.
if (!posix_kill($lock['pid'], 0)) {
$lock['locked'] = false;
}
// We want to lock something that was already locked by us? So we got the lock.
if ($lock['pid'] == getmypid()) {
$got_lock = true;
}
}
if (!$lock['locked']) {
dba::update('locks', ['locked' => true, 'pid' => getmypid()], ['name' => $fn_name]);
$got_lock = true;
}
} elseif (!DBM::is_result($lock)) {
dba::insert('locks', ['name' => $fn_name, 'locked' => true, 'pid' => getmypid()]);
$got_lock = true;
}
dba::unlock();
if (!$got_lock && ($timeout > 0)) {
usleep(rand(100000, 2000000));
}
} while (!$got_lock && ((time() - $start) < $timeout));
return $got_lock;
}
/**
* @brief Removes a lock if it was set by us
*
* @param string $fn_name Name of the lock
* @return mixed
*/
public static function remove($fn_name)
{
if (function_exists('sem_get') && version_compare(PHP_VERSION, '5.6.1', '>=')) {
if (empty(self::$semaphore[$fn_name])) {
return false;
} else {
$success = @sem_release(self::$semaphore[$fn_name]);
unset(self::$semaphore[$fn_name]);
return $success;
}
}
$memcache = self::connectMemcache();
if (is_object($memcache)) {
$cachekey = get_app()->get_hostname().";lock:".$fn_name;
$lock = $memcache->get($cachekey);
if (!is_bool($lock)) {
if ((int)$lock == getmypid()) {
$memcache->delete($cachekey);
}
}
return;
}
dba::update('locks', ['locked' => false, 'pid' => 0], ['name' => $fn_name, 'pid' => getmypid()]);
return;
}
/**
* @brief Removes all lock that were set by us
* @return void
*/
public static function removeAll()
{
$memcache = self::connectMemcache();
if (is_object($memcache)) {
// We cannot delete all cache entries, but this doesn't matter with memcache
return;
}
dba::update('locks', ['locked' => false, 'pid' => 0], ['pid' => getmypid()]);
return;
}
}

View file

@ -29,6 +29,10 @@ class ApiTest extends DatabaseTest
global $a;
parent::setUp();
// Reusable App object
$this->app = new App(__DIR__.'/../');
$a = $this->app;
// User data that the test database is populated with
$this->selfUser = [
'id' => 42,
@ -36,6 +40,12 @@ class ApiTest extends DatabaseTest
'nick' => 'selfcontact',
'nurl' => 'http://localhost/profile/selfcontact'
];
$this->friendUser = [
'id' => 44,
'name' => 'Friend contact',
'nick' => 'friendcontact',
'nurl' => 'http://localhost/profile/friendcontact'
];
$this->otherUser = [
'id' => 43,
'name' => 'othercontact',
@ -53,10 +63,6 @@ class ApiTest extends DatabaseTest
'uid' => $this->selfUser['id']
];
// Reusable App object
$this->app = new App(__DIR__.'/../');
$a = $this->app;
// Default config
Config::set('config', 'hostname', 'localhost');
Config::set('system', 'throttle_limit_day', 100);
@ -481,7 +487,7 @@ class ApiTest extends DatabaseTest
$this->app->query_string = 'api_path.rss';
$this->assertEquals(
'<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL.
'<?xml version="1.0" encoding="UTF-8"?>'."\n".
'some_data',
api_call($this->app)
);
@ -505,7 +511,7 @@ class ApiTest extends DatabaseTest
$this->app->query_string = 'api_path.atom';
$this->assertEquals(
'<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL.
'<?xml version="1.0" encoding="UTF-8"?>'."\n".
'some_data',
api_call($this->app)
);
@ -571,14 +577,14 @@ class ApiTest extends DatabaseTest
public function testApiErrorWithXml()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<?xml version="1.0"?>'."\n".
'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
'xmlns:georss="http://www.georss.org/georss">'.PHP_EOL.
' <error>error_message</error>'.PHP_EOL.
' <code>200 Friendica\Network\HTTP</code>'.PHP_EOL.
' <request/>'.PHP_EOL.
'</status>'.PHP_EOL,
'xmlns:georss="http://www.georss.org/georss">'."\n".
' <error>error_message</error>'."\n".
' <code>200 Friendica\Network\HTTP</code>'."\n".
' <request/>'."\n".
'</status>'."\n",
api_error('xml', new HTTPException('error_message'))
);
}
@ -591,14 +597,14 @@ class ApiTest extends DatabaseTest
public function testApiErrorWithRss()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<?xml version="1.0"?>'."\n".
'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
'xmlns:georss="http://www.georss.org/georss">'.PHP_EOL.
' <error>error_message</error>'.PHP_EOL.
' <code>200 Friendica\Network\HTTP</code>'.PHP_EOL.
' <request/>'.PHP_EOL.
'</status>'.PHP_EOL,
'xmlns:georss="http://www.georss.org/georss">'."\n".
' <error>error_message</error>'."\n".
' <code>200 Friendica\Network\HTTP</code>'."\n".
' <request/>'."\n".
'</status>'."\n",
api_error('rss', new HTTPException('error_message'))
);
}
@ -611,14 +617,14 @@ class ApiTest extends DatabaseTest
public function testApiErrorWithAtom()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<?xml version="1.0"?>'."\n".
'<status xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
'xmlns:georss="http://www.georss.org/georss">'.PHP_EOL.
' <error>error_message</error>'.PHP_EOL.
' <code>200 Friendica\Network\HTTP</code>'.PHP_EOL.
' <request/>'.PHP_EOL.
'</status>'.PHP_EOL,
'xmlns:georss="http://www.georss.org/georss">'."\n".
' <error>error_message</error>'."\n".
' <code>200 Friendica\Network\HTTP</code>'."\n".
' <request/>'."\n".
'</status>'."\n",
api_error('atom', new HTTPException('error_message'))
);
}
@ -629,7 +635,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiRssExtra()
{
$user_info = ['url' => 'user_url'];
$user_info = ['url' => 'user_url', 'lang' => 'en'];
$result = api_rss_extra($this->app, [], $user_info);
$this->assertEquals($user_info, $result['$user']);
$this->assertEquals($user_info['url'], $result['$rss']['alternate']);
@ -818,7 +824,7 @@ class ApiTest extends DatabaseTest
public function testApiGetUserWithCalledApi()
{
global $called_api;
$called_api = ['api_path'];
$called_api = ['api', 'api_path'];
$this->assertSelfUser(api_get_user($this->app));
}
@ -853,7 +859,6 @@ class ApiTest extends DatabaseTest
$this->assertSelfUser(api_get_user($this->app, 0));
}
/**
* Test the api_item_get_user() function.
* @return void
@ -957,12 +962,12 @@ class ApiTest extends DatabaseTest
public function testApiCreateXml()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<?xml version="1.0"?>'."\n".
'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
'xmlns:georss="http://www.georss.org/georss">'.PHP_EOL.
' <data>some_data</data>'.PHP_EOL.
'</root_element>'.PHP_EOL,
'xmlns:georss="http://www.georss.org/georss">'."\n".
' <data>some_data</data>'."\n".
'</root_element>'."\n",
api_create_xml(['data' => ['some_data']], 'root_element')
);
}
@ -974,10 +979,10 @@ class ApiTest extends DatabaseTest
public function testApiCreateXmlWithoutNamespaces()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<ok>'.PHP_EOL.
' <data>some_data</data>'.PHP_EOL.
'</ok>'.PHP_EOL,
'<?xml version="1.0"?>'."\n".
'<ok>'."\n".
' <data>some_data</data>'."\n".
'</ok>'."\n",
api_create_xml(['data' => ['some_data']], 'ok')
);
}
@ -999,12 +1004,12 @@ class ApiTest extends DatabaseTest
public function testApiFormatDataWithXml()
{
$this->assertEquals(
'<?xml version="1.0"?>'.PHP_EOL.
'<?xml version="1.0"?>'."\n".
'<root_element xmlns="http://api.twitter.com" xmlns:statusnet="http://status.net/schema/api/1/" '.
'xmlns:friendica="http://friendi.ca/schema/api/1/" '.
'xmlns:georss="http://www.georss.org/georss">'.PHP_EOL.
' <data>some_data</data>'.PHP_EOL.
'</root_element>'.PHP_EOL,
'xmlns:georss="http://www.georss.org/georss">'."\n".
' <data>some_data</data>'."\n".
'</root_element>'."\n",
api_format_data('root_element', 'xml', ['data' => ['some_data']])
);
}
@ -1073,6 +1078,7 @@ class ApiTest extends DatabaseTest
'width' => 666,
'height' => 666,
'tmp_name' => $this->getTempImage(),
'name' => 'spacer.png',
'type' => 'image/png'
]
];
@ -1110,6 +1116,7 @@ class ApiTest extends DatabaseTest
'width' => 666,
'height' => 666,
'tmp_name' => $this->getTempImage(),
'name' => 'spacer.png',
'type' => 'image/png'
]
];
@ -1217,6 +1224,7 @@ class ApiTest extends DatabaseTest
'width' => 666,
'height' => 666,
'tmp_name' => $this->getTempImage(),
'name' => 'spacer.png',
'type' => 'image/png'
]
];
@ -1833,6 +1841,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroy()
{
$this->app->argv = ['api', '1.1', 'favorites', 'create'];
$this->app->argc = count($this->app->argv);
api_favorites_create_destroy('json');
}
@ -1843,9 +1853,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithInvalidId()
{
// This triggers a very specific condition ($action_argv_id + 2)
$this->app->argv[1] = '1.1';
$this->app->argc = 5;
$this->app->argv = ['api', '1.1', 'favorites', 'create', '12.json'];
$this->app->argc = count($this->app->argv);
api_favorites_create_destroy('json');
}
@ -1856,8 +1865,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithInvalidAction()
{
$this->app->argv[1] = '1.1';
$this->app->argc = 10;
$this->app->argv = ['api', '1.1', 'favorites', 'change.json'];
$this->app->argc = count($this->app->argv);
$_REQUEST['id'] = 1;
api_favorites_create_destroy('json');
}
@ -1868,9 +1877,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithCreateAction()
{
$this->app->argv[1] = '1.1';
$this->app->argv[3] = 'create';
$this->app->argc = 10;
$this->app->argv = ['api', '1.1', 'favorites', 'create.json'];
$this->app->argc = count($this->app->argv);
$_REQUEST['id'] = 3;
$result = api_favorites_create_destroy('json');
$this->assertStatus($result['status']);
@ -1882,9 +1890,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithCreateActionAndRss()
{
$this->app->argv[1] = '1.1';
$this->app->argv[3] = 'create';
$this->app->argc = 10;
$this->app->argv = ['api', '1.1', 'favorites', 'create.rss'];
$this->app->argc = count($this->app->argv);
$_REQUEST['id'] = 3;
$result = api_favorites_create_destroy('rss');
$this->assertXml($result, 'status');
@ -1896,9 +1903,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithDestroyAction()
{
$this->app->argv[1] = '1.1';
$this->app->argv[3] = 'destroy';
$this->app->argc = 10;
$this->app->argv = ['api', '1.1', 'favorites', 'destroy.json'];
$this->app->argc = count($this->app->argv);
$_REQUEST['id'] = 3;
$result = api_favorites_create_destroy('json');
$this->assertStatus($result['status']);
@ -1911,6 +1917,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFavoritesCreateDestroyWithoutAuthenticatedUser()
{
$this->app->argv = ['api', '1.1', 'favorites', 'create.json'];
$this->app->argc = count($this->app->argv);
$_SESSION['authenticated'] = false;
api_favorites_create_destroy('json');
}
@ -1962,7 +1970,7 @@ class ApiTest extends DatabaseTest
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
);
$this->assertEquals('item_title'.PHP_EOL.'item_body', $result['text']);
$this->assertEquals('item_title'."\n".'item_body', $result['text']);
$this->assertEquals(1, $result['id']);
$this->assertEquals(2, $result['recipient_id']);
$this->assertEquals(3, $result['sender_id']);
@ -2014,8 +2022,8 @@ class ApiTest extends DatabaseTest
['id' => 2, 'screen_name' => 'recipient_name'],
['id' => 3, 'screen_name' => 'sender_name']
);
$this->assertNull($result['sender']);
$this->assertNull($result['recipient']);
$this->assertTrue(!isset($result['sender']));
$this->assertTrue(!isset($result['recipient']));
}
/**
@ -2051,7 +2059,8 @@ class ApiTest extends DatabaseTest
'repellat officia illum quos impedit quam iste esse unde qui '.
'suscipit aut facilis ut inventore omnis exercitationem quo magnam '.
'consequatur maxime aut illum soluta quaerat natus unde aspernatur '.
'et sed beatae nihil ullam temporibus corporis ratione blanditiis'
'et sed beatae nihil ullam temporibus corporis ratione blanditiis',
'plink' => 'item_plink'
]
);
$this->assertStringStartsWith('item_title', $result['text']);
@ -2108,7 +2117,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiGetAttachmentsWithImage()
{
$body = '[img]img_url[/img]';
$body = '[img]http://via.placeholder.com/1x1.png[/img]';
$this->assertInternalType('array', api_get_attachments($body));
}
@ -2119,7 +2128,7 @@ class ApiTest extends DatabaseTest
public function testApiGetAttachmentsWithImageAndAndStatus()
{
$_SERVER['HTTP_USER_AGENT'] = 'AndStatus';
$body = '[img]img_url[/img]';
$body = '[img]http://via.placeholder.com/1x1.png[/img]';
$this->assertInternalType('array', api_get_attachments($body));
}
@ -2155,7 +2164,7 @@ class ApiTest extends DatabaseTest
public function testApiFormatItemsEmbededImages()
{
$this->assertEquals(
'text http://localhost/display/item_guid',
'text ' . \Friendica\Core\System::baseUrl() . '/display/item_guid',
api_format_items_embeded_images(['guid' => 'item_guid'], 'text data:image/foo')
);
}
@ -2196,7 +2205,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiFormatItemsActivities()
{
$item = [];
$item = ['uid' => 0, 'uri' => ''];
$result = api_format_items_activities($item);
$this->assertArrayHasKey('like', $result);
$this->assertArrayHasKey('dislike', $result);
@ -2211,7 +2220,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiFormatItemsActivitiesWithXml()
{
$item = [];
$item = ['uid' => 0, 'uri' => ''];
$result = api_format_items_activities($item, 'xml');
$this->assertArrayHasKey('friendica:like', $result);
$this->assertArrayHasKey('friendica:dislike', $result);
@ -2325,10 +2334,16 @@ class ApiTest extends DatabaseTest
[
'item_network' => 'item_network',
'source' => 'web',
'coord' => '5 7'
'coord' => '5 7',
'body' => '',
'verb' => '',
'author-id' => 43,
'author-network' => \Friendica\Core\Protocol::DFRN,
'author-link' => 'http://localhost/profile/othercontact',
'plink' => '',
]
];
$result = api_format_items($items, [], true);
$result = api_format_items($items, ['id' => 0], true);
foreach ($result as $status) {
$this->assertStatus($status);
}
@ -2342,10 +2357,16 @@ class ApiTest extends DatabaseTest
{
$items = [
[
'coord' => '5 7'
'coord' => '5 7',
'body' => '',
'verb' => '',
'author-id' => 43,
'author-network' => \Friendica\Core\Protocol::DFRN,
'author-link' => 'http://localhost/profile/othercontact',
'plink' => '',
]
];
$result = api_format_items($items, [], true, 'xml');
$result = api_format_items($items, ['id' => 0], true, 'xml');
foreach ($result as $status) {
$this->assertStatus($status);
}
@ -2389,7 +2410,6 @@ class ApiTest extends DatabaseTest
*/
public function testApiHelpTestWithXml()
{
$this->markTestIncomplete('Triggers this error: "key() expects parameter 1 to be array, string given"');
$result = api_help_test('xml');
$this->assertXml($result, 'ok');
}
@ -2615,7 +2635,7 @@ class ApiTest extends DatabaseTest
$result = api_statusnet_config('json');
$this->assertEquals('localhost', $result['config']['site']['server']);
$this->assertEquals('default', $result['config']['site']['theme']);
$this->assertEquals('http://localhost/images/friendica-64.png', $result['config']['site']['logo']);
$this->assertEquals(\Friendica\Core\System::baseUrl() . '/images/friendica-64.png', $result['config']['site']['logo']);
$this->assertTrue($result['config']['site']['fancy']);
$this->assertEquals('en', $result['config']['site']['language']);
$this->assertEquals('UTC', $result['config']['site']['timezone']);
@ -2725,7 +2745,7 @@ class ApiTest extends DatabaseTest
public function testApiDirectMessagesNewWithScreenName()
{
$_POST['text'] = 'message_text';
$_POST['screen_name'] = $this->otherUser['nick'];
$_POST['screen_name'] = $this->friendUser['nick'];
$result = api_direct_messages_new('json');
$this->assertEquals(1, $result['direct_message']['id']);
$this->assertContains('message_text', $result['direct_message']['text']);
@ -2740,7 +2760,7 @@ class ApiTest extends DatabaseTest
public function testApiDirectMessagesNewWithTitle()
{
$_POST['text'] = 'message_text';
$_POST['screen_name'] = $this->otherUser['nick'];
$_POST['screen_name'] = $this->friendUser['nick'];
$_REQUEST['title'] = 'message_title';
$result = api_direct_messages_new('json');
$this->assertEquals(1, $result['direct_message']['id']);
@ -2757,7 +2777,7 @@ class ApiTest extends DatabaseTest
public function testApiDirectMessagesNewWithRss()
{
$_POST['text'] = 'message_text';
$_POST['screen_name'] = $this->otherUser['nick'];
$_POST['screen_name'] = $this->friendUser['nick'];
$result = api_direct_messages_new('rss');
$this->assertXml($result, 'direct-messages');
}
@ -3357,7 +3377,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiShareAsRetweet()
{
$item = [];
$item = ['body' => ''];
$result = api_share_as_retweet($item);
$this->assertFalse($result);
}
@ -3397,7 +3417,7 @@ class ApiTest extends DatabaseTest
*/
public function testApiInReplyTo()
{
$result = api_in_reply_to([]);
$result = api_in_reply_to(['id' => 0, 'parent' => 0, 'uri' => '', 'thr-parent' => '']);
$this->assertArrayHasKey('status_id', $result);
$this->assertArrayHasKey('user_id', $result);
$this->assertArrayHasKey('status_id_str', $result);
@ -3562,7 +3582,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFriendicaNotificationWithArgumentCount()
{
$this->app->argc = 3;
$this->app->argv = ['api', 'friendica', 'notification'];
$this->app->argc = count($this->app->argv);
$result = api_friendica_notification('json');
$this->assertEquals(['note' => false], $result);
}
@ -3573,8 +3594,8 @@ class ApiTest extends DatabaseTest
*/
public function testApiFriendicaNotificationWithXmlResult()
{
$this->markTestIncomplete('Fails with "Invalid argument supplied for foreach()".');
$this->app->argc = 3;
$this->app->argv = ['api', 'friendica', 'notification'];
$this->app->argc = count($this->app->argv);
$result = api_friendica_notification('xml');
$this->assertXml($result, 'notes');
}

View file

@ -20,6 +20,54 @@ abstract class DatabaseTest extends TestCase
use TestCaseTrait;
/**
* Renames an eventually existing .htconfig.php to .htconfig.php.tmp
* Creates a new .htconfig.php for bin/worker.php execution
*/
public static function setUpBeforeClass()
{
parent::setUpBeforeClass();
$base_config_file_name = 'htconfig.php';
$config_file_name = '.htconfig.php';
$base_config_file_path = stream_resolve_include_path($base_config_file_name);
$config_file_path = dirname($base_config_file_path) . DIRECTORY_SEPARATOR . $config_file_name;
$config_file_path_tmp = $config_file_path . '.tmp';
if (file_exists($config_file_path)) {
rename($config_file_path, $config_file_path_tmp);
}
$config_string = file_get_contents($base_config_file_path);
$config_string = str_replace('die(', '// die(', $config_string);
file_put_contents($config_file_path, $config_string);
}
/**
* Delete the created .htconfig.php
* Renames an eventually existing .htconfig.php.tmp to .htconfig.php
*/
public static function tearDownAfterClass()
{
$base_config_file_name = 'htconfig.php';
$config_file_name = '.htconfig.php';
$base_config_file_path = stream_resolve_include_path($base_config_file_name);
$config_file_path = dirname($base_config_file_path) . DIRECTORY_SEPARATOR . $config_file_name;
$config_file_path_tmp = $config_file_path . '.tmp';
if (file_exists($config_file_path)) {
unlink($config_file_path);
}
if (file_exists($config_file_path_tmp)) {
rename($config_file_path_tmp, $config_file_path);
}
}
/**
* Get database connection.
*
@ -34,21 +82,23 @@ abstract class DatabaseTest extends TestCase
protected function getConnection()
{
if (!dba::$connected) {
dba::connect('localhost', getenv('USER'), getenv('PASS'), getenv('DB'));
dba::connect(getenv('MYSQL_HOST') . ':' . getenv('MYSQL_PORT'), getenv('MYSQL_USERNAME'), getenv('MYSQL_PASSWORD'), getenv('MYSQL_DATABASE'));
if (dba::$connected) {
$app = get_app();
// We need to do this in order to disable logging
$app->module = 'install';
$app->mode = \Friendica\App::MODE_INSTALL;
// Create database structure
DBStructure::update(false, true, true);
$app->mode = \Friendica\App::MODE_NORMAL;
} else {
$this->markTestSkipped('Could not connect to the database.');
$this->markTestSkipped('Could not connect to the database. Please check the MYSQL_* environment variables.');
}
}
return $this->createDefaultDBConnection(dba::get_db(), getenv('DB'));
return $this->createDefaultDBConnection(dba::get_db(), getenv('MYSQL_DATABASE'));
}
/**

View file

@ -14,7 +14,7 @@ user:
uid: 42
username: Test user
nickname: selfcontact
verified: true
verified: 1
password: $2y$10$DLRNTRmJgKe1cSrFJ5Jb0edCqvXlA9sh/RHdSnfxjbR.04yZRm4Qm
theme: frio
@ -24,12 +24,12 @@ contact:
uid: 42
name: Self contact
nick: selfcontact
self: true
self: 1
nurl: http://localhost/profile/selfcontact
url: http://localhost/profile/selfcontact
about: User used in tests
pending: false
blocked: false
pending: 0
blocked: 0
rel: 1
network: dfrn
-
@ -39,162 +39,162 @@ contact:
# the fallback to api_get_nick() in api_get_user()
name: othercontact
nick: othercontact
self: false
self: 0
nurl: http://localhost/profile/othercontact
url: http://localhost/profile/othercontact
pending: false
blocked: false
pending: 0
blocked: 0
rel: 0
network: dfrn
-
id: 44
uid: 0
uid: 42
name: Friend contact
nick: friendcontact
self: false
self: 0
nurl: http://localhost/profile/friendcontact
url: http://localhost/profile/friendcontact
pending: false
blocked: false
pending: 0
blocked: 0
rel: 2
network: dfrn
item:
-
id: 1
visible: true
visible: 1
contact-id: 42
author-id: 42
owner-id: 45
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: true
unseen: 1
body: Parent status
parent: 1
author-link: http://localhost/profile/selfcontact
wall: true
starred: true
origin: true
wall: 1
starred: 1
origin: 1
allow_cid: ''
allow_gid: ''
deny_cid: ''
deny_gid: ''
-
id: 2
visible: true
visible: 1
contact-id: 42
author-id: 42
owner-id: 45
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: false
unseen: 0
body: Reply
parent: 1
author-link: http://localhost/profile/selfcontact
wall: true
starred: false
origin: true
wall: 1
starred: 0
origin: 1
-
id: 3
visible: true
visible: 1
contact-id: 43
author-id: 43
owner-id: 42
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: false
unseen: 0
body: Other user status
parent: 3
author-link: http://localhost/profile/othercontact
wall: true
starred: false
origin: true
wall: 1
starred: 0
origin: 1
-
id: 4
visible: true
visible: 1
contact-id: 44
author-id: 44
owner-id: 42
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: false
unseen: 0
body: Friend user reply
parent: 1
author-link: http://localhost/profile/othercontact
wall: true
starred: false
origin: true
wall: 1
starred: 0
origin: 1
-
id: 5
visible: true
visible: 1
contact-id: 42
author-id: 42
owner-id: 42
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: false
unseen: 0
body: '[share]Shared status[/share]'
parent: 1
author-link: http://localhost/profile/othercontact
wall: true
starred: false
origin: true
wall: 1
starred: 0
origin: 1
allow_cid: ''
allow_gid: ''
deny_cid: ''
deny_gid: ''
-
id: 6
visible: true
visible: 1
contact-id: 44
author-id: 44
owner-id: 42
uid: 42
verb: http://activitystrea.ms/schema/1.0/post
unseen: false
unseen: 0
body: Friend user status
parent: 6
author-link: http://localhost/profile/othercontact
wall: true
starred: false
origin: true
wall: 1
starred: 0
origin: 1
thread:
-
iid: 1
visible: true
visible: 1
contact-id: 42
author-id: 42
owner-id: 42
uid: 42
wall: true
wall: 1
-
iid: 3
visible: true
visible: 1
contact-id: 43
author-id: 43
owner-id: 43
uid: 0
wall: true
wall: 1
-
iid: 6
visible: true
visible: 1
contact-id: 44
author-id: 44
owner-id: 44
uid: 0
wall: true
wall: 1
group:
-
id: 1
uid: 42
visible: true
visible: 1
name: Visible list
-
id: 2
uid: 42
visible: false
visible: 0
name: Private list
search:

View file

@ -0,0 +1,32 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\ArrayCache;
class ArrayCacheDriverTest extends MemoryCacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
$this->cache = new ArrayCache();
return $this->cache;
}
public function tearDown()
{
$this->cache->clear(false);
parent::tearDown();
}
public function testTTL()
{
// Array Cache doesn't support TTL
return true;
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\App;
use Friendica\Core\Config;
use Friendica\Test\DatabaseTest;
abstract class CacheTest extends DatabaseTest
{
/**
* @var \Friendica\Core\Cache\ICacheDriver
*/
protected $instance;
abstract protected function getInstance();
protected function setUp()
{
global $a;
parent::setUp();
$this->instance = $this->getInstance();
// Reusable App object
$this->app = new App(__DIR__.'/../');
$a = $this->app;
// Default config
Config::set('config', 'hostname', 'localhost');
Config::set('system', 'throttle_limit_day', 100);
Config::set('system', 'throttle_limit_week', 100);
Config::set('system', 'throttle_limit_month', 100);
Config::set('system', 'theme', 'system_theme');
}
function testSimple() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->set('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
$value = 'ipsum lorum';
$this->instance->set('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value not overwritten by second set');
$value2 = 'foobar';
$this->instance->set('value2', $value2);
$received2 = $this->instance->get('value2');
$this->assertEquals($value, $received, 'Value changed while setting other variable');
$this->assertEquals($value2, $received2, 'Second value not equal to original');
$this->assertNull($this->instance->get('not_set'), 'Unset value not equal to null');
$this->assertTrue($this->instance->delete('value1'));
$this->assertNull($this->instance->get('value1'));
}
function testClear() {
$value = 'ipsum lorum';
$this->instance->set('1_value1', $value . '1');
$this->instance->set('1_value2', $value . '2');
$this->instance->set('2_value1', $value . '3');
$this->instance->set('3_value1', $value . '4');
$this->assertEquals([
'1_value1' => 'ipsum lorum1',
'1_value2' => 'ipsum lorum2',
'2_value1' => 'ipsum lorum3',
'3_value1' => 'ipsum lorum4',
], [
'1_value1' => $this->instance->get('1_value1'),
'1_value2' => $this->instance->get('1_value2'),
'2_value1' => $this->instance->get('2_value1'),
'3_value1' => $this->instance->get('3_value1'),
]);
$this->assertTrue($this->instance->clear(false));
$this->assertEquals([
'1_value1' => null,
'1_value2' => null,
'2_value1' => null,
'3_value1' => null,
], [
'1_value1' => $this->instance->get('1_value1'),
'1_value2' => $this->instance->get('1_value2'),
'2_value1' => $this->instance->get('2_value1'),
'3_value1' => $this->instance->get('3_value1'),
]);
}
function testTTL() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->set('value1', $value, 1);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
sleep(2);
$this->assertNull($this->instance->get('value1'));
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\CacheDriverFactory;
class DatabaseCacheDriverTest extends CacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
$this->cache = CacheDriverFactory::create('database');
return $this->cache;
}
public function tearDown()
{
$this->cache->clear(false);
parent::tearDown();
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\CacheDriverFactory;
class MemcacheCacheDriverTest extends MemoryCacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Memcache')) {
try {
$this->cache = CacheDriverFactory::create('memcache');
} catch (\Exception $exception) {
print "Memcache - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return $this->cache;
} else {
$this->markTestSkipped('Memcache driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Memcache')) {
$this->cache->clear(false);
}
parent::tearDown();
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\CacheDriverFactory;
class MemcachedCacheDriverTest extends MemoryCacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Memcached')) {
try {
$this->cache = CacheDriverFactory::create('memcached');
} catch (\Exception $exception) {
print "Memcached - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return $this->cache;
} else {
$this->markTestSkipped('Memcached driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Memcached')) {
$this->cache->clear(false);
}
parent::tearDown();
}
}

View file

@ -0,0 +1,94 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\IMemoryCacheDriver;
abstract class MemoryCacheTest extends CacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
protected $instance;
function setUp()
{
parent::setUp();
if (!($this->instance instanceof IMemoryCacheDriver)) {
throw new \Exception('MemoryCacheTest unsupported');
}
}
function testCompareSet() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->add('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
$newValue = 'ipsum lorum';
$this->instance->compareSet('value1', $value, $newValue);
$received = $this->instance->get('value1');
$this->assertEquals($newValue, $received, 'Value not overwritten by compareSet');
}
function testNegativeCompareSet() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->add('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
$newValue = 'ipsum lorum';
$this->instance->compareSet('value1', 'wrong', $newValue);
$received = $this->instance->get('value1');
$this->assertNotEquals($newValue, $received, 'Value was wrongly overwritten by compareSet');
$this->assertEquals($value, $received, 'Value was wrongly overwritten by any other value');
}
function testCompareDelete() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->add('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
$this->instance->compareDelete('value1', $value);
$this->assertNull($this->instance->get('value1'), 'Value was not deleted by compareDelete');
}
function testNegativeCompareDelete() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->add('value1', $value);
$received = $this->instance->get('value1');
$this->assertEquals($value, $received, 'Value received from cache not equal to the original');
$this->instance->compareDelete('value1', 'wrong');
$this->assertNotNull($this->instance->get('value1'), 'Value was wrongly compareDeleted');
$this->instance->compareDelete('value1', $value);
$this->assertNull($this->instance->get('value1'), 'Value was wrongly NOT deleted by compareDelete');
}
function testAdd() {
$this->assertNull($this->instance->get('value1'));
$value = 'foobar';
$this->instance->add('value1', $value);
$newValue = 'ipsum lorum';
$this->instance->add('value1', $newValue);
$received = $this->instance->get('value1');
$this->assertNotEquals($newValue, $received, 'Value was wrongly overwritten by add');
$this->assertEquals($value, $received, 'Value was wrongly overwritten by any other value');
$this->instance->delete('value1');
$this->instance->add('value1', $newValue);
$received = $this->instance->get('value1');
$this->assertEquals($newValue, $received, 'Value was not overwritten by add');
$this->assertNotEquals($value, $received, 'Value was not overwritten by any other value');
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace Friendica\Test\src\Core\Cache;
use Friendica\Core\Cache\CacheDriverFactory;
class RedisCacheDriverTest extends MemoryCacheTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Redis')) {
try {
$this->cache = CacheDriverFactory::create('redis');
} catch (\Exception $exception) {
print "Redis - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return $this->cache;
} else {
$this->markTestSkipped('Redis driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Redis')) {
$this->cache->clear(false);
}
parent::tearDown();
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\ArrayCache;
use Friendica\Core\Lock\CacheLockDriver;
class ArrayCacheLockDriverTest extends LockTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
$this->cache = new ArrayCache();
return new CacheLockDriver($this->cache);
}
public function tearDown()
{
$this->cache->clear();
parent::tearDown();
}
public function testLockTTL()
{
// ArrayCache doesn't support TTL
return true;
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use dba;
use Friendica\Core\Lock\DatabaseLockDriver;
use Friendica\Database\DBStructure;
use PHPUnit\DbUnit\DataSet\YamlDataSet;
use PHPUnit\DbUnit\TestCaseTrait;
use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
class DatabaseLockDriverTest extends LockTest
{
protected function getInstance()
{
return new DatabaseLockDriver();
}
public function tearDown()
{
dba::delete('locks', [ 'id > 0']);
parent::tearDown();
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\App;
use Friendica\Core\Config;
use Friendica\Test\DatabaseTest;
use PHPUnit\Framework\TestCase;
abstract class LockTest extends DatabaseTest
{
/**
* @var \Friendica\Core\Lock\ILockDriver
*/
protected $instance;
abstract protected function getInstance();
protected function setUp()
{
global $a;
parent::setUp();
$this->instance = $this->getInstance();
// Reusable App object
$this->app = new App(__DIR__.'/../');
$a = $this->app;
// Default config
Config::set('config', 'hostname', 'localhost');
Config::set('system', 'throttle_limit_day', 100);
Config::set('system', 'throttle_limit_week', 100);
Config::set('system', 'throttle_limit_month', 100);
Config::set('system', 'theme', 'system_theme');
}
public function testLock() {
$this->instance->acquireLock('foo', 1);
$this->assertTrue($this->instance->isLocked('foo'));
$this->assertFalse($this->instance->isLocked('bar'));
}
public function testDoubleLock() {
$this->instance->acquireLock('foo', 1);
$this->assertTrue($this->instance->isLocked('foo'));
// We already locked it
$this->assertTrue($this->instance->acquireLock('foo', 1));
}
public function testReleaseLock() {
$this->instance->acquireLock('foo', 1);
$this->assertTrue($this->instance->isLocked('foo'));
$this->instance->releaseLock('foo');
$this->assertFalse($this->instance->isLocked('foo'));
}
public function testReleaseAll() {
$this->instance->acquireLock('foo', 1);
$this->instance->acquireLock('bar', 1);
$this->instance->acquireLock('nice', 1);
$this->assertTrue($this->instance->isLocked('foo'));
$this->assertTrue($this->instance->isLocked('bar'));
$this->assertTrue($this->instance->isLocked('nice'));
$this->instance->releaseAll();
$this->assertFalse($this->instance->isLocked('foo'));
$this->assertFalse($this->instance->isLocked('bar'));
$this->assertFalse($this->instance->isLocked('nice'));
}
public function testReleaseAfterUnlock() {
$this->instance->acquireLock('foo', 1);
$this->instance->acquireLock('bar', 1);
$this->instance->acquireLock('nice', 1);
$this->instance->releaseLock('foo');
$this->assertFalse($this->instance->isLocked('foo'));
$this->assertTrue($this->instance->isLocked('bar'));
$this->assertTrue($this->instance->isLocked('nice'));
$this->instance->releaseAll();
$this->assertFalse($this->instance->isLocked('bar'));
$this->assertFalse($this->instance->isLocked('nice'));
}
function testLockTTL() {
// TODO [nupplaphil] - Because of the Datetime-Utils for the database, we have to wait a FULL second between the checks to invalidate the db-locks/cache
$this->instance->acquireLock('foo', 1, 1);
$this->instance->acquireLock('bar', 1, 3);
$this->assertTrue($this->instance->isLocked('foo'));
$this->assertTrue($this->instance->isLocked('bar'));
sleep(2);
$this->assertFalse($this->instance->isLocked('foo'));
$this->assertTrue($this->instance->isLocked('bar'));
sleep(2);
$this->assertFalse($this->instance->isLocked('foo'));
$this->assertFalse($this->instance->isLocked('bar'));
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\CacheDriverFactory;
use Friendica\Core\Lock\CacheLockDriver;
class MemcacheCacheLockDriverTest extends LockTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Memcache')) {
try {
$this->cache = CacheDriverFactory::create('memcache');
} catch (\Exception $exception) {
print "Memcache - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return new CacheLockDriver($this->cache);
} else {
$this->markTestSkipped('Memcache driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Memcache')) {
$this->cache->clear();
}
parent::tearDown();
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\CacheDriverFactory;
use Friendica\Core\Lock\CacheLockDriver;
class MemcachedCacheLockDriverTest extends LockTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Memcached')) {
try {
$this->cache = CacheDriverFactory::create('memcached');
} catch (\Exception $exception) {
print "Memcached - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return new CacheLockDriver($this->cache);
} else {
$this->markTestSkipped('Memcached driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Memcached')) {
$this->cache->clear();
}
parent::tearDown();
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Cache\CacheDriverFactory;
use Friendica\Core\Lock\CacheLockDriver;
class RedisCacheLockDriverTest extends LockTest
{
/**
* @var \Friendica\Core\Cache\IMemoryCacheDriver
*/
private $cache;
protected function getInstance()
{
if (class_exists('Redis')) {
try {
$this->cache = CacheDriverFactory::create('redis');
} catch (\Exception $exception) {
print "Redis - TestCase failed: " . $exception->getMessage();
throw new \Exception();
}
return new CacheLockDriver($this->cache);
} else {
$this->markTestSkipped('Redis driver isn\'t available');
return null;
}
}
public function tearDown()
{
if (class_exists('Redis')) {
$this->cache->clear();
}
parent::tearDown();
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace Friendica\Test\src\Core\Lock;
use Friendica\Core\Lock\SemaphoreLockDriver;
class SemaphoreLockDriverTest extends LockTest
{
/**
* @var \Friendica\Core\Lock\SemaphoreLockDriver
*/
private $semaphoreLockDriver;
protected function getInstance()
{
$this->semaphoreLockDriver = new SemaphoreLockDriver();
return $this->semaphoreLockDriver;
}
public function tearDown()
{
$this->semaphoreLockDriver->releaseAll();
parent::tearDown();
}
function testLockTTL()
{
// Semaphore doesn't work with TTL
return true;
}
}

View file

@ -13,7 +13,7 @@ msgstr ""
"Project-Id-Version: friendica\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-06-30 17:33+0200\n"
"PO-Revision-Date: 2018-07-01 23:15+0000\n"
"PO-Revision-Date: 2018-07-06 22:09+0000\n"
"Last-Translator: Aditoo\n"
"Language-Team: Czech (http://www.transifex.com/Friendica/friendica/language/cs/)\n"
"MIME-Version: 1.0\n"
@ -91,7 +91,7 @@ msgstr "Vítejte "
#: include/security.php:82
msgid "Please upload a profile photo."
msgstr "Prosím nahrejte profilovou fotografii"
msgstr "Prosím nahrajte profilovou fotografii"
#: include/security.php:84
msgid "Welcome back "
@ -158,12 +158,12 @@ msgstr "fotka"
#: src/Protocol/Diaspora.php:1945
#, php-format
msgid "%1$s likes %2$s's %3$s"
msgstr "%1$s se líbí %3$s %2$s"
msgstr "Uživateli %1$s se líbí %3$s %2$s"
#: include/conversation.php:170 src/Model/Item.php:2535
#, php-format
msgid "%1$s doesn't like %2$s's %3$s"
msgstr "%1$s se nelíbí %3$s %2$s"
msgstr "Uživateli %1$s se nelíbí %3$s %2$s"
#: include/conversation.php:172
#, php-format
@ -183,7 +183,7 @@ msgstr "%1$s se možná účastní %3$s %2$s"
#: include/conversation.php:211
#, php-format
msgid "%1$s is now friends with %2$s"
msgstr "%1$s je nyní přítel s %2$s"
msgstr "%1$s je nyní přítel s uživatelem %2$s"
#: include/conversation.php:252
#, php-format
@ -193,7 +193,7 @@ msgstr "%1$s šťouchnul %2$s"
#: include/conversation.php:300 mod/tagger.php:108
#, php-format
msgid "%1$s tagged %2$s's %3$s with %4$s"
msgstr "%1$s označil %3$s %2$s s %4$s"
msgstr "%1$s označil/a %3$s uživatele %2$s jako %4$s"
#: include/conversation.php:322
msgid "post/item"
@ -202,15 +202,15 @@ msgstr "příspěvek/položka"
#: include/conversation.php:323
#, php-format
msgid "%1$s marked %2$s's %3$s as favorite"
msgstr "uživatel %1$s označil %3$s %2$s jako oblíbené"
msgstr "%1$s označil/a %3$s uživatele %2$s jako oblíbené"
#: include/conversation.php:504 mod/profiles.php:355 mod/photos.php:1464
msgid "Likes"
msgstr "Libí se mi"
msgstr "Libí se"
#: include/conversation.php:504 mod/profiles.php:359 mod/photos.php:1464
msgid "Dislikes"
msgstr "Nelibí se mi"
msgstr "Nelibí se"
#: include/conversation.php:505 include/conversation.php:1455
#: mod/photos.php:1465
@ -320,12 +320,12 @@ msgstr "Připojit / Následovat"
#: include/conversation.php:969
#, php-format
msgid "%s likes this."
msgstr "%s se to líbí."
msgstr "Uživateli %s se tohle líbí."
#: include/conversation.php:972
#, php-format
msgid "%s doesn't like this."
msgstr "%s se to nelíbí."
msgstr "Uživateli %s se tohle nelíbí."
#: include/conversation.php:975
#, php-format
@ -354,22 +354,22 @@ msgstr "a dalších %d lidí"
#: include/conversation.php:1007
#, php-format
msgid "<span %1$s>%2$d people</span> like this"
msgstr "<span %1$s>%2$d lidem</span> se to líbí"
msgstr "<span %1$s>%2$d lidem</span> se tohle líbí"
#: include/conversation.php:1008
#, php-format
msgid "%s like this."
msgstr "%s se tohle líbí."
msgstr "Uživatelům %s se tohle líbí."
#: include/conversation.php:1011
#, php-format
msgid "<span %1$s>%2$d people</span> don't like this"
msgstr "<span %1$s>%2$d lidem</span> se to nelíbí"
msgstr "<span %1$s>%2$d lidem</span> se tohle nelíbí"
#: include/conversation.php:1012
#, php-format
msgid "%s don't like this."
msgstr "%s se tohle nelíbí."
msgstr "Uživatelům %s se tohle nelíbí."
#: include/conversation.php:1015
#, php-format
@ -530,11 +530,11 @@ msgstr "Náhled"
#: include/conversation.php:1153
msgid "Post to Groups"
msgstr "Zveřejnit na Groups"
msgstr "Zveřejnit ve skupinách"
#: include/conversation.php:1154
msgid "Post to Contacts"
msgstr "Zveřejnit na Groups"
msgstr "Zveřejnit v kontaktech"
#: include/conversation.php:1155
msgid "Private post"
@ -596,12 +596,12 @@ msgstr "Děkujeme, "
#: include/enotify.php:38
#, php-format
msgid "%s Administrator"
msgstr "%s administrátor"
msgstr "Administrátor %s"
#: include/enotify.php:40
#, php-format
msgid "%1$s, %2$s Administrator"
msgstr "%1$s, %2$s administrátor"
msgstr "%1$s, administrátor %2$s"
#: include/enotify.php:96
#, php-format
@ -740,12 +740,12 @@ msgstr "[Friendica:Oznámení] Obdrženo představení"
#: include/enotify.php:262
#, php-format
msgid "You've received an introduction from '%1$s' at %2$s"
msgstr "Obdržel/a jste představení od \"%1$s\" na %2$s"
msgstr "Obdržel/a jste představení od uživatele \"%1$s\" na %2$s"
#: include/enotify.php:263
#, php-format
msgid "You've received [url=%1$s]an introduction[/url] from %2$s."
msgstr "Obdržel/a jste [url=%1$s]představení[/url] od %2$s."
msgstr "Obdržel/a jste [url=%1$s]představení[/url] od uživatele %2$s."
#: include/enotify.php:268 include/enotify.php:314
#, php-format
@ -782,13 +782,13 @@ msgstr "[Friendica:Oznámení] Obdržen návrh pro přátelství"
#: include/enotify.php:305
#, php-format
msgid "You've received a friend suggestion from '%1$s' at %2$s"
msgstr "Obdržel jste návrh pro přátelství od '%1$s' na %2$s"
msgstr "Obdržel/a jste návrh pro přátelství od uživatele \"%1$s\" na %2$s"
#: include/enotify.php:306
#, php-format
msgid ""
"You've received [url=%1$s]a friend suggestion[/url] for %2$s from %3$s."
msgstr "Obdržel jste [url=%1$s]návrh pro přátelství[/url] s %2$s from %3$s."
msgstr "Obdržel/a jste [url=%1$s]návrh pro přátelství[/url] s uživatelem %2$s od uživatele %3$s."
#: include/enotify.php:312
msgid "Name:"
@ -810,12 +810,12 @@ msgstr "[Friendica:Oznámení] Spojení akceptováno"
#: include/enotify.php:326 include/enotify.php:341
#, php-format
msgid "'%1$s' has accepted your connection request at %2$s"
msgstr "\"%1$s\" akceptoval váš požadavek na spojení na %2$s"
msgstr "\"%1$s\" akceptoval/a Váš požadavek na spojení na %2$s"
#: include/enotify.php:327 include/enotify.php:342
#, php-format
msgid "%2$s has accepted your [url=%1$s]connection request[/url]."
msgstr "%2$s akceptoval váš [url=%1$s]požadavek na spojení[/url]."
msgstr "%2$s akceptoval/a Váš [url=%1$s]požadavek na spojení[/url]."
#: include/enotify.php:332
msgid ""
@ -860,12 +860,12 @@ msgstr "žádost o registraci"
#: include/enotify.php:363
#, php-format
msgid "You've received a registration request from '%1$s' at %2$s"
msgstr "Obdržel jste žádost o registraci od '%1$s' na %2$s"
msgstr "Obdržel/a jste žádost o registraci od uživatele \"%1$s\" na %2$s"
#: include/enotify.php:364
#, php-format
msgid "You've received a [url=%1$s]registration request[/url] from %2$s."
msgstr "Obdržel jste [url=%1$s]žádost o registraci[/url] od '%2$s'."
msgstr "Obdržel/a jste [url=%1$s]žádost o registraci[/url] od uživatele \"%2$s\"."
#: include/enotify.php:369
#, php-format
@ -939,7 +939,7 @@ msgstr "Sledovat"
#: include/text.php:985 mod/search.php:156 src/Content/Nav.php:142
msgid "Search"
msgstr "Vyhledávání"
msgstr "Hledat"
#: include/text.php:988 src/Content/Nav.php:58
msgid "@name, !forum, #tags, content"
@ -972,7 +972,7 @@ msgstr "šťouchnout"
#: include/text.php:1043
msgid "poked"
msgstr "šťouchnul"
msgstr "šťouchnul/a"
#: include/text.php:1044
msgid "ping"
@ -980,7 +980,7 @@ msgstr "cinknout"
#: include/text.php:1044
msgid "pinged"
msgstr "cinknul"
msgstr "cinknul/a"
#: include/text.php:1045
msgid "prod"
@ -988,7 +988,7 @@ msgstr "dloubnout"
#: include/text.php:1045
msgid "prodded"
msgstr "dloubnul"
msgstr "dloubnul/a"
#: include/text.php:1046
msgid "slap"
@ -996,15 +996,15 @@ msgstr "uhodit"
#: include/text.php:1046
msgid "slapped"
msgstr "uhodil"
msgstr "uhodil/a"
#: include/text.php:1047
msgid "finger"
msgstr "osahávat"
msgstr "osahat"
#: include/text.php:1047
msgid "fingered"
msgstr "osahal"
msgstr "osahal/a"
#: include/text.php:1048
msgid "rebuff"
@ -1012,7 +1012,7 @@ msgstr "odmítnout"
#: include/text.php:1048
msgid "rebuffed"
msgstr "odmítnul"
msgstr "odmítnul/a"
#: include/text.php:1062 mod/settings.php:935 src/Model/Event.php:379
msgid "Monday"
@ -1361,7 +1361,7 @@ msgid ""
"On your <em>Settings</em> page - change your initial password. Also make a "
"note of your Identity Address. This looks just like an email address - and "
"will be useful in making friends on the free social web."
msgstr "Na Vaší stránce <em>Nastavení</em> si změňte Vaše první heslo.\nVěnujte také svou pozornost k adrese Identity. Vypadá jako emailová adresa a bude Vám užitečná pro navazování přátelství na svobodném sociálním webu."
msgstr "Na Vaší stránce <em>Nastavení</em> si změňte Vaše první heslo. Věnujte také svou pozornost k adrese Identity. Vypadá jako emailová adresa a bude Vám užitečná pro navazování přátelství na svobodném sociálním webu."
#: mod/newmember.php:22
msgid ""
@ -2089,7 +2089,7 @@ msgstr "Odpovězte, prosím, následující:"
#: mod/follow.php:150 mod/dfrn_request.php:647
#, php-format
msgid "Does %s know you?"
msgstr "Zná Vás uživatel %s ?"
msgstr "Zná Vás %s ?"
#: mod/follow.php:151 mod/dfrn_request.php:648
msgid "Add a personal note:"
@ -2607,7 +2607,7 @@ msgstr "Vaše databáze Friendica byla nainstalována."
msgid ""
"You may need to import the file \"database.sql\" manually using phpmyadmin "
"or mysql."
msgstr "Pravděpodobně budete muset manuálně importovat soubor \"database.sql\" pomocí phpMyAdmin či MySQL."
msgstr "Nejspíše budete muset manuálně importovat soubor \"database.sql\" pomocí phpMyAdmin či MySQL."
#: mod/install.php:109 mod/install.php:155 mod/install.php:267
msgid "Please see the file \"INSTALL.txt\"."
@ -2639,7 +2639,7 @@ msgstr "Pro instalaci Friendica potřeujeme znát připojení k Vaší databázi
msgid ""
"Please contact your hosting provider or site administrator if you have "
"questions about these settings."
msgstr "Pokud máte otázky k následujícím nastavením, obraťte se na svého poskytovatele hostingu nebo administrátora serveru, "
msgstr "Pokud máte otázky k následujícím nastavením, obraťte se na svého poskytovatele hostingu nebo administrátora serveru."
#: mod/install.php:180
msgid ""
@ -3491,11 +3491,11 @@ msgstr "Nastavení doplňků"
#: mod/settings.php:767 mod/admin.php:2463 mod/admin.php:2464
msgid "Off"
msgstr "Vypnuto"
msgstr "Vyp"
#: mod/settings.php:767 mod/admin.php:2463 mod/admin.php:2464
msgid "On"
msgstr "Zapnuto"
msgstr "Zap"
#: mod/settings.php:774
msgid "Additional Features"
@ -3524,11 +3524,11 @@ msgstr "GNU Social (OStatus)"
#: mod/settings.php:829
msgid "Email access is disabled on this site."
msgstr "Přístup k elektronické poště je na tomto serveru zakázán."
msgstr "Přístup k e-mailu je na tomto serveru zakázán."
#: mod/settings.php:839
msgid "General Social Media Settings"
msgstr "General Social Media nastavení"
msgstr "Obecná nastavení sociálních sítí"
#: mod/settings.php:840
msgid "Disable Content Warning"
@ -3833,7 +3833,7 @@ msgstr "Účet pro běžný osobní profil, který vyžaduje manuální potvrzen
#: mod/settings.php:1062 mod/admin.php:1760
msgid "Soapbox Page"
msgstr "Stránka \"Soapbox\""
msgstr "Propagační stránka"
#: mod/settings.php:1063
msgid ""
@ -3851,7 +3851,7 @@ msgstr "Automaticky potvrzuje všechny žádosti o přidání kontaktu."
#: mod/settings.php:1070 mod/admin.php:1762
msgid "Automatic Friend Page"
msgstr "Automatická stránka přítele"
msgstr "Stránka s automatickými přátely"
#: mod/settings.php:1071
msgid ""
@ -3945,7 +3945,7 @@ msgstr "Povolit, abychom vás navrhovali jako přátelé pro nové členy?"
#: mod/settings.php:1123
msgid ""
"If you like, Friendica may suggest new members to add you as a contact."
msgstr "Pokud budete chtít, Friendica může nabízet novým členům, abi si Vás přidali jako kontakt."
msgstr "Pokud budete chtít, může Friendica nabízet novým členům, aby si Vás přidali jako kontakt."
#: mod/settings.php:1127
msgid "Permit unknown people to send you private mail?"
@ -4078,7 +4078,7 @@ msgstr "Maximální počet žádostí o přátelství za den:"
#: mod/settings.php:1208 mod/settings.php:1237
msgid "(to prevent spam abuse)"
msgstr "(Aby se zabránilo spamu)"
msgstr "(ay se zabránilo spamu)"
#: mod/settings.php:1209
msgid "Default Post Permissions"
@ -4150,7 +4150,7 @@ msgstr "Jste označen v příspěvku"
#: mod/settings.php:1249
msgid "You are poked/prodded/etc. in a post"
msgstr "Byl jste šťouchnut(a)/dloubnut(a)/apod. v příspěvku"
msgstr "Byl/a jste šťouchnut(a)/dloubnut(a)/apod. v příspěvku"
#: mod/settings.php:1251
msgid "Activate desktop notifications"
@ -4222,7 +4222,7 @@ msgstr "Zobrazit album"
#: mod/videos.php:396
msgid "Recent Videos"
msgstr "Aktuální Videa"
msgstr "Aktuální videa"
#: mod/videos.php:398
msgid "Upload New Videos"
@ -4319,7 +4319,7 @@ msgstr "vyzkoušet webfinger"
#: mod/admin.php:223 src/Content/Nav.php:218
msgid "Admin"
msgstr "Administrace"
msgstr "Administrátor"
#: mod/admin.php:224
msgid "Addon Features"
@ -4535,7 +4535,7 @@ msgstr "Smazat tuto položku"
msgid ""
"On this page you can delete an item from your node. If the item is a top "
"level posting, the entire thread will be deleted."
msgstr "Na této stránce můžete smazat pološku z Vašeho serveru. Pokud je položkou příspěvek nejvyššího stupně, bude smazáno celé vlákno."
msgstr "Na této stránce můžete smazat položku z Vašeho serveru. Pokud je položkou příspěvek nejvyššího stupně, bude smazáno celé vlákno."
#: mod/admin.php:528
msgid ""
@ -5678,7 +5678,7 @@ msgid ""
"\n"
"\t\t\tDear %1$s,\n"
"\t\t\t\tthe administrator of %2$s has set up an account for you."
msgstr "\n\t\t\tVážený/á%1$s,\n\t\t\t\tadministrátor %2$s pro Vás vytvořil/a uživatelský účet."
msgstr "\n\t\t\tVážený/á%1$s,\n\t\t\t\tadministrátor %2$s pro Vás vytvořil uživatelský účet."
#: mod/admin.php:1597
#, php-format
@ -5809,7 +5809,7 @@ msgstr "Odmítnout"
#: mod/admin.php:1835
msgid "Site admin"
msgstr "Site administrátor"
msgstr "Administrátor webu"
#: mod/admin.php:1836
msgid "Account expired"
@ -6339,7 +6339,7 @@ msgstr "Vrátit z archívu"
#: mod/contacts.php:831
msgid "Batch Actions"
msgstr "Dávkové (batch) akce"
msgstr "Souhrnné akce"
#: mod/contacts.php:865 src/Model/Profile.php:894
msgid "Profile Details"

View file

@ -14,7 +14,7 @@ $a->strings["Permission denied."] = "Přístup odmítnut.";
$a->strings["Archives"] = "Archív";
$a->strings["show more"] = "zobrazit více";
$a->strings["Welcome "] = "Vítejte ";
$a->strings["Please upload a profile photo."] = "Prosím nahrejte profilovou fotografii";
$a->strings["Please upload a profile photo."] = "Prosím nahrajte profilovou fotografii";
$a->strings["Welcome back "] = "Vítejte zpět ";
$a->strings["The form security token was not correct. This probably happened because the form has been opened for too long (>3 hours) before submitting it."] = "Formulářový bezpečnostní token nebyl správný. To pravděpodobně nastalo kvůli tom, že formulář byl otevřen příliš dlouho (>3 hodiny) před jeho odesláním.";
$a->strings["Daily posting limit of %d post reached. The post was rejected."] = [
@ -34,18 +34,18 @@ $a->strings["Profile Photos"] = "Profilové fotky";
$a->strings["event"] = "událost";
$a->strings["status"] = "stav";
$a->strings["photo"] = "fotka";
$a->strings["%1\$s likes %2\$s's %3\$s"] = "%1\$s se líbí %3\$s %2\$s";
$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "%1\$s se nelíbí %3\$s %2\$s";
$a->strings["%1\$s likes %2\$s's %3\$s"] = "Uživateli %1\$s se líbí %3\$s %2\$s";
$a->strings["%1\$s doesn't like %2\$s's %3\$s"] = "Uživateli %1\$s se nelíbí %3\$s %2\$s";
$a->strings["%1\$s attends %2\$s's %3\$s"] = "%1\$s se účastní %3\$s %2\$s";
$a->strings["%1\$s doesn't attend %2\$s's %3\$s"] = "%1\$s se neúčastní %3\$s %2\$s";
$a->strings["%1\$s attends maybe %2\$s's %3\$s"] = "%1\$s se možná účastní %3\$s %2\$s";
$a->strings["%1\$s is now friends with %2\$s"] = "%1\$s je nyní přítel s %2\$s";
$a->strings["%1\$s is now friends with %2\$s"] = "%1\$s je nyní přítel s uživatelem %2\$s";
$a->strings["%1\$s poked %2\$s"] = "%1\$s šťouchnul %2\$s";
$a->strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s označil %3\$s %2\$s s %4\$s";
$a->strings["%1\$s tagged %2\$s's %3\$s with %4\$s"] = "%1\$s označil/a %3\$s uživatele %2\$s jako %4\$s";
$a->strings["post/item"] = "příspěvek/položka";
$a->strings["%1\$s marked %2\$s's %3\$s as favorite"] = "uživatel %1\$s označil %3\$s %2\$s jako oblíbené";
$a->strings["Likes"] = "Libí se mi";
$a->strings["Dislikes"] = "Nelibí se mi";
$a->strings["%1\$s marked %2\$s's %3\$s as favorite"] = "%1\$s označil/a %3\$s uživatele %2\$s jako oblíbené";
$a->strings["Likes"] = "Libí se";
$a->strings["Dislikes"] = "Nelibí se";
$a->strings["Attending"] = [
0 => "Účastní se",
1 => "Účastní se",
@ -73,17 +73,17 @@ $a->strings["View Contact"] = "Zobrazit kontakt";
$a->strings["Send PM"] = "Poslat soukromou zprávu";
$a->strings["Poke"] = "Šťouchnout";
$a->strings["Connect/Follow"] = "Připojit / Následovat";
$a->strings["%s likes this."] = "%s se to líbí.";
$a->strings["%s doesn't like this."] = "%s se to nelíbí.";
$a->strings["%s likes this."] = "Uživateli %s se tohle líbí.";
$a->strings["%s doesn't like this."] = "Uživateli %s se tohle nelíbí.";
$a->strings["%s attends."] = "%s se účastní.";
$a->strings["%s doesn't attend."] = "%s se neúčastní.";
$a->strings["%s attends maybe."] = "%s se možná účastní.";
$a->strings["and"] = "a";
$a->strings["and %d other people"] = "a dalších %d lidí";
$a->strings["<span %1\$s>%2\$d people</span> like this"] = "<span %1\$s>%2\$d lidem</span> se to líbí";
$a->strings["%s like this."] = "%s se tohle líbí.";
$a->strings["<span %1\$s>%2\$d people</span> don't like this"] = "<span %1\$s>%2\$d lidem</span> se to nelíbí";
$a->strings["%s don't like this."] = "%s se tohle nelíbí.";
$a->strings["<span %1\$s>%2\$d people</span> like this"] = "<span %1\$s>%2\$d lidem</span> se tohle líbí";
$a->strings["%s like this."] = "Uživatelům %s se tohle líbí.";
$a->strings["<span %1\$s>%2\$d people</span> don't like this"] = "<span %1\$s>%2\$d lidem</span> se tohle nelíbí";
$a->strings["%s don't like this."] = "Uživatelům %s se tohle nelíbí.";
$a->strings["<span %1\$s>%2\$d people</span> attend"] = "<span %1\$s>%2\$d lidí</span> se účastní";
$a->strings["%s attend."] = "%s se účastní.";
$a->strings["<span %1\$s>%2\$d people</span> don't attend"] = "<span %1\$s>%2\$d lidí</span> se neúčastní";
@ -120,8 +120,8 @@ $a->strings["Permission settings"] = "Nastavení oprávnění";
$a->strings["permissions"] = "oprávnění";
$a->strings["Public post"] = "Veřejný příspěvek";
$a->strings["Preview"] = "Náhled";
$a->strings["Post to Groups"] = "Zveřejnit na Groups";
$a->strings["Post to Contacts"] = "Zveřejnit na Groups";
$a->strings["Post to Groups"] = "Zveřejnit ve skupinách";
$a->strings["Post to Contacts"] = "Zveřejnit v kontaktech";
$a->strings["Private post"] = "Soukromý příspěvek";
$a->strings["Message"] = "Zpráva";
$a->strings["Browser"] = "Prohlížeč";
@ -152,8 +152,8 @@ $a->strings["Undecided"] = [
];
$a->strings["Friendica Notification"] = "Oznámení Friendica";
$a->strings["Thank You,"] = "Děkujeme, ";
$a->strings["%s Administrator"] = "%s administrátor";
$a->strings["%1\$s, %2\$s Administrator"] = "%1\$s, %2\$s administrátor";
$a->strings["%s Administrator"] = "Administrátor %s";
$a->strings["%1\$s, %2\$s Administrator"] = "%1\$s, administrátor %2\$s";
$a->strings["[Friendica:Notify] New mail received at %s"] = "[Friendica:Oznámení] Obdržena nová zpráva na %s";
$a->strings["%1\$s sent you a new private message at %2\$s."] = "%1\$s Vám poslal/a novou soukromou zprávu na %2\$s.";
$a->strings["a private message"] = "soukromou zprávu";
@ -181,8 +181,8 @@ $a->strings["[Friendica:Notify] %s tagged your post"] = "[Friendica:Oznámení]
$a->strings["%1\$s tagged your post at %2\$s"] = "%1\$s označil/a Váš příspěvek na%2\$s";
$a->strings["%1\$s tagged [url=%2\$s]your post[/url]"] = "%1\$s označil/a [url=%2\$s]Váš příspěvek[/url]";
$a->strings["[Friendica:Notify] Introduction received"] = "[Friendica:Oznámení] Obdrženo představení";
$a->strings["You've received an introduction from '%1\$s' at %2\$s"] = "Obdržel/a jste představení od \"%1\$s\" na %2\$s";
$a->strings["You've received [url=%1\$s]an introduction[/url] from %2\$s."] = "Obdržel/a jste [url=%1\$s]představení[/url] od %2\$s.";
$a->strings["You've received an introduction from '%1\$s' at %2\$s"] = "Obdržel/a jste představení od uživatele \"%1\$s\" na %2\$s";
$a->strings["You've received [url=%1\$s]an introduction[/url] from %2\$s."] = "Obdržel/a jste [url=%1\$s]představení[/url] od uživatele %2\$s.";
$a->strings["You may visit their profile at %s"] = "Můžete navštívit jejich profil na %s";
$a->strings["Please visit %s to approve or reject the introduction."] = "Prosím navštivte %s pro schválení či zamítnutí představení.";
$a->strings["[Friendica:Notify] A new person is sharing with you"] = "[Friendica:Oznámení] Nový člověk s vámi sdílí";
@ -190,14 +190,14 @@ $a->strings["%1\$s is sharing with you at %2\$s"] = "Uživatel %1\$s s vámi sd
$a->strings["[Friendica:Notify] You have a new follower"] = "[Friendica:Oznámení] Máte nového sledovatele";
$a->strings["You have a new follower at %2\$s : %1\$s"] = "Máte nového sledovatele na %2\$s : %1\$s";
$a->strings["[Friendica:Notify] Friend suggestion received"] = "[Friendica:Oznámení] Obdržen návrh pro přátelství";
$a->strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = "Obdržel jste návrh pro přátelství od '%1\$s' na %2\$s";
$a->strings["You've received [url=%1\$s]a friend suggestion[/url] for %2\$s from %3\$s."] = "Obdržel jste [url=%1\$s]návrh pro přátelství[/url] s %2\$s from %3\$s.";
$a->strings["You've received a friend suggestion from '%1\$s' at %2\$s"] = "Obdržel/a jste návrh pro přátelství od uživatele \"%1\$s\" na %2\$s";
$a->strings["You've received [url=%1\$s]a friend suggestion[/url] for %2\$s from %3\$s."] = "Obdržel/a jste [url=%1\$s]návrh pro přátelství[/url] s uživatelem %2\$s od uživatele %3\$s.";
$a->strings["Name:"] = "Jméno:";
$a->strings["Photo:"] = "Foto:";
$a->strings["Please visit %s to approve or reject the suggestion."] = "Prosím navštivte %s pro schválení či zamítnutí doporučení.";
$a->strings["[Friendica:Notify] Connection accepted"] = "[Friendica:Oznámení] Spojení akceptováno";
$a->strings["'%1\$s' has accepted your connection request at %2\$s"] = "\"%1\$s\" akceptoval váš požadavek na spojení na %2\$s";
$a->strings["%2\$s has accepted your [url=%1\$s]connection request[/url]."] = "%2\$s akceptoval váš [url=%1\$s]požadavek na spojení[/url].";
$a->strings["'%1\$s' has accepted your connection request at %2\$s"] = "\"%1\$s\" akceptoval/a Váš požadavek na spojení na %2\$s";
$a->strings["%2\$s has accepted your [url=%1\$s]connection request[/url]."] = "%2\$s akceptoval/a Váš [url=%1\$s]požadavek na spojení[/url].";
$a->strings["You are now mutual friends and may exchange status updates, photos, and email without restriction."] = "Jste nyní vzájemní přátelé a můžete si vyměňovat aktualizace stavu, fotky a e-maily bez omezení.";
$a->strings["Please visit %s if you wish to make any changes to this relationship."] = "Pokud chcete provést změny s tímto vztahem, prosím navštivte %s.";
$a->strings["'%1\$s' has chosen to accept you a fan, which restricts some forms of communication - such as private messaging and some profile interactions. If this is a celebrity or community page, these settings were applied automatically."] = "\"%1\$s\" se rozhodl/a Vás přijmout jako fanouška, což omezuje některé formy komunikace - například soukoromé zprávy a některé interakce s profily. Pokud je toto stránka celebrity či komunity, byla tato nastavení aplikována automaticky.";
@ -205,8 +205,8 @@ $a->strings["'%1\$s' may choose to extend this into a two-way or more permissive
$a->strings["Please visit %s if you wish to make any changes to this relationship."] = "Prosím navštivte %s pokud chcete změnit tento vztah.";
$a->strings["[Friendica System Notify]"] = "[Oznámení systému Friendica]";
$a->strings["registration request"] = "žádost o registraci";
$a->strings["You've received a registration request from '%1\$s' at %2\$s"] = "Obdržel jste žádost o registraci od '%1\$s' na %2\$s";
$a->strings["You've received a [url=%1\$s]registration request[/url] from %2\$s."] = "Obdržel jste [url=%1\$s]žádost o registraci[/url] od '%2\$s'.";
$a->strings["You've received a registration request from '%1\$s' at %2\$s"] = "Obdržel/a jste žádost o registraci od uživatele \"%1\$s\" na %2\$s";
$a->strings["You've received a [url=%1\$s]registration request[/url] from %2\$s."] = "Obdržel/a jste [url=%1\$s]žádost o registraci[/url] od uživatele \"%2\$s\".";
$a->strings["Full Name:\t%s\nSite Location:\t%s\nLogin Name:\t%s (%s)"] = "Celé jméno:\t\t%s\nAdresa stránky:\t\t%s\nPřihlašovací jméno:\t%s (%s)";
$a->strings["Please visit %s to approve or reject the request."] = "Prosím navštivte %s k odsouhlasení nebo k zamítnutí požadavku.";
$a->strings["newer"] = "novější";
@ -227,24 +227,24 @@ $a->strings["%d Contact"] = [
$a->strings["View Contacts"] = "Zobrazit kontakty";
$a->strings["Save"] = "Uložit";
$a->strings["Follow"] = "Sledovat";
$a->strings["Search"] = "Vyhledávání";
$a->strings["Search"] = "Hledat";
$a->strings["@name, !forum, #tags, content"] = "@jméno, !fórum, #štítky, obsah";
$a->strings["Full Text"] = "Celý text";
$a->strings["Tags"] = "Štítky:";
$a->strings["Contacts"] = "Kontakty";
$a->strings["Forums"] = "Fóra";
$a->strings["poke"] = "šťouchnout";
$a->strings["poked"] = "šťouchnul";
$a->strings["poked"] = "šťouchnul/a";
$a->strings["ping"] = "cinknout";
$a->strings["pinged"] = "cinknul";
$a->strings["pinged"] = "cinknul/a";
$a->strings["prod"] = "dloubnout";
$a->strings["prodded"] = "dloubnul";
$a->strings["prodded"] = "dloubnul/a";
$a->strings["slap"] = "uhodit";
$a->strings["slapped"] = "uhodil";
$a->strings["finger"] = "osahávat";
$a->strings["fingered"] = "osahal";
$a->strings["slapped"] = "uhodil/a";
$a->strings["finger"] = "osahat";
$a->strings["fingered"] = "osahal/a";
$a->strings["rebuff"] = "odmítnout";
$a->strings["rebuffed"] = "odmítnul";
$a->strings["rebuffed"] = "odmítnul/a";
$a->strings["Monday"] = "Pondělí";
$a->strings["Tuesday"] = "Úterý";
$a->strings["Wednesday"] = "Středa";
@ -328,7 +328,7 @@ $a->strings["Friendica Walk-Through"] = "Prohlídka Friendica ";
$a->strings["On your <em>Quick Start</em> page - find a brief introduction to your profile and network tabs, make some new connections, and find some groups to join."] = "Na Vaší stránce <em>Rychlý Start</em> najděte stručné představení k Vašemu profilu a síťovým záložkám, spojte ses novými kontakty a jděte skupiny, ke kterým se můžete připojit.";
$a->strings["Settings"] = "Nastavení";
$a->strings["Go to Your Settings"] = "Navštivte své nastavení";
$a->strings["On your <em>Settings</em> page - change your initial password. Also make a note of your Identity Address. This looks just like an email address - and will be useful in making friends on the free social web."] = "Na Vaší stránce <em>Nastavení</em> si změňte Vaše první heslo.\nVěnujte také svou pozornost k adrese Identity. Vypadá jako emailová adresa a bude Vám užitečná pro navazování přátelství na svobodném sociálním webu.";
$a->strings["On your <em>Settings</em> page - change your initial password. Also make a note of your Identity Address. This looks just like an email address - and will be useful in making friends on the free social web."] = "Na Vaší stránce <em>Nastavení</em> si změňte Vaše první heslo. Věnujte také svou pozornost k adrese Identity. Vypadá jako emailová adresa a bude Vám užitečná pro navazování přátelství na svobodném sociálním webu.";
$a->strings["Review the other settings, particularly the privacy settings. An unpublished directory listing is like having an unlisted phone number. In general, you should probably publish your listing - unless all of your friends and potential friends know exactly how to find you."] = "Prohlédněte si další nastavení, a to zejména nastavení soukromí. Nezveřejnění svého účtu v adresáři je jako mít nezveřejněné telefonní číslo. Obecně platí, že je lepší mít svůj účet zveřejněný, leda by všichni vaši přátelé a potenciální přátelé věděli, jak vás přesně najít.";
$a->strings["Profile"] = "Profil";
$a->strings["Upload Profile Photo"] = "Nahrát profilovou fotografii";
@ -484,7 +484,7 @@ $a->strings["Diaspora support isn't enabled. Contact can't be added."] = "Podpor
$a->strings["OStatus support is disabled. Contact can't be added."] = "Podpora pro OStatus je vypnnuta. Kontakt nemůže být přidán.";
$a->strings["The network type couldn't be detected. Contact can't be added."] = "Typ sítě nemohl být detekován. Kontakt nemůže být přidán.";
$a->strings["Please answer the following:"] = "Odpovězte, prosím, následující:";
$a->strings["Does %s know you?"] = "Zná Vás uživatel %s ?";
$a->strings["Does %s know you?"] = "Zná Vás %s ?";
$a->strings["Add a personal note:"] = "Přidat osobní poznámku:";
$a->strings["Your Identity Address:"] = "Verze PHP pro příkazový řádek na Vašem systému nemá povolen \"register_argc_argv\".";
$a->strings["Profile URL"] = "URL profilu";
@ -594,14 +594,14 @@ $a->strings["Friendica Communications Server - Setup"] = "Friendica Komunikačn
$a->strings["Could not connect to database."] = "Nelze se připojit k databázi.";
$a->strings["Could not create table."] = "Nelze vytvořit tabulku.";
$a->strings["Your Friendica site database has been installed."] = "Vaše databáze Friendica byla nainstalována.";
$a->strings["You may need to import the file \"database.sql\" manually using phpmyadmin or mysql."] = "Pravděpodobně budete muset manuálně importovat soubor \"database.sql\" pomocí phpMyAdmin či MySQL.";
$a->strings["You may need to import the file \"database.sql\" manually using phpmyadmin or mysql."] = "Nejspíše budete muset manuálně importovat soubor \"database.sql\" pomocí phpMyAdmin či MySQL.";
$a->strings["Please see the file \"INSTALL.txt\"."] = "Přečtěte si prosím informace v souboru \"INSTALL.txt\".";
$a->strings["Database already in use."] = "Databáze se již používá.";
$a->strings["System check"] = "Testování systému";
$a->strings["Check again"] = "Otestovat znovu";
$a->strings["Database connection"] = "Databázové spojení";
$a->strings["In order to install Friendica we need to know how to connect to your database."] = "Pro instalaci Friendica potřeujeme znát připojení k Vaší databázi.";
$a->strings["Please contact your hosting provider or site administrator if you have questions about these settings."] = "Pokud máte otázky k následujícím nastavením, obraťte se na svého poskytovatele hostingu nebo administrátora serveru, ";
$a->strings["Please contact your hosting provider or site administrator if you have questions about these settings."] = "Pokud máte otázky k následujícím nastavením, obraťte se na svého poskytovatele hostingu nebo administrátora serveru.";
$a->strings["The database you specify below should already exist. If it does not, please create it before continuing."] = "Databáze, kterou uvedete níže, by již měla existovat. Pokud to tak není, prosíme, vytvořte ji před pokračováním.";
$a->strings["Database Server Name"] = "Jméno databázového serveru";
$a->strings["Database Login Name"] = "Přihlašovací jméno k databázi";
@ -804,16 +804,16 @@ $a->strings["No name"] = "Bez názvu";
$a->strings["Remove authorization"] = "Odstranit oprávnění";
$a->strings["No Addon settings configured"] = "Žádná nastavení doplňků nenakonfigurována";
$a->strings["Addon Settings"] = "Nastavení doplňků";
$a->strings["Off"] = "Vypnuto";
$a->strings["On"] = "Zapnuto";
$a->strings["Off"] = "Vyp";
$a->strings["On"] = "Zap";
$a->strings["Additional Features"] = "Další Funkčnosti";
$a->strings["Diaspora"] = "Diaspora";
$a->strings["enabled"] = "povoleno";
$a->strings["disabled"] = "zakázáno";
$a->strings["Built-in support for %s connectivity is %s"] = "Vestavěná podpora pro připojení s %s je %s";
$a->strings["GNU Social (OStatus)"] = "GNU Social (OStatus)";
$a->strings["Email access is disabled on this site."] = "Přístup k elektronické poště je na tomto serveru zakázán.";
$a->strings["General Social Media Settings"] = "General Social Media nastavení";
$a->strings["Email access is disabled on this site."] = "Přístup k e-mailu je na tomto serveru zakázán.";
$a->strings["General Social Media Settings"] = "Obecná nastavení sociálních sítí";
$a->strings["Disable Content Warning"] = "Vypnout varování o obsahu";
$a->strings["Users on networks like Mastodon or Pleroma are able to set a content warning field which collapse their post by default. This disables the automatic collapsing and sets the content warning as the post title. Doesn't affect any other content filtering you eventually set up."] = "Uživatelé na sítích, jako je Mastodon nebo Pleroma, si mohou nastavit pole s varováním o obsahu, která ve výchozim nastavení skryje jejich příspěvek. Tato možnost vypíná automatické skrývání a nastavuje varování o obsahu jako titulek příspěvku. Toto se netýká žádného dalšího filtrování obsahu, které se rozhodnete nastavit.";
$a->strings["Disable intelligent shortening"] = "Vypnout inteligentní zkracování";
@ -881,11 +881,11 @@ $a->strings["Community Forum"] = "Komunitní fórum";
$a->strings["Account for community discussions."] = "Účet pro komunitní diskuze.";
$a->strings["Normal Account Page"] = "Normální stránka účtu";
$a->strings["Account for a regular personal profile that requires manual approval of \"Friends\" and \"Followers\"."] = "Účet pro běžný osobní profil, který vyžaduje manuální potvrzení \"Přátel\" a \"Sledovatelů\".";
$a->strings["Soapbox Page"] = "Stránka \"Soapbox\"";
$a->strings["Soapbox Page"] = "Propagační stránka";
$a->strings["Account for a public profile that automatically approves contact requests as \"Followers\"."] = "Účet pro veřejný profil, který vyžaduje manuální potvrzení \"Přátel\" a \"Sledovatelů\".";
$a->strings["Public Forum"] = "Veřejné fórum";
$a->strings["Automatically approves all contact requests."] = "Automaticky potvrzuje všechny žádosti o přidání kontaktu.";
$a->strings["Automatic Friend Page"] = "Automatická stránka přítele";
$a->strings["Automatic Friend Page"] = "Stránka s automatickými přátely";
$a->strings["Account for a popular profile that automatically approves contact requests as \"Friends\"."] = "Účet pro populární profil, který automaticky potvrzuje žádosti o přidání kontaktu jako \"Přátele\".";
$a->strings["Private Forum [Experimental]"] = "Soukromé fórum [Experimentální]";
$a->strings["Requires manual approval of contact requests."] = "Vyžaduje manuální potvrzení žádostí o přidání kontaktu.";
@ -904,7 +904,7 @@ $a->strings["Your contacts may write posts on your profile wall. These posts wil
$a->strings["Allow friends to tag your posts?"] = "Povolit přátelům označovat Vaše příspěvky?";
$a->strings["Your contacts can add additional tags to your posts."] = "Vaše kontakty mohou přidávat k Vašim příspěvkům dodatečné štítky.";
$a->strings["Allow us to suggest you as a potential friend to new members?"] = "Povolit, abychom vás navrhovali jako přátelé pro nové členy?";
$a->strings["If you like, Friendica may suggest new members to add you as a contact."] = "Pokud budete chtít, Friendica může nabízet novým členům, abi si Vás přidali jako kontakt.";
$a->strings["If you like, Friendica may suggest new members to add you as a contact."] = "Pokud budete chtít, může Friendica nabízet novým členům, aby si Vás přidali jako kontakt.";
$a->strings["Permit unknown people to send you private mail?"] = "Povolit neznámým lidem Vám zasílat soukromé zprávy?";
$a->strings["Friendica network users may send you private messages even if they are not in your contact list."] = "Uživatelé sítě Friendica Vám mohou posílat soukromé zprávy, i pokud nejsou ve Vašich kontaktech.";
$a->strings["Profile is <strong>not published</strong>."] = "Profil <strong>není zveřejněn</strong>.";
@ -936,7 +936,7 @@ $a->strings["Default Post Location:"] = "Výchozí umístění příspěvků:";
$a->strings["Use Browser Location:"] = "Používat umístění dle prohlížeče:";
$a->strings["Security and Privacy Settings"] = "Nastavení zabezpečení a soukromí";
$a->strings["Maximum Friend Requests/Day:"] = "Maximální počet žádostí o přátelství za den:";
$a->strings["(to prevent spam abuse)"] = "(Aby se zabránilo spamu)";
$a->strings["(to prevent spam abuse)"] = "(ay se zabránilo spamu)";
$a->strings["Default Post Permissions"] = "Výchozí oprávnění pro příspěvek";
$a->strings["(click to open/close)"] = "(Klikněte pro otevření/zavření)";
$a->strings["Show to Groups"] = "Zobrazit ve Skupinách";
@ -954,7 +954,7 @@ $a->strings["Someone writes a followup comment"] = "někdo Vám napíše násled
$a->strings["You receive a private message"] = "obdržíte soukromou zprávu";
$a->strings["You receive a friend suggestion"] = "Obdržel jste návrh přátelství";
$a->strings["You are tagged in a post"] = "Jste označen v příspěvku";
$a->strings["You are poked/prodded/etc. in a post"] = "Byl jste šťouchnut(a)/dloubnut(a)/apod. v příspěvku";
$a->strings["You are poked/prodded/etc. in a post"] = "Byl/a jste šťouchnut(a)/dloubnut(a)/apod. v příspěvku";
$a->strings["Activate desktop notifications"] = "Aktivovat upozornění na desktopu";
$a->strings["Show desktop popup on new notifications"] = "Zobrazit dektopové zprávy nových upozornění.";
$a->strings["Text-only notification emails"] = "Pouze textové notifikační e-maily";
@ -971,7 +971,7 @@ $a->strings["Delete Video"] = "Odstranit video";
$a->strings["No videos selected"] = "Není vybráno žádné video";
$a->strings["Access to this item is restricted."] = "Přístup k této položce je omezen.";
$a->strings["View Album"] = "Zobrazit album";
$a->strings["Recent Videos"] = "Aktuální Videa";
$a->strings["Recent Videos"] = "Aktuální videa";
$a->strings["Upload New Videos"] = "Nahrát nová videa";
$a->strings["Theme settings updated."] = "Nastavení motivu bylo aktualizováno.";
$a->strings["Information"] = "Informace";
@ -995,7 +995,7 @@ $a->strings["Diagnostics"] = "Diagnostica";
$a->strings["PHP Info"] = "Info o PHP";
$a->strings["probe address"] = "vyzkoušet adresu";
$a->strings["check webfinger"] = "vyzkoušet webfinger";
$a->strings["Admin"] = "Administrace";
$a->strings["Admin"] = "Administrátor";
$a->strings["Addon Features"] = "Vlastnosti doplňků";
$a->strings["User registrations waiting for confirmation"] = "Registrace uživatele čeká na potvrzení";
$a->strings["Administration"] = "Administrace";
@ -1050,7 +1050,7 @@ $a->strings["%s total blocked contact"] = [
];
$a->strings["URL of the remote contact to block."] = "Adresa URL vzdáleného kontaktu k zablokování.";
$a->strings["Delete this Item"] = "Smazat tuto položku";
$a->strings["On this page you can delete an item from your node. If the item is a top level posting, the entire thread will be deleted."] = "Na této stránce můžete smazat pološku z Vašeho serveru. Pokud je položkou příspěvek nejvyššího stupně, bude smazáno celé vlákno.";
$a->strings["On this page you can delete an item from your node. If the item is a top level posting, the entire thread will be deleted."] = "Na této stránce můžete smazat položku z Vašeho serveru. Pokud je položkou příspěvek nejvyššího stupně, bude smazáno celé vlákno.";
$a->strings["You need to know the GUID of the item. You can find it e.g. by looking at the display URL. The last part of http://example.com/display/123456 is the GUID, here 123456."] = "Budete muset znát číslo GUID položky. Můžete jej najít např. v adrese URL. Poslední část adresy http://example.com/display/123456 je GUID, v tomto případě 123456";
$a->strings["GUID"] = "GUID";
$a->strings["The GUID of the item you want to delete."] = "Číslo GUID položky, kterou chcete smazat";
@ -1290,7 +1290,7 @@ $a->strings["Failed Updates"] = "Neúspěšné aktualizace";
$a->strings["This does not include updates prior to 1139, which did not return a status."] = "To nezahrnuje aktualizace do verze 1139, které nevracejí žádný status.";
$a->strings["Mark success (if update was manually applied)"] = "Označit za úspěšné (pokud byla aktualizace aplikována manuálně)";
$a->strings["Attempt to execute this update step automatically"] = "Pokusit se provést tuto aktualizaci automaticky.";
$a->strings["\n\t\t\tDear %1\$s,\n\t\t\t\tthe administrator of %2\$s has set up an account for you."] = "\n\t\t\tVážený/á%1\$s,\n\t\t\t\tadministrátor %2\$s pro Vás vytvořil/a uživatelský účet.";
$a->strings["\n\t\t\tDear %1\$s,\n\t\t\t\tthe administrator of %2\$s has set up an account for you."] = "\n\t\t\tVážený/á%1\$s,\n\t\t\t\tadministrátor %2\$s pro Vás vytvořil uživatelský účet.";
$a->strings["\n\t\t\tThe login details are as follows:\n\n\t\t\tSite Location:\t%1\$s\n\t\t\tLogin Name:\t\t%2\$s\n\t\t\tPassword:\t\t%3\$s\n\n\t\t\tYou may change your password from your account \"Settings\" page after logging\n\t\t\tin.\n\n\t\t\tPlease take a few moments to review the other account settings on that page.\n\n\t\t\tYou may also wish to add some basic information to your default profile\n\t\t\t(on the \"Profiles\" page) so that other people can easily find you.\n\n\t\t\tWe recommend setting your full name, adding a profile photo,\n\t\t\tadding some profile \"keywords\" (very useful in making new friends) - and\n\t\t\tperhaps what country you live in; if you do not wish to be more specific\n\t\t\tthan that.\n\n\t\t\tWe fully respect your right to privacy, and none of these items are necessary.\n\t\t\tIf you are new and do not know anybody here, they may help\n\t\t\tyou to make some new and interesting friends.\n\n\t\t\tIf you ever want to delete your account, you can do so at %1\$s/removeme\n\n\t\t\tThank you and welcome to %4\$s."] = "\n\t\t\tZde jsou vaše přihlašovací detaily:\n\n\t\t\tAdresa stránky:\t\t%1\$s\n\t\t\tPřihlašovací jméno:\t%2\$s\n\t\t\tHeslo:\t\t\t%3\$s\n\n\t\t\tSvé heslo si po přihlášení můžete změnit na stránce \"Nastavení\" vašeho\n\t\t\túčtu.\n\n\t\t\tProsím, prohlédněte si na chvilku ostatní nastavení účtu na té stránce.\n\n\t\t\tMožná byste si také přáli přidat pár základních informací na svůj výchozí\n\t\t\tprofil (na stránce \"Profily\") aby vás další lidé mohli snadno najít.\n\t\t\tDoporučujeme nastavit si vaše celé jméno, přidat profilovou fotku,\n\t\t\tpřidat pár \"klíčových slov\" k profilu (velmi užitečné při získávání nových\n\t\t\tpřátel) - a možná v jaké zemi žijete; pokud nechcete být konkrétnější.\n\n\t\t\tZcela respektujeme vaše právo na soukromí a žádnou z těchto položek\n\t\t\tnení potřeba vyplňovat. Pokud jste zde nový/á a nikoho zde neznáte, mohou vám\n\t\t\tpomoci si získat nové a zajímavé přátele.\n\t\t\tPokud byste si někdy přál/a smazat účet, může tak učinit na stránce\n\t\t\t%1\$s/removeme.\n\n\t\t\tDěkujeme vám a vítáme vás na %4\$s.";
$a->strings["Registration details for %s"] = "Registrační údaje pro %s";
$a->strings["%s user blocked/unblocked"] = [
@ -1322,7 +1322,7 @@ $a->strings["No registrations."] = "Žádné registrace.";
$a->strings["Note from the user"] = "Poznámka od uživatele";
$a->strings["Approve"] = "Schválit";
$a->strings["Deny"] = "Odmítnout";
$a->strings["Site admin"] = "Site administrátor";
$a->strings["Site admin"] = "Administrátor webu";
$a->strings["Account expired"] = "Účtu vypršela platnost";
$a->strings["New User"] = "Nový uživatel";
$a->strings["Deleted since"] = "Smazán od";
@ -1447,7 +1447,7 @@ $a->strings["Results for: %s"] = "Výsledky pro: %s";
$a->strings["Find"] = "Najít";
$a->strings["Archive"] = "Archivovat";
$a->strings["Unarchive"] = "Vrátit z archívu";
$a->strings["Batch Actions"] = "Dávkové (batch) akce";
$a->strings["Batch Actions"] = "Souhrnné akce";
$a->strings["Profile Details"] = "Detaily profilu";
$a->strings["View all contacts"] = "Zobrazit všechny kontakty";
$a->strings["View all common friends"] = "Zobrazit všechny společné přátele";

View file

@ -78,6 +78,7 @@
<h3>{{$corporate}}</h3>
{{include file="field_input.tpl" field=$allowed_sites}}
{{include file="field_input.tpl" field=$allowed_email}}
{{include file="field_input.tpl" field=$forbidden_nicknames}}
{{include file="field_checkbox.tpl" field=$no_oembed_rich_content}}
{{include file="field_input.tpl" field=$allowed_oembed}}
{{include file="field_checkbox.tpl" field=$block_public}}