define('FRIENDICA_CODENAME', 'The Tazmans Flax-lily');
define('FRIENDICA_VERSION', '2018.08-dev');
define('DFRN_PROTOCOL_VERSION', '2.23');
-define('DB_UPDATE_VERSION', 1274);
+define('DB_UPDATE_VERSION', 1275);
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
/**
$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);
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);
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, [
'$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")],
*/
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
/**
* @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);
-
- 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();
- }
+ $driver_name = Config::get('system', 'cache_driver', 'database');
+
+ self::$driver = CacheDriverFactory::create($driver_name);
}
/**
--- /dev/null
+<?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) {
+ return self::getApp()->get_hostname() . ":" . $key;
+ }
+}
--- /dev/null
+<?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()
+ {
+ $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;
+ }
+ }
+}
--- /dev/null
+<?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();
+ }
+ }
+}
*
* @author Hypolite Petovan <mrpetovan@gmail.com>
*/
-class DatabaseCacheDriver implements ICacheDriver
+class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver
{
public function get($key)
{
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()
];
*
* @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
*/
--- /dev/null
+<?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);
+}
namespace Friendica\Core\Cache;
-use Friendica\BaseObject;
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;
}
}
+ /**
+ * (@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)) {
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
- return $this->memcache->set(
- self::getApp()->get_hostname() . ":" . $key,
- serialize($value),
- MEMCACHE_COMPRESSED,
- time() + $duration
- );
+ if ($ttl > 0) {
+ return $this->memcache->set(
+ $cachekey,
+ serialize($value),
+ MEMCACHE_COMPRESSED,
+ 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);
}
+ /**
+ * (@inheritdoc)
+ */
public function clear()
{
- return true;
+ return $this->memcache->flush();
+ }
+
+ /**
+ * (@inheritdoc)
+ */
+ public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
+ {
+ $cachekey = $this->getCacheKey($key);
+ return $this->memcache->add($cachekey, $value, $ttl);
}
}
namespace Friendica\Core\Cache;
-use Friendica\BaseObject;
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;
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;
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
- return $this->memcached->set(
- self::getApp()->get_hostname() . ':' . $key,
- $value,
- time() + $duration
- );
+ if ($ttl > 0) {
+ return $this->memcached->set(
+ $cachekey,
+ $value,
+ time() + $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()
{
return true;
}
+
+ /**
+ * @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);
+ }
}
namespace Friendica\Core\Cache;
-use Friendica\BaseObject;
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;
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);
+ $cached = $this->redis->get($cachekey);
// @see http://php.net/manual/en/redis.get.php#84275
if (is_bool($cached) || is_double($cached) || is_long($cached)) {
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
- return $this->redis->set(
- self::getApp()->get_hostname() . ":" . $key,
- serialize($value),
- time() + $duration
- );
+ if ($ttl > 0) {
+ return $this->redis->setex(
+ $cachekey,
+ time() + $ttl,
+ serialize($value)
+ );
+ } else {
+ return $this->redis->set(
+ $cachekey,
+ serialize($value)
+ );
+ }
}
public function delete($key)
{
return true;
}
+
+
+ /**
+ * (@inheritdoc)
+ */
+ public function add($key, $value, $ttl = Cache::FIVE_MINUTES)
+ {
+ $cachekey = $this->getCacheKey($key);
+
+ if (!is_int($value)) {
+ $value = serialize($value);
+ }
+
+ return $this->redis->setnx($cachekey, $value);
+ }
+
+ /**
+ * (@inheritdoc)
+ */
+ public function compareSet($key, $oldValue, $newValue, $ttl = Cache::FIVE_MINUTES)
+ {
+ $cachekey = $this->getCacheKey($key);
+
+ if (!is_int($newValue)) {
+ $newValue = serialize($newValue);
+ }
+
+ $this->redis->watch($cachekey);
+ // If the old value isn't what we expected, somebody else changed the key meanwhile
+ if ($this->get($cachekey) === $oldValue) {
+ if ($ttl > 0) {
+ $result = $this->redis->multi()
+ ->setex($cachekey, $ttl, $newValue)
+ ->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;
+ }
}
--- /dev/null
+<?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;
+ }
+ }
+}
--- /dev/null
+<?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;
+ }
+ }
+}
--- /dev/null
+<?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();
+ }
+}
--- /dev/null
+<?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);
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Core\Lock;
+
+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)
+ {
+ $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(), 300)) {
+ $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;
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Core\Lock;
+
+use dba;
+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)
+ {
+ $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;
+ $this->markAcquire($key);
+ }
+ }
+ if (!$lock['locked']) {
+ dba::update('locks', ['locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + 300seconds')], ['name' => $key]);
+ $got_lock = true;
+ $this->markAcquire($key);
+ }
+ } else {
+ dba::insert('locks', ['name' => $key, 'locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + 300seconds')]);
+ $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;
+ }
+ }
+}
--- /dev/null
+<?php
+
+namespace Friendica\Core\Lock;
+
+/**
+ * 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
+ *
+ * @return boolean Was the lock successful?
+ */
+ public function acquireLock($key, $timeout = 120);
+
+ /**
+ * 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();
+}
--- /dev/null
+<?php
+
+namespace Friendica\Core\Lock;
+
+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)
+ {
+ 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]);
+ }
+}
*/
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';
}
// 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()) {
logger('Memory limit reached, quitting.', LOGGER_DEBUG);
return;
}
- Lock::remove('worker');
+ Lock::release('worker');
self::$db_duration += (microtime(true) - $stamp);
}
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);
$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]);
}
// 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;
*/
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';
"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"] = [
if ($event['id']) {
// has the event actually changed?
$existing_event = dba::selectFirst('event', ['edited'], ['id' => $event['id'], 'uid' => $event['uid']]);
- if ((! DBM::is_result($existing_event)) || ($existing_event['edited'] === $event['edited'])) {
+ if (!DBM::is_result($existing_event) || ($existing_event['edited'] === $event['edited'])) {
- $item = dba::selectFirst('item', [], ['event-id' => $event['id'], 'uid' => $event['uid']]);
+ $item = Item::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]);
return DBM::is_result($item) ? $item['id'] : 0;
}
dba::update('event', $updated_fields, ['id' => $event['id'], 'uid' => $event['uid']]);
- $item = dba::selectFirst('item', ['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]);
+ $item = Item::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]);
if (DBM::is_result($item)) {
$object = '<object><type>' . xmlify(ACTIVITY_OBJ_EVENT) . '</type><title></title><id>' . xmlify($event['uri']) . '</id>';
$object .= '<content>' . xmlify(self::getBBCode($event)) . '</content>';
}
// Query for the event by event id
- $r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
- `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
+ $r = q("SELECT `event`.*, `item`.`id` AS `itemid` FROM `event`
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
WHERE `event`.`uid` = %d AND `event`.`id` = %d $sql_extra",
intval($owner_uid),
// Query for the event by date.
// @todo Slow query (518 seconds to run), to be optimzed
- $r = q("SELECT `event`.*, `item`.`id` AS `itemid`,`item`.`plink`,
- `item`.`author-name`, `item`.`author-avatar`, `item`.`author-link` FROM `event`
+ $r = q("SELECT `event`.*, `item`.`id` AS `itemid` FROM `event`
LEFT JOIN `item` ON `item`.`event-id` = `event`.`id` AND `item`.`uid` = `event`.`uid`
WHERE `event`.`uid` = %d AND event.ignore = %d
AND ((`adjust` = 0 AND (`finish` >= '%s' OR (nofinish AND start >= '%s')) AND `start` <= '%s')
$last_date = '';
$fmt = L10n::t('l, F j');
foreach ($event_result as $event) {
+ $item = Item::selectFirst(['plink', 'author-name', 'author-avatar', 'author-link'], ['id' => $event['itemid']]);
+ if (DBM::is_result($item)) {
+ $event = array_merge($event, $item);
+ }
+
$start = $event['adjust'] ? DateTimeFormat::local($event['start'], 'c') : DateTimeFormat::utc($event['start'], 'c');
$j = $event['adjust'] ? DateTimeFormat::local($event['start'], 'j') : DateTimeFormat::utc($event['start'], 'j');
$day = $event['adjust'] ? DateTimeFormat::local($event['start'], $fmt) : DateTimeFormat::utc($event['start'], $fmt);
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';
$param_string = self::addTablesToFields(dba::buildParameter($params), $fields);
- $table = "`item` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, false);
+ $table = "`item` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, false, $usermode);
$sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string;
$param_string = self::addTablesToFields($param_string, $threadfields);
$param_string = self::addTablesToFields($param_string, $fields);
- $table = "`thread` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, true);
+ $table = "`thread` " . self::constructJoins($uid, $select_fields . $condition_string . $param_string, true, $usermode);
$sql = "SELECT " . $select_fields . " FROM " . $table . $condition_string . $param_string;
*
* @return string The SQL joins for the "select" functions
*/
- private static function constructJoins($uid, $sql_commands, $thread_mode)
+ private static function constructJoins($uid, $sql_commands, $thread_mode, $user_mode)
{
if ($thread_mode) {
$master_table = "`thread`";
$joins = '';
}
- $joins .= sprintf("STRAIGHT_JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`
- AND NOT `contact`.`blocked`
- AND ((NOT `contact`.`readonly` AND NOT `contact`.`pending` AND (`contact`.`rel` IN (%s, %s)))
- OR `contact`.`self` OR (`item`.`id` != `item`.`parent`) OR `contact`.`uid` = 0)
- STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id` AND NOT `author`.`blocked`
- STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id` AND NOT `owner`.`blocked`
- LEFT JOIN `user-item` ON `user-item`.`iid` = $master_table_key AND `user-item`.`uid` = %d",
- CONTACT_IS_SHARING, CONTACT_IS_FRIEND, intval($uid));
+ if ($user_mode) {
+ $joins .= sprintf("STRAIGHT_JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`
+ AND NOT `contact`.`blocked`
+ AND ((NOT `contact`.`readonly` AND NOT `contact`.`pending` AND (`contact`.`rel` IN (%s, %s)))
+ OR `contact`.`self` OR (`item`.`id` != `item`.`parent`) OR `contact`.`uid` = 0)
+ STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id` AND NOT `author`.`blocked`
+ STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id` AND NOT `owner`.`blocked`
+ LEFT JOIN `user-item` ON `user-item`.`iid` = $master_table_key AND `user-item`.`uid` = %d",
+ CONTACT_IS_SHARING, CONTACT_IS_FRIEND, intval($uid));
+ } else {
+ if (strpos($sql_commands, "`contact`.") !== false) {
+ $joins .= "STRAIGHT_JOIN `contact` ON `contact`.`id` = $master_table.`contact-id`";
+ }
+ if (strpos($sql_commands, "`author`.") !== false) {
+ $joins .= " STRAIGHT_JOIN `contact` AS `author` ON `author`.`id` = $master_table.`author-id`";
+ }
+ if (strpos($sql_commands, "`owner`.") !== false) {
+ $joins .= " STRAIGHT_JOIN `contact` AS `owner` ON `owner`.`id` = $master_table.`owner-id`";
+ }
+ }
if (strpos($sql_commands, "`group_member`.") !== false) {
$joins .= " STRAIGHT_JOIN `group_member` ON `group_member`.`contact-id` = $master_table.`contact-id`";
}
// 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.");
}
logger('Could not insert content for URI ' . $item['uri'] . ' - trying asynchronously');
}
if ($locked) {
- Lock::remove('item_insert_content');
+ Lock::release('item_insert_content');
}
}
}
$base_condition = ['verb' => $verbs, 'deleted' => false, 'gravity' => GRAVITY_ACTIVITY,
- 'author-id' => $author_contact['id'], 'uid' => item['uid']];
+ 'author-id' => $author_contact['id'], 'uid' => $item['uid']];
$condition = array_merge($base_condition, ['parent' => $item_id]);
$like_item = self::selectFirst(['id', 'guid', 'verb'], $condition);
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
*
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.'));
$owner = ['uid' => 0, 'id' => $this->getDataValue('owner-id'),
'network' => $this->getDataValue('owner-network'),
- 'url' => $this->getDataValue('ownerr-link')];
+ 'url' => $this->getDataValue('owner-link')];
$this->owner_url = Contact::magicLinkbyContact($owner);
}
}
*/
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;
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';
$header["type"] = "remote";
$header["wall"] = 0;
$header["origin"] = 0;
- $header["gravity"] = GRAVITY_PARENT;
+ $header["gravity"] = GRAVITY_COMMENT;
$first_child = $doc->firstChild->tagName;
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);
}
$item["type"] = 'remote-comment';
- $item["gravity"] = GRAVITY_COMMENT;
} else {
$item["parent-uri"] = $item["uri"];
+ $item["gravity"] = GRAVITY_PARENT;
}
if (($item['author-link'] != '') && !empty($item['protocol'])) {
+++ /dev/null
-<?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;
- }
-}
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Core\Lock;
+
+
+use Friendica\Core\Cache\ArrayCache;
+use Friendica\Core\Lock\CacheLockDriver;
+
+class CacheLockDriverTest 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();
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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
+{
+ use TestCaseTrait;
+
+ /**
+ * Get database connection.
+ *
+ * This function is executed before each test in order to get a database connection that can be used by tests.
+ * If no prior connection is available, it tries to create one using the USER, PASS and DB environment variables.
+ *
+ * If it could not connect to the database, the test is skipped.
+ *
+ * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
+ * @see https://phpunit.de/manual/5.7/en/database.html
+ */
+ protected function getConnection()
+ {
+ if (!dba::$connected) {
+ dba::connect('localhost', getenv('USER'), getenv('PASS'), getenv('DB'));
+
+ if (dba::$connected) {
+ $app = get_app();
+ // We need to do this in order to disable logging
+ $app->module = 'install';
+
+ // Create database structure
+ DBStructure::update(false, true, true);
+ } else {
+ $this->markTestSkipped('Could not connect to the database.');
+ }
+ }
+
+ return $this->createDefaultDBConnection(dba::get_db(), getenv('DB'));
+ }
+
+ /**
+ * Get dataset to populate the database with.
+ * @return YamlDataSet
+ * @see https://phpunit.de/manual/5.7/en/database.html
+ */
+ protected function getDataSet()
+ {
+ return new YamlDataSet(__DIR__ . '/../../../datasets/api.yml');
+ }
+
+ protected function getInstance()
+ {
+ return new DatabaseLockDriver();
+ }
+
+ public function tearDown()
+ {
+ dba::delete('locks', [ 'id > 0']);
+ parent::tearDown();
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace Friendica\Test\src\Core\Lock;
+
+use Friendica\App;
+use Friendica\Core\Config;
+use PHPUnit\Framework\TestCase;
+
+abstract class LockTest extends TestCase
+{
+ /**
+ * @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->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->instance->releaseAll();
+
+ $this->assertFalse($this->instance->isLocked('bar'));
+ $this->assertFalse($this->instance->isLocked('#/$%§'));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?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();
+ }
+}
\ No newline at end of file
"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 02:27+0000\n"
"Last-Translator: Aditoo\n"
"Language-Team: Czech (http://www.transifex.com/Friendica/friendica/language/cs/)\n"
"MIME-Version: 1.0\n"
#: 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
#: 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
#: 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
#: 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
#: 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
#: 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
#: 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:"
#: 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 ""
#: 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
#: include/text.php:1043
msgid "poked"
-msgstr "šťouchnul"
+msgstr "šťouchnul/a"
#: include/text.php:1044
msgid "ping"
#: include/text.php:1044
msgid "pinged"
-msgstr "cinknul"
+msgstr "cinknul/a"
#: include/text.php:1045
msgid "prod"
#: include/text.php:1045
msgid "prodded"
-msgstr "dloubnul"
+msgstr "dloubnul/a"
#: include/text.php:1046
msgid "slap"
#: 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"
#: 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"
"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 ""
#: 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:"
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\"."
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 ""
#: 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"
#: mod/admin.php:223 src/Content/Nav.php:218
msgid "Admin"
-msgstr "Administrace"
+msgstr "Administrátor"
#: mod/admin.php:224
msgid "Addon Features"
"\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
#: mod/admin.php:1835
msgid "Site admin"
-msgstr "Site administrátor"
+msgstr "Administrátor webu"
#: mod/admin.php:1836
msgid "Account expired"
$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 tagged %2\$s's %3\$s with %4\$s"] = "%1\$s označil %3\$s %2\$s s %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["Likes"] = "Libí se";
+$a->strings["Dislikes"] = "Nelibí se";
$a->strings["Attending"] = [
0 => "Účastní se",
1 => "Účastní se",
$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í";
];
$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";
$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í";
$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.";
$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ší";
$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";
$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";
$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";
$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";
$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";
$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";
$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"] = [
$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";
<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}}