]> git.mxchange.org Git - friendica.git/commitdiff
Merge pull request #6611 from annando/worker-performance
authorHypolite Petovan <hypolite@mrpetovan.com>
Fri, 8 Feb 2019 22:46:04 +0000 (17:46 -0500)
committerGitHub <noreply@github.com>
Fri, 8 Feb 2019 22:46:04 +0000 (17:46 -0500)
Improved worker performance (changed keys, changed queries)

78 files changed:
bin/auth_ejabberd.php
bin/console.php
bin/daemon.php
bin/worker.php
boot.php
index.php
mod/admin.php
mod/friendica.php
mod/item.php
mod/parse_url.php
src/App.php
src/BaseObject.php
src/Core/Addon.php
src/Core/Config.php
src/Core/Config/AbstractDbaConfigAdapter.php [new file with mode: 0644]
src/Core/Config/ConfigCache.php [new file with mode: 0644]
src/Core/Config/ConfigCacheLoader.php [new file with mode: 0644]
src/Core/Config/IConfigAdapter.php
src/Core/Config/IConfigCache.php [new file with mode: 0644]
src/Core/Config/IPConfigAdapter.php
src/Core/Config/IPConfigCache.php [new file with mode: 0644]
src/Core/Config/JITConfigAdapter.php
src/Core/Config/JITPConfigAdapter.php
src/Core/Config/PreloadConfigAdapter.php
src/Core/Config/PreloadPConfigAdapter.php
src/Core/Console/AutomaticInstallation.php
src/Core/Console/Config.php
src/Core/Console/DatabaseStructure.php
src/Core/Console/PostUpdate.php
src/Core/Console/Typo.php
src/Core/Installer.php
src/Core/Logger.php
src/Core/PConfig.php
src/Core/System.php
src/Core/Update.php
src/Database/DBA.php
src/Database/DBStructure.php
src/Factory/ConfigFactory.php [new file with mode: 0644]
src/Factory/LoggerFactory.php [new file with mode: 0644]
src/Model/Attach.php
src/Model/Item.php
src/Module/Install.php
src/Util/BasePath.php [new file with mode: 0644]
src/Util/LoggerFactory.php [deleted file]
src/Worker/DBUpdate.php
tests/DatabaseTest.php
tests/Util/AppMockTrait.php
tests/Util/ConfigMockTrait.php [deleted file]
tests/Util/RendererMockTrait.php
tests/include/ApiTest.php
tests/src/App/ModeTest.php
tests/src/BaseObjectTest.php
tests/src/Core/Cache/ArrayCacheDriverTest.php
tests/src/Core/Cache/CacheTest.php
tests/src/Core/Cache/DatabaseCacheDriverTest.php
tests/src/Core/Cache/MemcacheCacheDriverTest.php
tests/src/Core/Cache/MemcachedCacheDriverTest.php
tests/src/Core/Cache/RedisCacheDriverTest.php
tests/src/Core/Console/AutomaticInstallationConsoleTest.php
tests/src/Core/Console/ConfigConsoleTest.php
tests/src/Core/Console/ConsoleTest.php
tests/src/Core/Lock/ArrayCacheLockDriverTest.php
tests/src/Core/Lock/LockTest.php
tests/src/Core/Lock/MemcacheCacheLockDriverTest.php
tests/src/Core/Lock/MemcachedCacheLockDriverTest.php
tests/src/Core/Lock/RedisCacheLockDriverTest.php
tests/src/Core/Lock/SemaphoreLockDriverTest.php
tests/src/Database/DBATest.php
tests/src/Database/DBStructureTest.php
view/js/linkPreview.js [new file with mode: 0644]
view/theme/frio/css/style.css
view/theme/frio/js/jot.js [new file with mode: 0644]
view/theme/frio/js/modal.js
view/theme/frio/js/textedit.js
view/theme/frio/templates/acl_selector.tpl
view/theme/frio/templates/jot-header.tpl
view/theme/frio/templates/search_item.tpl
view/theme/frio/templates/wall_thread.tpl

index a6f30d20193275192d808dc5a9f9190d7d5f5e9e..11df438952a06e96ee700a7d6345cba1c90007d8 100755 (executable)
  */
 
 use Friendica\App;
+use Friendica\Core\Config;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 use Friendica\Util\ExAuth;
-use Friendica\Util\LoggerFactory;
 
 if (sizeof($_SERVER["argv"]) == 0) {
        die();
@@ -52,9 +54,12 @@ chdir($directory);
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$logger = LoggerFactory::create('auth_ejabberd');
+$basedir = BasePath::create(dirname(__DIR__), $_SERVER);
+$configLoader = new Config\ConfigCacheLoader($basedir);
+$config = Factory\ConfigFactory::createCache($configLoader);
+$logger = Factory\LoggerFactory::create('auth_ejabberd', $config);
 
-$a = new App(dirname(__DIR__), $logger);
+$a = new App($config, $logger);
 
 if ($a->getMode()->isNormal()) {
        $oAuth = new ExAuth();
index 9264e3eee46080d294ff79fd28041340ed239942..9061824d875b0be10f8f1fad8aa94c1f883c55a0 100755 (executable)
@@ -3,11 +3,16 @@
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-use Friendica\Util\LoggerFactory;
+use Friendica\Core\Config;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 
-$logger = LoggerFactory::create('console');
+$basedir = BasePath::create(dirname(__DIR__), $_SERVER);
+$configLoader = new Config\ConfigCacheLoader($basedir);
+$config = Factory\ConfigFactory::createCache($configLoader);
+$logger = Factory\LoggerFactory::create('console', $config);
 
-$a = new Friendica\App(dirname(__DIR__), $logger);
+$a = new Friendica\App($config, $logger);
 \Friendica\BaseObject::setApp($a);
 
 (new Friendica\Core\Console($argv))->execute();
index c7b321c11c3c455ab8d31fc61b48b30ee68a0ebc..5c014a92702429d58af4dfab1ac163597d34b95d 100755 (executable)
@@ -11,7 +11,8 @@ use Friendica\App;
 use Friendica\Core\Config;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
-use Friendica\Util\LoggerFactory;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 
 // Get options
 $shortopts = 'f';
@@ -32,9 +33,12 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) {
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$logger = LoggerFactory::create('daemon');
+$basedir = BasePath::create(dirname(__DIR__), $_SERVER);
+$configLoader = new Config\ConfigCacheLoader($basedir);
+$config = Factory\ConfigFactory::createCache($configLoader);
+$logger = Factory\LoggerFactory::create('daemon', $config);
 
-$a = new App(dirname(__DIR__), $logger);
+$a = new App($config, $logger);
 
 if ($a->getMode()->isInstall()) {
        die("Friendica isn't properly installed yet.\n");
index a64b6a83305660f705bf5530141061ddcf379269..553e9849774fe307e5f1535b9c090809ce043e80 100755 (executable)
@@ -4,11 +4,13 @@
  * @file bin/worker.php
  * @brief Starts the background processing
  */
+
 use Friendica\App;
 use Friendica\Core\Config;
-use Friendica\Core\Worker;
 use Friendica\Core\Update;
-use Friendica\Util\LoggerFactory;
+use Friendica\Core\Worker;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 
 // Get options
 $shortopts = 'sn';
@@ -29,12 +31,15 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) {
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$logger = LoggerFactory::create('worker');
+$basedir = BasePath::create(dirname(__DIR__), $_SERVER);
+$configLoader = new Config\ConfigCacheLoader($basedir);
+$config = Factory\ConfigFactory::createCache($configLoader);
+$logger = Factory\LoggerFactory::create('worker', $config);
 
-$a = new App(dirname(__DIR__), $logger);
+$a = new App($config, $logger);
 
 // Check the database structure and possibly fixes it
-Update::check(true);
+Update::check($a->getBasePath(), true);
 
 // Quit when in maintenance
 if (!$a->getMode()->has(App\Mode::MAINTENANCEDISABLED)) {
index 7dc99bbe88c3621e72babf21a08823088ad08694..318798e324c3fd693313654feb8da3c7496d6e82 100644 (file)
--- a/boot.php
+++ b/boot.php
 
 use Friendica\App;
 use Friendica\BaseObject;
-use Friendica\Core\Addon;
-use Friendica\Core\Cache;
 use Friendica\Core\Config;
-use Friendica\Core\L10n;
 use Friendica\Core\PConfig;
 use Friendica\Core\Protocol;
 use Friendica\Core\System;
-use Friendica\Core\Update;
-use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Model\Contact;
-use Friendica\Model\Conversation;
+use Friendica\Util\BasePath;
 use Friendica\Util\DateTimeFormat;
 
 define('FRIENDICA_PLATFORM',     'Friendica');
@@ -640,18 +635,18 @@ function get_temppath()
 
        $temppath = Config::get("system", "temppath");
 
-       if (($temppath != "") && App::isDirectoryUsable($temppath)) {
+       if (($temppath != "") && System::isDirectoryUsable($temppath)) {
                // We have a temp path and it is usable
-               return App::getRealPath($temppath);
+               return BasePath::getRealPath($temppath);
        }
 
        // We don't have a working preconfigured temp path, so we take the system path.
        $temppath = sys_get_temp_dir();
 
        // Check if it is usable
-       if (($temppath != "") && App::isDirectoryUsable($temppath)) {
+       if (($temppath != "") && System::isDirectoryUsable($temppath)) {
                // Always store the real path, not the path through symlinks
-               $temppath = App::getRealPath($temppath);
+               $temppath = BasePath::getRealPath($temppath);
 
                // To avoid any interferences with other systems we create our own directory
                $new_temppath = $temppath . "/" . $a->getHostName();
@@ -660,7 +655,7 @@ function get_temppath()
                        mkdir($new_temppath);
                }
 
-               if (App::isDirectoryUsable($new_temppath)) {
+               if (System::isDirectoryUsable($new_temppath)) {
                        // The new path is usable, we are happy
                        Config::set("system", "temppath", $new_temppath);
                        return $new_temppath;
@@ -742,8 +737,8 @@ function get_itemcachepath()
        }
 
        $itemcache = Config::get('system', 'itemcache');
-       if (($itemcache != "") && App::isDirectoryUsable($itemcache)) {
-               return App::getRealPath($itemcache);
+       if (($itemcache != "") && System::isDirectoryUsable($itemcache)) {
+               return BasePath::getRealPath($itemcache);
        }
 
        $temppath = get_temppath();
@@ -754,7 +749,7 @@ function get_itemcachepath()
                        mkdir($itemcache);
                }
 
-               if (App::isDirectoryUsable($itemcache)) {
+               if (System::isDirectoryUsable($itemcache)) {
                        Config::set("system", "itemcache", $itemcache);
                        return $itemcache;
                }
@@ -770,7 +765,7 @@ function get_itemcachepath()
 function get_spoolpath()
 {
        $spoolpath = Config::get('system', 'spoolpath');
-       if (($spoolpath != "") && App::isDirectoryUsable($spoolpath)) {
+       if (($spoolpath != "") && System::isDirectoryUsable($spoolpath)) {
                // We have a spool path and it is usable
                return $spoolpath;
        }
@@ -785,7 +780,7 @@ function get_spoolpath()
                        mkdir($spoolpath);
                }
 
-               if (App::isDirectoryUsable($spoolpath)) {
+               if (System::isDirectoryUsable($spoolpath)) {
                        // The new path is usable, we are happy
                        Config::set("system", "spoolpath", $spoolpath);
                        return $spoolpath;
index c2dd31bcf24289362cb4f8e223617b5275c84be1..7e7396785f5993c7b17f7a1e74870b7be5b4afcd 100644 (file)
--- a/index.php
+++ b/index.php
@@ -5,7 +5,9 @@
  */
 
 use Friendica\App;
-use Friendica\Util\LoggerFactory;
+use Friendica\Core\Config;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 
 if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
        die('Vendor path not found. Please execute "bin/composer.phar --no-dev install" on the command line in the web root.');
@@ -13,10 +15,13 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
 
 require __DIR__ . '/vendor/autoload.php';
 
-$logger = LoggerFactory::create('index');
+$basedir = BasePath::create(__DIR__, $_SERVER);
+$configLoader = new Config\ConfigCacheLoader($basedir);
+$config = Factory\ConfigFactory::createCache($configLoader);
+$logger = Factory\LoggerFactory::create('index', $config);
 
 // We assume that the index.php is called by a frontend process
 // The value is set to "true" by default in App
-$a = new App(__DIR__, $logger, false);
+$a = new App($config, $logger, false);
 
 $a->runFrontend();
index be28168e62275a8a3a6e5f4af3ed387af8eb1285..39f75021889c0eba414af9094bb93df9b5281942 100644 (file)
@@ -15,11 +15,11 @@ use Friendica\Core\Config;
 use Friendica\Core\L10n;
 use Friendica\Core\Logger;
 use Friendica\Core\Renderer;
+use Friendica\Core\StorageManager;
 use Friendica\Core\System;
 use Friendica\Core\Theme;
 use Friendica\Core\Update;
 use Friendica\Core\Worker;
-use Friendica\Core\StorageManager;
 use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
 use Friendica\Model\Contact;
@@ -30,6 +30,7 @@ use Friendica\Module;
 use Friendica\Module\Login;
 use Friendica\Module\Tos;
 use Friendica\Util\Arrays;
+use Friendica\Util\BasePath;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Network;
 use Friendica\Util\Strings;
@@ -915,7 +916,7 @@ function admin_page_summary(App $a)
        }
 
        if (Config::get('system', 'dbupdate', DBStructure::UPDATE_NOT_CHECKED) == DBStructure::UPDATE_NOT_CHECKED) {
-               DBStructure::update(false, true);
+               DBStructure::update($a->getBasePath(), false, true);
        }
        if (Config::get('system', 'dbupdate') == DBStructure::UPDATE_FAILED) {
                $showwarning = true;
@@ -1375,7 +1376,7 @@ function admin_page_site_post(App $a)
        Config::set('system', 'dbclean-expire-unclaimed', $dbclean_unclaimed);
 
        if ($itemcache != '') {
-               $itemcache = App::getRealPath($itemcache);
+               $itemcache = BasePath::getRealPath($itemcache);
        }
 
        Config::set('system', 'itemcache', $itemcache);
@@ -1383,13 +1384,13 @@ function admin_page_site_post(App $a)
        Config::set('system', 'max_comments', $max_comments);
 
        if ($temppath != '') {
-               $temppath = App::getRealPath($temppath);
+               $temppath = BasePath::getRealPath($temppath);
        }
 
        Config::set('system', 'temppath', $temppath);
 
        if ($basepath != '') {
-               $basepath = App::getRealPath($basepath);
+               $basepath = BasePath::getRealPath($basepath);
        }
 
        Config::set('system', 'basepath'         , $basepath);
@@ -1607,7 +1608,7 @@ function admin_page_site(App $a)
                '$hide_help'        => ['hide_help', L10n::t("Hide help entry from navigation menu"), Config::get('system', 'hide_help'), L10n::t("Hides the menu entry for the Help pages from the navigation menu. You can still access it calling /help directly.")],
                '$singleuser'       => ['singleuser', L10n::t("Single user instance"), Config::get('system', 'singleuser', '---'), L10n::t("Make this instance multi-user or single-user for the named user"), $user_names],
 
-               '$storagebackend'   => ['storagebackend', L10n::t("File storage backend"), $storage_current_backend, L10n::t('The backend used to store uploaded files data. If you change the storage backend, you have to manually move the existing files see <a href="/help/Settings#1_2_3_1">the settings documentation</a> for more information about the choices and the moving procedure.'), $storage_backends_choices],
+               '$storagebackend'   => ['storagebackend', L10n::t("File storage backend"), $storage_current_backend, L10n::t('The backend used to store uploaded data. If you change the storage backend, you can manually move the existing files. If you do not do so, the files uploaded before the change will still be available at the old backend. Please see <a href="/help/Settings#1_2_3_1">the settings documentation</a> for more information about the choices and the moving procedure.'), $storage_backends_choices],
                '$storageform'      => $storage_form,
                '$maximagesize'     => ['maximagesize', L10n::t("Maximum image size"), Config::get('system', 'maximagesize'), L10n::t("Maximum size in bytes of uploaded images. Default is 0, which means no limits.")],
                '$maximagelength'   => ['maximagelength', L10n::t("Maximum image length"), Config::get('system', 'max_image_length'), L10n::t("Maximum length in pixels of the longest side of uploaded images. Default is -1, which means no limits.")],
@@ -1724,7 +1725,7 @@ function admin_page_dbsync(App $a)
        }
 
        if (($a->argc > 2) && (intval($a->argv[2]) || ($a->argv[2] === 'check'))) {
-               $retval = DBStructure::update(false, true);
+               $retval = DBStructure::update($a->getBasePath(), false, true);
                if ($retval === '') {
                        $o .= L10n::t("Database structure update %s was successfully applied.", DB_UPDATE_VERSION) . "<br />";
                        Config::set('database', 'last_successful_update', DB_UPDATE_VERSION);
index d10deb2e88452ce94cbdf4704b35f98053a789ea..80d5f34cc814ee0da16d24cd9d7c31a216079ff4 100644 (file)
@@ -29,7 +29,7 @@ function friendica_init(App $a)
                }
 
                $sql_extra = '';
-               if (!empty($a->config['admin_nickname'])) {
+               if (Config::get('config', 'admin_nickname') !== null) {
                        $sql_extra = sprintf(" AND `nickname` = '%s' ", DBA::escape(Config::get('config', 'admin_nickname')));
                }
                if (!empty(Config::get('config', 'admin_email'))) {
@@ -48,8 +48,9 @@ function friendica_init(App $a)
 
                Config::load('feature_lock');
                $locked_features = [];
-               if (!empty($a->config['feature_lock'])) {
-                       foreach ($a->config['feature_lock'] as $k => $v) {
+               $featureLock = Config::get('config', 'feature_lock');
+               if (isset($featureLock)) {
+                       foreach ($featureLock as $k => $v) {
                                if ($k === 'config_loaded') {
                                        continue;
                                }
index 7cb2849a89e137e54a85fbcecbb4434b2dcc47dd..40c01da20f48d5a83d489c676e2a5b30770d4a71 100644 (file)
@@ -40,6 +40,8 @@ use Friendica\Util\Emailer;
 use Friendica\Util\Security;
 use Friendica\Util\Strings;
 
+require_once 'include/items.php';
+
 function item_post(App $a) {
        if (!local_user() && !remote_user()) {
                return 0;
@@ -188,6 +190,40 @@ function item_post(App $a) {
        $categories = '';
        $postopts = '';
        $emailcc = '';
+       $body = defaults($_REQUEST, 'body', '');
+       $has_attachment = defaults($_REQUEST, 'has_attachment', 0);
+
+       // If we have a speparate attachment, we need to add it to the body.
+       if (!empty($has_attachment)) {
+               $attachment_type  = defaults($_REQUEST, 'attachment_type',  '');
+               $attachment_title = defaults($_REQUEST, 'attachment_title', '');
+               $attachment_text  = defaults($_REQUEST, 'attachment_text',  '');
+
+               $attachment_url     = hex2bin(defaults($_REQUEST, 'attachment_url',     ''));
+               $attachment_img_src = hex2bin(defaults($_REQUEST, 'attachment_img_src', ''));
+
+               $attachment_img_width  = defaults($_REQUEST, 'attachment_img_width',  0);
+               $attachment_img_height = defaults($_REQUEST, 'attachment_img_height', 0);
+               $attachment = [
+                       'type'   => $attachment_type,
+                       'title'  => $attachment_title,
+                       'text'   => $attachment_text,
+                       'url'    => $attachment_url,
+               ];
+
+               if (!empty($attachment_img_src)) {
+                       $attachment['images'] = [
+                               0 => [
+                                       'src'    => $attachment_img_src,
+                                       'width'  => $attachment_img_width,
+                                       'height' => $attachment_img_height
+                               ]
+                       ];
+               }
+
+               $att_bbcode = add_page_info_data($attachment);
+               $body .= $att_bbcode;
+       }
 
        if (!empty($orig_post)) {
                $str_group_allow   = $orig_post['allow_gid'];
@@ -201,7 +237,7 @@ function item_post(App $a) {
                $app               = $orig_post['app'];
                $categories        = $orig_post['file'];
                $title             = Strings::escapeTags(trim($_REQUEST['title']));
-               $body              = Strings::escapeHtml(trim($_REQUEST['body']));
+               $body              = Strings::escapeHtml(trim($body));
                $private           = $orig_post['private'];
                $pubmail_enabled   = $orig_post['pubmail'];
                $network           = $orig_post['network'];
@@ -237,7 +273,7 @@ function item_post(App $a) {
                $coord             = Strings::escapeTags(trim(defaults($_REQUEST, 'coord'   , '')));
                $verb              = Strings::escapeTags(trim(defaults($_REQUEST, 'verb'    , '')));
                $emailcc           = Strings::escapeTags(trim(defaults($_REQUEST, 'emailcc' , '')));
-               $body              = Strings::escapeHtml(trim(defaults($_REQUEST, 'body'    , '')));
+               $body              = Strings::escapeHtml(trim($body));
                $network           = Strings::escapeTags(trim(defaults($_REQUEST, 'network' , Protocol::DFRN)));
                $guid              = System::createUUID();
 
index b982ccf084f9e4a6b3c8caa9f0d3aa7e67e1d3d1..3b2522ab12fb05184bcd4f54f1af6a85ebbc2c00 100644 (file)
@@ -12,6 +12,7 @@
 use Friendica\App;
 use Friendica\Core\Hook;
 use Friendica\Core\Logger;
+use Friendica\Core\System;
 use Friendica\Util\Network;
 use Friendica\Util\ParseUrl;
 
@@ -19,6 +20,8 @@ function parse_url_content(App $a)
 {
        $text = null;
        $str_tags = '';
+       $format = '';
+       $ret= ['success' => false, 'contentType' => ''];
 
        $br = "\n";
 
@@ -43,6 +46,10 @@ function parse_url_content(App $a)
                }
        }
 
+       if (isset($_GET['format']) && $_GET['format'] == 'json') {
+               $format = 'json';
+       }
+
        // Add url scheme if it is missing
        $arrurl = parse_url($url);
        if (empty($arrurl['scheme'])) {
@@ -73,22 +80,35 @@ function parse_url_content(App $a)
                        }
                }
                $type = null;
+               $content_type = '';
+               $bbcode = '';
                if (array_key_exists('Content-Type', $hdrs)) {
                        $type = $hdrs['Content-Type'];
                }
                if ($type) {
                        if (stripos($type, 'image/') !== false) {
-                               echo $br . '[img]' . $url . '[/img]' . $br;
-                               exit();
+                               $content_type = 'image';
+                               $bbcode = $br . '[img]' . $url . '[/img]' . $br;
                        }
                        if (stripos($type, 'video/') !== false) {
-                               echo $br . '[video]' . $url . '[/video]' . $br;
-                               exit();
+                               $content_type = 'video';
+                               $bbcode = $br . '[video]' . $url . '[/video]' . $br;
                        }
                        if (stripos($type, 'audio/') !== false) {
-                               echo $br . '[audio]' . $url . '[/audio]' . $br;
-                               exit();
+                               $content_type = 'audio';
+                               $bbcode = $br . '[audio]' . $url . '[/audio]' . $br;
+                       }
+               }
+               if (!empty($content_type)) {
+                       if ($format == 'json') {
+                               $ret['contentType'] = $content_type;
+                               $ret['data'] = ['url' => $url];
+                               $ret['success'] = true;
+                               System::jsonExit($ret);
                        }
+
+                       echo $bbcode;
+                       exit();
                }
        }
 
@@ -130,6 +150,14 @@ function parse_url_content(App $a)
                exit();
        }
 
+       if ($format == 'json') {
+               $ret['data'] = $siteinfo;
+               $ret['contentType'] = 'attachment';
+               $ret['success'] = true;
+
+               System::jsonExit($ret);
+       }
+
        // Format it as BBCode attachment
        $info = add_page_info_data($siteinfo);
 
index b910765e12f02c35719523d9c9393c6a2cfd86f6..8068a1530bad71a2e3c462e48c710f2751d99b68 100644 (file)
@@ -8,7 +8,10 @@ use Detection\MobileDetect;
 use DOMDocument;
 use DOMXPath;
 use Exception;
+use Friendica\Core\Config\ConfigCache;
+use Friendica\Core\Config\ConfigCacheLoader;
 use Friendica\Database\DBA;
+use Friendica\Factory\ConfigFactory;
 use Friendica\Network\HTTPException\InternalServerErrorException;
 use Psr\Log\LoggerInterface;
 
@@ -30,7 +33,6 @@ class App
        public $module_loaded = false;
        public $module_class = null;
        public $query_string = '';
-       public $config = [];
        public $page = [];
        public $profile;
        public $profile_uid;
@@ -112,6 +114,31 @@ class App
         */
        private $logger;
 
+       /**
+        * @var ConfigCache The cached config
+        */
+       private $config;
+
+       /**
+        * Returns the current config cache of this node
+        *
+        * @return ConfigCache
+        */
+       public function getConfig()
+       {
+               return $this->config;
+       }
+
+       /**
+        * The basepath of this app
+        *
+        * @return string
+        */
+       public function getBasePath()
+       {
+               return $this->basePath;
+       }
+
        /**
         * Register a stylesheet file path to be included in the <head> tag of every page.
         * Inclusion is done in App->initHead().
@@ -124,7 +151,7 @@ class App
         */
        public function registerStylesheet($path)
        {
-               $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path);
+               $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path);
 
                $this->stylesheets[] = trim($url, '/');
        }
@@ -141,7 +168,7 @@ class App
         */
        public function registerFooterScript($path)
        {
-               $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path);
+               $url = str_replace($this->basePath . DIRECTORY_SEPARATOR, '', $path);
 
                $this->footerScripts[] = trim($url, '/');
        }
@@ -154,23 +181,25 @@ class App
        /**
         * @brief App constructor.
         *
-        * @param string           $basePath  Path to the app base folder
+        * @param ConfigCache      $config    The Cached Config
         * @param LoggerInterface  $logger    Logger of this application
         * @param bool             $isBackend Whether it is used for backend or frontend (Default true=backend)
         *
         * @throws Exception if the Basepath is not usable
         */
-       public function __construct($basePath, LoggerInterface $logger, $isBackend = true)
+       public function __construct(ConfigCache $config, LoggerInterface $logger, $isBackend = true)
        {
-               $this->logger = $logger;
+               $this->config   = $config;
+               $this->logger   = $logger;
+               $this->basePath = $this->config->get('system', 'basepath');
 
-               if (!static::isDirectoryUsable($basePath, false)) {
-                       throw new Exception('Basepath ' . $basePath . ' isn\'t usable.');
+               if (!Core\System::isDirectoryUsable($this->basePath, false)) {
+                       throw new Exception('Basepath ' . $this->basePath . ' isn\'t usable.');
                }
+               $this->basePath = rtrim($this->basePath, DIRECTORY_SEPARATOR);
 
                BaseObject::setApp($this);
 
-               $this->basePath = rtrim($basePath, DIRECTORY_SEPARATOR);
                $this->checkBackend($isBackend);
                $this->checkFriendicaApp();
 
@@ -195,7 +224,7 @@ class App
                $this->callstack['rendering'] = [];
                $this->callstack['parser'] = [];
 
-               $this->mode = new App\Mode($basePath);
+               $this->mode = new App\Mode($this->basePath);
 
                $this->reload();
 
@@ -226,9 +255,9 @@ class App
 
                set_include_path(
                        get_include_path() . PATH_SEPARATOR
-                       . $this->getBasePath() . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR
-                       . $this->getBasePath() . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR
-                       . $this->getBasePath());
+                       . $this->basePath . DIRECTORY_SEPARATOR . 'include' . PATH_SEPARATOR
+                       . $this->basePath . DIRECTORY_SEPARATOR . 'library' . PATH_SEPARATOR
+                       . $this->basePath);
 
                if (!empty($_SERVER['QUERY_STRING']) && strpos($_SERVER['QUERY_STRING'], 'pagename=') === 0) {
                        $this->query_string = substr($_SERVER['QUERY_STRING'], 9);
@@ -332,21 +361,32 @@ class App
         */
        public function reload()
        {
-               // The order of the following calls is important to ensure proper initialization
-               $this->loadConfigFiles();
+               Core\Config::init($this->config);
+               Core\PConfig::init($this->config);
 
                $this->loadDatabase();
 
-               $this->getMode()->determine($this->getBasePath());
+               $this->getMode()->determine($this->basePath);
 
                $this->determineURLPath();
 
-               Core\Config::load();
+               if ($this->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
+                       $adapterType = $this->config->get('system', 'config_adapter');
+                       $adapter = ConfigFactory::createConfig($adapterType, $this->config);
+                       Core\Config::setAdapter($adapter);
+                       $adapterP = ConfigFactory::createPConfig($adapterType, $this->config);
+                       Core\PConfig::setAdapter($adapterP);
+                       Core\Config::load();
+               }
+
+               // again because DB-config could change the config
+               $this->getMode()->determine($this->basePath);
 
                if ($this->getMode()->has(App\Mode::DBAVAILABLE)) {
                        Core\Hook::loadHooks();
-
-                       $this->loadAddonConfig();
+                       $loader = new ConfigCacheLoader($this->basePath);
+                       Core\Hook::callAll('load_config', $loader);
+                       $this->config->loadConfigArray($loader->loadCoreConfig('addon'), true);
                }
 
                $this->loadDefaultTimezone();
@@ -358,158 +398,6 @@ class App
                Core\Logger::setLogger($this->logger);
        }
 
-       /**
-        * Load the configuration files
-        *
-        * First loads the default value for all the configuration keys, then the legacy configuration files, then the
-        * expected local.config.php
-        */
-       private function loadConfigFiles()
-       {
-               $this->loadConfigFile($this->getBasePath() . '/config/defaults.config.php');
-               $this->loadConfigFile($this->getBasePath() . '/config/settings.config.php');
-
-               // Legacy .htconfig.php support
-               if (file_exists($this->getBasePath() . '/.htpreconfig.php')) {
-                       $a = $this;
-                       include $this->getBasePath() . '/.htpreconfig.php';
-               }
-
-               // Legacy .htconfig.php support
-               if (file_exists($this->getBasePath() . '/.htconfig.php')) {
-                       $a = $this;
-
-                       include $this->getBasePath() . '/.htconfig.php';
-
-                       $this->setConfigValue('database', 'hostname', $db_host);
-                       $this->setConfigValue('database', 'username', $db_user);
-                       $this->setConfigValue('database', 'password', $db_pass);
-                       $this->setConfigValue('database', 'database', $db_data);
-                       if (isset($a->config['system']['db_charset'])) {
-                               $this->setConfigValue('database', 'charset', $a->config['system']['db_charset']);
-                       }
-
-                       unset($db_host, $db_user, $db_pass, $db_data);
-
-                       if (isset($default_timezone)) {
-                               $this->setConfigValue('system', 'default_timezone', $default_timezone);
-                               unset($default_timezone);
-                       }
-
-                       if (isset($pidfile)) {
-                               $this->setConfigValue('system', 'pidfile', $pidfile);
-                               unset($pidfile);
-                       }
-
-                       if (isset($lang)) {
-                               $this->setConfigValue('system', 'language', $lang);
-                               unset($lang);
-                       }
-               }
-
-               if (file_exists($this->getBasePath() . '/config/local.config.php')) {
-                       $this->loadConfigFile($this->getBasePath() . '/config/local.config.php', true);
-               } elseif (file_exists($this->getBasePath() . '/config/local.ini.php')) {
-                       $this->loadINIConfigFile($this->getBasePath() . '/config/local.ini.php', true);
-               }
-       }
-
-       /**
-        * Tries to load the specified legacy configuration file into the App->config array.
-        * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
-        *
-        * @deprecated since version 2018.12
-        * @param string $filepath
-        * @param bool $overwrite Force value overwrite if the config key already exists
-        * @throws Exception
-        */
-       public function loadINIConfigFile($filepath, $overwrite = false)
-       {
-               if (!file_exists($filepath)) {
-                       throw new Exception('Error parsing non-existent INI config file ' . $filepath);
-               }
-
-               $contents = include($filepath);
-
-               $config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
-
-               if ($config === false) {
-                       throw new Exception('Error parsing INI config file ' . $filepath);
-               }
-
-               $this->loadConfigArray($config, $overwrite);
-       }
-
-       /**
-        * Tries to load the specified configuration file into the App->config array.
-        * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
-        *
-        * The config format is PHP array and the template for configuration files is the following:
-        *
-        * <?php return [
-        *      'section' => [
-        *          'key' => 'value',
-        *      ],
-        * ];
-        *
-        * @param string $filepath
-        * @param bool $overwrite Force value overwrite if the config key already exists
-        * @throws Exception
-        */
-       public function loadConfigFile($filepath, $overwrite = false)
-       {
-               if (!file_exists($filepath)) {
-                       throw new Exception('Error loading non-existent config file ' . $filepath);
-               }
-
-               $config = include($filepath);
-
-               if (!is_array($config)) {
-                       throw new Exception('Error loading config file ' . $filepath);
-               }
-
-               $this->loadConfigArray($config, $overwrite);
-       }
-
-       /**
-        * Loads addons configuration files
-        *
-        * First loads all activated addons default configuration through the load_config hook, then load the local.config.php
-        * again to overwrite potential local addon configuration.
-        */
-       private function loadAddonConfig()
-       {
-               // Loads addons default config
-               Core\Hook::callAll('load_config');
-
-               // Load the local addon config file to overwritten default addon config values
-               if (file_exists($this->getBasePath() . '/config/addon.config.php')) {
-                       $this->loadConfigFile($this->getBasePath() . '/config/addon.config.php', true);
-               } elseif (file_exists($this->getBasePath() . '/config/addon.ini.php')) {
-                       $this->loadINIConfigFile($this->getBasePath() . '/config/addon.ini.php', true);
-               }
-       }
-
-       /**
-        * Tries to load the specified configuration array into the App->config array.
-        * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
-        *
-        * @param array $config
-        * @param bool  $overwrite Force value overwrite if the config key already exists
-        */
-       private function loadConfigArray(array $config, $overwrite = false)
-       {
-               foreach ($config as $category => $values) {
-                       foreach ($values as $key => $value) {
-                               if ($overwrite) {
-                                       $this->setConfigValue($category, $key, $value);
-                               } else {
-                                       $this->setDefaultConfigValue($category, $key, $value);
-                               }
-                       }
-               }
-       }
-
        /**
         * Loads the default timezone
         *
@@ -519,8 +407,8 @@ class App
         */
        private function loadDefaultTimezone()
        {
-               if ($this->getConfigValue('system', 'default_timezone')) {
-                       $this->timezone = $this->getConfigValue('system', 'default_timezone');
+               if ($this->config->get('system', 'default_timezone')) {
+                       $this->timezone = $this->config->get('system', 'default_timezone');
                } else {
                        global $default_timezone;
                        $this->timezone = !empty($default_timezone) ? $default_timezone : 'UTC';
@@ -546,7 +434,7 @@ class App
                $relative_script_path = defaults($_SERVER, 'SCRIPT_URL'         , $relative_script_path);
                $relative_script_path = defaults($_SERVER, 'REQUEST_URI'        , $relative_script_path);
 
-               $this->urlPath = $this->getConfigValue('system', 'urlpath');
+               $this->urlPath = $this->config->get('system', 'urlpath');
 
                /* $relative_script_path gives /relative/path/to/friendica/module/parameter
                 * QUERY_STRING gives pagename=module/parameter
@@ -574,11 +462,11 @@ class App
                        return;
                }
 
-               $db_host = $this->getConfigValue('database', 'hostname');
-               $db_user = $this->getConfigValue('database', 'username');
-               $db_pass = $this->getConfigValue('database', 'password');
-               $db_data = $this->getConfigValue('database', 'database');
-               $charset = $this->getConfigValue('database', 'charset');
+               $db_host = $this->config->get('database', 'hostname');
+               $db_user = $this->config->get('database', 'username');
+               $db_pass = $this->config->get('database', 'password');
+               $db_data = $this->config->get('database', 'database');
+               $charset = $this->config->get('database', 'charset');
 
                // Use environment variables for mysql if they are set beforehand
                if (!empty(getenv('MYSQL_HOST'))
@@ -601,9 +489,9 @@ class App
 
                $stamp1 = microtime(true);
 
-               if (DBA::connect($db_host, $db_user, $db_pass, $db_data, $charset)) {
+               if (DBA::connect($this->config, $db_host, $db_user, $db_pass, $db_data, $charset)) {
                        // Loads DB_UPDATE_VERSION constant
-                       Database\DBStructure::definition(false);
+                       Database\DBStructure::definition($this->basePath, false);
                }
 
                unset($db_host, $db_user, $db_pass, $db_data, $charset);
@@ -611,55 +499,6 @@ class App
                $this->saveTimestamp($stamp1, 'network');
        }
 
-       /**
-        * @brief Returns the base filesystem path of the App
-        *
-        * It first checks for the internal variable, then for DOCUMENT_ROOT and
-        * finally for PWD
-        *
-        * @return string
-        * @throws InternalServerErrorException
-        */
-       public function getBasePath()
-       {
-               $basepath = $this->basePath;
-
-               if (!$basepath) {
-                       $basepath = Core\Config::get('system', 'basepath');
-               }
-
-               if (!$basepath && !empty($_SERVER['DOCUMENT_ROOT'])) {
-                       $basepath = $_SERVER['DOCUMENT_ROOT'];
-               }
-
-               if (!$basepath && !empty($_SERVER['PWD'])) {
-                       $basepath = $_SERVER['PWD'];
-               }
-
-               return self::getRealPath($basepath);
-       }
-
-       /**
-        * @brief Returns a normalized file path
-        *
-        * This is a wrapper for the "realpath" function.
-        * That function cannot detect the real path when some folders aren't readable.
-        * Since this could happen with some hosters we need to handle this.
-        *
-        * @param string $path The path that is about to be normalized
-        * @return string normalized path - when possible
-        */
-       public static function getRealPath($path)
-       {
-               $normalized = realpath($path);
-
-               if (!is_bool($normalized)) {
-                       return $normalized;
-               } else {
-                       return $path;
-               }
-       }
-
        public function getScheme()
        {
                return $this->scheme;
@@ -735,8 +574,8 @@ class App
                                $this->urlPath = trim($parsed['path'], '\\/');
                        }
 
-                       if (file_exists($this->getBasePath() . '/.htpreconfig.php')) {
-                               include $this->getBasePath() . '/.htpreconfig.php';
+                       if (file_exists($this->basePath . '/.htpreconfig.php')) {
+                               include $this->basePath . '/.htpreconfig.php';
                        }
 
                        if (Core\Config::get('config', 'hostname') != '') {
@@ -789,9 +628,9 @@ class App
                // compose the page title from the sitename and the
                // current module called
                if (!$this->module == '') {
-                       $this->page['title'] = $this->config['sitename'] . ' (' . $this->module . ')';
+                       $this->page['title'] = $this->config->get('config', 'sitename') . ' (' . $this->module . ')';
                } else {
-                       $this->page['title'] = $this->config['sitename'];
+                       $this->page['title'] = $this->config->get('config', 'sitename');
                }
 
                if (!empty(Core\Renderer::$theme['stylesheet'])) {
@@ -912,7 +751,9 @@ class App
         */
        public function saveTimestamp($timestamp, $value)
        {
-               if (!isset($this->config['system']['profiler']) || !$this->config['system']['profiler']) {
+               $profiler = $this->config->get('system', 'profiler');
+
+               if (!isset($profiler) || !$profiler) {
                        return;
                }
 
@@ -1150,7 +991,7 @@ class App
                        return;
                }
 
-               $cmdline = $this->getConfigValue('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
+               $cmdline = $this->config->get('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
 
                foreach ($args as $key => $value) {
                        if (!is_null($value) && is_bool($value) && !$value) {
@@ -1168,9 +1009,9 @@ class App
                }
 
                if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
-                       $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->getBasePath());
+                       $resource = proc_open('cmd /c start /b ' . $cmdline, [], $foo, $this->basePath);
                } else {
-                       $resource = proc_open($cmdline . ' &', [], $foo, $this->getBasePath());
+                       $resource = proc_open($cmdline . ' &', [], $foo, $this->basePath);
                }
                if (!is_resource($resource)) {
                        Core\Logger::log('We got no resource for command ' . $cmdline, Core\Logger::DEBUG);
@@ -1179,203 +1020,6 @@ class App
                proc_close($resource);
        }
 
-       /**
-        * @brief Returns the system user that is executing the script
-        *
-        * This mostly returns something like "www-data".
-        *
-        * @return string system username
-        */
-       private static function getSystemUser()
-       {
-               if (!function_exists('posix_getpwuid') || !function_exists('posix_geteuid')) {
-                       return '';
-               }
-
-               $processUser = posix_getpwuid(posix_geteuid());
-               return $processUser['name'];
-       }
-
-       /**
-        * @brief Checks if a given directory is usable for the system
-        *
-        * @param      $directory
-        * @param bool $check_writable
-        * @return boolean the directory is usable
-        * @throws Exception
-        */
-       public static function isDirectoryUsable($directory, $check_writable = true)
-       {
-               if ($directory == '') {
-                       Core\Logger::log('Directory is empty. This shouldn\'t happen.', Core\Logger::DEBUG);
-                       return false;
-               }
-
-               if (!file_exists($directory)) {
-                       Core\Logger::log('Path "' . $directory . '" does not exist for user ' . self::getSystemUser(), Core\Logger::DEBUG);
-                       return false;
-               }
-
-               if (is_file($directory)) {
-                       Core\Logger::log('Path "' . $directory . '" is a file for user ' . self::getSystemUser(), Core\Logger::DEBUG);
-                       return false;
-               }
-
-               if (!is_dir($directory)) {
-                       Core\Logger::log('Path "' . $directory . '" is not a directory for user ' . self::getSystemUser(), Core\Logger::DEBUG);
-                       return false;
-               }
-
-               if ($check_writable && !is_writable($directory)) {
-                       Core\Logger::log('Path "' . $directory . '" is not writable for user ' . self::getSystemUser(), Core\Logger::DEBUG);
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * @param string $cat     Config category
-        * @param string $k       Config key
-        * @param mixed  $default Default value if it isn't set
-        *
-        * @return string Returns the value of the Config entry
-        */
-       public function getConfigValue($cat, $k, $default = null)
-       {
-               $return = $default;
-
-               if ($cat === 'config') {
-                       if (isset($this->config[$k])) {
-                               $return = $this->config[$k];
-                       }
-               } else {
-                       if (isset($this->config[$cat][$k])) {
-                               $return = $this->config[$cat][$k];
-                       }
-               }
-
-               return $return;
-       }
-
-       /**
-        * Sets a default value in the config cache. Ignores already existing keys.
-        *
-        * @param string $cat Config category
-        * @param string $k   Config key
-        * @param mixed  $v   Default value to set
-        */
-       private function setDefaultConfigValue($cat, $k, $v)
-       {
-               if (!isset($this->config[$cat][$k])) {
-                       $this->setConfigValue($cat, $k, $v);
-               }
-       }
-
-       /**
-        * Sets a value in the config cache. Accepts raw output from the config table
-        *
-        * @param string $cat Config category
-        * @param string $k   Config key
-        * @param mixed  $v   Value to set
-        */
-       public function setConfigValue($cat, $k, $v)
-       {
-               // Only arrays are serialized in database, so we have to unserialize sparingly
-               $value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
-
-               if ($cat === 'config') {
-                       $this->config[$k] = $value;
-               } else {
-                       if (!isset($this->config[$cat])) {
-                               $this->config[$cat] = [];
-                       }
-
-                       $this->config[$cat][$k] = $value;
-               }
-       }
-
-       /**
-        * Deletes a value from the config cache
-        *
-        * @param string $cat Config category
-        * @param string $k   Config key
-        */
-       public function deleteConfigValue($cat, $k)
-       {
-               if ($cat === 'config') {
-                       if (isset($this->config[$k])) {
-                               unset($this->config[$k]);
-                       }
-               } else {
-                       if (isset($this->config[$cat][$k])) {
-                               unset($this->config[$cat][$k]);
-                       }
-               }
-       }
-
-
-       /**
-        * Retrieves a value from the user config cache
-        *
-        * @param int    $uid     User Id
-        * @param string $cat     Config category
-        * @param string $k       Config key
-        * @param mixed  $default Default value if key isn't set
-        *
-        * @return string The value of the config entry
-        */
-       public function getPConfigValue($uid, $cat, $k, $default = null)
-       {
-               $return = $default;
-
-               if (isset($this->config[$uid][$cat][$k])) {
-                       $return = $this->config[$uid][$cat][$k];
-               }
-
-               return $return;
-       }
-
-       /**
-        * Sets a value in the user config cache
-        *
-        * Accepts raw output from the pconfig table
-        *
-        * @param int    $uid User Id
-        * @param string $cat Config category
-        * @param string $k   Config key
-        * @param mixed  $v   Value to set
-        */
-       public function setPConfigValue($uid, $cat, $k, $v)
-       {
-               // Only arrays are serialized in database, so we have to unserialize sparingly
-               $value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
-
-               if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) {
-                       $this->config[$uid] = [];
-               }
-
-               if (!isset($this->config[$uid][$cat]) || !is_array($this->config[$uid][$cat])) {
-                       $this->config[$uid][$cat] = [];
-               }
-
-               $this->config[$uid][$cat][$k] = $value;
-       }
-
-       /**
-        * Deletes a value from the user config cache
-        *
-        * @param int    $uid User Id
-        * @param string $cat Config category
-        * @param string $k   Config key
-        */
-       public function deletePConfigValue($uid, $cat, $k)
-       {
-               if (isset($this->config[$uid][$cat][$k])) {
-                       unset($this->config[$uid][$cat][$k]);
-               }
-       }
-
        /**
         * Generates the site's default sender email address
         *
@@ -1643,7 +1287,7 @@ class App
                        $this->module = 'maintenance';
                } else {
                        $this->checkURL();
-                       Core\Update::check(false);
+                       Core\Update::check($this->basePath, false);
                        Core\Addon::loadAddons();
                        Core\Hook::loadHooks();
                }
index 84ff193823113a1534e3e407ac2eb712f3fda5f0..4a6fa12d24b03ad18f4542114f1302d8a9e3573e 100644 (file)
@@ -6,7 +6,7 @@ namespace Friendica;
 
 require_once 'boot.php';
 
-use Friendica\Util\LoggerFactory;
+use Friendica\Network\HTTPException\InternalServerErrorException;
 
 /**
  * Basic object
@@ -28,8 +28,7 @@ class BaseObject
        public static function getApp()
        {
                if (empty(self::$app)) {
-                       $logger = $logger = LoggerFactory::create('app');
-                       self::$app = new App(dirname(__DIR__), $logger);
+                       throw new InternalServerErrorException('App isn\'t initialized.');
                }
 
                return self::$app;
index 1118280a53e4bd0ca02394394a36e1fdc173d56f..6697a44aea13bd60b480fa0cb067d4fa5e9f2be5 100644 (file)
@@ -12,6 +12,12 @@ use Friendica\Database\DBA;
  */
 class Addon extends BaseObject
 {
+       /**
+        * The addon sub-directory
+        * @var string
+        */
+       const DIRECTORY = 'addon';
+
        /**
         * List of the names of enabled addons
         *
@@ -106,7 +112,7 @@ class Addon extends BaseObject
                @include_once('addon/' . $addon . '/' . $addon . '.php');
                if (function_exists($addon . '_install')) {
                        $func = $addon . '_install';
-                       $func();
+                       $func(self::getApp());
 
                        $addon_admin = (function_exists($addon . "_addon_admin") ? 1 : 0);
 
@@ -162,11 +168,11 @@ class Addon extends BaseObject
 
                                                                if (function_exists($addon . '_uninstall')) {
                                                                        $func = $addon . '_uninstall';
-                                                                       $func();
+                                                                       $func(self::getApp());
                                                                }
                                                                if (function_exists($addon . '_install')) {
                                                                        $func = $addon . '_install';
-                                                                       $func();
+                                                                       $func(self::getApp());
                                                                }
                                                                DBA::update('addon', ['timestamp' => $t], ['id' => $i['id']]);
                                                        }
index 755dc6ebbcf50bfcc82e9f31a7877ea5d89ea8e3..559ee83ece57e08b8bfedb53d94510b082606459 100644 (file)
@@ -8,8 +8,9 @@
  */
 namespace Friendica\Core;
 
-use Friendica\App;
-use Friendica\BaseObject;
+use Friendica\Core\Config\ConfigCache;
+use Friendica\Core\Config\IConfigAdapter;
+use Friendica\Core\Config\IConfigCache;
 
 /**
  * @brief Arbitrary system configuration storage
@@ -18,49 +19,53 @@ use Friendica\BaseObject;
  * If we ever would decide to return exactly the variable type as entered,
  * we will have fun with the additional features. :-)
  */
-class Config extends BaseObject
+class Config
 {
        /**
-        * @var \Friendica\Core\Config\IConfigAdapter
+        * @var Config\IConfigAdapter|null
         */
-       private static $adapter = null;
+       private static $adapter;
 
-       public static function init()
+       /**
+        * @var Config\IConfigCache
+        */
+       private static $cache;
+
+       /**
+        * Initialize the config with only the cache
+        *
+        * @param Config\IConfigCache $cache  The configuration cache
+        */
+       public static function init(Config\IConfigCache $cache)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return;
-               }
+               self::$cache  = $cache;
+       }
 
-               if (self::getApp()->getConfigValue('system', 'config_adapter') == 'preload') {
-                       self::$adapter = new Config\PreloadConfigAdapter();
-               } else {
-                       self::$adapter = new Config\JITConfigAdapter();
-               }
+       /**
+        * Add the adapter for DB-backend
+        *
+        * @param Config\IConfigAdapter $adapter
+        */
+       public static function setAdapter(Config\IConfigAdapter $adapter)
+       {
+               self::$adapter = $adapter;
        }
 
        /**
         * @brief Loads all configuration values of family into a cached storage.
         *
-        * All configuration values of the system are stored in global cache
-        * which is available under the global variable $a->config
+        * All configuration values of the system are stored in the cache ( @see IConfigCache )
         *
         * @param string $family The category of the configuration value
         *
         * @return void
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function load($family = "config")
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
+               if (!isset(self::$adapter) || !self::$adapter->isConnected()) {
                        return;
                }
 
-               if (empty(self::$adapter)) {
-                       self::init();
-               }
-
                self::$adapter->load($family);
        }
 
@@ -69,12 +74,8 @@ class Config extends BaseObject
         * ($family) and a key.
         *
         * Get a particular config value from the given category ($family)
-        * and the $key from a cached storage in $a->config[$uid].
-        * $instore is only used by the set_config function
-        * to determine if the key already exists in the DB
-        * If a key is found in the DB but doesn't exist in
-        * local config cache, pull it into the cache so we don't have
-        * to hit the DB again for this item.
+        * and the $key from a cached storage either from the self::$adapter
+        * (@see IConfigAdapter ) or from the static::$cache (@see IConfigCache ).
         *
         * @param string  $family        The category of the configuration value
         * @param string  $key           The configuration key to query
@@ -82,17 +83,11 @@ class Config extends BaseObject
         * @param boolean $refresh       optional, If true the config is loaded from the db and not from the cache (default: false)
         *
         * @return mixed Stored value or null if it does not exist
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function get($family, $key, $default_value = null, $refresh = false)
        {
-               // Database isn't ready or populated yet, fallback to file config
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return self::getApp()->getConfigValue($family, $key, $default_value);
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init();
+               if (!isset(self::$adapter) || !self::$adapter->isConnected()) {
+                       return self::$cache->get($family, $key, $default_value);
                }
 
                return self::$adapter->get($family, $key, $default_value, $refresh);
@@ -102,7 +97,6 @@ class Config extends BaseObject
         * @brief Sets a configuration value for system config
         *
         * Stores a config value ($value) in the category ($family) under the key ($key)
-        * for the user_id $uid.
         *
         * Note: Please do not store booleans - convert to 0/1 integer values!
         *
@@ -111,17 +105,11 @@ class Config extends BaseObject
         * @param mixed  $value  The value to store
         *
         * @return bool Operation success
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function set($family, $key, $value)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return false;
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init();
+               if (!isset(self::$adapter) || !self::$adapter->isConnected()) {
+                       return self::$cache->set($family, $key, $value);
                }
 
                return self::$adapter->set($family, $key, $value);
@@ -130,24 +118,18 @@ class Config extends BaseObject
        /**
         * @brief Deletes the given key from the system configuration.
         *
-        * Removes the configured value from the stored cache in $a->config
-        * and removes it from the database.
+        * Removes the configured value from the stored cache in self::$config
+        * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter ).
         *
         * @param string $family The category of the configuration value
         * @param string $key    The configuration key to delete
         *
         * @return mixed
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function delete($family, $key)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return false;
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init();
+               if (!isset(self::$adapter) || !self::$adapter->isConnected()) {
+                       self::$cache->delete($family, $key);
                }
 
                return self::$adapter->delete($family, $key);
diff --git a/src/Core/Config/AbstractDbaConfigAdapter.php b/src/Core/Config/AbstractDbaConfigAdapter.php
new file mode 100644 (file)
index 0000000..f7fd701
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+use Friendica\Database\DBA;
+
+abstract class AbstractDbaConfigAdapter
+{
+       public function isConnected()
+       {
+               return DBA::connected();
+       }
+}
diff --git a/src/Core/Config/ConfigCache.php b/src/Core/Config/ConfigCache.php
new file mode 100644 (file)
index 0000000..0c9e1f4
--- /dev/null
@@ -0,0 +1,175 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+/**
+ * The Friendica config cache for the application
+ * Initial, all *.config.php files are loaded into this cache with the
+ * ConfigCacheLoader ( @see ConfigCacheLoader )
+ *
+ * Is used for further caching operations too (depending on the ConfigAdapter )
+ */
+class ConfigCache implements IConfigCache, IPConfigCache
+{
+       private $config;
+
+       /**
+        * @param array $config    A initial config array
+        */
+       public function __construct(array $config = [])
+       {
+               $this->config = $config;
+       }
+
+       /**
+        * Tries to load the specified configuration array into the App->config array.
+        * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
+        *
+        * @param array $config
+        * @param bool  $overwrite Force value overwrite if the config key already exists
+        */
+       public function loadConfigArray(array $config, $overwrite = false)
+       {
+               foreach ($config as $category => $values) {
+                       foreach ($values as $key => $value) {
+                               if ($overwrite) {
+                                       $this->set($category, $key, $value);
+                               } else {
+                                       $this->setDefault($category, $key, $value);
+                               }
+                       }
+               }
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function get($cat, $key = null, $default = null)
+       {
+               $return = $default;
+
+               if ($cat === 'config') {
+                       if (isset($this->config[$key])) {
+                               $return = $this->config[$key];
+                       }
+               } else {
+                       if (isset($this->config[$cat][$key])) {
+                               $return = $this->config[$cat][$key];
+                       } elseif ($key == null && isset($this->config[$cat])) {
+                               $return = $this->config[$cat];
+                       }
+               }
+
+               return $return;
+       }
+
+       /**
+        * Sets a default value in the config cache. Ignores already existing keys.
+        *
+        * @param string $cat Config category
+        * @param string $k   Config key
+        * @param mixed  $v   Default value to set
+        */
+       private function setDefault($cat, $k, $v)
+       {
+               if (!isset($this->config[$cat][$k])) {
+                       $this->set($cat, $k, $v);
+               }
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function set($cat, $key, $value)
+       {
+               // Only arrays are serialized in database, so we have to unserialize sparingly
+               $value = is_string($value) && preg_match("|^a:[0-9]+:{.*}$|s", $value) ? unserialize($value) : $value;
+
+               if ($cat === 'config') {
+                       $this->config[$key] = $value;
+               } else {
+                       if (!isset($this->config[$cat])) {
+                               $this->config[$cat] = [];
+                       }
+
+                       $this->config[$cat][$key] = $value;
+               }
+
+               return true;
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function delete($cat, $key)
+       {
+               if ($cat === 'config') {
+                       if (isset($this->config[$key])) {
+                               unset($this->config[$key]);
+                       }
+               } else {
+                       if (isset($this->config[$cat][$key])) {
+                               unset($this->config[$cat][$key]);
+                       }
+               }
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function getP($uid, $cat, $key = null, $default = null)
+       {
+               $return = $default;
+
+               if (isset($this->config[$uid][$cat][$key])) {
+                       $return = $this->config[$uid][$cat][$key];
+               } elseif ($key === null && isset($this->config[$uid][$cat])) {
+                       $return = $this->config[$uid][$cat];
+               }
+
+               return $return;
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function setP($uid, $cat, $key, $value)
+       {
+               // Only arrays are serialized in database, so we have to unserialize sparingly
+               $value = is_string($value) && preg_match("|^a:[0-9]+:{.*}$|s", $value) ? unserialize($value) : $value;
+
+               if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) {
+                       $this->config[$uid] = [];
+               }
+
+               if (!isset($this->config[$uid][$cat]) || !is_array($this->config[$uid][$cat])) {
+                       $this->config[$uid][$cat] = [];
+               }
+
+               if ($key === null) {
+                       $this->config[$uid][$cat] = $value;
+               } else {
+                       $this->config[$uid][$cat][$key] = $value;
+               }
+       }
+
+       /**
+        * {@inheritdoc}
+        */
+       public function deleteP($uid, $cat, $key)
+       {
+               if (isset($this->config[$uid][$cat][$key])) {
+                       unset($this->config[$uid][$cat][$key]);
+               }
+       }
+
+       /**
+        * Returns the whole configuration
+        *
+        * @return array The configuration
+        */
+       public function getAll()
+       {
+               return $this->config;
+       }
+}
diff --git a/src/Core/Config/ConfigCacheLoader.php b/src/Core/Config/ConfigCacheLoader.php
new file mode 100644 (file)
index 0000000..0b87614
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+use Friendica\Core\Addon;
+
+/**
+ * The ConfigCacheLoader loads config-files and stores them in a ConfigCache ( @see ConfigCache )
+ *
+ * It is capable of loading the following config files:
+ * - *.config.php   (current)
+ * - *.ini.php      (deprecated)
+ * - *.htconfig.php (deprecated)
+ */
+class ConfigCacheLoader
+{
+       /**
+        * The Sub directory of the config-files
+        * @var string
+        */
+       const SUBDIRECTORY = 'config';
+
+       private $baseDir;
+       private $configDir;
+
+       public function __construct($baseDir)
+       {
+               $this->baseDir = $baseDir;
+               $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::SUBDIRECTORY;
+       }
+
+       /**
+        * Load the configuration files
+        *
+        * First loads the default value for all the configuration keys, then the legacy configuration files, then the
+        * expected local.config.php
+        */
+       public function loadConfigFiles(ConfigCache $config)
+       {
+               // Setting at least the basepath we know
+               $config->set('system', 'basepath', $this->baseDir);
+
+               $config->loadConfigArray($this->loadCoreConfig('defaults'));
+               $config->loadConfigArray($this->loadCoreConfig('settings'));
+
+               $config->loadConfigArray($this->loadLegacyConfig('htpreconfig'), true);
+               $config->loadConfigArray($this->loadLegacyConfig('htconfig'), true);
+
+               $config->loadConfigArray($this->loadCoreConfig('local'), true);
+       }
+
+       /**
+        * Tries to load the specified core-configuration and returns the config array.
+        *
+        * @param string $name The name of the configuration
+        *
+        * @return array The config array (empty if no config found)
+        *
+        * @throws \Exception if the configuration file isn't readable
+        */
+       public function loadCoreConfig($name)
+       {
+               if (file_exists($this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php')) {
+                       return $this->loadConfigFile($this->configDir . DIRECTORY_SEPARATOR . $name . '.config.php');
+               } elseif (file_exists($this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php')) {
+                       return $this->loadINIConfigFile($this->configDir . DIRECTORY_SEPARATOR . $name . '.ini.php');
+               } else {
+                       return [];
+               }
+       }
+
+       /**
+        * Tries to load the specified addon-configuration and returns the config array.
+        *
+        * @param string $name The name of the configuration
+        *
+        * @return array The config array (empty if no config found)
+        *
+        * @throws \Exception if the configuration file isn't readable
+        */
+       public function loadAddonConfig($name)
+       {
+               $filepath = $this->baseDir . DIRECTORY_SEPARATOR . // /var/www/html/
+                       Addon::DIRECTORY       . DIRECTORY_SEPARATOR . // addon/
+                       $name                  . DIRECTORY_SEPARATOR . // openstreetmap/
+                       self::SUBDIRECTORY     . DIRECTORY_SEPARATOR . // config/
+                       $name . ".config.php";                         // openstreetmap.config.php
+
+               if (file_exists($filepath)) {
+                       return $this->loadConfigFile($filepath);
+               } else {
+                       return [];
+               }
+       }
+
+       /**
+        * Tries to load the legacy config files (.htconfig.php, .htpreconfig.php) and returns the config array.
+        *
+        * @param string $name The name of the config file
+        *
+        * @return array The configuration array (empty if no config found)
+        *
+        * @deprecated since version 2018.09
+        */
+       private function loadLegacyConfig($name)
+       {
+               $filePath = $this->baseDir  . DIRECTORY_SEPARATOR . '.' . $name . '.php';
+
+               if (file_exists($filePath)) {
+                       $a = new \stdClass();
+                       $a->config = [];
+                       include $filePath;
+
+                       if (isset($db_host)) {
+                               $a->config['database']['hostname'] = $db_host;
+                               unset($db_host);
+                       }
+                       if (isset($db_user)) {
+                               $a->config['database']['username'] = $db_user;
+                               unset($db_user);
+                       }
+                       if (isset($db_pass)) {
+                               $a->config['database']['password'] = $db_pass;
+                               unset($db_pass);
+                       }
+                       if (isset($db_data)) {
+                               $a->config['database']['database'] = $db_data;
+                               unset($db_data);
+                       }
+                       if (isset($a->config['system']['db_charset'])) {
+                               $a->config['database']['charset'] = $a->config['system']['charset'];
+                       }
+                       if (isset($pidfile)) {
+                               $a->config['system']['pidfile'] = $pidfile;
+                               unset($pidfile);
+                       }
+                       if (isset($default_timezone)) {
+                               $a->config['system']['default_timezone'] = $default_timezone;
+                               unset($default_timezone);
+                       }
+                       if (isset($lang)) {
+                               $a->config['system']['language'] = $lang;
+                               unset($lang);
+                       }
+
+                       return $a->config;
+               } else {
+                       return [];
+               }
+       }
+
+       /**
+        * Tries to load the specified legacy configuration file and returns the config array.
+        *
+        * @deprecated since version 2018.12
+        * @param string $filepath
+        *
+        * @return array The configuration array
+        * @throws \Exception
+        */
+       private function loadINIConfigFile($filepath)
+       {
+               if (!file_exists($filepath)) {
+                       throw new \Exception('Error parsing non-existent INI config file ' . $filepath);
+               }
+
+               $contents = include($filepath);
+
+               $config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
+
+               if ($config === false) {
+                       throw new \Exception('Error parsing INI config file ' . $filepath);
+               }
+
+               return $config;
+       }
+
+       /**
+        * Tries to load the specified configuration file and returns the config array.
+        *
+        * The config format is PHP array and the template for configuration files is the following:
+        *
+        * <?php return [
+        *      'section' => [
+        *          'key' => 'value',
+        *      ],
+        * ];
+        *
+        * @param  string $filepath The filepath of the
+        * @return array The config array0
+        *
+        * @throws \Exception if the config cannot get loaded.
+        */
+       private function loadConfigFile($filepath)
+       {
+               if (!file_exists($filepath)) {
+                       throw new \Exception('Error loading non-existent config file ' . $filepath);
+               }
+
+               $config = include($filepath);
+
+               if (!is_array($config)) {
+                       throw new \Exception('Error loading config file ' . $filepath);
+               }
+
+               return $config;
+       }
+}
index 9d8eefd777406181afd1bfa5b95e549886abdcdc..70e141484eb3bd05cd6b3586dd2bdb0751c47fcd 100644 (file)
@@ -9,10 +9,7 @@ namespace Friendica\Core\Config;
 interface IConfigAdapter
 {
        /**
-        * @brief Loads all configuration values into a cached storage.
-        *
-        * All configuration values of the system are stored in global cache
-        * which is available under the global variable $a->config
+        * Loads all configuration values into a cached storage.
         *
         * @param string  $cat The category of the configuration values to load
         *
@@ -21,17 +18,9 @@ interface IConfigAdapter
        public function load($cat = "config");
 
        /**
-        * @brief Get a particular user's config variable given the category name
+        * Get a particular user's config variable given the category name
         * ($family) and a key.
         *
-        * Get a particular config value from the given category ($family)
-        * and the $key from a cached storage in $a->config[$uid].
-        * $instore is only used by the set_config function
-        * to determine if the key already exists in the DB
-        * If a key is found in the DB but doesn't exist in
-        * local config cache, pull it into the cache so we don't have
-        * to hit the DB again for this item.
-        *
         * @param string  $cat           The category of the configuration value
         * @param string  $k             The configuration key to query
         * @param mixed   $default_value optional, The value to return if key is not set (default: null)
@@ -42,8 +31,6 @@ interface IConfigAdapter
        public function get($cat, $k, $default_value = null, $refresh = false);
 
        /**
-        * @brief Sets a configuration value for system config
-        *
         * Stores a config value ($value) in the category ($family) under the key ($key)
         * for the user_id $uid.
         *
@@ -58,9 +45,7 @@ interface IConfigAdapter
        public function set($cat, $k, $value);
 
        /**
-        * @brief Deletes the given key from the system configuration.
-        *
-        * Removes the configured value from the stored cache in $a->config
+        * Removes the configured value from the stored cache
         * and removes it from the database.
         *
         * @param string $cat The category of the configuration value
@@ -69,4 +54,11 @@ interface IConfigAdapter
         * @return mixed
         */
        public function delete($cat, $k);
+
+       /**
+        * Checks, if the current adapter is connected to the backend
+        *
+        * @return bool
+        */
+       public function isConnected();
 }
diff --git a/src/Core/Config/IConfigCache.php b/src/Core/Config/IConfigCache.php
new file mode 100644 (file)
index 0000000..898e3c0
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+/**
+ * The interface for a system-wide ConfigCache
+ */
+interface IConfigCache
+{
+       /**
+        * @param string $cat     Config category
+        * @param string $key       Config key
+        * @param mixed  $default Default value if it isn't set
+        *
+        * @return mixed Returns the value of the Config entry
+        */
+       function get($cat, $key = null, $default = null);
+
+       /**
+        * Sets a value in the config cache. Accepts raw output from the config table
+        *
+        * @param string $cat   Config category
+        * @param string $key   Config key
+        * @param mixed  $value Value to set
+        *
+        * @return bool True, if the value is set
+        */
+       function set($cat, $key, $value);
+
+       /**
+        * Deletes a value from the config cache
+        *
+        * @param string $cat  Config category
+        * @param string $key  Config key
+        */
+       function delete($cat, $key);
+
+       function getAll();
+}
index b912418432e1eb86d15005a4da20911b3cd84bbd..e62fc9c93fb83e0a427fcc638d05839229fb2c35 100644 (file)
@@ -15,10 +15,7 @@ namespace Friendica\Core\Config;
 interface IPConfigAdapter
 {
        /**
-        * @brief Loads all configuration values of a user's config family into a cached storage.
-        *
-        * All configuration values of the given user are stored in global cache
-        * which is available under the global variable $a->config[$uid].
+        * Loads all configuration values of a user's config family into a cached storage.
         *
         * @param string $uid The user_id
         * @param string $cat The category of the configuration value
@@ -28,12 +25,9 @@ interface IPConfigAdapter
        public function load($uid, $cat);
 
        /**
-        * @brief Get a particular user's config variable given the category name
+        * Get a particular user's config variable given the category name
         * ($family) and a key.
         *
-        * Get a particular user's config value from the given category ($family)
-        * and the $key from a cached storage in $a->config[$uid].
-        *
         * @param string  $uid           The user_id
         * @param string  $cat           The category of the configuration value
         * @param string  $k             The configuration key to query
@@ -45,8 +39,6 @@ interface IPConfigAdapter
        public function get($uid, $cat, $k, $default_value = null, $refresh = false);
 
        /**
-        * @brief Sets a configuration value for a user
-        *
         * Stores a config value ($value) in the category ($family) under the key ($key)
         * for the user_id $uid.
         *
@@ -62,9 +54,7 @@ interface IPConfigAdapter
        public function set($uid, $cat, $k, $value);
 
        /**
-        * @brief Deletes the given key from the users's configuration.
-        *
-        * Removes the configured value from the stored cache in $a->config[$uid]
+        * Removes the configured value from the stored cache
         * and removes it from the database.
         *
         * @param string $uid The user_id
diff --git a/src/Core/Config/IPConfigCache.php b/src/Core/Config/IPConfigCache.php
new file mode 100644 (file)
index 0000000..6a57daf
--- /dev/null
@@ -0,0 +1,44 @@
+<?php
+
+namespace Friendica\Core\Config;
+
+/**
+ * The interface for a user-specific config cache
+ */
+interface IPConfigCache
+{
+       /**
+        * Retrieves a value from the user config cache
+        *
+        * @param int    $uid     User Id
+        * @param string $cat     Config category
+        * @param string $key     Config key
+        * @param mixed  $default Default value if key isn't set
+        *
+        * @return string The value of the config entry
+        */
+       function getP($uid, $cat, $key = null, $default = null);
+
+       /**
+        * Sets a value in the user config cache
+        *
+        * Accepts raw output from the pconfig table
+        *
+        * @param int    $uid   User Id
+        * @param string $cat   Config category
+        * @param string $key   Config key
+        * @param mixed  $value Value to set
+        */
+       function setP($uid, $cat, $key, $value);
+
+       /**
+        * Deletes a value from the user config cache
+        *
+        * @param int    $uid User Id
+        * @param string $cat Config category
+        * @param string $key Config key
+        */
+       function deleteP($uid, $cat, $key);
+
+       function getAll();
+}
index ce1c13b2cae8b82b8e9e776064ac3027756dfece..76476be3aa4e8c94021658557a88dd13343d1369 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 namespace Friendica\Core\Config;
 
-use Friendica\BaseObject;
 use Friendica\Database\DBA;
 
 /**
@@ -11,13 +10,33 @@ use Friendica\Database\DBA;
  *
  * @author Hypolite Petovan <hypolite@mrpetovan.com>
  */
-class JITConfigAdapter extends BaseObject implements IConfigAdapter
+class JITConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter
 {
        private $cache;
        private $in_db;
 
+       /**
+        * @var IConfigCache The config cache of this driver
+        */
+       private $configCache;
+
+       /**
+        * @param IConfigCache $configCache The config cache of this driver
+        */
+       public function __construct(IConfigCache $configCache)
+       {
+               $this->configCache = $configCache;
+       }
+
+       /**
+        * {@inheritdoc}
+        */
        public function load($cat = "config")
        {
+               if (!$this->isConnected()) {
+                       return;
+               }
+
                // We don't preload "system" anymore.
                // This reduces the number of database reads a lot.
                if ($cat === 'system') {
@@ -28,7 +47,7 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                while ($config = DBA::fetch($configs)) {
                        $k = $config['k'];
 
-                       self::getApp()->setConfigValue($cat, $k, $config['v']);
+                       $this->configCache->set($cat, $k, $config['v']);
 
                        if ($cat !== 'config') {
                                $this->cache[$cat][$k] = $config['v'];
@@ -38,9 +57,14 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                DBA::close($configs);
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function get($cat, $k, $default_value = null, $refresh = false)
        {
-               $a = self::getApp();
+               if (!$this->isConnected()) {
+                       return $default_value;
+               }
 
                if (!$refresh) {
                        // Do we have the cached value? Then return it
@@ -62,18 +86,18 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                        $this->cache[$cat][$k] = $value;
                        $this->in_db[$cat][$k] = true;
                        return $value;
-               } elseif (isset($a->config[$cat][$k])) {
+               } elseif ($this->configCache->get($cat, $k) !== null) {
                        // Assign the value (mostly) from config/local.config.php file to the cache
-                       $this->cache[$cat][$k] = $a->config[$cat][$k];
+                       $this->cache[$cat][$k] = $this->configCache->get($cat, $k);
                        $this->in_db[$cat][$k] = false;
 
-                       return $a->config[$cat][$k];
-               } elseif (isset($a->config[$k])) {
+                       return $this->configCache->get($cat, $k);
+               } elseif ($this->configCache->get('config', $k) !== null) {
                        // Assign the value (mostly) from config/local.config.php file to the cache
-                       $this->cache[$k] = $a->config[$k];
+                       $this->cache[$k] = $this->configCache->get('config', $k);
                        $this->in_db[$k] = false;
 
-                       return $a->config[$k];
+                       return $this->configCache->get('config', $k);
                }
 
                $this->cache[$cat][$k] = '!<unset>!';
@@ -82,8 +106,15 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                return $default_value;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function set($cat, $k, $value)
        {
+               if (!$this->isConnected()) {
+                       return false;
+               }
+
                // We store our setting values in a string variable.
                // So we have to do the conversion here so that the compare below works.
                // The exception are array values.
@@ -102,7 +133,7 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                        return true;
                }
 
-               self::getApp()->setConfigValue($cat, $k, $value);
+               $this->configCache->set($cat, $k, $value);
 
                // Assign the just added value to the cache
                $this->cache[$cat][$k] = $dbvalue;
@@ -119,8 +150,15 @@ class JITConfigAdapter extends BaseObject implements IConfigAdapter
                return $result;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function delete($cat, $k)
        {
+               if (!$this->isConnected()) {
+                       return false;
+               }
+
                if (isset($this->cache[$cat][$k])) {
                        unset($this->cache[$cat][$k]);
                        unset($this->in_db[$cat][$k]);
index bdaca407ffa583f88aa0c5c36a2dcf0fb4275471..b1a15601cc4cdaa28efbd7687c7c0aeb65e65097 100644 (file)
@@ -1,7 +1,6 @@
 <?php
 namespace Friendica\Core\Config;
 
-use Friendica\BaseObject;
 use Friendica\Database\DBA;
 
 /**
@@ -11,47 +10,63 @@ use Friendica\Database\DBA;
  *
  * @author Hypolite Petovan <hypolite@mrpetovan.com>
  */
-class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
+class JITPConfigAdapter implements IPConfigAdapter
 {
        private $in_db;
 
-       public function load($uid, $cat)
+       /**
+        * The config cache of this adapter
+        * @var IPConfigCache
+        */
+       private $configCache;
+
+       /**
+        * @param IPConfigCache $configCache The config cache of this adapter
+        */
+       public function __construct(IPConfigCache $configCache)
        {
-               $a = self::getApp();
+               $this->configCache = $configCache;
+       }
 
+       /**
+        * {@inheritdoc}
+        */
+       public function load($uid, $cat)
+       {
                $pconfigs = DBA::select('pconfig', ['v', 'k'], ['cat' => $cat, 'uid' => $uid]);
                if (DBA::isResult($pconfigs)) {
                        while ($pconfig = DBA::fetch($pconfigs)) {
                                $k = $pconfig['k'];
 
-                               self::getApp()->setPConfigValue($uid, $cat, $k, $pconfig['v']);
+                               $this->configCache->setP($uid, $cat, $k, $pconfig['v']);
 
                                $this->in_db[$uid][$cat][$k] = true;
                        }
                } else if ($cat != 'config') {
                        // Negative caching
-                       $a->config[$uid][$cat] = "!<unset>!";
+                       $this->configCache->setP($uid, $cat, null, "!<unset>!");
                }
                DBA::close($pconfigs);
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function get($uid, $cat, $k, $default_value = null, $refresh = false)
        {
-               $a = self::getApp();
-
                if (!$refresh) {
                        // Looking if the whole family isn't set
-                       if (isset($a->config[$uid][$cat])) {
-                               if ($a->config[$uid][$cat] === '!<unset>!') {
+                       if ($this->configCache->getP($uid, $cat) !== null) {
+                               if ($this->configCache->getP($uid, $cat) === '!<unset>!') {
                                        return $default_value;
                                }
                        }
 
-                       if (isset($a->config[$uid][$cat][$k])) {
-                               if ($a->config[$uid][$cat][$k] === '!<unset>!') {
+                       if ($this->configCache->getP($uid, $cat, $k) !== null) {
+                               if ($this->configCache->getP($uid, $cat, $k) === '!<unset>!') {
                                        return $default_value;
                                }
-                               return $a->config[$uid][$cat][$k];
+                               return $this->configCache->getP($uid, $cat, $k);
                        }
                }
 
@@ -59,13 +74,13 @@ class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
                if (DBA::isResult($pconfig)) {
                        $val = (preg_match("|^a:[0-9]+:{.*}$|s", $pconfig['v']) ? unserialize($pconfig['v']) : $pconfig['v']);
 
-                       self::getApp()->setPConfigValue($uid, $cat, $k, $val);
+                       $this->configCache->setP($uid, $cat, $k, $val);
 
                        $this->in_db[$uid][$cat][$k] = true;
 
                        return $val;
                } else {
-                       self::getApp()->setPConfigValue($uid, $cat, $k, '!<unset>!');
+                       $this->configCache->setP($uid, $cat, $k, '!<unset>!');
 
                        $this->in_db[$uid][$cat][$k] = false;
 
@@ -73,6 +88,9 @@ class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
                }
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function set($uid, $cat, $k, $value)
        {
                // We store our setting values in a string variable.
@@ -86,7 +104,7 @@ class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
                        return true;
                }
 
-               self::getApp()->setPConfigValue($uid, $cat, $k, $value);
+               $this->configCache->setP($uid, $cat, $k, $value);
 
                // manage array value
                $dbvalue = (is_array($value) ? serialize($value) : $dbvalue);
@@ -100,9 +118,12 @@ class JITPConfigAdapter extends BaseObject implements IPConfigAdapter
                return $result;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function delete($uid, $cat, $k)
        {
-               self::getApp()->deletePConfigValue($uid, $cat, $k);
+               $this->configCache->deleteP($uid, $cat, $k);
 
                if (!empty($this->in_db[$uid][$cat][$k])) {
                        unset($this->in_db[$uid][$cat][$k]);
index ac59d945580ca6d6e55ffebda4fdff8a042a5c4a..2fe3d4cdad61ed213e283d0b31d10467fdd15405 100644 (file)
@@ -3,7 +3,6 @@
 namespace Friendica\Core\Config;
 
 use Exception;
-use Friendica\BaseObject;
 use Friendica\Database\DBA;
 
 /**
@@ -13,56 +12,86 @@ use Friendica\Database\DBA;
  *
  * @author Hypolite Petovan <hypolite@mrpetovan.com>
  */
-class PreloadConfigAdapter extends BaseObject implements IConfigAdapter
+class PreloadConfigAdapter extends AbstractDbaConfigAdapter implements IConfigAdapter
 {
        private $config_loaded = false;
 
-       public function __construct()
+       /**
+        * @var IConfigCache The config cache of this driver
+        */
+       private $configCache;
+
+       /**
+        * @param IConfigCache $configCache The config cache of this driver
+        */
+       public function __construct(IConfigCache $configCache)
        {
+               $this->configCache = $configCache;
                $this->load();
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function load($family = 'config')
        {
+               if (!$this->isConnected()) {
+                       return;
+               }
+
                if ($this->config_loaded) {
                        return;
                }
 
                $configs = DBA::select('config', ['cat', 'v', 'k']);
                while ($config = DBA::fetch($configs)) {
-                       self::getApp()->setConfigValue($config['cat'], $config['k'], $config['v']);
+                       $this->configCache->set($config['cat'], $config['k'], $config['v']);
                }
                DBA::close($configs);
 
                $this->config_loaded = true;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function get($cat, $k, $default_value = null, $refresh = false)
        {
+               if (!$this->isConnected()) {
+                       return $default_value;
+               }
+
                if ($refresh) {
                        $config = DBA::selectFirst('config', ['v'], ['cat' => $cat, 'k' => $k]);
                        if (DBA::isResult($config)) {
-                               self::getApp()->setConfigValue($cat, $k, $config['v']);
+                               $this->configCache->set($cat, $k, $config['v']);
                        }
                }
 
-               $return = self::getApp()->getConfigValue($cat, $k, $default_value);
+               $return = $this->configCache->get($cat, $k, $default_value);
 
                return $return;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function set($cat, $k, $value)
        {
+               if (!$this->isConnected()) {
+                       return false;
+               }
+
                // We store our setting values as strings.
                // So we have to do the conversion here so that the compare below works.
                // The exception are array values.
                $compare_value = !is_array($value) ? (string)$value : $value;
 
-               if (self::getApp()->getConfigValue($cat, $k) === $compare_value) {
+               if ($this->configCache->get($cat, $k) === $compare_value) {
                        return true;
                }
 
-               self::getApp()->setConfigValue($cat, $k, $value);
+               $this->configCache->set($cat, $k, $value);
 
                // manage array value
                $dbvalue = is_array($value) ? serialize($value) : $value;
@@ -75,9 +104,16 @@ class PreloadConfigAdapter extends BaseObject implements IConfigAdapter
                return true;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function delete($cat, $k)
        {
-               self::getApp()->deleteConfigValue($cat, $k);
+               if (!$this->isConnected()) {
+                       return false;
+               }
+
+               $this->configCache->delete($cat, $k);
 
                $result = DBA::delete('config', ['cat' => $cat, 'k' => $k]);
 
index 6658efa3f67f571e2d1c93f60aa17bb67392a8ba..af97815adef38d159ab6ba44f27766d10ed7ca9d 100644 (file)
@@ -3,7 +3,6 @@
 namespace Friendica\Core\Config;
 
 use Exception;
-use Friendica\BaseObject;
 use Friendica\Database\DBA;
 
 /**
@@ -13,15 +12,31 @@ use Friendica\Database\DBA;
  *
  * @author Hypolite Petovan <hypolite@mrpetovan.com>
  */
-class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
+class PreloadPConfigAdapter implements IPConfigAdapter
 {
        private $config_loaded = false;
 
-       public function __construct($uid)
+       /**
+        * The config cache of this adapter
+        * @var IPConfigCache
+        */
+       private $configCache;
+
+       /**
+        * @param IPConfigCache $configCache The config cache of this adapter
+        * @param int           $uid    The UID of the current user
+        */
+       public function __construct(IPConfigCache $configCache, $uid = null)
        {
-               $this->load($uid, 'config');
+               $this->configCache = $configCache;
+               if (isset($uid)) {
+                       $this->load($uid, 'config');
+               }
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function load($uid, $family)
        {
                if ($this->config_loaded) {
@@ -34,13 +49,16 @@ class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
 
                $pconfigs = DBA::select('pconfig', ['cat', 'v', 'k'], ['uid' => $uid]);
                while ($pconfig = DBA::fetch($pconfigs)) {
-                       self::getApp()->setPConfigValue($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']);
+                       $this->configCache->setP($uid, $pconfig['cat'], $pconfig['k'], $pconfig['v']);
                }
                DBA::close($pconfigs);
 
                $this->config_loaded = true;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function get($uid, $cat, $k, $default_value = null, $refresh = false)
        {
                if (!$this->config_loaded) {
@@ -50,17 +68,18 @@ class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
                if ($refresh) {
                        $config = DBA::selectFirst('pconfig', ['v'], ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
                        if (DBA::isResult($config)) {
-                               self::getApp()->setPConfigValue($uid, $cat, $k, $config['v']);
+                               $this->configCache->setP($uid, $cat, $k, $config['v']);
                        } else {
-                               self::getApp()->deletePConfigValue($uid, $cat, $k);
+                               $this->configCache->deleteP($uid, $cat, $k);
                        }
                }
 
-               $return = self::getApp()->getPConfigValue($uid, $cat, $k, $default_value);
-
-               return $return;
+               return $this->configCache->getP($uid, $cat, $k, $default_value);;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function set($uid, $cat, $k, $value)
        {
                if (!$this->config_loaded) {
@@ -71,11 +90,11 @@ class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
                // The exception are array values.
                $compare_value = !is_array($value) ? (string)$value : $value;
 
-               if (self::getApp()->getPConfigValue($uid, $cat, $k) === $compare_value) {
+               if ($this->configCache->getP($uid, $cat, $k) === $compare_value) {
                        return true;
                }
 
-               self::getApp()->setPConfigValue($uid, $cat, $k, $value);
+               $this->configCache->setP($uid, $cat, $k, $value);
 
                // manage array value
                $dbvalue = is_array($value) ? serialize($value) : $value;
@@ -88,13 +107,16 @@ class PreloadPConfigAdapter extends BaseObject implements IPConfigAdapter
                return true;
        }
 
+       /**
+        * {@inheritdoc}
+        */
        public function delete($uid, $cat, $k)
        {
                if (!$this->config_loaded) {
                        $this->load($uid, $cat);
                }
 
-               self::getApp()->deletePConfigValue($uid, $cat, $k);
+               $this->configCache->deleteP($uid, $cat, $k);
 
                $result = DBA::delete('pconfig', ['uid' => $uid, 'cat' => $cat, 'k' => $k]);
 
index c38dab9806b5d22a21dadcac9fc01ac73bb7e072..da078545e3eb0dd4c52c98ab0f329ca83fc1dd59 100644 (file)
@@ -100,10 +100,10 @@ HELP;
                                }
                        }
 
-                       $db_host = $a->getConfigValue('database', 'hostname');
-                       $db_user = $a->getConfigValue('database', 'username');
-                       $db_pass = $a->getConfigValue('database', 'password');
-                       $db_data = $a->getConfigValue('database', 'database');
+                       $db_host = $a->getConfig()->get('database', 'hostname');
+                       $db_user = $a->getConfig()->get('database', 'username');
+                       $db_pass = $a->getConfig()->get('database', 'password');
+                       $db_data = $a->getConfig()->get('database', 'database');
                } else {
                        // Creating config file
                        $this->out("Creating config file...\n");
@@ -158,7 +158,7 @@ HELP;
 
                $installer->resetChecks();
 
-               if (!$installer->installDatabase()) {
+               if (!$installer->installDatabase($a->getBasePath())) {
                        $errorMessage = $this->extractErrors($installer->getChecks());
                        throw new RuntimeException($errorMessage);
                }
index ce367fffbf748785c4f81dbc1c46ef341d4bdfc5..b1c3df54e024fc59738c259c9cddf0b54327292b 100644 (file)
@@ -113,7 +113,7 @@ HELP;
 
                        if (is_array($value)) {
                                foreach ($value as $k => $v) {
-                                       $this->out("{$cat}.{$key}[{$k}] => " . $v);
+                                       $this->out("{$cat}.{$key}[{$k}] => " . (is_array($v) ? implode(', ', $v) : $v));
                                }
                        } else {
                                $this->out("{$cat}.{$key} => " . $value);
@@ -124,12 +124,13 @@ HELP;
                        $cat = $this->getArgument(0);
                        Core\Config::load($cat);
 
-                       if (!is_null($a->config[$cat])) {
+                       if ($a->getConfig()->get($cat) !== null) {
                                $this->out("[{$cat}]");
-                               foreach ($a->config[$cat] as $key => $value) {
+                               $catVal = $a->getConfig()->get($cat);
+                               foreach ($catVal as $key => $value) {
                                        if (is_array($value)) {
                                                foreach ($value as $k => $v) {
-                                                       $this->out("{$key}[{$k}] => " . $v);
+                                                       $this->out("{$key}[{$k}] => " . (is_array($v) ? implode(', ', $v) : $v));
                                                }
                                        } else {
                                                $this->out("{$key} => " . $value);
@@ -147,12 +148,13 @@ HELP;
                                $this->out('Warning: The JIT (Just In Time) Config adapter doesn\'t support loading the entire configuration, showing file config only');
                        }
 
-                       foreach ($a->config as $cat => $section) {
+                       $config = $a->getConfig()->getAll();
+                       foreach ($config as $cat => $section) {
                                if (is_array($section)) {
                                        foreach ($section as $key => $value) {
                                                if (is_array($value)) {
                                                        foreach ($value as $k => $v) {
-                                                               $this->out("{$cat}.{$key}[{$k}] => " . $v);
+                                                               $this->out("{$cat}.{$key}[{$k}] => " . (is_array($v) ? implode(', ', $v) : $v));
                                                        }
                                                } else {
                                                        $this->out("{$cat}.{$key} => " . $value);
index 724feea5e44f947e2178ef47915bc22ce257360c..1ec108d2e61cfd2f0d5d2fc07502b733ba3843b3 100644 (file)
@@ -61,17 +61,19 @@ HELP;
 
                Core\Config::load();
 
+               $a = get_app();
+
                switch ($this->getArgument(0)) {
                        case "dryrun":
-                               $output = DBStructure::update(true, false);
+                               $output = DBStructure::update($a->getBasePath(), true, false);
                                break;
                        case "update":
                                $force = $this->getOption(['f', 'force'], false);
-                               $output = Update::run($force, true, false);
+                               $output = Update::run($a->getBasePath(), $force, true, false);
                                break;
                        case "dumpsql":
                                ob_start();
-                               DBStructure::printStructure();
+                               DBStructure::printStructure($a->getBasePath());
                                $output = ob_get_clean();
                                break;
                        case "toinnodb":
index 17ed231f978f1d3a0e365bf738c84e472483ffc1..103d0fef7e68062928cdfd79100234a8dfd0187d 100644 (file)
@@ -2,8 +2,8 @@
 
 namespace Friendica\Core\Console;
 
-use Friendica\Core\L10n;
 use Friendica\Core\Config;
+use Friendica\Core\L10n;
 use Friendica\Core\Update;
 
 /**
@@ -56,7 +56,7 @@ HELP;
                }
 
                echo L10n::t('Check for pending update actions.') . "\n";
-               Update::run(true, true, false);
+               Update::run($a->getBasePath(), true, true, false);
                echo L10n::t('Done.') . "\n";
 
                echo L10n::t('Execute pending post updates.') . "\n";
index 82357b475397ded28ae9e41ff5794cdea5af970a..32ba6ded35186878bea79e9bf8cac456c8444172 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace Friendica\Core\Console;
 
+use Friendica\BaseObject;
+
 /**
  * Tired of chasing typos and finding them after a commit.
  * Run this and quickly see if we've got any parse errors in our application files.
@@ -41,9 +43,7 @@ HELP;
                        throw new \Asika\SimpleConsole\CommandArgsException('Too many arguments');
                }
 
-               $a = \get_app();
-
-               $php_path = $a->getConfigValue('config', 'php_path', 'php');
+               $php_path = BaseObject::getApp()->getConfig()->get('config', 'php_path', 'php');
 
                if ($this->getOption('v')) {
                        $this->out('Directory: src');
index a487aec78b34ee24053c53c58d2b6e46c7999f80..7e79f29f153dd6efe81efc50993c19106c916032 100644 (file)
@@ -168,12 +168,14 @@ class Installer
        /***
         * Installs the DB-Scheme for Friendica
         *
+        * @param string $basePath The base path of this application
+        *
         * @return bool true if the installation was successful, otherwise false
         * @throws Exception
         */
-       public function installDatabase()
+       public function installDatabase($basePath)
        {
-               $result = DBStructure::update(false, true, true);
+               $result = DBStructure::update($basePath, false, true, true);
 
                if ($result) {
                        $txt = L10n::t('You may need to import the file "database.sql" manually using phpmyadmin or mysql.') . EOL;
index 277a0916417aa562bf260b7f62dad1e7a9c626ef..6b8112796f557f00dea156a9c22fef3993097584 100644 (file)
@@ -5,8 +5,8 @@
 namespace Friendica\Core;
 
 use Friendica\BaseObject;
+use Friendica\Factory\LoggerFactory;
 use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Util\LoggerFactory;
 use Psr\Log\LoggerInterface;
 use Psr\Log\LogLevel;
 
index 584318adbc65a4c0efa1368940d1d1961f15e649..df024f0f3426dfa84abc665789066a1c6427316a 100644 (file)
@@ -8,9 +8,6 @@
  */
 namespace Friendica\Core;
 
-use Friendica\App;
-use Friendica\BaseObject;
-
 /**
  * @brief Management of user configuration storage
  * Note:
@@ -18,52 +15,55 @@ use Friendica\BaseObject;
  * The PConfig::get() functions return boolean false for keys that are unset,
  * and this could lead to subtle bugs.
  */
-class PConfig extends BaseObject
+class PConfig
 {
        /**
-        * @var \Friendica\Core\Config\IPConfigAdapter
+        * @var Config\IPConfigAdapter
         */
-       private static $adapter = null;
+       private static $adapter;
 
-       public static function init($uid)
-       {
-               $a = self::getApp();
+       /**
+        * @var Config\IPConfigCache
+        */
+       private static $cache;
 
-               // Database isn't ready or populated yet
-               if (!$a->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return;
-               }
+       /**
+        * Initialize the config with only the cache
+        *
+        * @param Config\IPConfigCache $cache  The configuration cache
+        */
+       public static function init(Config\IPConfigCache $cache)
+       {
+               self::$cache  = $cache;
+       }
 
-               if ($a->getConfigValue('system', 'config_adapter') == 'preload') {
-                       self::$adapter = new Config\PreloadPConfigAdapter($uid);
-               } else {
-                       self::$adapter = new Config\JITPConfigAdapter();
-               }
+       /**
+        * Add the adapter for DB-backend
+        *
+        * @param Config\IPConfigAdapter $adapter
+        */
+       public static function setAdapter(Config\IPConfigAdapter $adapter)
+       {
+               self::$adapter = $adapter;
        }
 
        /**
         * @brief Loads all configuration values of a user's config family into a cached storage.
         *
-        * All configuration values of the given user are stored in global cache
-        * which is available under the global variable $a->config[$uid].
+        * All configuration values of the given user are stored with the $uid in
+        * the cache ( @see IPConfigCache )
         *
         * @param string $uid    The user_id
         * @param string $family The category of the configuration value
         *
         * @return void
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function load($uid, $family)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
+               if (!isset(self::$adapter)) {
                        return;
                }
 
-               if (empty(self::$adapter)) {
-                       self::init($uid);
-               }
-
                self::$adapter->load($uid, $family);
        }
 
@@ -72,7 +72,8 @@ class PConfig extends BaseObject
         * ($family) and a key.
         *
         * Get a particular user's config value from the given category ($family)
-        * and the $key from a cached storage in $a->config[$uid].
+        * and the $key with the $uid from a cached storage either from the self::$adapter
+        * (@see IConfigAdapter ) or from the static::$cache (@see IConfigCache ).
         *
         * @param string  $uid           The user_id
         * @param string  $family        The category of the configuration value
@@ -81,17 +82,11 @@ class PConfig extends BaseObject
         * @param boolean $refresh       optional, If true the config is loaded from the db and not from the cache (default: false)
         *
         * @return mixed Stored value or null if it does not exist
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function get($uid, $family, $key, $default_value = null, $refresh = false)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return;
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init($uid);
+               if (!isset(self::$adapter)) {
+                       return self::$cache->getP($uid, $family, $key, $default_value);
                }
 
                return self::$adapter->get($uid, $family, $key, $default_value, $refresh);
@@ -111,17 +106,11 @@ class PConfig extends BaseObject
         * @param mixed  $value  The value to store
         *
         * @return bool Operation success
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function set($uid, $family, $key, $value)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return false;
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init($uid);
+               if (!isset(self::$adapter)) {
+                       return self::$cache->setP($uid, $family, $key, $value);
                }
 
                return self::$adapter->set($uid, $family, $key, $value);
@@ -130,25 +119,20 @@ class PConfig extends BaseObject
        /**
         * @brief Deletes the given key from the users's configuration.
         *
-        * Removes the configured value from the stored cache in $a->config[$uid]
-        * and removes it from the database.
+        * Removes the configured value from the stored cache in self::$config
+        * (@see ConfigCache ) and removes it from the database (@see IConfigAdapter )
+        * with the given $uid.
         *
         * @param string $uid    The user_id
         * @param string $family The category of the configuration value
         * @param string $key    The configuration key to delete
         *
         * @return mixed
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function delete($uid, $family, $key)
        {
-               // Database isn't ready or populated yet
-               if (!self::getApp()->getMode()->has(App\Mode::DBCONFIGAVAILABLE)) {
-                       return false;
-               }
-
-               if (empty(self::$adapter)) {
-                       self::init($uid);
+               if (!isset(self::$adapter)) {
+                       return self::$cache->deleteP($uid, $family, $key);
                }
 
                return self::$adapter->delete($uid, $family, $key);
index ba3d03e6229f283e3d0d7250366869f144e8caeb..f0ed08357359f5cc75f1d3a77652085e35ca657e 100644 (file)
@@ -286,6 +286,61 @@ class System extends BaseObject
                exit();
        }
 
+       /**
+        * @brief Returns the system user that is executing the script
+        *
+        * This mostly returns something like "www-data".
+        *
+        * @return string system username
+        */
+       public static function getUser()
+       {
+               if (!function_exists('posix_getpwuid') || !function_exists('posix_geteuid')) {
+                       return '';
+               }
+
+               $processUser = posix_getpwuid(posix_geteuid());
+               return $processUser['name'];
+       }
+
+       /**
+        * @brief Checks if a given directory is usable for the system
+        *
+        * @param      $directory
+        * @param bool $check_writable
+        *
+        * @return boolean the directory is usable
+        */
+       public static function isDirectoryUsable($directory, $check_writable = true)
+       {
+               if ($directory == '') {
+                       Logger::log('Directory is empty. This shouldn\'t happen.', Logger::DEBUG);
+                       return false;
+               }
+
+               if (!file_exists($directory)) {
+                       Logger::log('Path "' . $directory . '" does not exist for user ' . static::getUser(), Logger::DEBUG);
+                       return false;
+               }
+
+               if (is_file($directory)) {
+                       Logger::log('Path "' . $directory . '" is a file for user ' . static::getUser(), Logger::DEBUG);
+                       return false;
+               }
+
+               if (!is_dir($directory)) {
+                       Logger::log('Path "' . $directory . '" is not a directory for user ' . static::getUser(), Logger::DEBUG);
+                       return false;
+               }
+
+               if ($check_writable && !is_writable($directory)) {
+                       Logger::log('Path "' . $directory . '" is not writable for user ' . static::getUser(), Logger::DEBUG);
+                       return false;
+               }
+
+               return true;
+       }
+
        /// @todo Move the following functions from boot.php
        /*
        function killme()
index 368f5f55cc777bb28ab38f8fa440116ff50bfafa..5df0675cdaeb46cd7520468d693e79f349b034c2 100644 (file)
@@ -14,10 +14,11 @@ class Update
        /**
         * @brief Function to check if the Database structure needs an update.
         *
+        * @param string $basePath The base path of this application
         * @param boolean $via_worker boolean Is the check run via the worker?
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       public static function check($via_worker)
+       public static function check($basePath, $via_worker)
        {
                if (!DBA::connected()) {
                        return;
@@ -38,7 +39,7 @@ class Update
                if ($build < DB_UPDATE_VERSION) {
                        // When we cannot execute the database update via the worker, we will do it directly
                        if (!Worker::add(PRIORITY_CRITICAL, 'DBUpdate') && $via_worker) {
-                               self::run();
+                               self::run($basePath);
                        }
                }
        }
@@ -46,14 +47,15 @@ class Update
        /**
         * Automatic database updates
         *
-        * @param bool $force    Force the Update-Check even if the lock is set
-        * @param bool $verbose  Run the Update-Check verbose
-        * @param bool $sendMail Sends a Mail to the administrator in case of success/failure
+        * @param string $basePath The base path of this application
+        * @param bool $force      Force the Update-Check even if the lock is set
+        * @param bool $verbose    Run the Update-Check verbose
+        * @param bool $sendMail   Sends a Mail to the administrator in case of success/failure
         *
         * @return string Empty string if the update is successful, error messages otherwise
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       public static function run($force = false, $verbose = false, $sendMail = true)
+       public static function run($basePath, $force = false, $verbose = false, $sendMail = true)
        {
                // In force mode, we release the dbupdate lock first
                // Necessary in case of an stuck update
@@ -91,7 +93,7 @@ class Update
                                        }
 
                                        // update the structure in one call
-                                       $retval = DBStructure::update($verbose, true);
+                                       $retval = DBStructure::update($basePath, $verbose, true);
                                        if ($retval) {
                                                if ($sendMail) {
                                                        self::updateFailed(
@@ -125,7 +127,7 @@ class Update
                                }
                        }
                } elseif ($force) {
-                       DBStructure::update($verbose, true);
+                       DBStructure::update($basePath, $verbose, true);
                }
 
                return '';
index e14d94ce04fc8cb5acb7e8fae7fb6965a66ece8d..d2a739e9313aa07b9be4aaf948bab6398c1930e2 100644 (file)
@@ -2,10 +2,7 @@
 
 namespace Friendica\Database;
 
-// Do not use Core\Config in this class at risk of infinite loop.
-// Please use App->getConfigVariable() instead.
-//use Friendica\Core\Config;
-
+use Friendica\Core\Config\IConfigCache;
 use Friendica\Core\Logger;
 use Friendica\Core\System;
 use Friendica\Util\DateTimeFormat;
@@ -34,6 +31,10 @@ class DBA
 
        public static $connected = false;
 
+       /**
+        * @var IConfigCache
+        */
+       private static $configCache;
        private static $server_info = '';
        private static $connection;
        private static $driver;
@@ -49,13 +50,14 @@ class DBA
        private static $db_name = '';
        private static $db_charset = '';
 
-       public static function connect($serveraddr, $user, $pass, $db, $charset = null)
+       public static function connect($configCache, $serveraddr, $user, $pass, $db, $charset = null)
        {
                if (!is_null(self::$connection) && self::connected()) {
                        return true;
                }
 
                // We are storing these values for being able to perform a reconnect
+               self::$configCache = $configCache;
                self::$db_serveraddr = $serveraddr;
                self::$db_user = $user;
                self::$db_pass = $pass;
@@ -156,7 +158,7 @@ class DBA
        public static function reconnect() {
                self::disconnect();
 
-               $ret = self::connect(self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset);
+               $ret = self::connect(self::$configCache, self::$db_serveraddr, self::$db_user, self::$db_pass, self::$db_name, self::$db_charset);
                return $ret;
        }
 
@@ -210,9 +212,8 @@ class DBA
         * @throws \Exception
         */
        private static function logIndex($query) {
-               $a = \get_app();
 
-               if (!$a->getConfigVariable('system', 'db_log_index')) {
+               if (!self::$configCache->get('system', 'db_log_index')) {
                        return;
                }
 
@@ -231,18 +232,18 @@ class DBA
                        return;
                }
 
-               $watchlist = explode(',', $a->getConfigVariable('system', 'db_log_index_watch'));
-               $blacklist = explode(',', $a->getConfigVariable('system', 'db_log_index_blacklist'));
+               $watchlist = explode(',', self::$configCache->get('system', 'db_log_index_watch'));
+               $blacklist = explode(',', self::$configCache->get('system', 'db_log_index_blacklist'));
 
                while ($row = self::fetch($r)) {
-                       if ((intval($a->getConfigVariable('system', 'db_loglimit_index')) > 0)) {
+                       if ((intval(self::$configCache->get('system', 'db_loglimit_index')) > 0)) {
                                $log = (in_array($row['key'], $watchlist) &&
-                                       ($row['rows'] >= intval($a->getConfigVariable('system', 'db_loglimit_index'))));
+                                       ($row['rows'] >= intval(self::$configCache->get('system', 'db_loglimit_index'))));
                        } else {
                                $log = false;
                        }
 
-                       if ((intval($a->getConfigVariable('system', 'db_loglimit_index_high')) > 0) && ($row['rows'] >= intval($a->getConfigVariable('system', 'db_loglimit_index_high')))) {
+                       if ((intval(self::$configCache->get('system', 'db_loglimit_index_high')) > 0) && ($row['rows'] >= intval(self::$configCache->get('system', 'db_loglimit_index_high')))) {
                                $log = true;
                        }
 
@@ -252,7 +253,7 @@ class DBA
 
                        if ($log) {
                                $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-                               @file_put_contents($a->getConfigVariable('system', 'db_log_index'), DateTimeFormat::utcNow()."\t".
+                               @file_put_contents(self::$configCache->get('system', 'db_log_index'), DateTimeFormat::utcNow()."\t".
                                                $row['key']."\t".$row['rows']."\t".$row['Extra']."\t".
                                                basename($backtrace[1]["file"])."\t".
                                                $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
@@ -422,7 +423,7 @@ class DBA
 
                $orig_sql = $sql;
 
-               if ($a->getConfigValue('system', 'db_callstack')) {
+               if (self::$configCache->get('system', 'db_callstack') !== null) {
                        $sql = "/*".System::callstack()." */ ".$sql;
                }
 
@@ -583,15 +584,15 @@ class DBA
 
                $a->saveTimestamp($stamp1, 'database');
 
-               if ($a->getConfigValue('system', 'db_log')) {
+               if (self::$configCache->get('system', 'db_log')) {
                        $stamp2 = microtime(true);
                        $duration = (float)($stamp2 - $stamp1);
 
-                       if (($duration > $a->getConfigValue('system', 'db_loglimit'))) {
+                       if (($duration > self::$configCache->get('system', 'db_loglimit'))) {
                                $duration = round($duration, 3);
                                $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
 
-                               @file_put_contents($a->getConfigValue('system', 'db_log'), DateTimeFormat::utcNow()."\t".$duration."\t".
+                               @file_put_contents(self::$configCache->get('system', 'db_log'), DateTimeFormat::utcNow()."\t".$duration."\t".
                                                basename($backtrace[1]["file"])."\t".
                                                $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t".
                                                substr(self::replaceParameters($sql, $args), 0, 2000)."\n", FILE_APPEND);
@@ -1030,7 +1031,7 @@ class DBA
         * This process must only be started once, since the value is cached.
         */
        private static function buildRelationData() {
-               $definition = DBStructure::definition();
+               $definition = DBStructure::definition(self::$configCache->get('system', 'basepath'));
 
                foreach ($definition AS $table => $structure) {
                        foreach ($structure['fields'] AS $field => $field_struct) {
index 7d52e60caec13d53a0b0b811754932c76ca121ed..75a5c86241fc63497e3c9fa4f31a73b07eed5733 100644 (file)
@@ -74,9 +74,9 @@ class DBStructure
                return L10n::t('Errors encountered performing database changes: ') . $message . EOL;
        }
 
-       public static function printStructure()
+       public static function printStructure($basePath)
        {
-               $database = self::definition(false);
+               $database = self::definition($basePath, false);
 
                echo "-- ------------------------------------------\n";
                echo "-- " . FRIENDICA_PLATFORM . " " . FRIENDICA_VERSION . " (" . FRIENDICA_CODENAME, ")\n";
@@ -98,15 +98,15 @@ class DBStructure
         *
         * @see config/dbstructure.config.php
         * @param boolean $with_addons_structure Whether to tack on addons additional tables
+        * @param string  $basePath              The base path of this application
         * @return array
         * @throws Exception
         */
-       public static function definition($with_addons_structure = true)
+       public static function definition($basePath, $with_addons_structure = true)
        {
                if (!self::$definition) {
-                       $a = \Friendica\BaseObject::getApp();
 
-                       $filename = $a->getBasePath() . '/config/dbstructure.config.php';
+                       $filename = $basePath . '/config/dbstructure.config.php';
 
                        if (!is_readable($filename)) {
                                throw new Exception('Missing database structure config file config/dbstructure.config.php');
@@ -247,15 +247,16 @@ class DBStructure
        /**
         * Updates DB structure and returns eventual errors messages
         *
-        * @param bool  $verbose
-        * @param bool  $action     Whether to actually apply the update
-        * @param bool  $install    Is this the initial update during the installation?
-        * @param array $tables     An array of the database tables
-        * @param array $definition An array of the definition tables
+        * @param string $basePath   The base path of this application
+        * @param bool   $verbose
+        * @param bool   $action     Whether to actually apply the update
+        * @param bool   $install    Is this the initial update during the installation?
+        * @param array  $tables     An array of the database tables
+        * @param array  $definition An array of the definition tables
         * @return string Empty string if the update is successful, error messages otherwise
         * @throws Exception
         */
-       public static function update($verbose, $action, $install = false, array $tables = null, array $definition = null)
+       public static function update($basePath, $verbose, $action, $install = false, array $tables = null, array $definition = null)
        {
                if ($action && !$install) {
                        Config::set('system', 'maintenance', 1);
@@ -284,7 +285,7 @@ class DBStructure
 
                // Get the definition
                if (is_null($definition)) {
-                       $definition = self::definition();
+                       $definition = self::definition($basePath);
                }
 
                // MySQL >= 5.7.4 doesn't support the IGNORE keyword in ALTER TABLE statements
diff --git a/src/Factory/ConfigFactory.php b/src/Factory/ConfigFactory.php
new file mode 100644 (file)
index 0000000..269daea
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+namespace Friendica\Factory;
+
+use Friendica\Core\Config;
+
+class ConfigFactory
+{
+       /**
+        * @param Config\ConfigCacheLoader $loader The Config Cache loader (INI/config/.htconfig)
+        *
+        * @return Config\ConfigCache
+        */
+       public static function createCache(Config\ConfigCacheLoader $loader)
+       {
+               $configCache = new Config\ConfigCache();
+               $loader->loadConfigFiles($configCache);
+
+               return $configCache;
+       }
+
+       /**
+        * @param string              $type   The adapter type
+        * @param Config\IConfigCache $config The config cache of this adapter
+        *
+        * @return Config\IConfigAdapter
+        */
+       public static function createConfig($type, Config\IConfigCache $config)
+       {
+               if ($type == 'preload') {
+                       return new Config\PreloadConfigAdapter($config);
+               } else {
+                       return new Config\JITConfigAdapter($config);
+               }
+       }
+
+       /**
+        * @param string               $type   The adapter type
+        * @param Config\IPConfigCache $config The config cache of this adapter
+        * @param int                  $uid    The UID of the current user
+        *
+        * @return Config\IPConfigAdapter
+        */
+       public static function createPConfig($type, Config\IPConfigCache $config, $uid = null)
+       {
+               if ($type == 'preload') {
+                       return new Config\PreloadPConfigAdapter($config, $uid);
+               } else {
+                       return new Config\JITPConfigAdapter($config);
+               }
+       }
+}
diff --git a/src/Factory/LoggerFactory.php b/src/Factory/LoggerFactory.php
new file mode 100644 (file)
index 0000000..751a635
--- /dev/null
@@ -0,0 +1,135 @@
+<?php
+
+namespace Friendica\Factory;
+
+use Friendica\Core\Config\ConfigCache;
+use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Logger\FriendicaDevelopHandler;
+use Friendica\Util\Logger\FriendicaIntrospectionProcessor;
+use Monolog;
+use Psr\Log\LoggerInterface;
+use Psr\Log\LogLevel;
+
+/**
+ * A logger factory
+ *
+ * Currently only Monolog is supported
+ */
+class LoggerFactory
+{
+       /**
+        * Creates a new PSR-3 compliant logger instances
+        *
+        * @param string      $channel The channel of the logger instance
+        * @param ConfigCache $config  The config
+        *
+        * @return LoggerInterface The PSR-3 compliant logger instance
+        */
+       public static function create($channel, ConfigCache $config = null)
+       {
+               $logger = new Monolog\Logger($channel);
+               $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
+               $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
+               $logger->pushProcessor(new Monolog\Processor\UidProcessor());
+               $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger']));
+
+               if (isset($config)) {
+                       $debugging = $config->get('system', 'debugging');
+                       $stream = $config->get('system', 'logfile');
+                       $level = $config->get('system', 'loglevel');
+
+                       if ($debugging) {
+                               static::addStreamHandler($logger, $stream, $level);
+                       }
+               }
+
+               return $logger;
+       }
+
+       /**
+        * Creates a new PSR-3 compliant develop logger
+        *
+        * If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
+        * you'll use this logger instance for the duration of your work.
+        *
+        * It should never get filled during normal usage of Friendica
+        *
+        * @param string $channel      The channel of the logger instance
+        * @param string $developerIp  The IP of the developer who wants to use the logger
+        *
+        * @return LoggerInterface The PSR-3 compliant logger instance
+        */
+       public static function createDev($channel, $developerIp)
+       {
+               $logger = new Monolog\Logger($channel);
+               $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
+               $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
+               $logger->pushProcessor(new Monolog\Processor\UidProcessor());
+               $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger']));
+
+
+               $logger->pushHandler(new FriendicaDevelopHandler($developerIp));
+
+               return $logger;
+       }
+
+       /**
+        * Adding a handler to a given logger instance
+        *
+        * @param LoggerInterface $logger  The logger instance
+        * @param mixed           $stream  The stream which handles the logger output
+        * @param string          $level   The level, for which this handler at least should handle logging
+        *
+        * @return void
+        *
+        * @throws InternalServerErrorException if the logger is incompatible to the logger factory
+        * @throws \Exception in case of general failures
+        */
+       public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE)
+       {
+               if ($logger instanceof Monolog\Logger) {
+                       $loglevel = Monolog\Logger::toMonologLevel($level);
+
+                       // fallback to notice if an invalid loglevel is set
+                       if (!is_int($loglevel)) {
+                               $loglevel = LogLevel::NOTICE;
+                       }
+                       $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
+
+                       $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
+                       $fileHandler->setFormatter($formatter);
+
+                       $logger->pushHandler($fileHandler);
+               } else {
+                       throw new InternalServerErrorException('Logger instance incompatible for MonologFactory');
+               }
+       }
+
+       /**
+        * This method enables the test mode of a given logger
+        *
+        * @param LoggerInterface $logger The logger
+        *
+        * @return Monolog\Handler\TestHandler the Handling for tests
+        *
+        * @throws InternalServerErrorException if the logger is incompatible to the logger factory
+        */
+       public static function enableTest($logger)
+       {
+               if ($logger instanceof Monolog\Logger) {
+                       // disable every handler so far
+                       $logger->pushHandler(new Monolog\Handler\NullHandler());
+
+                       // enable the test handler
+                       $fileHandler = new Monolog\Handler\TestHandler();
+                       $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
+                       $fileHandler->setFormatter($formatter);
+
+                       $logger->pushHandler($fileHandler);
+
+                       return $fileHandler;
+               } else {
+                       throw new InternalServerErrorException('Logger instance incompatible for MonologFactory');
+               }
+       }
+}
index 7efb56f95a03a87fcd38dc5823aa73b5de2a5883..d65e67fe3e6a8c34adec09eb7ffca2091f8e7f2c 100644 (file)
@@ -7,15 +7,15 @@
 namespace Friendica\Model;
 
 use Friendica\BaseObject;
-use Friendica\Core\System;
 use Friendica\Core\StorageManager;
+use Friendica\Core\System;
 use Friendica\Database\DBA;
 use Friendica\Database\DBStructure;
 use Friendica\Model\Storage\IStorage;
 use Friendica\Object\Image;
-use Friendica\Util\Security;
 use Friendica\Util\DateTimeFormat;
 use Friendica\Util\Mimetype;
+use Friendica\Util\Security;
 
 /**
  * Class to handle attach dabatase table
@@ -31,7 +31,7 @@ class Attach extends BaseObject
         */
        private static function getFields()
        {
-               $allfields = DBStructure::definition(false);
+               $allfields = DBStructure::definition(self::getApp()->getBasePath(), false);
                $fields = array_keys($allfields['attach']['fields']);
                array_splice($fields, array_search('data', $fields), 1);
                return $fields;
index b460d5f79fdf886364acfe50a0c7ddf99a4580e6..6a5ff01d596c32faa5ff6cd67ea42ffb5483d6cb 100644 (file)
@@ -1535,17 +1535,10 @@ class Item extends BaseObject
                                        $item['private'] = 0;
                                }
 
-                               // If its a post from myself then tag the thread as "mention"
-                               Logger::log("Checking if parent ".$parent_id." has to be tagged as mention for user ".$item['uid'], Logger::DEBUG);
-                               $user = DBA::selectFirst('user', ['nickname'], ['uid' => $item['uid']]);
-                               if (DBA::isResult($user)) {
-                                       $self = Strings::normaliseLink(System::baseUrl() . '/profile/' . $user['nickname']);
-                                       $self_id = Contact::getIdForURL($self, 0, true);
-                                       Logger::log("'myself' is ".$self_id." for parent ".$parent_id." checking against ".$item['author-id']." and ".$item['owner-id'], Logger::DEBUG);
-                                       if (($item['author-id'] == $self_id) || ($item['owner-id'] == $self_id)) {
-                                               DBA::update('thread', ['mention' => true], ['iid' => $parent_id]);
-                                               Logger::log("tagged thread ".$parent_id." as mention for user ".$self, Logger::DEBUG);
-                                       }
+                               // If its a post that originated here then tag the thread as "mention"
+                               if ($item['origin'] && $item['uid']) {
+                                       DBA::update('thread', ['mention' => true], ['iid' => $parent_id]);
+                                       Logger::log('tagged thread ' . $parent_id . ' as mention for user ' . $item['uid'], Logger::DEBUG);
                                }
                        } else {
                                /*
index d1a35c1518597eff9e631ec1a15a6818f9ac8981..327e59422c9f97083b34ee6cf5133b3c2d03600c 100644 (file)
@@ -103,7 +103,7 @@ class Install extends BaseModule
                                        return;
                                }
 
-                               self::$installer->installDatabase();
+                               self::$installer->installDatabase($a->getBasePath());
 
                                break;
                }
diff --git a/src/Util/BasePath.php b/src/Util/BasePath.php
new file mode 100644 (file)
index 0000000..a284983
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+
+namespace Friendica\Util;
+
+class BasePath
+{
+       /**
+        * @brief Returns the base filesystem path of the App
+        *
+        * It first checks for the internal variable, then for DOCUMENT_ROOT and
+        * finally for PWD
+        *
+        * @param string|null $basePath The default base path
+        * @param array       $server   server arguments
+        *
+        * @return string
+        *
+        * @throws \Exception if directory isn't usable
+        */
+       public static function create($basePath, $server = [])
+       {
+               if (!$basePath && !empty($server['DOCUMENT_ROOT'])) {
+                       $basePath = $server['DOCUMENT_ROOT'];
+               }
+
+               if (!$basePath && !empty($server['PWD'])) {
+                       $basePath = $server['PWD'];
+               }
+
+               return self::getRealPath($basePath);
+       }
+
+       /**
+        * @brief Returns a normalized file path
+        *
+        * This is a wrapper for the "realpath" function.
+        * That function cannot detect the real path when some folders aren't readable.
+        * Since this could happen with some hosters we need to handle this.
+        *
+        * @param string $path The path that is about to be normalized
+        * @return string normalized path - when possible
+        */
+       public static function getRealPath($path)
+       {
+               $normalized = realpath($path);
+
+               if (!is_bool($normalized)) {
+                       return $normalized;
+               } else {
+                       return $path;
+               }
+       }
+}
diff --git a/src/Util/LoggerFactory.php b/src/Util/LoggerFactory.php
deleted file mode 100644 (file)
index 4d3a287..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-namespace Friendica\Util;
-
-use Friendica\Network\HTTPException\InternalServerErrorException;
-use Friendica\Util\Logger\FriendicaDevelopHandler;
-use Friendica\Util\Logger\FriendicaIntrospectionProcessor;
-use Monolog;
-use Psr\Log\LoggerInterface;
-use Psr\Log\LogLevel;
-
-/**
- * A logger factory
- *
- * Currently only Monolog is supported
- */
-class LoggerFactory
-{
-       /**
-        * Creates a new PSR-3 compliant logger instances
-        *
-        * @param string $channel The channel of the logger instance
-        *
-        * @return LoggerInterface The PSR-3 compliant logger instance
-        */
-       public static function create($channel)
-       {
-               $logger = new Monolog\Logger($channel);
-               $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
-               $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
-               $logger->pushProcessor(new Monolog\Processor\UidProcessor());
-               $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger']));
-
-               return $logger;
-       }
-
-       /**
-        * Creates a new PSR-3 compliant develop logger
-        *
-        * If you want to debug only interactions from your IP or the IP of a remote server for federation debug,
-        * you'll use this logger instance for the duration of your work.
-        *
-        * It should never get filled during normal usage of Friendica
-        *
-        * @param string $channel      The channel of the logger instance
-        * @param string $developerIp  The IP of the developer who wants to use the logger
-        *
-        * @return LoggerInterface The PSR-3 compliant logger instance
-        */
-       public static function createDev($channel, $developerIp)
-       {
-               $logger = new Monolog\Logger($channel);
-               $logger->pushProcessor(new Monolog\Processor\PsrLogMessageProcessor());
-               $logger->pushProcessor(new Monolog\Processor\ProcessIdProcessor());
-               $logger->pushProcessor(new Monolog\Processor\UidProcessor());
-               $logger->pushProcessor(new FriendicaIntrospectionProcessor(LogLevel::DEBUG, ['Friendica\\Core\\Logger']));
-
-
-               $logger->pushHandler(new FriendicaDevelopHandler($developerIp));
-
-               return $logger;
-       }
-
-       /**
-        * Adding a handler to a given logger instance
-        *
-        * @param LoggerInterface $logger  The logger instance
-        * @param mixed           $stream  The stream which handles the logger output
-        * @param string          $level   The level, for which this handler at least should handle logging
-        *
-        * @return void
-        *
-        * @throws InternalServerErrorException if the logger is incompatible to the logger factory
-        * @throws \Exception in case of general failures
-        */
-       public static function addStreamHandler($logger, $stream, $level = LogLevel::NOTICE)
-       {
-               if ($logger instanceof Monolog\Logger) {
-                       $loglevel = Monolog\Logger::toMonologLevel($level);
-
-                       // fallback to notice if an invalid loglevel is set
-                       if (!is_int($loglevel)) {
-                               $loglevel = LogLevel::NOTICE;
-                       }
-                       $fileHandler = new Monolog\Handler\StreamHandler($stream, $loglevel);
-
-                       $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
-                       $fileHandler->setFormatter($formatter);
-
-                       $logger->pushHandler($fileHandler);
-               } else {
-                       throw new InternalServerErrorException('Logger instance incompatible for MonologFactory');
-               }
-       }
-
-       /**
-        * This method enables the test mode of a given logger
-        *
-        * @param LoggerInterface $logger The logger
-        *
-        * @return Monolog\Handler\TestHandler the Handling for tests
-        *
-        * @throws InternalServerErrorException if the logger is incompatible to the logger factory
-        */
-       public static function enableTest($logger)
-       {
-               if ($logger instanceof Monolog\Logger) {
-                       // disable every handler so far
-                       $logger->pushHandler(new Monolog\Handler\NullHandler());
-
-                       // enable the test handler
-                       $fileHandler = new Monolog\Handler\TestHandler();
-                       $formatter = new Monolog\Formatter\LineFormatter("%datetime% %channel% [%level_name%]: %message% %context% %extra%\n");
-                       $fileHandler->setFormatter($formatter);
-
-                       $logger->pushHandler($fileHandler);
-
-                       return $fileHandler;
-               } else {
-                       throw new InternalServerErrorException('Logger instance incompatible for MonologFactory');
-               }
-       }
-}
index 48c7e7ce6a20ec22fa3bb5389aea01970aeb00c8..05bace14679d3da1718d75b2df8f62066e79e110 100644 (file)
@@ -5,12 +5,13 @@
  */
 namespace Friendica\Worker;
 
+use Friendica\BaseObject;
 use Friendica\Core\Update;
 
-class DBUpdate
+class DBUpdate extends BaseObject
 {
        public static function execute()
        {
-               Update::run();
+               Update::run(self::getApp()->getBasePath());
        }
 }
index 2cb76dcad9ab39a013987026d6c4248bbe2afa56..79af5b5468a9495f86d390ceda58d1afdc4d64eb 100644 (file)
@@ -5,7 +5,10 @@
 
 namespace Friendica\Test;
 
+use Friendica\Core\Config;
 use Friendica\Database\DBA;
+use Friendica\Factory;
+use Friendica\Util\BasePath;
 use PHPUnit\DbUnit\DataSet\YamlDataSet;
 use PHPUnit\DbUnit\TestCaseTrait;
 use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
@@ -36,7 +39,13 @@ abstract class DatabaseTest extends MockedTest
                        $this->markTestSkipped('Please set the MYSQL_* environment variables to your test database credentials.');
                }
 
-               DBA::connect(getenv('MYSQL_HOST'),
+               $basedir = BasePath::create(dirname(__DIR__));
+               $configLoader = new Config\ConfigCacheLoader($basedir);
+               $config = Factory\ConfigFactory::createCache($configLoader);
+
+               DBA::connect(
+                       $config,
+                       getenv('MYSQL_HOST'),
                        getenv('MYSQL_USERNAME'),
                        getenv('MYSQL_PASSWORD'),
                        getenv('MYSQL_DATABASE'));
index c6793218446cc168bc01e9628b822e98609ff108..18188239f1760ee2aff511f9a4a3a5524d10a270 100644 (file)
@@ -4,6 +4,8 @@ namespace Friendica\Test\Util;
 
 use Friendica\App;
 use Friendica\BaseObject;
+use Friendica\Core\Config;
+use Friendica\Core\Config\ConfigCache;
 use Friendica\Render\FriendicaSmartyEngine;
 use Mockery\MockInterface;
 use org\bovigo\vfs\vfsStreamDirectory;
@@ -13,44 +15,60 @@ use org\bovigo\vfs\vfsStreamDirectory;
  */
 trait AppMockTrait
 {
-       use ConfigMockTrait;
-
        /**
         * @var MockInterface|App The mocked Friendica\App
         */
        protected $app;
 
+       /**
+        * @var MockInterface|ConfigCache The mocked Config Cache
+        */
+       protected $configCache;
+
        /**
         * Mock the App
         *
         * @param vfsStreamDirectory $root The root directory
+        * @param MockInterface|ConfigCache $config The config cache
         */
-       public function mockApp($root)
+       public function mockApp($root, $config)
        {
-               $this->mockConfigGet('system', 'theme', 'testtheme');
-
+               $this->configCache = $config;
                // Mocking App and most used functions
                $this->app = \Mockery::mock(App::class);
                $this->app
                        ->shouldReceive('getBasePath')
                        ->andReturn($root->url());
 
-               $this->app
-                       ->shouldReceive('getConfigValue')
+               $config
+                       ->shouldReceive('get')
                        ->with('database', 'hostname')
                        ->andReturn(getenv('MYSQL_HOST'));
-               $this->app
-                       ->shouldReceive('getConfigValue')
+               $config
+                       ->shouldReceive('get')
                        ->with('database', 'username')
                        ->andReturn(getenv('MYSQL_USERNAME'));
-               $this->app
-                       ->shouldReceive('getConfigValue')
+               $config
+                       ->shouldReceive('get')
                        ->with('database', 'password')
                        ->andReturn(getenv('MYSQL_PASSWORD'));
-               $this->app
-                       ->shouldReceive('getConfigValue')
+               $config
+                       ->shouldReceive('get')
                        ->with('database', 'database')
                        ->andReturn(getenv('MYSQL_DATABASE'));
+               $config
+                       ->shouldReceive('get')
+                       ->with('config', 'hostname')
+                       ->andReturn('localhost');
+               $config
+                       ->shouldReceive('get')
+                       ->with('system', 'theme', NULL)
+                       ->andReturn('system_theme');
+
+               $this->app
+                       ->shouldReceive('getConfig')
+                       ->andReturn($config);
+
                $this->app
                        ->shouldReceive('getTemplateEngine')
                        ->andReturn(new FriendicaSmartyEngine());
@@ -64,6 +82,14 @@ trait AppMockTrait
                        ->shouldReceive('getBaseUrl')
                        ->andReturn('http://friendica.local');
 
+               // Initialize empty Config
+               Config::init($config);
+               $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter');
+               $configAdapter
+                       ->shouldReceive('isConnected')
+                       ->andReturn(false);
+               Config::setAdapter($configAdapter);
+
                BaseObject::setApp($this->app);
        }
 }
diff --git a/tests/Util/ConfigMockTrait.php b/tests/Util/ConfigMockTrait.php
deleted file mode 100644 (file)
index d2867a5..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-namespace Friendica\Test\Util;
-
-use Mockery\MockInterface;
-
-/**
- * Trait to Mock Config settings
- */
-trait ConfigMockTrait
-{
-       /**
-        * @var MockInterface The mocking interface of Friendica\Core\Config
-        */
-       private $configMock;
-
-       /**
-        * Mocking a config setting
-        *
-        * @param string $family The family of the config double
-        * @param string $key The key of the config double
-        * @param mixed $value The value of the config double
-        * @param null|int $times How often the Config will get used
-        */
-       public function mockConfigGet($family, $key, $value, $times = null)
-       {
-               if (!isset($this->configMock)) {
-                       $this->configMock = \Mockery::mock('alias:Friendica\Core\Config');
-               }
-
-               $this->configMock
-                       ->shouldReceive('get')
-                       ->times($times)
-                       ->with($family, $key)
-                       ->andReturn($value);
-       }
-
-       /**
-        * Mocking setting a new config entry
-        *
-        * @param string $family The family of the config double
-        * @param string $key The key of the config double
-        * @param mixed $value The value of the config double
-        * @param null|int $times How often the Config will get used
-        * @param bool $return Return value of the set (default is true)
-        */
-       public function mockConfigSet($family, $key, $value, $times = null, $return = true)
-       {
-               if (!isset($this->configMock)) {
-                       $this->configMock = \Mockery::mock('alias:Friendica\Core\Config');
-               }
-
-               $this->mockConfigGet($family, $key, false, 1);
-               if ($return) {
-                       $this->mockConfigGet($family, $key, $value, 1);
-               }
-
-               $this->configMock
-                       ->shouldReceive('set')
-                       ->times($times)
-                       ->with($family, $key, $value)
-                       ->andReturn($return);
-       }
-}
index b12327f49cf0de18345fc9d926e3ec5d880702e4..bea0fe16fc24db01e00e969b2455982fe3b03003 100644 (file)
@@ -1,14 +1,7 @@
 <?php
-/**
- * Created by PhpStorm.
- * User: philipp
- * Date: 01.11.18
- * Time: 10:08
- */
 
 namespace Friendica\Test\Util;
 
-
 use Mockery\MockInterface;
 
 trait RendererMockTrait
index cf7571073addfdd38397aef30b4377a80948cf1b..be70d923bd4866c42e3f084cf4a543cb5ef72f2a 100644 (file)
@@ -5,13 +5,14 @@
 
 namespace Friendica\Test;
 
-use Friendica\BaseObject;
+use Friendica\App;
 use Friendica\Core\Config;
 use Friendica\Core\PConfig;
 use Friendica\Core\Protocol;
 use Friendica\Core\System;
+use Friendica\Factory;
 use Friendica\Network\HTTPException;
-use Friendica\Util\LoggerFactory;
+use Friendica\Util\BasePath;
 use Monolog\Handler\TestHandler;
 
 require_once __DIR__ . '/../../include/api.php';
@@ -34,10 +35,14 @@ class ApiTest extends DatabaseTest
         */
        public function setUp()
        {
-               parent::setUp();
+               $basedir = BasePath::create(dirname(__DIR__) . '/../');
+               $configLoader = new Config\ConfigCacheLoader($basedir);
+               $config = Factory\ConfigFactory::createCache($configLoader);
+               $logger = Factory\LoggerFactory::create('test', $config);
+               $this->app = new App($config, $logger, false);
+               $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger());
 
-               $this->app = BaseObject::getApp();
-               $this->logOutput = LoggerFactory::enableTest($this->app->getLogger());
+               parent::setUp();
 
                // User data that the test database is populated with
                $this->selfUser = [
index d534e24edea507c9a8eb5bf3049883976e998607..19dad07cd6eae94af0e590cc0f410889eaa6e38b 100644 (file)
@@ -3,24 +3,19 @@
 namespace Friendica\Test\src\App;
 
 use Friendica\App\Mode;
+use Friendica\Core\Config;
 use Friendica\Test\MockedTest;
-use Friendica\Test\Util\ConfigMockTrait;
 use Friendica\Test\Util\DBAMockTrait;
 use Friendica\Test\Util\VFSTrait;
 
-/**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
- */
 class ModeTest extends MockedTest
 {
        use VFSTrait;
        use DBAMockTrait;
-       use ConfigMockTrait;
 
        public function setUp()
        {
-               parent::setUp(); // TODO: Change the autogenerated stub
+               parent::setUp();
 
                $this->setUpVfsDir();
        }
@@ -50,6 +45,10 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::LOCALCONFIGPRESENT));
        }
 
+       /**
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
+        */
        public function testWithoutDatabase()
        {
                $this->mockConnected(false, 1);
@@ -64,6 +63,10 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::DBAVAILABLE));
        }
 
+       /**
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
+        */
        public function testWithoutDatabaseSetup()
        {
                $this->mockConnected(true, 1);
@@ -78,11 +81,28 @@ class ModeTest extends MockedTest
                $this->assertTrue($mode->has(Mode::LOCALCONFIGPRESENT));
        }
 
+       /**
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
+        */
        public function testWithMaintenanceMode()
        {
                $this->mockConnected(true, 1);
                $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-               $this->mockConfigGet('system', 'maintenance', true, 1);
+
+               $config = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $config
+                       ->shouldReceive('get')
+                       ->with('system', 'maintenance', null)
+                       ->andReturn(true)
+                       ->once();
+               // Initialize empty Config
+               Config::init($config);
+               $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter');
+               $configAdapter
+                       ->shouldReceive('isConnected')
+                       ->andReturn(false);
+               Config::setAdapter($configAdapter);
 
                $mode = new Mode($this->root->url());
                $mode->determine();
@@ -94,11 +114,28 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::MAINTENANCEDISABLED));
        }
 
+       /**
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
+        */
        public function testNormalMode()
        {
                $this->mockConnected(true, 1);
                $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-               $this->mockConfigGet('system', 'maintenance', false, 1);
+
+               $config = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $config
+                       ->shouldReceive('get')
+                       ->with('system', 'maintenance', null)
+                       ->andReturn(false)
+                       ->once();
+               // Initialize empty Config
+               Config::init($config);
+               $configAdapter = \Mockery::mock('Friendica\Core\Config\IConfigAdapter');
+               $configAdapter
+                       ->shouldReceive('isConnected')
+                       ->andReturn(false);
+               Config::setAdapter($configAdapter);
 
                $mode = new Mode($this->root->url());
                $mode->determine();
index 2b10556af67d79885342b3f05e88f698b4294ff1..784944c3a06425a35a6aa8b78da5f5245c6fa299 100644 (file)
@@ -5,7 +5,6 @@
 
 namespace Friendica\Test;
 
-use Friendica\App;
 use Friendica\BaseObject;
 use Friendica\Test\Util\AppMockTrait;
 use Friendica\Test\Util\VFSTrait;
@@ -13,8 +12,6 @@ use PHPUnit\Framework\TestCase;
 
 /**
  * Tests for the BaseObject class.
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  */
 class BaseObjectTest extends TestCase
 {
@@ -27,32 +24,29 @@ class BaseObjectTest extends TestCase
        private $baseObject;
 
        /**
-        * Create variables used in tests.
+        * Test the setApp() and getApp() function.
+        * @return void
         */
-       protected function setUp()
+       public function testGetSetApp()
        {
+               $baseObject = new BaseObject();
                $this->setUpVfsDir();
-               $this->mockApp($this->root);
-
-               $this->baseObject = new BaseObject();
-       }
+               $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $this->mockApp($this->root, $configMock);
 
-       /**
-        * Test the getApp() function.
-        * @return void
-        */
-       public function testGetApp()
-       {
-               $this->assertInstanceOf(App::class, $this->baseObject->getApp());
+               $this->assertNull($baseObject->setApp($this->app));
+               $this->assertEquals($this->app, $baseObject->getApp());
        }
 
        /**
-        * Test the setApp() function.
-        * @return void
+        * Test the getApp() function without App
+        * @expectedException Friendica\Network\HTTPException\InternalServerErrorException
+        * @runInSeparateProcess
+        * @preserveGlobalState disabled
         */
-       public function testSetApp()
+       public function testGetAppFailed()
        {
-               $this->assertNull($this->baseObject->setApp($this->app));
-               $this->assertEquals($this->app, $this->baseObject->getApp());
+               $baseObject = new BaseObject();
+               $baseObject->getApp();
        }
 }
index 62c599d54003258e8e180f9fed4aa4a85f2fd777..c92fb98dacc2c370dd24ed4554fcb6132fee60df 100644 (file)
@@ -2,13 +2,8 @@
 
 namespace Friendica\Test\src\Core\Cache;
 
-
 use Friendica\Core\Cache\ArrayCache;
 
-/**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
- */
 class ArrayCacheDriverTest extends MemoryCacheTest
 {
        protected function getInstance()
index b9a22ee9ca375ac8c0495fa67af77e9b817cee6d..e8bd65cbfe5670c0865dd28537b6f39b67684c44 100644 (file)
@@ -5,7 +5,6 @@ namespace Friendica\Test\src\Core\Cache;
 use Friendica\Core\Cache\MemcachedCacheDriver;
 use Friendica\Test\MockedTest;
 use Friendica\Test\Util\AppMockTrait;
-use Friendica\Test\Util\DateTimeFormatMockTrait;
 use Friendica\Test\Util\VFSTrait;
 use Friendica\Util\PidFile;
 
@@ -13,7 +12,6 @@ abstract class CacheTest extends MockedTest
 {
        use VFSTrait;
        use AppMockTrait;
-       use DateTimeFormatMockTrait;
 
        /**
         * @var int Start time of the mock (used for time operations)
@@ -69,24 +67,16 @@ abstract class CacheTest extends MockedTest
        protected function setUp()
        {
                $this->setUpVfsDir();
-               $this->mockApp($this->root);
+               $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $this->mockApp($this->root, $configMock);
                $this->app
                        ->shouldReceive('getHostname')
                        ->andReturn('friendica.local');
 
-               $this->mockUtcNow($this->startTime);
-
                parent::setUp();
 
                $this->instance = $this->getInstance();
 
-               // Default config
-               $this->mockConfigGet('config', 'hostname', 'localhost');
-               $this->mockConfigGet('system', 'throttle_limit_day', 100);
-               $this->mockConfigGet('system', 'throttle_limit_week', 100);
-               $this->mockConfigGet('system', 'throttle_limit_month', 100);
-               $this->mockConfigGet('system', 'theme', 'system_theme');
-
                $this->instance->clear(false);
        }
 
index f035f3fecde902cdc2be23364f239ccb9f4ad150..775a083a9b30059d3f8db6f1f2f8c9336402babe 100644 (file)
@@ -16,6 +16,8 @@ class DatabaseCacheDriverTest extends CacheTest
 
        public function setUp()
        {
+               $this->mockUtcNow($this->startTime);
+
                $this->mockConnected();
                $this->mockConnect();
 
index 4a4c4ebd7eb7be134602caa17758c19e326598a4..7832344a89522071a47da49efada0689d9e8256c 100644 (file)
@@ -6,16 +6,21 @@ namespace Friendica\Test\src\Core\Cache;
 use Friendica\Core\Cache\CacheDriverFactory;
 
 /**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  * @requires extension memcache
  */
 class MemcacheCacheDriverTest extends MemoryCacheTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'memcache_host', 'localhost', 1);
-               $this->mockConfigGet('system', 'memcache_port', 11211, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcache_host', NULL)
+                       ->andReturn('localhost');
+
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcache_port', NULL)
+                       ->andReturn(11211);
 
                $this->cache = CacheDriverFactory::create('memcache');
                return $this->cache;
index b118ee0f6ef3a4f5245587f79ede8df0ad6c9691..fe401f97dd40d95d718ebf8fe5959884684300de 100644 (file)
@@ -6,15 +6,16 @@ namespace Friendica\Test\src\Core\Cache;
 use Friendica\Core\Cache\CacheDriverFactory;
 
 /**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  * @requires extension memcached
  */
 class MemcachedCacheDriverTest extends MemoryCacheTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'memcached_hosts', [0 => 'localhost, 11211']);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcached_hosts', NULL)
+                       ->andReturn([0 => 'localhost, 11211']);
 
                $this->cache = CacheDriverFactory::create('memcached');
                return $this->cache;
index 4530ff1c025b16dedbf54d624bfdc0cbe9751846..0a3dba439d83ace9f5b254667e06d73e50de131f 100644 (file)
@@ -6,16 +6,21 @@ namespace Friendica\Test\src\Core\Cache;
 use Friendica\Core\Cache\CacheDriverFactory;
 
 /**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  * @requires extension redis
  */
 class RedisCacheDriverTest extends MemoryCacheTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'redis_host', 'localhost', 1);
-               $this->mockConfigGet('system', 'redis_port', null, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'redis_host', NULL)
+                       ->andReturn('localhost');
+
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'redis_port', NULL)
+                       ->andReturn(null);
 
                $this->cache = CacheDriverFactory::create('redis');
                return $this->cache;
index 361608d375f3f06f0b1564c4e402768de6cc3afe..41ccce0b28827410ebe67a6695d9788032af77b1 100644 (file)
@@ -52,7 +52,10 @@ class AutomaticInstallationConsoleTest extends ConsoleTest
                $this->db_user = getenv('MYSQL_USERNAME') . getenv('MYSQL_USER');
                $this->db_pass = getenv('MYSQL_PASSWORD');
 
-               $this->mockConfigGet('config', 'php_path', false);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('config', 'php_path', NULL)
+                       ->andReturn(false);
 
                $this->mockL10nT();
        }
@@ -181,7 +184,7 @@ FIN;
                $this->mockConnect(true, 1);
                $this->mockConnected(true, 1);
                $this->mockExistsTable('user', false, 1);
-               $this->mockUpdate([false, true, true], null, 1);
+               $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
 
                $config = <<<CONF
 <?php
@@ -241,7 +244,7 @@ CONF;
                $this->mockConnect(true, 1);
                $this->mockConnected(true, 1);
                $this->mockExistsTable('user', false, 1);
-               $this->mockUpdate([false, true, true], null, 1);
+               $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
 
                $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
                $this->mockReplaceMacros('testTemplate', $this->createArgumentsForMacro(true), '', 1);
@@ -267,7 +270,7 @@ CONF;
                $this->mockConnect(true, 1);
                $this->mockConnected(true, 1);
                $this->mockExistsTable('user', false, 1);
-               $this->mockUpdate([false, true, true], null, 1);
+               $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
 
                $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
                $this->mockReplaceMacros('testTemplate', $this->createArgumentsForMacro(false), '', 1);
@@ -292,7 +295,7 @@ CONF;
                $this->mockConnect(true, 1);
                $this->mockConnected(true, 1);
                $this->mockExistsTable('user', false, 1);
-               $this->mockUpdate([false, true, true], null, 1);
+               $this->mockUpdate([$this->root->url(), false, true, true], null, 1);
 
                $this->mockGetMarkupTemplate('local.config.tpl', 'testTemplate', 1);
                $this->mockReplaceMacros('testTemplate', $this->createArgumentsForMacro(true), '', 1);
index 4ee34917d8a5d9a77fec70dfd6d659218655ea7b..505c4f794dc58db64f1299d673798e2cb7c14a70 100644 (file)
@@ -32,7 +32,17 @@ class ConfigConsoleTest extends ConsoleTest
        }
 
        function testSetGetKeyValue() {
-               $this->mockConfigSet('config', 'test', 'now', 1);
+               $this->configCache
+                       ->shouldReceive('set')
+                       ->with('config', 'test', 'now')
+                       ->andReturn(true)
+                       ->once();
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('config', 'test', NULL)
+                       ->andReturn('now')
+                       ->twice();
+
                $console = new Config($this->consoleArgv);
                $console->setArgument(0, 'config');
                $console->setArgument(1, 'test');
@@ -40,14 +50,24 @@ class ConfigConsoleTest extends ConsoleTest
                $txt = $this->dumpExecute($console);
                $this->assertEquals("config.test <= now\n", $txt);
 
-               $this->mockConfigGet('config', 'test', 'now', 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('config', 'test', null)
+                       ->andReturn('now')
+                       ->once();
+
                $console = new Config($this->consoleArgv);
                $console->setArgument(0, 'config');
                $console->setArgument(1, 'test');
                $txt = $this->dumpExecute($console);
                $this->assertEquals("config.test => now\n", $txt);
 
-               $this->mockConfigGet('config', 'test', null, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('config', 'test', null)
+                       ->andReturn(null)
+                       ->once();
+
                $console = new Config($this->consoleArgv);
                $console->setArgument(0, 'config');
                $console->setArgument(1, 'test');
@@ -57,7 +77,11 @@ class ConfigConsoleTest extends ConsoleTest
 
        function testSetArrayValue() {
                $testArray = [1, 2, 3];
-               $this->mockConfigGet('config', 'test', $testArray, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('config', 'test', null)
+                       ->andReturn($testArray)
+                       ->once();
 
                $console = new Config($this->consoleArgv);
                $console->setArgument(0, 'config');
@@ -81,7 +105,11 @@ class ConfigConsoleTest extends ConsoleTest
        }
 
        function testVerbose() {
-               $this->mockConfigGet('test', 'it', 'now', 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('test', 'it', null)
+                       ->andReturn('now')
+                       ->once();
                $console = new Config($this->consoleArgv);
                $console->setArgument(0, 'test');
                $console->setArgument(1, 'it');
@@ -105,7 +133,16 @@ CONF;
        }
 
        function testUnableToSet() {
-               $this->mockConfigSet('test', 'it', 'now', 1, false);
+               $this->configCache
+                       ->shouldReceive('set')
+                       ->with('test', 'it', 'now')
+                       ->andReturn(false)
+                       ->once();
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('test', 'it', NULL)
+                       ->andReturn(NULL)
+                       ->once();
                $console = new Config();
                $console->setArgument(0, 'test');
                $console->setArgument(1, 'it');
index 4f7acc9c4229f62223b42cbdfca7121e46bffc81..905d214cac2b6282ceb298c5c5a391d3073d0587 100644 (file)
@@ -29,7 +29,8 @@ abstract class ConsoleTest extends MockedTest
                Intercept::setUp();
 
                $this->setUpVfsDir();
-               $this->mockApp($this->root);
+               $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $this->mockApp($this->root, $configMock);
        }
 
        /**
index bfea27253f1599af697be02569504fb89a9498e8..671341718b78168e5d15ffaadcd05d50747ed34d 100644 (file)
@@ -6,10 +6,6 @@ namespace Friendica\Test\src\Core\Lock;
 use Friendica\Core\Cache\ArrayCache;
 use Friendica\Core\Lock\CacheLockDriver;
 
-/**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
- */
 class ArrayCacheLockDriverTest extends LockTest
 {
        protected function getInstance()
index 320beb3054e2f95638bdf6ec88f801d5124852e0..ab8e1b2f2efc1f60171c22b021b908e4f33bcde4 100644 (file)
@@ -25,23 +25,17 @@ abstract class LockTest extends MockedTest
 
        protected function setUp()
        {
-               parent::setUp();
-               $this->instance = $this->getInstance();
-               $this->instance->releaseAll();
-
                // Reusable App object
                $this->setUpVfsDir();
-               $this->mockApp($this->root);
+               $configMock = \Mockery::mock('Friendica\Core\Config\ConfigCache');
+               $this->mockApp($this->root, $configMock);
                $this->app
                        ->shouldReceive('getHostname')
                        ->andReturn('friendica.local');
 
-               // Default config
-               $this->mockConfigGet('config', 'hostname', 'localhost');
-               $this->mockConfigGet('system', 'throttle_limit_day', 100);
-               $this->mockConfigGet('system', 'throttle_limit_week', 100);
-               $this->mockConfigGet('system', 'throttle_limit_month', 100);
-               $this->mockConfigGet('system', 'theme', 'system_theme');
+               parent::setUp();
+               $this->instance = $this->getInstance();
+               $this->instance->releaseAll();
        }
 
        protected function tearDown()
index d7002037e0def7f4f6dcff67c06088ef94c949f9..46f29f52e2f9da8a758f2d3a68e9e063bfa390d6 100644 (file)
@@ -8,15 +8,20 @@ use Friendica\Core\Lock\CacheLockDriver;
 
 /**
  * @requires extension Memcache
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  */
 class MemcacheCacheLockDriverTest extends LockTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'memcache_host', 'localhost', 1);
-               $this->mockConfigGet('system', 'memcache_port', 11211, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcache_host', NULL)
+                       ->andReturn('localhost');
+
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcache_port', NULL)
+                       ->andReturn(11211);
 
                return new CacheLockDriver(CacheDriverFactory::create('memcache'));
        }
index 3ba6de53efce02e8832752ef85e48fe362149827..72271c98b9ef98d2c87dd72998e0e687e39156bf 100644 (file)
@@ -8,14 +8,15 @@ use Friendica\Core\Lock\CacheLockDriver;
 
 /**
  * @requires extension memcached
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  */
 class MemcachedCacheLockDriverTest extends LockTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'memcached_hosts', [0 => 'localhost, 11211']);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'memcached_hosts', NULL)
+                       ->andReturn([0 => 'localhost, 11211']);
 
                return new CacheLockDriver(CacheDriverFactory::create('memcached'));
        }
index 765055add0ad7fe5783e7eee7edf96358ab59f52..0c9deea16e3ba9124d43e526f4fa0d7f8e53a3fd 100644 (file)
@@ -8,15 +8,20 @@ use Friendica\Core\Lock\CacheLockDriver;
 
 /**
  * @requires extension redis
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
  */
 class RedisCacheLockDriverTest extends LockTest
 {
        protected function getInstance()
        {
-               $this->mockConfigGet('system', 'redis_host', 'localhost', 1);
-               $this->mockConfigGet('system', 'redis_port', null, 1);
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'redis_host', NULL)
+                       ->andReturn('localhost');
+
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'redis_port', NULL)
+                       ->andReturn(null);
 
                return new CacheLockDriver(CacheDriverFactory::create('redis'));
        }
index 58daa3516cba633f45ffa9edaaf9accf709a672b..c2b94145725187d7e951d644903b15e4cdf06f3f 100644 (file)
@@ -4,10 +4,6 @@ namespace Friendica\Test\src\Core\Lock;
 
 use Friendica\Core\Lock\SemaphoreLockDriver;
 
-/**
- * @runTestsInSeparateProcesses
- * @preserveGlobalState disabled
- */
 class SemaphoreLockDriverTest extends LockTest
 {
        public function setUp()
@@ -15,7 +11,11 @@ class SemaphoreLockDriverTest extends LockTest
                parent::setUp();
 
                $this->app->shouldReceive('getHostname')->andReturn('friendica.local');
-               $this->mockConfigGet('system', 'temppath', '/tmp/');
+
+               $this->configCache
+                       ->shouldReceive('get')
+                       ->with('system', 'temppath', NULL)
+                       ->andReturn('/tmp/');
        }
 
        protected function getInstance()
index 17ac55fd7a5bd58d031b5fdf609c09647c018aec..f2a5cc5558f7dcd853a377d2065f905fbe245331 100644 (file)
@@ -1,19 +1,25 @@
 <?php
 namespace Friendica\Test\Database;
 
-use Friendica\BaseObject;
+use Friendica\App;
 use Friendica\Core\Config;
 use Friendica\Database\DBA;
+use Friendica\Factory;
 use Friendica\Test\DatabaseTest;
+use Friendica\Util\BasePath;
 
 class DBATest extends DatabaseTest
 {
        public function setUp()
        {
-               parent::setUp();
+               $basedir = BasePath::create(dirname(__DIR__) . '/../../');
+               $configLoader = new Config\ConfigCacheLoader($basedir);
+               $config = Factory\ConfigFactory::createCache($configLoader);
+               $logger = Factory\LoggerFactory::create('test', $config);
+               $this->app = new App($config, $logger, false);
+               $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger());
 
-               // Reusable App object
-               $this->app = BaseObject::getApp();
+               parent::setUp();
 
                // Default config
                Config::set('config', 'hostname', 'localhost');
index 268bf8eeddcb08694b4ac18a40521982969ab232..bc50a0a691aa43cb4cf2121196767be1aa403f60 100644 (file)
@@ -2,26 +2,25 @@
 
 namespace Friendica\Test\Database;
 
-use Friendica\BaseObject;
+use Friendica\App;
 use Friendica\Core\Config;
 use Friendica\Database\DBStructure;
+use Friendica\Factory;
 use Friendica\Test\DatabaseTest;
+use Friendica\Util\BasePath;
 
 class DBStructureTest extends DatabaseTest
 {
        public function setUp()
        {
-               parent::setUp();
-
-               // Reusable App object
-               $this->app = BaseObject::getApp();
+               $basedir = BasePath::create(dirname(__DIR__) . '/../../');
+               $configLoader = new Config\ConfigCacheLoader($basedir);
+               $config = Factory\ConfigFactory::createCache($configLoader);
+               $logger = Factory\LoggerFactory::create('test', $config);
+               $this->app = new App($config, $logger, false);
+               $this->logOutput = FActory\LoggerFactory::enableTest($this->app->getLogger());
 
-               // 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');
+               parent::setUp();
        }
 
        /**
diff --git a/view/js/linkPreview.js b/view/js/linkPreview.js
new file mode 100644 (file)
index 0000000..ae2f0af
--- /dev/null
@@ -0,0 +1,877 @@
+/**\r
+ * Copyright (c) 2014 Leonardo Cardoso (http://leocardz.com)\r
+ * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)\r
+ * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.\r
+ * \r
+ * Restructured by Rabzuarus (https://friendica.kommune4.de/profile/rabuzarus)\r
+ * to use it in the decentralized social network Friendica (https://friendi.ca).\r
+ * \r
+ * Version: 1.4.0\r
+ */\r
+(function ($) {\r
+       $.fn.linkPreview = function (options) {\r
+               var opts = jQuery.extend({}, $.fn.linkPreview.defaults, options);\r
+\r
+               var selector = $(this).selector;\r
+               selector = selector.substr(1);\r
+\r
+               var previewTpl = '\\r
+                       <div id="preview_' + selector + '" class="preview {0}">\\r
+                               {1}\\r
+                               <input type="hidden" name="has_attachment" id="hasAttachment_' + selector + '" value="{2}" />\\r
+                               <input type="hidden" name="attachment_url" id="attachmentUrl_' + selector + '" value="{3}" />\\r
+                               <input type="hidden" name="attachment_type" id="attachmentType_' + selector + '" value="{4}" />\\r
+                       </div>';\r
+\r
+               var attachmentTpl = '\\r
+                       <hr class="previewseparator">\\r
+                       <div id="closePreview_' + selector + '" title="Remove" class="closePreview" >\\r
+                               <button type="button" class="previewActionBtn">×</button>\\r
+                       </div>\\r
+                       <div id="previewImages_' + selector + '" class="previewImages">\\r
+                               <div id="previewImgBtn_' + selector + '" class="previewImgBtn">\\r
+                                       <button type="button" id="previewChangeImg_' + selector + '" class="buttonChangeDeactive previewActionBtn" style="display: none">\\r
+                                               <i class="fa fa-exchange" aria-hidden="true"></i>\\r
+                                       </button>\\r
+                               </div>\\r
+                               <div id="previewImage_' + selector + '" class="previewImage">\\r
+                               </div>\\r
+                               <input type="hidden" id="photoNumber_' + selector + '" class="photoNumber" value="0" />\\r
+                               <input type="hidden" name="attachment_img_src" id="attachmentImageSrc_' + selector + '" value="" />\\r
+                               <input type="hidden" name="attachment_img_width" id="attachmentImageWidth_' + selector + '" value="0" />\\r
+                               <input type="hidden" name="attachment_img_height" id="attachmentImageHeight_' + selector + '" value="0" />\\r
+                       </div>\\r
+                       <div id="previewContent_' + selector + '" class="previewContent">\\r
+                               <h4 id="previewTitle_' + selector + '" class="previewTitle"></h4>\\r
+                               <blockquote id="previewDescription_' + selector + '" class="previewDescription"></blockquote>\\r
+                               <div id="hiddenDescription_' + selector + '" class="hiddenDescription"></div>\\r
+                               <sup id="previewUrl_' + selector + '" class="previewUrl"></sup>\\r
+                       </div>\\r
+                       <div class="clear"></div>\\r
+                       <hr class="previewseparator">';\r
+               var text;\r
+               var urlRegex = /(https?\:\/\/|\s)[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,4})(\/+[a-z0-9_.\:\;-]*)*(\?[\&\%\|\+a-z0-9_=,\.\:\;-]*)?([\&\%\|\+&a-z0-9_=,\:\;\.-]*)([\!\#\/\&\%\|\+a-z0-9_=,\:\;\.-]*)}*/i;\r
+               var binurl;\r
+               var block = false;\r
+               var blockTitle = false;\r
+               var blockDescription = false;\r
+               var cache = {};\r
+               var images = "";\r
+               var isExtern = false;\r
+               var photoNumber = 0;\r
+               var firstPosted = false;\r
+               var isActive = false;\r
+               var isCrawling = false;\r
+               var defaultTitle = opts.defaultTitle;\r
+               var defaultDescription = opts.defaultDescription;\r
+\r
+               /**\r
+                * Initialize the plugin\r
+                * \r
+                * @returns {void}\r
+                */\r
+               var init = function() {\r
+                       $('#' + selector).bind({\r
+                               paste: function () {\r
+                                       setTimeout(function () {\r
+                                               crawlText();\r
+                                       }, 100);\r
+                               },\r
+                               keyup: function (e) {\r
+                                       // on enter, space, ctrl\r
+                                       if ((e.which === 13 || e.which === 32 || e.which === 17)) {\r
+                                               crawlText();\r
+                                       }\r
+                               }\r
+                       });\r
+\r
+                       // Check if we have already attachment bbcode in the textarea\r
+                       // and add it to the attachment preview.\r
+                       var content = $('#' + selector).val();\r
+                       addBBCodeToPreview(content);\r
+               };\r
+\r
+               /**\r
+                * Reset some values.\r
+                * \r
+                * @returns {void}\r
+                */\r
+               var resetPreview = function() {\r
+                       $('#hasAttachment_' + selector).val(0);\r
+                       photoNumber = 0;\r
+                       images = "";\r
+               };\r
+\r
+               /**\r
+                * Crawl a text string if it contains an url and try\r
+                * to attach it.\r
+                * \r
+                * If no text is passed to crawlText() we take\r
+                * the previous word before the cursor of the textarea.\r
+                * \r
+                * @param {string} text (optional)\r
+                * @returns {void}\r
+                */\r
+               var crawlText = function (text) {\r
+                       block = false;\r
+                       images = '';\r
+                       isExtern = false;\r
+\r
+                       // If no text is passed to crawlText() we \r
+                       // take the previous word before the cursor.\r
+                       if (typeof text === 'undefined') {\r
+                               text = getPrevWord(selector);\r
+                       } else {\r
+                               isExtern = true;\r
+                       }\r
+\r
+                       // Don't procces the textarea input if we have already\r
+                       // an attachment preview.\r
+                       if (!isExtern && isActive) {\r
+                               return;\r
+                       }\r
+\r
+                       if (trim(text) !== "") {\r
+                               if (block === false && urlRegex.test(text)) {\r
+                                       binurl = bin2hex(text);\r
+                                       block = true;\r
+\r
+                                       isCrawling = true;\r
+                                       $('#profile-rotator').show();\r
+\r
+                                       if (binurl in cache) {\r
+                                               isCrawling = false;\r
+                                               processContentData(cache[binurl]);\r
+                                       } else {\r
+                                               getContentData(binurl, processContentData);\r
+                                       }\r
+                               }\r
+                       }\r
+               };\r
+\r
+               /**\r
+                * Process the attachment data according to\r
+                * its content type (image, audio, video, attachment)\r
+                * \r
+                * @param {object} result\r
+                * @returns {void}\r
+                */\r
+               var processContentData = function(result) {\r
+                       if (result.contentType === 'image') {\r
+                               insertImage(result.data);\r
+                       }\r
+                       if (result.contentType === 'audio') {\r
+                               insertAudio(result.data);\r
+                       }\r
+                       if (result.contentType === 'video') {\r
+                               insertVideo(result.data);\r
+                       }\r
+                       if (result.contentType === 'attachment') {\r
+                               insertAttachment(result.data);\r
+                       }\r
+                       $('#profile-rotator').hide();\r
+               };\r
+\r
+               /**\r
+                * Fetch the content of link which should be attached.\r
+                * \r
+                * @param {string} binurl Link which should be attached as hexadecimal string.\r
+                * @param {type} callback\r
+                * @returns {void}\r
+                */\r
+               var getContentData = function(binurl, callback) {\r
+                       $.get('parse_url?binurl='+ binurl + '&format=json', function (answer) {\r
+                               obj = sanitizeInputData(answer);\r
+\r
+                               // Put the data into a cache\r
+                               cache[binurl] = obj;\r
+\r
+                               callback(obj);\r
+\r
+                               isCrawling = false;\r
+                       });\r
+               };\r
+\r
+               /*\r
+                * Add a [img] bbtag with the image url to the jot editor.\r
+                * \r
+                * @param {type} data\r
+                * @returns {void}\r
+                */\r
+               var insertImage = function(data) {\r
+                       if (!isExtern) {\r
+                               return;\r
+                       }\r
+                       var bbcode = '\n[img]' + data.url + '[/img]\n';\r
+                       addeditortext(bbcode);\r
+               };\r
+\r
+               /*\r
+                * Add a [audio] bbtag with the audio url to the jot editor.\r
+                * \r
+                * @param {type} data\r
+                * @returns {void}\r
+                */\r
+               var insertAudio = function(data) {\r
+                       if (!isExtern) {\r
+                               return;\r
+                       }\r
+                       var bbcode = '\n[audio]' + data.url + '[/audio]\n';\r
+                       addeditortext(bbcode);\r
+               };\r
+\r
+               /*\r
+                * Add a [video] bbtag with the video url to the jot editor.\r
+                * \r
+                * @param {type} data\r
+                * @returns {void}\r
+                */\r
+               var insertVideo = function(data) {\r
+                       if (!isExtern) {\r
+                               return;\r
+                       }\r
+                       var bbcode = '\n[video]' + json.url + '[/video]\n';\r
+                       addeditortext(bbcode);\r
+               };\r
+\r
+               /**\r
+                * Proccess all attachment data and show up a html\r
+                * attachment preview.\r
+                * \r
+                * @param {obj} data Attachment data.\r
+                * @returns {void}\r
+                */\r
+               var insertAttachment = function(data) {\r
+                       // If we have already a preview, leaver here.\r
+                       // Note: if we finish the Preview of other media content type,\r
+                       // we can move this condition to the beggining of crawlText();\r
+                       if (isActive) {\r
+                               $('#profile-rotator').hide();\r
+                               return;\r
+                       }\r
+\r
+                       if (data.type !== 'link' && data.type !== 'video' && data.type !== 'photo' || data.url === data.title) {\r
+                               $('#profile-rotator').hide();\r
+                               return;\r
+                       }\r
+\r
+                       $('#photoNumber_' + selector).val(0);\r
+                       resetPreview();\r
+\r
+                       processAttachmentTpl(data, 'type-' + data.type);\r
+                       addTitleDescription(data);\r
+                       addHostToAttachment(data.url);\r
+                       addImagesToAttachment(data.images);\r
+\r
+                       processEventListener();\r
+                       $('#profile-rotator').hide();\r
+               };\r
+\r
+               /**\r
+                * Construct the attachment html from the attachment template and\r
+                * add it to the DOM.\r
+                * \r
+                * @param {object} data Attachment data.\r
+                * @returns {void}\r
+                */\r
+               var processAttachmentTpl = function(data) {\r
+                       // Load and add the template if it isn't allready loaded.\r
+                       if ($('#preview_' + selector).length === 0) {\r
+                               var tpl = previewTpl.format(\r
+                                       'type-' + data.type,\r
+                                       attachmentTpl,\r
+                                       1,\r
+                                       bin2hex(data.url),\r
+                                       data.type\r
+                               );\r
+                               $('#' + selector).after(tpl);\r
+                       }\r
+\r
+                       isActive = true;\r
+               };\r
+\r
+               /**\r
+                * Add the attachment title and the description\r
+                * to the attachment preview.\r
+                * \r
+                * @param {object} data Attachment data.\r
+                * @returns {void}\r
+                */\r
+               var addTitleDescription = function(data) {\r
+                       var description = data.text;\r
+\r
+                       if (description === '') {\r
+                               description = defaultDescription;\r
+                       }\r
+\r
+                       $('#previewTitle_' + selector).html("\\r
+                               <span id='previewSpanTitle_" + selector + "' class='previewSpanTitle' >" + escapeHTML(data.title) + "</span>\\r
+                               <input type='text' name='attachment_title' value='" + escapeHTML(data.title) + "' id='previewInputTitle_" + selector + "' class='previewInputTitle inputPreview' style='display: none;'/>"\r
+                       );\r
+\r
+                       $('#previewDescription_' + selector).html("\\r
+                               <span id='previewSpanDescription_" + selector + "' class='previewSpanDescription' >" + escapeHTML(description) + "</span>\n\\r
+                               <textarea id='previewInputDescription_" + selector + "' name='attachment_text' class='previewInputDescription' style='display: none;' class='inputPreview' >" + escapeHTML(data.text) + "</textarea>"\r
+                       );\r
+               };\r
+\r
+               /**\r
+                * Add the host to the attachment preview.\r
+                * \r
+                * @param {string} url The url of the link attachment.\r
+                * @returns {void}\r
+                */\r
+               var addHostToAttachment = function(url) {\r
+                       if (url) {\r
+                               var regexpr = "(https?://)([^:^/]*)(:\\d*)?(.*)?";\r
+                               var regResult = url.match(regexpr);\r
+                               var urlHost = regResult[1] + regResult[2];\r
+                               $('#previewUrl_' + selector).html("<a href='" + url + "'>" + urlHost + "</a>");\r
+                       }\r
+               };\r
+\r
+               /**\r
+                * Add preview images to the attachment.\r
+                * \r
+                * @param {array} images\r
+                * \r
+                * @returns {void}\r
+                */\r
+               var addImagesToAttachment = function(images) {\r
+                       var imageClass = 'attachment-preview';\r
+       \r
+                       if (Array.isArray(images)) {\r
+                               $('#previewImages_' + selector).show();\r
+                               $('#attachmentImageSrc_' + selector).val(bin2hex(images[photoNumber].src));\r
+                               $('#attachmentImageWidth_' + selector).val(images[photoNumber].width);\r
+                               $('#attachmentImageHeight_' + selector).val(images[photoNumber].height);\r
+                       } else {\r
+                               $('#previewImages_' + selector).hide();\r
+                       }\r
+\r
+                       images.length = parseInt(images.length);\r
+                       var appendImage = "";\r
+\r
+                       for (i = 0; i < images.length; i++) {\r
+                               // For small preview images we use a smaller attachment format.\r
+                               ///@todo here we need to add a check for !Config::get('system', 'always_show_preview').\r
+                               if (images[i].width >= 500 && images[i].width >= images[i].height) {\r
+                                               imageClass = 'attachment-image';\r
+                               }\r
+\r
+                               if (i === 0) {\r
+                                       appendImage += "<img id='imagePreview_" + selector + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' ></img>";\r
+                               } else {\r
+                                       appendImage += "<img id='imagePreview_" + selector + "_" + i + "' src='" + images[i].src + "' class='" + imageClass + "' style='display: none;'></img>";\r
+                               }\r
+                       }\r
+\r
+                       $('#previewImage_' + selector).html(appendImage + "<div id='whiteImage' style='color: transparent; display:none;'>...</div>");\r
+\r
+                       // More than just one image.\r
+                       if (images.length > 1) {\r
+                               // Enable the the button to change the preview pictures.\r
+                               $('#previewChangeImg_' + selector).show();\r
+\r
+                               if (firstPosted === false) {\r
+                                       firstPosted = true;\r
+\r
+                                       $('#previewChangeImg_' + selector).unbind('click').click(function (e) {\r
+                                               e.stopPropagation();\r
+                                               if (images.length > 1) {\r
+                                                       $('#imagePreview_' + selector + '_' + photoNumber).css({\r
+                                                               'display': 'none'\r
+                                                       });\r
+                                                       photoNumber += 1;\r
+\r
+                                                       // If have reached the last image, begin with the first image.\r
+                                                       if (photoNumber === images.length) {\r
+                                                               photoNumber = 0;\r
+                                                       }\r
+\r
+                                                       $('#imagePreview_' + selector + '_' + photoNumber).css({\r
+                                                               'display': 'block'\r
+                                                       });\r
+                                                       $('#photoNumber_' + selector).val(photoNumber);\r
+                                                       $('#attachmentImageSrc_' + selector).val(bin2hex(images[photoNumber].src));\r
+                                                       $('#attachmentImageWidth_' + selector).val(images[photoNumber].width);\r
+                                                       $('#attachmentImageHeight_' + selector).val(images[photoNumber].height);\r
+                                               }\r
+                                       });\r
+                               }\r
+                       }\r
+               };\r
+\r
+               /**\r
+                * Add event listener to control the attachment preview.\r
+                * \r
+                * @returns {void}\r
+                */\r
+               var processEventListener = function() {\r
+                       $('#previewSpanTitle_' + selector).unbind('click').click(function (e) {\r
+                               e.stopPropagation();\r
+                               if (blockTitle === false) {\r
+                                       blockTitle = true;\r
+                                       $('#previewSpanTitle_' + selector).hide();\r
+                                       $('#previewInputTitle_' + selector).show();\r
+                                       $('#previewInputTitle_' + selector).val($('#previewInputTitle_' + selector).val());\r
+                                       $('#previewInputTitle_' + selector).focus().select();\r
+                               }\r
+                       });\r
+\r
+                       $('#previewInputTitle_' + selector).blur(function () {\r
+                               blockTitle = false;\r
+                               $('#previewSpanTitle_' + selector).html($('#previewInputTitle_' + selector).val());\r
+                               $('#previewSpanTitle_' + selector).show();\r
+                               $('#previewInputTitle_' + selector).hide();\r
+                       });\r
+\r
+                       $('#previewInputTitle_' + selector).keypress(function (e) {\r
+                               if (e.which === 13) {\r
+                                       blockTitle = false;\r
+                                       $('#previewSpanTitle_' + selector).html($('#previewInputTitle_' + selector).val());\r
+                                       $('#previewSpanTitle_' + selector).show();\r
+                                       $('#previewInputTitle_' + selector).hide();\r
+                               }\r
+                       });\r
+\r
+                       $('#previewSpanDescription_' + selector).unbind('click').click(function (e) {\r
+                               e.stopPropagation();\r
+                               if (blockDescription === false) {\r
+                                       blockDescription = true;\r
+                                       $('#previewSpanDescription_' + selector).hide();\r
+                                       $('#previewInputDescription_' + selector).show();\r
+                                       $('#previewInputDescription_' + selector).val($('#previewInputDescription_' + selector).val());\r
+                                       $('#previewInputDescription_' + selector).focus().select();\r
+                               }\r
+                       });\r
+\r
+                       $('#previewInputDescription_' + selector).blur(function () {\r
+                               blockDescription = false;\r
+                               $('#previewSpanDescription_' + selector).html($('#previewInputDescription_' + selector).val());\r
+                               $('#previewSpanDescription_' + selector).show();\r
+                               $('#previewInputDescription_' + selector).hide();\r
+                       });\r
+\r
+                       $('#previewInputDescription_' + selector).keypress(function (e) {\r
+                               if (e.which === 13) {\r
+                                       blockDescription = false;\r
+                                       $('#previewSpanDescription_' + selector).html($('#previewInputDescription_' + selector).val());\r
+                                       $('#previewSpanDescription_' + selector).show();\r
+                                       $('#previewInputDescription_' + selector).hide();\r
+                               }\r
+                       });\r
+\r
+                       $('#previewSpanTitle_' + selector).mouseover(function () {\r
+                               $('#previewSpanTitle_' + selector).css({\r
+                                       "background-color": "#ff9"\r
+                               });\r
+                       });\r
+\r
+                       $('#previewSpanTitle_' + selector).mouseout(function () {\r
+                               $('#previewSpanTitle_' + selector).css({\r
+                                       "background-color": "transparent"\r
+                               });\r
+                       });\r
+\r
+                       $('#previewSpanDescription_' + selector).mouseover(function () {\r
+                               $('#previewSpanDescription_' + selector).css({\r
+                                       "background-color": "#ff9"\r
+                               });\r
+                       });\r
+\r
+                       $('#previewSpanDescription_' + selector).mouseout(function () {\r
+                               $('#previewSpanDescription_' + selector).css({\r
+                                       "background-color": "transparent"\r
+                               });\r
+                       });\r
+\r
+                       $('#closePreview_' + selector).unbind('click').click(function (e) {\r
+                               e.stopPropagation();\r
+                               block = false;\r
+                               images = '';\r
+                               isActive = false;\r
+                               firstPosted = false;\r
+                               $('#preview_' + selector).fadeOut("fast", function () {\r
+                                       $('#preview_' + selector).remove();\r
+                                       $('#profile-rotator').hide();\r
+                                       $('#' + selector).focus();\r
+                               });\r
+\r
+                       });\r
+               };\r
+\r
+               /**\r
+                * Convert attachmant bbcode into an array.\r
+                * \r
+                * @param {string} content Text content with the attachment bbcode.\r
+                * @returns {object || null}\r
+                */\r
+               var getAttachmentData = function(content) {\r
+                       var data = {};\r
+\r
+                       var match = content.match(/(.*)\[attachment(.*?)\](.*?)\[\/attachment\](.*)/ism);\r
+                       if (match === null || match.length < 5) {\r
+                               return null;\r
+                       }\r
+\r
+                       var attributes = match[2];\r
+                       data.text = trim(match[1]);\r
+\r
+                       var type = '';\r
+                       var matches = attributes.match(/type='(.*?)'/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               type = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       matches = attributes.match(/type="(.*?)"/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               type = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       if (type === '') {\r
+                               return null;\r
+                       }\r
+\r
+                       if (\r
+                               type !== 'link'\r
+                               && type !== 'audio'\r
+                               && type !== 'photo'\r
+                               && type !== 'video')\r
+                       {\r
+                               return null;\r
+                       }\r
+\r
+                       if (type !== '') {\r
+                               data.type = type;\r
+                       }\r
+\r
+                       var url = '';\r
+\r
+                       matches = attributes.match(/url='(.*?)'/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               url = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       matches = attributes.match(/url="(.*?)"/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               url = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       if(url !== '') {\r
+                               data.url = escapeHTML(url);\r
+                       }\r
+\r
+                       var title = '';\r
+\r
+                       matches = attributes.match(/title='(.*?)'/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               title = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       matches = attributes.match(/title="(.*?)"/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               title = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       if (title !== '') {\r
+                               data.title = escapeHTML(title);\r
+                       }\r
+\r
+                       var image = '';\r
+\r
+                       matches = attributes.match(/image='(.*?)'/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               image = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       matches = attributes.match(/image="(.*?)"/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               image = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       if (image !== '') {\r
+                               data.image = escapeHTML(image);\r
+                       }\r
+\r
+                       var preview = '';\r
+\r
+                       matches = attributes.match(/preview='(.*?)'/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               preview = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       matches = attributes.match(/preview="(.*?)"/ism);\r
+                       if (matches !== null && typeof matches[1] !== 'undefined') {\r
+                               preview = matches[1].toLowerCase();\r
+                       }\r
+\r
+                       if (preview !== '') {\r
+                               data.preview = escapeHTML(preview);\r
+                       }\r
+\r
+                       data.text = trim(match[3]);\r
+                       data.after = trim(match[4]);\r
+\r
+                       return data;\r
+               };\r
+\r
+               /**\r
+                * Process txt content and if it contains attachment bbcode\r
+                * add it to the attachment preview .\r
+                * \r
+                * @param {string} content\r
+                * @returns {void}\r
+                */\r
+               var addBBCodeToPreview =function(content) {\r
+                       var attachmentData = getAttachmentData(content);\r
+                       if (attachmentData) {\r
+                               reAddAttachment(attachmentData);\r
+                               // Remove the attachment bbcode from the textarea.\r
+                               var content = content.replace(/\[attachment.*\[\/attachment]/ism, '');\r
+                               $('#' + selector).val(content);\r
+                               $('#' + selector).focus();\r
+                       }\r
+               };\r
+\r
+               /**\r
+                * Add an Attachment with data from an old bbcode\r
+                * generated attachment.\r
+                * \r
+                * @param {object} json The attachment data.\r
+                * @returns {void}\r
+                */\r
+               var reAddAttachment = function(json) {\r
+                       if (isActive) {\r
+                               $('#profile-rotator').hide();\r
+                               return;\r
+                       }\r
+\r
+                       if (json.type !== 'link' && json.type !== 'video' && json.type !== 'photo' || json.url === json.title) {\r
+                               $('#profile-rotator').hide();\r
+                               return;\r
+                       }\r
+\r
+                       var obj = {data: json};\r
+                       obj = sanitizeInputData(obj);\r
+\r
+                       var data = obj.data;\r
+\r
+                       resetPreview();\r
+\r
+                       processAttachmentTpl(data);\r
+                       addTitleDescription(data);\r
+                       addHostToAttachment(data.url);\r
+\r
+                       // Since we don't have an array of image data,\r
+                       // we need to add the preview images in a different way\r
+                       // than in function addImagesToAttachment().\r
+                       var imageClass = 'attachment-preview';\r
+                       var image = '';\r
+\r
+                       if (data.image !== '') {\r
+                               imageClass = 'attachment-image';\r
+                               image = data.image;\r
+                       } else {\r
+                               image = data.preview;\r
+                       }\r
+\r
+                       if (image !== '') {\r
+                               var appendImage = "<img id='imagePreview_" + selector + "' src='" + image + "' class='" + imageClass + "' ></img>"\r
+                               $('#previewImage_' + selector).html(appendImage);\r
+                               $('#attachmentImageSrc_' + selector).val(bin2hex(image));\r
+\r
+                               // We need to add the image widht and height when it is \r
+                               // loaded.\r
+                               $('<img/>' ,{\r
+                                       load : function(){\r
+                                               $('#attachmentImageWidth_' + selector).val(this.width);\r
+                                               $('#attachmentImageHeight_' + selector).val(this.height);\r
+                                       },\r
+                                       src  : image\r
+                               });\r
+                       }\r
+\r
+                       processEventListener();\r
+                       $('#profile-rotator').hide();\r
+               };\r
+\r
+               /**\r
+                * Add missing default properties to the input data object.\r
+                * \r
+                * @param {object} obj Input data.\r
+                * @returns {object}\r
+                */\r
+               var sanitizeInputData = function(obj) {\r
+                       if (typeof obj.contentType === 'undefined'\r
+                               || obj.contentType === null)\r
+                       {\r
+                               obj.contentType = "";\r
+                       }\r
+                       if (typeof obj.data.url === 'undefined'\r
+                               || obj.data.url === null)\r
+                       {\r
+                               obj.data.url = "";\r
+                       }\r
+                       if (typeof obj.data.title === 'undefined'\r
+                               || obj.data.title === null\r
+                               || obj.data.title === "")\r
+                       {\r
+                               obj.data.title = defaultTitle;\r
+                       }\r
+                       if (typeof obj.data.text === 'undefined'\r
+                               || obj.data.text === null\r
+                               || obj.data.text === "")\r
+                       {\r
+                               obj.data.text = "";\r
+                       }\r
+                       if (typeof obj.data.images === 'undefined'\r
+                               || obj.data.images === null)\r
+                       {\r
+                               obj.data.images = "";\r
+                       }\r
+\r
+                       if (typeof obj.data.image === 'undefined'\r
+                               || obj.data.image === null)\r
+                       {\r
+                               obj.data.image = "";\r
+                       }\r
+\r
+                       if (typeof obj.data.preview === 'undefined'\r
+                               || obj.data.preview === null)\r
+                       {\r
+                               obj.data.preview = "";\r
+                       }\r
+\r
+                       return obj;\r
+               };\r
+\r
+               /**\r
+                * Destroy the plugin.\r
+                * \r
+                * @returns {void}\r
+                */\r
+               var destroy = function() {\r
+                       $('#' + selector).unbind();\r
+                       $('#preview_' + selector).remove();\r
+                       binurl;\r
+                       block = false;\r
+                       blockTitle = false;\r
+                       blockDescription = false;\r
+                       cache = {};\r
+                       images = "";\r
+                       isExtern = false;\r
+                       photoNumber = 0;\r
+                       firstPosted = false;\r
+                       isActive = false;\r
+                       isCrawling = false;\r
+                       selector = "";\r
+               };\r
+\r
+               var trim = function(str) {\r
+                       return str.replace(/^\s+|\s+$/g, "");\r
+               };\r
+               var escapeHTML = function(unsafe_str) {\r
+                       return unsafe_str\r
+                               .replace(/&/g, '&amp;')\r
+                               .replace(/</g, '&lt;')\r
+                               .replace(/>/g, '&gt;')\r
+                               .replace(/\"/g, '&quot;')\r
+                               .replace(/\[/g, '&#91;')\r
+                               .replace(/\]/g, '&#93;')\r
+                               .replace(/\'/g, '&#39;'); // '&apos;' is not valid HTML 4\r
+               };\r
+\r
+               // Initialize LinkPreview \r
+               init();\r
+\r
+               return {\r
+                       // make crawlText() accessable from the outside.\r
+                       crawlText: function(text) {\r
+                               crawlText(text);\r
+                       },\r
+                       addBBCodeToPreview: function(content) {\r
+                               addBBCodeToPreview(content);\r
+                       },\r
+                       destroy: function() {\r
+                               destroy();\r
+                       }\r
+               };\r
+       };\r
+\r
+       $.fn.linkPreview.defaults = {\r
+               defaultDescription: "Enter a description",\r
+               defaultTitle: "Enter a title"\r
+       };\r
+\r
+       /**\r
+       * Get in a textarea the previous word before the cursor.\r
+       * \r
+       * @param {object} text Textarea elemet.\r
+       * @param {integer} caretPos Cursor position.\r
+       * \r
+       * @returns {string} Previous word.\r
+       */\r
+       function returnWord(text, caretPos) {\r
+               var index = text.indexOf(caretPos);\r
+               var preText = text.substring(0, caretPos);\r
+               // If the last charachter is a space or enter remove it\r
+               // We need this in friendica for the url  preview.\r
+               var lastChar = preText.slice(-1)\r
+               if ( lastChar === " "\r
+                       || lastChar === "\n"\r
+                       || lastChar === "\r"\r
+                       )\r
+               {\r
+                       preText = preText.substring(0, preText.length -1);\r
+               }\r
+\r
+               // Replace new line with space.\r
+               preText = preText.replace(/\n/g, " ");\r
+\r
+               if (preText.indexOf(" ") > 0) {\r
+                       var words = preText.split(" ");\r
+                       return words[words.length - 1]; //return last word\r
+               }\r
+               else {\r
+                       return preText;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get in a textarea the previous word before the cursor.\r
+        * \r
+        * @param {string} id The ID of a textarea element.\r
+        * @returns {sting|null} Previous word or null if no word is available.\r
+        */\r
+       function getPrevWord(id) {\r
+               var text = document.getElementById(id);\r
+               var caretPos = getCaretPosition(text);\r
+               var word = returnWord(text.value, caretPos);\r
+               if (word != null) {\r
+                       return word\r
+               }\r
+\r
+       }\r
+\r
+       /**\r
+        * Get the cursor posiotion in an text element.\r
+        * \r
+        * @param {object} ctrl Textarea elemet.\r
+        * @returns {integer} Position of the cursor.\r
+        */\r
+       function getCaretPosition(ctrl) {\r
+               var CaretPos = 0;   // IE Support\r
+               if (document.selection) {\r
+                       ctrl.focus();\r
+                       var Sel = document.selection.createRange();\r
+                       Sel.moveStart('character', -ctrl.value.length);\r
+                       CaretPos = Sel.text.length;\r
+               }\r
+               // Firefox support\r
+               else if (ctrl.selectionStart || ctrl.selectionStart == '0') {\r
+                       CaretPos = ctrl.selectionStart;\r
+               }\r
+               return (CaretPos);\r
+       }\r
+})(jQuery);\r
index b293a3f48ad173c05e4c8f9698000da88c546e68..c2ad62d3c3eec05dfbf9b8c26a3463bc77ee4e8f 100644 (file)
@@ -1326,6 +1326,29 @@ section #jotOpen {
 .jothidden {
     /*display: none;*/
 }
+.modal #jot-sections {
+    max-height: calc(100vh - 22px);
+}
+@media (min-width: 768px) {
+    .modal #jot-sections {
+        max-height: calc(100vh - 62px);
+    }
+}
+#jot-modal #jot-sections,
+#jot-modal #jot-modal-body,
+#jot-modal #profile-jot-form,
+#jot-modal #profile-jot-wrapper,
+#jot-modal #jot-text-wrap,
+#jot-modal #jot-preview-content,
+#jot-modal #tread-wrapper--1,
+#jot-modal #item-Q0,
+#jot-modal #profile-jot-acl-wrapper,
+#jot-modal #acl-wrapper {
+    overflow: hidden;
+    display: flex;
+    flex: auto;
+    flex-direction: column;
+}
 #jot-modal .modal-header a, #jot-modal .modal-header .btn-link,
 #profile-jot-submit-wrapper a, #profile-jot-submit-wrapper .btn-link {
     color: #555;
@@ -1342,6 +1365,69 @@ section #jotOpen {
 }
 #jot-text-wrap textarea {
     min-height: 100px;
+    overflow-y: auto !important;
+    overflow-y: overlay !important;
+}
+/*#jot-attachment-preview {
+    display: none;
+}*/
+#jot-text-wrap .preview textarea {
+    width: 100%;
+}
+#preview_profile-jot-text {
+    position: relative;
+    padding: 0px 10px;
+    margin-top: -2px;
+    border: 2px solid #ededed;
+    border-top: none;
+    box-shadow: none;
+    border-radius: 0 0 4px 4px;
+    background: #fff;
+    color: #555;
+}
+textarea#profile-jot-text:focus + #preview_profile-jot-text {
+    border: 2px solid #6fdbe8;
+    border-top: none;
+}
+.preview hr.previewseparator {
+    margin-top: 0px;
+    border-color: #D2D2D2;
+}
+#previewImgBtn_profile-jot-text,
+.closePreview  {
+    position: absolute;
+    top: 15px;
+}
+.closePreview {
+    right: 15px;
+    z-index: 1;
+}
+.previewImgBtn {
+    left: 15px;
+}
+.preview button.previewActionBtn {
+    display:block;
+    height: 25px;
+    width: 25px;
+    border-radius: 50%;
+    color: #fff;
+    border: 2px solid #fff;
+    box-shadow: 0 0 3px gray;
+    background: #777;
+    text-align: center;
+    line-height: 2px;
+    text-decoration: none;
+    padding: 0 0 1px 1px;
+    opacity: 0.7;
+}
+.preview button.previewActionBtn:hover {
+    opacity: 1;
+}
+.preview .closePreview button.previewActionBtn {
+    font-size: 25px;
+}
+#previewInputTitle_profile-jot-text {
+    width: 100%;
 }
 #profile-jot-wrapper button#profile-jot-submit {
     margin-top: 5px;
@@ -1349,7 +1435,10 @@ section #jotOpen {
 #profile-jot-wrapper #character-counter {
     padding: 10px 15px;
 }
-
+.modal .wall-item-container.preview {
+    overflow-y: auto;
+    overflow-y: overlay;
+}
 /* ACL */
 /*#jot-modal-body {
     height: auto;
@@ -1357,7 +1446,7 @@ section #jotOpen {
     overflow-y: hidden;
 }*/
 #acl-search {
-    margin-top: 20px;
+    /*margin-top: 20px;*/
     /*padding: 8px;*/
     /*border: 1px solid #ccc;*/
     width: 100%;
@@ -1365,7 +1454,6 @@ section #jotOpen {
 #acl-list {
     display: block;
     border: 1px solid #ccc;
-    overflow: auto;
     clear: both;
     min-height: 62px;
     margin-top: 20px;
@@ -1373,10 +1461,10 @@ section #jotOpen {
     -webkit-border-radius: 4px;
     -moz-border-radius: 4px;
     border-radius: 4px;
+    overflow-y: auto;
 }
 #acl-list-content {
-    overflow-y: auto;
-    max-height: calc(100vh - 330px);
+    overflow-y: hidden;
     height: auto !important;
 }
 .acl-list-item {
@@ -1819,6 +1907,10 @@ code > .hl-main {
 .wall-item-bottom .label a {
     color: #fff;
 }
+.wall-item-tags .category,
+.wall-item-tags .folder {
+    margin-right: 3px;
+}
 
 /* item social action buttons */
 .wall-item-actions {
diff --git a/view/theme/frio/js/jot.js b/view/theme/frio/js/jot.js
new file mode 100644 (file)
index 0000000..d55e509
--- /dev/null
@@ -0,0 +1,39 @@
+// We append the linkPreview to a global Variable to make linkPreview
+// accessable on other places. Note: search on other places before you
+// delete or move the variable.
+var linkPreview;
+
+/**
+ * Insert a link into friendica jot.
+ * 
+ * @returns {void}
+ */
+function jotGetLink() {
+       var currentText = $("#profile-jot-text").val();
+       var noAttachment = '';
+       reply = prompt(aStr.linkurl);
+       if(reply && reply.length) {
+               // There should be only one attachment per post.
+               // So we need to remove the old one.
+               $('#jot-attachment-preview').empty();
+               $('#profile-rotator').show();
+               if (currentText.includes("[attachment") && currentText.includes("[/attachment]")) {
+                       noAttachment = '&noAttachment=1';
+               }
+
+               // We use the linkPreview library to have a preview
+               // of the attachments.
+               if (typeof linkPreview === 'object') {
+                       linkPreview.crawlText(reply + noAttachment);
+
+               // Fallback: insert the attachment bbcode directly into the textarea
+               // if the attachment live preview isn't available
+               } else {
+                       $.get('parse_url?binurl=' + bin2hex(reply) + noAttachment, function(data) {
+                               addeditortext(data);
+                               $('#profile-rotator').hide();
+                       });
+               }
+               autosize.update($("#profile-jot-text"));
+       }
+}
index 80694b64da52971d890adf9be0fa130975b15b0d..1c5314c4b410b6052a86830b44e867ffe01dddaf 100644 (file)
@@ -22,6 +22,10 @@ $(document).ready(function(){
                $("#jot-content").append(jotcache);
                // Clear the jotcache.
                jotcache = '';
+               // Destroy the attachment linkPreviw for Jot.
+               if (typeof linkPreview === 'object') {
+                       linkPreview.destroy();
+               }
        });
 
        // Add Colorbox for viewing Network page images.
@@ -292,6 +296,7 @@ function editpost(url) {
 
                                modal.show();
                                $("#jot-popup").show();
+                               linkPreview = $('#profile-jot-text').linkPreview();
                        }
                });
 }
index 76a04a32f2887b49913c6e80bed291184c692881..e0c06af581f134ce8a7e9750beab82d0c0488336 100644 (file)
@@ -46,34 +46,34 @@ function commentGetLink(id, prompttext) {
 }
 
 function addCommentText(data, id) {
-    // get the textfield
-    var textfield = document.getElementById("comment-edit-text-" + id);
-    // check if the textfield does have the default-value
-    commentOpenUI(textfield, id);
-    // save already existent content
-    var currentText = $("#comment-edit-text-" + id).val();
-    //insert the data as new value
-    textfield.value = currentText + data;
-    autosize.update($("#comment-edit-text-" + id));
+       // get the textfield
+       var textfield = document.getElementById("comment-edit-text-" + id);
+       // check if the textfield does have the default-value
+       commentOpenUI(textfield, id);
+       // save already existent content
+       var currentText = $("#comment-edit-text-" + id).val();
+       //insert the data as new value
+       textfield.value = currentText + data;
+       autosize.update($("#comment-edit-text-" + id));
 }
 
 function commentLinkDrop(event, id) {
-    var reply = event.dataTransfer.getData("text/uri-list");
-    event.target.textContent = reply;
-    event.preventDefault();
-    if (reply && reply.length) {
-        reply = bin2hex(reply);
-        $.get('parse_url?noAttachment=1&binurl=' + reply, function(data) {
+       var reply = event.dataTransfer.getData("text/uri-list");
+       event.target.textContent = reply;
+       event.preventDefault();
+       if (reply && reply.length) {
+               reply = bin2hex(reply);
+               $.get('parse_url?noAttachment=1&binurl=' + reply, function(data) {
                        addCommentText(data, id);
-        });
-    }
+               });
+       }
 }
 
 function commentLinkDropper(event) {
-    var linkFound = event.dataTransfer.types.contains("text/uri-list");
-    if (linkFound) {
-        event.preventDefault();
-    }
+       var linkFound = event.dataTransfer.types.contains("text/uri-list");
+       if (linkFound) {
+               event.preventDefault();
+       }
 }
 
 
index 6a9e38eb37168bef2023537fb6839e8992991240..49a7226eca31c752ad3196ee96975d04223f4556 100644 (file)
@@ -1,6 +1,8 @@
 
 <div id="acl-wrapper">
-       <button id="acl-showall" class="btn btn-block btn-default"><i class="fa fa-globe"></i> {{$showall}}</button>
+       <div class="form-group form-group-search">
+               <button id="acl-showall" class="btn btn-block btn-default"><i class="fa fa-globe"></i> {{$showall}}</button>
+       </div>
        <div class="form-group form-group-search">
                <input type="text" id="acl-search" class="form-control form-search">
        </div>
index 70370e42eba3049a5248d10695b2323973376602..ac59caed8e16dd18ff32052ce925088d41caed70 100644 (file)
@@ -1,5 +1,7 @@
 
-<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js" ></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/ajaxupload.js"></script>
+<script type="text/javascript" src="{{$baseurl}}/view/js/linkPreview.js"></script>
+<script type="text/javascript" src="{{$baseurl}}/view/theme/frio/js/jot.js"></script>
 
 <script type="text/javascript">
        var editor = false;
@@ -39,6 +41,7 @@
 
 <script type="text/javascript">
        var ispublic = '{{$ispublic}}';
+       aStr.linkurl = '{{$linkurl}}';
 
 
        $(document).ready(function() {
                }
        }
 
-       function jotGetLink() {
-               var currentText = $("#profile-jot-text").val();
-               var noAttachment = '';
-               reply = prompt("{{$linkurl}}");
-               if(reply && reply.length) {
-                       reply = bin2hex(reply);
-                       $('#profile-rotator').show();
-                       if (currentText.includes("[attachment") && currentText.includes("[/attachment]")) {
-                               noAttachment = '&noAttachment=1';
-                       }
-                       $.get('parse_url?binurl=' + reply + noAttachment, function(data) {
-                               addeditortext(data);
-                               $('#profile-rotator').hide();
-                       });
-                       autosize.update($("#profile-jot-text"));
-               }
-       }
-
        function jotVideoURL() {
                reply = prompt("{{$vidurl}}");
                if(reply && reply.length) {
                }
        }
 
-
        function jotGetLocation() {
                reply = prompt("{{$whereareu}}", $('#jot-location').val());
                if(reply && reply.length) {
        }
 
        function itemFiler(id) {
-
                var bordercolor = $("input").css("border-color");
 
                $.get('filer/', function(data){
                        .find('#jot-modal-content')
                        .append(jotcache)
                        .modal.show;
+
+               // Jot attachment live preview.
+               linkPreview = $('#profile-jot-text').linkPreview();
        }
 
        // Activate the jot text section in the jot modal
index b668229e8af5ebb2b71017475cafabd905c8ab12..c6f90ac05d63dce6d584201d1dfb38f1e1af5c85 100644 (file)
                        {{/if}}
 
                                {{foreach $item.folders as $cat}}
-                                       <span class="folder label btn-danger sm">{{$cat.name}}</a>{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
+                                       <span class="folder label btn-danger sm">{{$cat.name}}{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
                                {{/foreach}}
 
                                {{foreach $item.categories as $cat}}
-                                       <span class="category label btn-success sm">{{$cat.name}}</a>{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
+                                       <span class="category label btn-success sm">{{$cat.name}}{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
                                {{/foreach}}
                                </div>
                                {{if $item.edited}}<div class="itemedited text-muted">{{$item.edited['label']}} (<span title="{{$item.edited['date']}}">{{$item.edited['relative']}}</span>)</div>{{/if}}
index 12e419f8a59f8562ac06fea94aa475d8171ad090..c149b58a515edabedab9fc051469d09eb653614d 100644 (file)
@@ -293,11 +293,11 @@ as the value of $top_child_total (this is done at the end of this file)
                {{/if}}
 
                        {{foreach $item.folders as $cat}}
-                               <span class="folder label btn-danger sm"><span class="p-category">{{$cat.name}}</span></a>{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
+                               <span class="folder label btn-danger sm p-category">{{$cat.name}}{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
                        {{/foreach}}
 
                        {{foreach $item.categories as $cat}}
-                               <span class="category label btn-success sm"><span class="p-category">{{$cat.name}}</span></a>{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
+                               <span class="category label btn-success sm p-category">{{$cat.name}}{{if $cat.removeurl}} (<a href="{{$cat.removeurl}}" title="{{$remove}}">x</a>) {{/if}} </span>
                        {{/foreach}}
                        </div>
                        {{if $item.edited}}<div class="itemedited text-muted">{{$item.edited['label']}} (<span title="{{$item.edited['date']}}">{{$item.edited['relative']}}</span>)</div>{{/if}}