diff --git a/.gitignore b/.gitignore index 2523b839cf..235875ff5e 100644 --- a/.gitignore +++ b/.gitignore @@ -10,7 +10,7 @@ docs/public user.css user.js -config.ini +*.ini node_modules .env vendor/ diff --git a/NEWS.md b/NEWS.md index 5075c92951..07ba39fcdc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -94,6 +94,7 @@ - Placeholders are now used for images before they are loaded to avoid content jumping around ([#1204](https://github.com/fossar/selfoss/pull/1204)) - Search button is now always on the screen, avoiding the need to scroll to top to be able to use it. ([#1231](https://github.com/fossar/selfoss/issues/1231)) - Button for opening articles, tags, sources and filters in the sidebar, as well as the source and tag links in articles are now real links, allowing to open them in a new tab by middle-clicking them. ([#1216](https://github.com/fossar/selfoss/issues/1216), [#695](https://github.com/fossar/selfoss/issues/695)) +- Configuration is no longer managed by F3 framework. ([#1261](https://github.com/fossar/selfoss/pull/1261)) ## 2.18 – 2018-03-05 diff --git a/defaults.ini b/defaults.ini deleted file mode 100644 index d5525ad1b8..0000000000 --- a/defaults.ini +++ /dev/null @@ -1,43 +0,0 @@ -; see https://selfoss.aditu.de/docs/administration/options/ -; for more information about the configuration parameters -[globals] -db_type=sqlite -db_file=%datadir%/sqlite/selfoss.db -db_host=localhost -db_database=selfoss -db_username=root -db_password= -db_port= -db_prefix= -logger_destination=file:%datadir%/logs/default.log -logger_level=ERROR -items_perpage=50 -items_lifetime=30 -base_url= -username= -password= -salt=lkjl1289 -public= -html_title=selfoss -rss_title=selfoss feed -rss_max_items=300 -rss_mark_as_read=0 -homepage=newest -language=0 -auto_mark_as_read=0 -auto_collapse=0 -auto_stream_more=1 -anonymizer= -share=atfpde -wallabag= -wallabag_version=1 -allow_public_update_access= -unread_order= -load_images_on_mobile=0 -auto_hide_read_on_mobile=0 -env_prefix=selfoss_ -camo_domain= -camo_key= -scroll_to_article_header=1 -show_thumbnails=1 -reading_speed_wpm=0 diff --git a/docs/content/_index.md b/docs/content/_index.md index b1f672cb8d..a80abd6dfb 100644 --- a/docs/content/_index.md +++ b/docs/content/_index.md @@ -35,7 +35,7 @@ For further questions or any problems, use our [support forum](forum). For a mor ## Configuring selfoss {#configuration}
-All [configuration options](@/docs/administration/options.md) are optional. Any settings in `config.ini` will override the settings in `defaults.ini`. To customize settings follow these instructions: +All [configuration options](@/docs/administration/options.md) are optional. Any settings in `config.ini` will override the settings in `src/helpers/Configuration.php`. For convenience, the archive includes `defaults.ini` file containing the default configuration exported in INI format. To customize settings follow these instructions: 1. Copy `defaults.ini` to `config.ini`. 2. Edit `config.ini` and delete any lines you do not wish to override. diff --git a/src/common.php b/src/common.php index 9140129b4e..637de03463 100644 --- a/src/common.php +++ b/src/common.php @@ -1,6 +1,7 @@ set('DEBUG', 0); -$f3->set('version', '2.19-SNAPSHOT'); +const SELFOSS_VERSION = '2.19-SNAPSHOT'; // independent of selfoss version // needs to be bumped each time public API is changed (follows semver) // keep in sync with docs/api-description.json -$f3->set('apiversion', '4.0.0'); +const SELFOSS_API_VERSION = '4.0.0'; $f3->set('AUTOLOAD', false); $f3->set('BASEDIR', BASEDIR); $f3->set('LOCALES', BASEDIR . '/assets/locale/'); -// internal but overridable values -$f3->set('datadir', BASEDIR . '/data'); -$f3->set('cache', '%datadir%/cache'); -$f3->set('ftrss_custom_data_dir', '%datadir%/fulltextrss'); +$configuration = new Configuration(__DIR__ . '/../config.ini', $_ENV); -// read defaults -$f3->config('defaults.ini'); - -// read config, if it exists -if (file_exists('config.ini')) { - $f3->config('config.ini'); -} - -// overwrite config with ENV variables -$env_prefix = $f3->get('env_prefix'); -foreach ($f3->get('ENV') as $key => $value) { - if (strncasecmp($key, $env_prefix, strlen($env_prefix)) === 0) { - $f3->set(strtolower(substr($key, strlen($env_prefix))), $value); - } -} - -// interpolate variables in the config values -$interpolatedKeys = [ - 'db_file', - 'logger_destination', - 'cache', - 'ftrss_custom_data_dir', -]; -$datadir = $f3->get('datadir'); -foreach ($interpolatedKeys as $key) { - $value = $f3->get($key); - $f3->set($key, str_replace('%datadir%', $datadir, $value)); -} +$f3->set('DEBUG', $configuration->debug); +$f3->set('cache', $configuration->cache); +$f3->set('language', $configuration->language); $dice = new Dice(); // DI rules -// Choose database implementation based on config $substitutions = [ 'substitutions' => [ - daos\DatabaseInterface::class => ['instance' => 'daos\\' . $f3->get('db_type') . '\\Database'], - daos\ItemsInterface::class => ['instance' => 'daos\\' . $f3->get('db_type') . '\\Items'], - daos\SourcesInterface::class => ['instance' => 'daos\\' . $f3->get('db_type') . '\\Sources'], - daos\TagsInterface::class => ['instance' => 'daos\\' . $f3->get('db_type') . '\\Tags'], + // Instantiate configuration container. + Configuration::class => [ + 'instance' => function() use ($configuration) { + return $configuration; + }, + 'shared' => true, + ], + + // Choose database implementation based on config + daos\DatabaseInterface::class => ['instance' => 'daos\\' . $configuration->dbType . '\\Database'], + daos\ItemsInterface::class => ['instance' => 'daos\\' . $configuration->dbType . '\\Items'], + daos\SourcesInterface::class => ['instance' => 'daos\\' . $configuration->dbType . '\\Sources'], + daos\TagsInterface::class => ['instance' => 'daos\\' . $configuration->dbType . '\\Tags'], + Dice::class => ['instance' => function() use ($dice) { return $dice; }], @@ -111,8 +92,8 @@ $dice->addRule(daos\TagsInterface::class, $shared); // Database connection -if ($f3->get('db_type') === 'sqlite') { - $db_file = $f3->get('db_file'); +if ($configuration->dbType === 'sqlite') { + $db_file = $configuration->dbFile; // create empty database file if it does not exist if (!is_file($db_file)) { @@ -123,12 +104,12 @@ $dbParams = [ $dsn ]; -} elseif ($f3->get('db_type') === 'mysql') { - $host = $f3->get('db_host'); - $port = $f3->get('db_port'); - $database = $f3->get('db_database'); +} elseif ($configuration->dbType === 'mysql') { + $host = $configuration->dbHost; + $port = $configuration->dbPort; + $database = $configuration->dbDatabase; - if ($port) { + if ($port !== null) { $dsn = "mysql:host=$host; port=$port; dbname=$database"; } else { $dsn = "mysql:host=$host; dbname=$database"; @@ -136,16 +117,16 @@ $dbParams = [ $dsn, - $f3->get('db_username'), - $f3->get('db_password'), + $configuration->dbUsername, + $configuration->dbPassword, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4;'] ]; -} elseif ($f3->get('db_type') === 'pgsql') { - $host = $f3->get('db_host'); - $port = $f3->get('db_port'); - $database = $f3->get('db_database'); +} elseif ($configuration->dbType === 'pgsql') { + $host = $configuration->dbHost; + $port = $configuration->dbPort; + $database = $configuration->dbDatabase; - if ($port) { + if ($port !== null) { $dsn = "pgsql:host=$host; port=$port; dbname=$database"; } else { $dsn = "pgsql:host=$host; dbname=$database"; @@ -153,8 +134,8 @@ $dbParams = [ $dsn, - $f3->get('db_username'), - $f3->get('db_password') + $configuration->dbUsername, + $configuration->dbPassword ]; } @@ -163,7 +144,7 @@ ]); // Define regexp function for SQLite -if ($f3->get('db_type') === 'sqlite') { +if ($configuration->dbType === 'sqlite') { $sqlParams = array_merge($sqlParams, [ 'call' => [ [ @@ -194,7 +175,7 @@ function($pattern, $text) { $dice->addRule('$iconStorageBackend', [ 'instanceOf' => helpers\Storage\FileStorage::class, 'constructParams' => [ - \F3::get('datadir') . '/favicons' + $configuration->datadir . '/favicons' ], ]); @@ -207,7 +188,7 @@ function($pattern, $text) { $dice->addRule('$thumbnailStorageBackend', [ 'instanceOf' => helpers\Storage\FileStorage::class, 'constructParams' => [ - \F3::get('datadir') . '/thumbnails' + $configuration->datadir . '/thumbnails' ], ]); @@ -231,22 +212,22 @@ function($pattern, $text) { $dice->addRule(helpers\FeedReader::class, [ 'constructParams' => [ - \F3::get('cache'), + $configuration->cache, ], ]); // init logger $log = $dice->create(Logger::class); -if ($f3->get('logger_level') === 'NONE') { +if ($configuration->loggerLevel === 'NONE') { $handler = new NullHandler(); } else { - $logger_destination = $f3->get('logger_destination'); + $logger_destination = $configuration->loggerDestination; if (strpos($logger_destination, 'file:') === 0) { - $handler = new StreamHandler(substr($logger_destination, 5), $f3->get('logger_level')); + $handler = new StreamHandler(substr($logger_destination, 5), $configuration->loggerLevel); } elseif ($logger_destination === 'error_log') { - $handler = new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, $f3->get('logger_level')); + $handler = new ErrorLogHandler(ErrorLogHandler::OPERATING_SYSTEM, $configuration->loggerLevel); } else { echo 'The `logger_destination` option needs to be either `error_log` or a file path prefixed by `file:`.'; exit; @@ -264,7 +245,7 @@ function($pattern, $text) { // init error handling $f3->set('ONERROR', - function(Base $f3) use ($log, $handler) { + function(Base $f3) use ($configuration, $log, $handler) { $exception = $f3->get('EXCEPTION'); try { @@ -274,7 +255,7 @@ function(Base $f3) use ($log, $handler) { $log->error($f3->get('ERROR.text')); } - if ($f3->get('DEBUG') != 0) { + if ($configuration->debug !== 0) { echo $f3->get('lang_error') . ': '; echo $f3->get('ERROR.text') . "\n"; echo $f3->get('ERROR.trace'); @@ -294,6 +275,6 @@ function(Base $f3) use ($log, $handler) { } ); -if ($f3->get('DEBUG') != 0) { +if ($configuration->debug !== 0) { ini_set('display_errors', '0'); } diff --git a/src/controllers/About.php b/src/controllers/About.php index 64206c2478..d829cede10 100644 --- a/src/controllers/About.php +++ b/src/controllers/About.php @@ -2,8 +2,8 @@ namespace controllers; -use Base; use helpers\Authentication; +use helpers\Configuration; use helpers\View; /** @@ -13,11 +13,15 @@ class About { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var View view helper */ private $view; - public function __construct(Authentication $authentication, View $view) { + public function __construct(Authentication $authentication, Configuration $configuration, View $view) { $this->authentication = $authentication; + $this->configuration = $configuration; $this->view = $view; } @@ -27,37 +31,36 @@ public function __construct(Authentication $authentication, View $view) { * * @return void */ - public function about(Base $f3) { - $anonymizer = \helpers\Anonymizer::getAnonymizer(); - $wallabag = !empty($f3->get('wallabag')) ? [ - 'url' => $f3->get('wallabag'), // string - 'version' => $f3->get('wallabag_version'), // int + public function about() { + $wallabag = !empty($this->configuration->wallabag) ? [ + 'url' => $this->configuration->wallabag, // string + 'version' => $this->configuration->wallabagVersion, // int ] : null; $configuration = [ - 'version' => $f3->get('version'), - 'apiversion' => $f3->get('apiversion'), + 'version' => SELFOSS_VERSION, + 'apiversion' => SELFOSS_API_VERSION, 'configuration' => [ - 'homepage' => $f3->get('homepage') ? $f3->get('homepage') : 'newest', // string - 'anonymizer' => $anonymizer === '' ? null : $anonymizer, // ?string - 'share' => (string) $f3->get('share'), // string + 'homepage' => $this->configuration->homepage ? $this->configuration->homepage : 'newest', // string + 'anonymizer' => $this->configuration->anonymizer, // ?string + 'share' => $this->configuration->share, // string 'wallabag' => $wallabag, // ?array - 'wordpress' => $f3->get('wordpress'), // ?string - 'autoMarkAsRead' => $f3->get('auto_mark_as_read') == 1, // bool - 'autoCollapse' => $f3->get('auto_collapse') == 1, // bool - 'autoStreamMore' => $f3->get('auto_stream_more') == 1, // bool - 'loadImagesOnMobile' => $f3->get('load_images_on_mobile') == 1, // bool - 'itemsPerPage' => $f3->get('items_perpage'), // int - 'unreadOrder' => $f3->get('unread_order'), // string - 'autoHideReadOnMobile' => $f3->get('auto_hide_read_on_mobile') == 1, // bool - 'scrollToArticleHeader' => $f3->get('scroll_to_article_header') == 1, // bool - 'showThumbnails' => $f3->get('show_thumbnails') == 1, // bool - 'htmlTitle' => trim($f3->get('html_title')), // string - 'allowPublicUpdate' => $f3->get('allow_public_update_access') == 1, // bool - 'publicMode' => $f3->get('public') == 1, // bool + 'wordpress' => $this->configuration->wordpress, // ?string + 'autoMarkAsRead' => $this->configuration->autoMarkAsRead, // bool + 'autoCollapse' => $this->configuration->autoCollapse, // bool + 'autoStreamMore' => $this->configuration->autoStreamMore, // bool + 'loadImagesOnMobile' => $this->configuration->loadImagesOnMobile, // bool + 'itemsPerPage' => $this->configuration->itemsPerpage, // int + 'unreadOrder' => $this->configuration->unreadOrder, // string + 'autoHideReadOnMobile' => $this->configuration->autoHideReadOnMobile, // bool + 'scrollToArticleHeader' => $this->configuration->scrollToArticleHeader, // bool + 'showThumbnails' => $this->configuration->showThumbnails, // bool + 'htmlTitle' => trim($this->configuration->htmlTitle), // string + 'allowPublicUpdate' => $this->configuration->allowPublicUpdateAccess, // bool + 'publicMode' => $this->configuration->public, // bool 'authEnabled' => $this->authentication->enabled() === true, // bool - 'readingSpeed' => $f3->get('reading_speed_wpm') > 0 ? (int) $f3->get('reading_speed_wpm') : null, // ?int - 'language' => $f3->get('language') === 0 ? null : $f3->get('language'), // ?string + 'readingSpeed' => $this->configuration->readingSpeedWpm > 0 ? $this->configuration->readingSpeedWpm : null, // ?int + 'language' => $this->configuration->language === '0' ? null : $this->configuration->language, // ?string 'userCss' => file_exists(BASEDIR . '/user.css') ? filemtime(BASEDIR . '/user.css') : null, // ?int 'userJs' => file_exists(BASEDIR . '/user.js') ? filemtime(BASEDIR . '/user.js') : null, // ?int ], diff --git a/src/controllers/Index.php b/src/controllers/Index.php index b2af258f68..a5f4b14b86 100644 --- a/src/controllers/Index.php +++ b/src/controllers/Index.php @@ -36,7 +36,10 @@ class Index { /** @var View view helper */ private $view; - public function __construct(Authentication $authentication, \daos\Items $itemsDao, Sources $sourcesController, \daos\Sources $sourcesDao, Tags $tagsController, \daos\Tags $tagsDao, View $view) { + /** @var ViewHelper */ + private $viewHelper; + + public function __construct(Authentication $authentication, \daos\Items $itemsDao, Sources $sourcesController, \daos\Sources $sourcesDao, Tags $tagsController, \daos\Tags $tagsDao, View $view, ViewHelper $viewHelper) { $this->authentication = $authentication; $this->itemsDao = $itemsDao; $this->sourcesController = $sourcesController; @@ -44,6 +47,7 @@ public function __construct(Authentication $authentication, \daos\Items $itemsDa $this->tagsController = $tagsController; $this->tagsDao = $tagsDao; $this->view = $view; + $this->viewHelper = $viewHelper; } /** @@ -128,7 +132,7 @@ public function home(Base $f3) { private function loadItems(array $params, array $tags, $search = null) { $entries = []; foreach ($this->itemsDao->get($params) as $item) { - $entries[] = ViewHelper::preprocessEntry($item, $this->tagsController, $tags, $search); + $entries[] = $this->viewHelper->preprocessEntry($item, $this->tagsController, $tags, $search); } return [ diff --git a/src/controllers/Items/Sync.php b/src/controllers/Items/Sync.php index 5499173454..e7740248ed 100644 --- a/src/controllers/Items/Sync.php +++ b/src/controllers/Items/Sync.php @@ -2,8 +2,8 @@ namespace controllers\Items; -use Base; use helpers\Authentication; +use helpers\Configuration; use function helpers\json_response; use helpers\View; use helpers\ViewHelper; @@ -15,6 +15,9 @@ class Sync { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var \daos\Items items */ private $itemsDao; @@ -30,13 +33,18 @@ class Sync { /** @var View view helper */ private $view; - public function __construct(Authentication $authentication, \daos\Items $itemsDao, \daos\Sources $sourcesDao, \controllers\Tags $tagsController, \daos\Tags $tagsDao, View $view) { + /** @var ViewHelper */ + private $viewHelper; + + public function __construct(Authentication $authentication, Configuration $configuration, \daos\Items $itemsDao, \daos\Sources $sourcesDao, \controllers\Tags $tagsController, \daos\Tags $tagsDao, View $view, ViewHelper $viewHelper) { $this->authentication = $authentication; + $this->configuration = $configuration; $this->itemsDao = $itemsDao; $this->sourcesDao = $sourcesDao; $this->tagsController = $tagsController; $this->tagsDao = $tagsDao; $this->view = $view; + $this->viewHelper = $viewHelper; } /** @@ -45,7 +53,7 @@ public function __construct(Authentication $authentication, \daos\Items $itemsDa * * @return void */ - public function sync(Base $f3) { + public function sync() { $this->authentication->needsLoggedInOrPublicMode(); $params = null; @@ -80,7 +88,7 @@ public function sync(Base $f3) { $notBefore->setTimeZone(new \DateTimeZone(date_default_timezone_get())); } - $itemsHowMany = $f3->get('items_perpage'); + $itemsHowMany = $this->configuration->itemsPerpage; if (array_key_exists('itemsHowMany', $params) && is_int($params['itemsHowMany'])) { $itemsHowMany = min($params['itemsHowMany'], @@ -90,7 +98,7 @@ public function sync(Base $f3) { $sync['newItems'] = function() use ($sinceId, $notBefore, $since, $itemsHowMany) { foreach ($this->itemsDao->sync($sinceId, $notBefore, $since, $itemsHowMany) as $newItem) { - yield ViewHelper::preprocessEntry($newItem, $this->tagsController); + yield $this->viewHelper->preprocessEntry($newItem, $this->tagsController); } }; @@ -122,7 +130,7 @@ public function sync(Base $f3) { * * @return void */ - public function updateStatuses(Base $f3) { + public function updateStatuses() { $this->authentication->needsLoggedIn(); if (isset($_POST['updatedStatuses']) @@ -130,6 +138,6 @@ public function updateStatuses(Base $f3) { $this->itemsDao->bulkStatusUpdate($_POST['updatedStatuses']); } - $this->sync($f3); + $this->sync(); } } diff --git a/src/controllers/Opml/Export.php b/src/controllers/Opml/Export.php index d3ab5f5993..27bd6b28f8 100644 --- a/src/controllers/Opml/Export.php +++ b/src/controllers/Opml/Export.php @@ -3,6 +3,7 @@ namespace controllers\Opml; use helpers\Authentication; +use helpers\Configuration; use helpers\SpoutLoader; use Monolog\Logger; @@ -18,6 +19,9 @@ class Export { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var Logger */ private $logger; @@ -33,8 +37,9 @@ class Export { /** @var \daos\Tags */ private $tagsDao; - public function __construct(Authentication $authentication, Logger $logger, \daos\Sources $sourcesDao, SpoutLoader $spoutLoader, \daos\Tags $tagsDao, \XMLWriter $writer) { + public function __construct(Authentication $authentication, Configuration $configuration, Logger $logger, \daos\Sources $sourcesDao, SpoutLoader $spoutLoader, \daos\Tags $tagsDao, \XMLWriter $writer) { $this->authentication = $authentication; + $this->configuration = $configuration; $this->logger = $logger; $this->sourcesDao = $sourcesDao; $this->spoutLoader = $spoutLoader; @@ -102,14 +107,14 @@ public function export() { // selfoss version, XML format version and creation date $this->writer->startElementNS('selfoss', 'meta', null); - $this->writer->writeAttribute('generatedBy', 'selfoss-' . \F3::get('version')); + $this->writer->writeAttribute('generatedBy', 'selfoss-' . SELFOSS_VERSION); $this->writer->writeAttribute('version', '1.0'); $this->writer->writeAttribute('createdOn', date('r')); $this->writer->endElement(); // meta $this->logger->debug('OPML export: finished writing meta'); $this->writer->startElement('head'); - $user = \F3::get('username'); + $user = $this->configuration->username; $this->writer->writeElement('title', ($user ? $user . '\'s' : 'My') . ' subscriptions in selfoss'); $this->writer->endElement(); // head $this->logger->debug('OPML export: finished writing head'); diff --git a/src/controllers/Rss.php b/src/controllers/Rss.php index e1e9b8be4f..2a9d57d9fb 100644 --- a/src/controllers/Rss.php +++ b/src/controllers/Rss.php @@ -5,6 +5,7 @@ use Base; use FeedWriter\RSS2; use helpers\Authentication; +use helpers\Configuration; use helpers\View; /** @@ -18,6 +19,9 @@ class Rss { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var RSS2 feed writer */ private $feedWriter; @@ -30,8 +34,9 @@ class Rss { /** @var View view helper */ private $view; - public function __construct(Authentication $authentication, RSS2 $feedWriter, \daos\Items $itemsDao, \daos\Sources $sourcesDao, View $view) { + public function __construct(Authentication $authentication, Configuration $configuration, RSS2 $feedWriter, \daos\Items $itemsDao, \daos\Sources $sourcesDao, View $view) { $this->authentication = $authentication; + $this->configuration = $configuration; $this->feedWriter = $feedWriter; $this->itemsDao = $itemsDao; $this->sourcesDao = $sourcesDao; @@ -49,7 +54,7 @@ public function __construct(Authentication $authentication, RSS2 $feedWriter, \d public function rss(Base $f3, array $params) { $this->authentication->needsLoggedInOrPublicMode(); - $this->feedWriter->setTitle($f3->get('rss_title')); + $this->feedWriter->setTitle($this->configuration->rssTitle); $this->feedWriter->setChannelElement('description', ''); $this->feedWriter->setSelfLink($this->view->base . 'feed'); @@ -64,7 +69,7 @@ public function rss(Base $f3, array $params) { if (count($_GET) > 0) { $options = $_GET; } - $options['items'] = $f3->get('rss_max_items'); + $options['items'] = $this->configuration->rssMaxItems; if (isset($params['tag'])) { $options['tag'] = $params['tag']; } @@ -110,7 +115,7 @@ public function rss(Base $f3, array $params) { $lastid = $item['id']; // mark as read - if ($f3->get('rss_mark_as_read') == 1 && $lastid !== null) { + if ($this->configuration->rssMarkAsRead && $lastid !== null) { $this->itemsDao->mark($lastid); } } diff --git a/src/daos/Database.php b/src/daos/Database.php index 475164dbee..c6e294c321 100644 --- a/src/daos/Database.php +++ b/src/daos/Database.php @@ -2,6 +2,7 @@ namespace daos; +use helpers\Configuration; use Monolog\Logger; const PARAM_INT = 1; @@ -21,6 +22,9 @@ class Database { /** @var DatabaseInterface Instance of backend specific database access class */ private $backend = null; + /** @var Configuration configuration */ + private $configuration; + /** @var Logger */ private $logger; @@ -28,8 +32,9 @@ class Database { * establish connection and * create undefined tables */ - public function __construct(DatabaseInterface $backend, Logger $logger) { + public function __construct(Configuration $configuration, DatabaseInterface $backend, Logger $logger) { $this->backend = $backend; + $this->configuration = $configuration; $this->logger = $logger; } @@ -45,7 +50,7 @@ public function __call($name, $args) { if (method_exists($this->backend, $name)) { return call_user_func_array([$this->backend, $name], $args); } else { - $this->logger->error('Unimplemented method for ' . \F3::get('db_type') . ': ' . $name); + $this->logger->error('Unimplemented method for ' . $this->configuration->dbType . ': ' . $name); } } } diff --git a/src/daos/Items.php b/src/daos/Items.php index 2403a78ddc..07075fa9a5 100644 --- a/src/daos/Items.php +++ b/src/daos/Items.php @@ -3,6 +3,7 @@ namespace daos; use helpers\Authentication; +use helpers\Configuration; use Monolog\Logger; /** @@ -20,6 +21,9 @@ class Items { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var Logger */ private $logger; @@ -28,9 +32,10 @@ class Items { * * @return void */ - public function __construct(Authentication $authentication, Logger $logger, ItemsInterface $backend) { + public function __construct(Authentication $authentication, Configuration $configuration, Logger $logger, ItemsInterface $backend) { $this->authentication = $authentication; $this->backend = $backend; + $this->configuration = $configuration; $this->logger = $logger; } @@ -46,7 +51,7 @@ public function __call($name, $args) { if (method_exists($this->backend, $name)) { return call_user_func_array([$this->backend, $name], $args); } else { - $this->logger->error('Unimplemented method for ' . \F3::get('db_type') . ': ' . $name); + $this->logger->error('Unimplemented method for ' . $this->configuration->dbType . ': ' . $name); } } @@ -79,7 +84,7 @@ public function get($options = []) { 'starred' => false, 'offset' => 0, 'search' => false, - 'items' => \F3::get('items_perpage') + 'items' => $this->configuration->itemsPerpage ], $options ); diff --git a/src/daos/Sources.php b/src/daos/Sources.php index 39f0a9601a..da56feea07 100644 --- a/src/daos/Sources.php +++ b/src/daos/Sources.php @@ -3,6 +3,7 @@ namespace daos; use helpers\Authentication; +use helpers\Configuration; use helpers\SpoutLoader; use Monolog\Logger; @@ -22,6 +23,9 @@ class Sources { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var Logger */ private $logger; @@ -33,8 +37,9 @@ class Sources { * * @return void */ - public function __construct(Authentication $authentication, Logger $logger, SourcesInterface $backend, SpoutLoader $spoutLoader) { + public function __construct(Authentication $authentication, Configuration $configuration, Logger $logger, SourcesInterface $backend, SpoutLoader $spoutLoader) { $this->authentication = $authentication; + $this->configuration = $configuration; $this->backend = $backend; $this->logger = $logger; $this->spoutLoader = $spoutLoader; @@ -52,7 +57,7 @@ public function __call($name, $args) { if (method_exists($this->backend, $name)) { return call_user_func_array([$this->backend, $name], $args); } else { - $this->logger->error('Unimplemented method for ' . \F3::get('db_type') . ': ' . $name); + $this->logger->error('Unimplemented method for ' . $this->configuration->dbType . ': ' . $name); } } diff --git a/src/daos/Tags.php b/src/daos/Tags.php index 75a19b6e58..5994438f67 100644 --- a/src/daos/Tags.php +++ b/src/daos/Tags.php @@ -3,6 +3,7 @@ namespace daos; use helpers\Authentication; +use helpers\Configuration; use Monolog\Logger; /** @@ -19,6 +20,9 @@ class Tags { /** @var Authentication authentication helper */ private $authentication; + /** @var Configuration configuration */ + private $configuration; + /** @var Logger */ private $logger; @@ -27,9 +31,10 @@ class Tags { * * @return void */ - public function __construct(Authentication $authentication, Logger $logger, TagsInterface $backend) { + public function __construct(Authentication $authentication, Configuration $configuration, Logger $logger, TagsInterface $backend) { $this->authentication = $authentication; $this->backend = $backend; + $this->configuration = $configuration; $this->logger = $logger; } @@ -60,7 +65,7 @@ public function __call($name, $args) { if (method_exists($this->backend, $name)) { return call_user_func_array([$this->backend, $name], $args); } else { - $this->logger->error('Unimplemented method for ' . \F3::get('db_type') . ': ' . $name); + $this->logger->error('Unimplemented method for ' . $this->configuration->dbType . ': ' . $name); } } } diff --git a/src/daos/mysql/Database.php b/src/daos/mysql/Database.php index 0e2c2983e7..144c680e52 100644 --- a/src/daos/mysql/Database.php +++ b/src/daos/mysql/Database.php @@ -2,6 +2,7 @@ namespace daos\mysql; +use helpers\Configuration; use Monolog\Logger; /** @@ -12,6 +13,9 @@ * @author Tobias Zeising */ class Database implements \daos\DatabaseInterface { + /** @var Configuration configuration */ + private $configuration; + /** @var \DB\SQL database connection */ private $connection; @@ -24,7 +28,8 @@ class Database implements \daos\DatabaseInterface { * * @return void */ - public function __construct(\DB\SQL $connection, Logger $logger) { + public function __construct(Configuration $configuration, \DB\SQL $connection, Logger $logger) { + $this->configuration = $configuration; $this->connection = $connection; $this->logger = $logger; @@ -39,9 +44,9 @@ public function __construct(\DB\SQL $connection, Logger $logger) { } } - if (!in_array(\F3::get('db_prefix') . 'items', $tables, true)) { + if (!in_array($this->configuration->dbPrefix . 'items', $tables, true)) { $this->exec(' - CREATE TABLE ' . \F3::get('db_prefix') . 'items ( + CREATE TABLE ' . $this->configuration->dbPrefix . 'items ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY , datetime DATETIME NOT NULL , title TEXT NOT NULL , @@ -60,14 +65,14 @@ public function __construct(\DB\SQL $connection, Logger $logger) { '); $this->exec(' CREATE TRIGGER insert_updatetime_trigger - BEFORE INSERT ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + BEFORE INSERT ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN SET NEW.updatetime = NOW(); END; '); $this->exec(' CREATE TRIGGER update_updatetime_trigger - BEFORE UPDATE ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + BEFORE UPDATE ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN SET NEW.updatetime = NOW(); END; @@ -75,9 +80,9 @@ public function __construct(\DB\SQL $connection, Logger $logger) { } $isNewestSourcesTable = false; - if (!in_array(\F3::get('db_prefix') . 'sources', $tables, true)) { + if (!in_array($this->configuration->dbPrefix . 'sources', $tables, true)) { $this->exec(' - CREATE TABLE ' . \F3::get('db_prefix') . 'sources ( + CREATE TABLE ' . $this->configuration->dbPrefix . 'sources ( id INT NOT NULL AUTO_INCREMENT PRIMARY KEY , title TEXT NOT NULL , tags TEXT, @@ -93,19 +98,19 @@ public function __construct(\DB\SQL $connection, Logger $logger) { } // version 1 or new - if (!in_array(\F3::get('db_prefix') . 'version', $tables, true)) { + if (!in_array($this->configuration->dbPrefix . 'version', $tables, true)) { $this->exec(' - CREATE TABLE ' . \F3::get('db_prefix') . 'version ( + CREATE TABLE ' . $this->configuration->dbPrefix . 'version ( version INT ) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (8); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (8); '); $this->exec(' - CREATE TABLE ' . \F3::get('db_prefix') . 'tags ( + CREATE TABLE ' . $this->configuration->dbPrefix . 'tags ( tag TEXT NOT NULL, color VARCHAR(7) NOT NULL ) ENGINE = InnoDB DEFAULT CHARSET=utf8mb4; @@ -113,57 +118,57 @@ public function __construct(\DB\SQL $connection, Logger $logger) { if ($isNewestSourcesTable === false) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'sources ADD tags TEXT; + ALTER TABLE ' . $this->configuration->dbPrefix . 'sources ADD tags TEXT; '); } } else { - $version = @$this->exec('SELECT version FROM ' . \F3::get('db_prefix') . 'version ORDER BY version DESC LIMIT 0, 1'); + $version = @$this->exec('SELECT version FROM ' . $this->configuration->dbPrefix . 'version ORDER BY version DESC LIMIT 0, 1'); $version = $version[0]['version']; if (strnatcmp($version, '3') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'sources ADD lastupdate INT; + ALTER TABLE ' . $this->configuration->dbPrefix . 'sources ADD lastupdate INT; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (3); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (3); '); } if (strnatcmp($version, '4') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD updatetime DATETIME; + ALTER TABLE ' . $this->configuration->dbPrefix . 'items ADD updatetime DATETIME; '); $this->exec(' CREATE TRIGGER insert_updatetime_trigger - BEFORE INSERT ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + BEFORE INSERT ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN SET NEW.updatetime = NOW(); END; '); $this->exec(' CREATE TRIGGER update_updatetime_trigger - BEFORE UPDATE ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + BEFORE UPDATE ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN SET NEW.updatetime = NOW(); END; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (4); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (4); '); } if (strnatcmp($version, '5') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD author VARCHAR(255); + ALTER TABLE ' . $this->configuration->dbPrefix . 'items ADD author VARCHAR(255); '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (5); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (5); '); } if (strnatcmp($version, '6') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'sources ADD filter TEXT; + ALTER TABLE ' . $this->configuration->dbPrefix . 'sources ADD filter TEXT; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (6); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (6); '); } // Jump straight from v6 to v8 due to bug in previous version of the code @@ -171,47 +176,47 @@ public function __construct(\DB\SQL $connection, Logger $logger) { // set the database version to "7" for initial installs. if (strnatcmp($version, '8') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'sources ADD lastentry INT; + ALTER TABLE ' . $this->configuration->dbPrefix . 'sources ADD lastentry INT; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (8); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (8); '); } if (strnatcmp($version, '9') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD shared BOOL; + ALTER TABLE ' . $this->configuration->dbPrefix . 'items ADD shared BOOL; '); $this->exec(' - INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (9); + INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (9); '); } if (strnatcmp($version, '10') < 0) { $this->exec([ - 'ALTER TABLE `' . \F3::get('db_prefix') . 'items` CONVERT TO CHARACTER SET utf8mb4;', - 'ALTER TABLE `' . \F3::get('db_prefix') . 'sources` CONVERT TO CHARACTER SET utf8mb4;', - 'ALTER TABLE `' . \F3::get('db_prefix') . 'tags` CONVERT TO CHARACTER SET utf8mb4;', - 'ALTER TABLE `' . \F3::get('db_prefix') . 'version` CONVERT TO CHARACTER SET utf8mb4;', - 'INSERT INTO `' . \F3::get('db_prefix') . 'version` (version) VALUES (10);' + 'ALTER TABLE `' . $this->configuration->dbPrefix . 'items` CONVERT TO CHARACTER SET utf8mb4;', + 'ALTER TABLE `' . $this->configuration->dbPrefix . 'sources` CONVERT TO CHARACTER SET utf8mb4;', + 'ALTER TABLE `' . $this->configuration->dbPrefix . 'tags` CONVERT TO CHARACTER SET utf8mb4;', + 'ALTER TABLE `' . $this->configuration->dbPrefix . 'version` CONVERT TO CHARACTER SET utf8mb4;', + 'INSERT INTO `' . $this->configuration->dbPrefix . 'version` (version) VALUES (10);' ]); } if (strnatcmp($version, '11') < 0) { $this->exec([ 'DROP TRIGGER insert_updatetime_trigger', 'DROP TRIGGER update_updatetime_trigger', - 'ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD lastseen DATETIME', - 'UPDATE ' . \F3::get('db_prefix') . 'items SET lastseen = CURRENT_TIMESTAMP', + 'ALTER TABLE ' . $this->configuration->dbPrefix . 'items ADD lastseen DATETIME', + 'UPDATE ' . $this->configuration->dbPrefix . 'items SET lastseen = CURRENT_TIMESTAMP', // Needs to be a trigger since MySQL before 5.6.5 does not support default value for DATETIME. // https://dev.mysql.com/doc/relnotes/mysql/5.6/en/news-5-6-5.html#mysqld-5-6-5-data-types // Needs to be a single trigger due to MySQL before 5.7.2 not supporting multiple triggers for the same event on the same table. // https://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-2.html#mysqld-5-7-2-triggers - 'CREATE TRIGGER ' . \F3::get('db_prefix') . 'items_insert_trigger - BEFORE INSERT ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + 'CREATE TRIGGER ' . $this->configuration->dbPrefix . 'items_insert_trigger + BEFORE INSERT ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN SET NEW.updatetime = NOW(); SET NEW.lastseen = NOW(); END;', - 'CREATE TRIGGER ' . \F3::get('db_prefix') . 'items_update_trigger - BEFORE UPDATE ON ' . \F3::get('db_prefix') . 'items FOR EACH ROW + 'CREATE TRIGGER ' . $this->configuration->dbPrefix . 'items_update_trigger + BEFORE UPDATE ON ' . $this->configuration->dbPrefix . 'items FOR EACH ROW BEGIN IF ( OLD.unread <> NEW.unread OR @@ -220,21 +225,21 @@ public function __construct(\DB\SQL $connection, Logger $logger) { SET NEW.updatetime = NOW(); END IF; END;', - 'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (11)' + 'INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (11)' ]); } if (strnatcmp($version, '12') < 0) { $this->exec([ - 'UPDATE ' . \F3::get('db_prefix') . 'items SET updatetime = datetime WHERE updatetime IS NULL', - 'ALTER TABLE ' . \F3::get('db_prefix') . 'items MODIFY updatetime DATETIME NOT NULL', - 'ALTER TABLE ' . \F3::get('db_prefix') . 'items MODIFY lastseen DATETIME NOT NULL', - 'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (12)' + 'UPDATE ' . $this->configuration->dbPrefix . 'items SET updatetime = datetime WHERE updatetime IS NULL', + 'ALTER TABLE ' . $this->configuration->dbPrefix . 'items MODIFY updatetime DATETIME NOT NULL', + 'ALTER TABLE ' . $this->configuration->dbPrefix . 'items MODIFY lastseen DATETIME NOT NULL', + 'INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (12)' ]); } if (strnatcmp($version, '13') < 0) { $this->exec([ - 'UPDATE ' . \F3::get('db_prefix') . "sources SET spout = 'spouts\\\\rss\\\\fulltextrss' WHERE spout = 'spouts\\\\rss\\\\instapaper'", - 'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (13)' + 'UPDATE ' . $this->configuration->dbPrefix . "sources SET spout = 'spouts\\\\rss\\\\fulltextrss' WHERE spout = 'spouts\\\\rss\\\\instapaper'", + 'INSERT INTO ' . $this->configuration->dbPrefix . 'version (version) VALUES (13)' ]); } } @@ -297,6 +302,6 @@ public function commit() { * @return void */ public function optimize() { - @$this->exec('OPTIMIZE TABLE `' . \F3::get('db_prefix') . 'sources`, `' . \F3::get('db_prefix') . 'items`'); + @$this->exec('OPTIMIZE TABLE `' . $this->configuration->dbPrefix . 'sources`, `' . $this->configuration->dbPrefix . 'items`'); } } diff --git a/src/daos/mysql/Items.php b/src/daos/mysql/Items.php index 5c0d9e53df..7ba7a22873 100644 --- a/src/daos/mysql/Items.php +++ b/src/daos/mysql/Items.php @@ -3,6 +3,7 @@ namespace daos\mysql; use DateTime; +use helpers\Configuration; use Monolog\Logger; /** @@ -20,13 +21,17 @@ class Items implements \daos\ItemsInterface { /** @var class-string SQL helper */ protected static $stmt = Statements::class; + /** @var Configuration configuration */ + private $configuration; + /** @var \daos\Database database connection */ protected $database; /** @var Logger */ private $logger; - public function __construct(Logger $logger, \daos\Database $database) { + public function __construct(Logger $logger, Configuration $configuration, \daos\Database $database) { + $this->configuration = $configuration; $this->database = $database; $this->logger = $logger; } @@ -48,7 +53,7 @@ public function mark($id) { } // i used string concatenation after validating $id - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . "items SET unread=? WHERE id IN ($id)", false); + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . "items SET unread=? WHERE id IN ($id)", false); } /** @@ -64,7 +69,7 @@ public function unmark($id) { } elseif (!is_numeric($id)) { return; } - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . "items SET unread=? WHERE id IN ($id)", true); + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . "items SET unread=? WHERE id IN ($id)", true); } /** @@ -75,7 +80,7 @@ public function unmark($id) { * @return void */ public function starr($id) { - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'items SET starred=:bool WHERE id=:id', [ + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'items SET starred=:bool WHERE id=:id', [ ':bool' => true, ':id' => $id ]); @@ -89,7 +94,7 @@ public function starr($id) { * @return void */ public function unstarr($id) { - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'items SET starred=:bool WHERE id=:id', [ + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'items SET starred=:bool WHERE id=:id', [ ':bool' => false, ':id' => $id ]); @@ -103,7 +108,7 @@ public function unstarr($id) { * @return void */ public function add($values) { - $this->database->exec('INSERT INTO ' . \F3::get('db_prefix') . 'items ( + $this->database->exec('INSERT INTO ' . $this->configuration->dbPrefix . 'items ( datetime, title, content, @@ -152,7 +157,7 @@ public function add($values) { * @return bool */ public function exists($uid) { - $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . \F3::get('db_prefix') . 'items WHERE uid=:uid', + $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . $this->configuration->dbPrefix . 'items WHERE uid=:uid', [':uid' => [$uid, \PDO::PARAM_STR]]); return $res[0]['amount'] > 0; @@ -175,7 +180,7 @@ public function findAll($itemsInFeed, $sourceId) { array_walk($itemsInFeed, function(&$value) { $value = $this->database->quote($value); }); - $query = 'SELECT id, uid AS uid FROM ' . \F3::get('db_prefix') . 'items WHERE source = ' . $this->database->quote($sourceId) . ' AND uid IN (' . implode(',', $itemsInFeed) . ')'; + $query = 'SELECT id, uid AS uid FROM ' . $this->configuration->dbPrefix . 'items WHERE source = ' . $this->database->quote($sourceId) . ' AND uid IN (' . implode(',', $itemsInFeed) . ')'; $res = $this->database->exec($query); foreach ($res as $row) { $uid = $row['uid']; @@ -194,7 +199,7 @@ public function findAll($itemsInFeed, $sourceId) { */ public function updateLastSeen(array $itemIds) { $stmt = static::$stmt; - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'items SET lastseen = CURRENT_TIMESTAMP + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'items SET lastseen = CURRENT_TIMESTAMP WHERE ' . $stmt::intRowMatches('id', $itemIds)); } @@ -207,11 +212,11 @@ public function updateLastSeen(array $itemIds) { */ public function cleanup(DateTime $date = null) { $stmt = static::$stmt; - $this->database->exec('DELETE FROM ' . \F3::get('db_prefix') . 'items + $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'items WHERE source NOT IN ( - SELECT id FROM ' . \F3::get('db_prefix') . 'sources)'); + SELECT id FROM ' . $this->configuration->dbPrefix . 'sources)'); if ($date !== null) { - $this->database->exec('DELETE FROM ' . \F3::get('db_prefix') . 'items + $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'items WHERE ' . $stmt::isFalse('starred') . ' AND lastseen<:date', [':date' => $date->format('Y-m-d') . ' 00:00:00'] ); @@ -239,7 +244,7 @@ public function get($options = []) { // only unread elseif (isset($options['type']) && $options['type'] === 'unread') { $where[] = $stmt::isTrue('unread'); - if (\F3::get('unread_order') === 'asc') { + if ($this->configuration->unreadOrder === 'asc') { $order = 'ASC'; } } @@ -314,7 +319,7 @@ public function get($options = []) { if (isset($options['extraIds']) && count($options['extraIds']) > 0 // limit the query to a sensible max - && count($options['extraIds']) <= \F3::get('items_perpage')) { + && count($options['extraIds']) <= $this->configuration->itemsPerpage) { $extra_ids_stmt = $stmt::intRowMatches('items.id', $options['extraIds']); if ($extra_ids_stmt !== null) { $where_ids = $extra_ids_stmt; @@ -326,7 +331,7 @@ public function get($options = []) { // set limit if (!isset($options['items']) || !is_numeric($options['items']) || $options['items'] > 200) { - $options['items'] = \F3::get('items_perpage'); + $options['items'] = $this->configuration->itemsPerpage; } // set offset @@ -336,7 +341,7 @@ public function get($options = []) { // first check whether more items are available $result = $this->database->exec('SELECT items.id - FROM ' . \F3::get('db_prefix') . 'items AS items, ' . \F3::get('db_prefix') . 'sources AS sources + FROM ' . $this->configuration->dbPrefix . 'items AS items, ' . $this->configuration->dbPrefix . 'sources AS sources WHERE items.source=sources.id AND ' . $where_sql . ' LIMIT 1 OFFSET ' . ($options['offset'] + $options['items']), $params); $this->hasMore = count($result) > 0; @@ -344,7 +349,7 @@ public function get($options = []) { // get items from database $select = 'SELECT items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags - FROM ' . \F3::get('db_prefix') . 'items AS items, ' . \F3::get('db_prefix') . 'sources AS sources + FROM ' . $this->configuration->dbPrefix . 'items AS items, ' . $this->configuration->dbPrefix . 'sources AS sources WHERE items.source=sources.id AND'; $order_sql = 'ORDER BY items.datetime ' . $order . ', items.id ' . $order; @@ -402,7 +407,7 @@ public function sync($sinceId, DateTime $notBefore, DateTime $since, $howMany) { $stmt = static::$stmt; $query = 'SELECT items.id, datetime, items.title AS title, content, unread, starred, source, thumbnail, icon, uid, link, updatetime, author, sources.title as sourcetitle, sources.tags as tags - FROM ' . \F3::get('db_prefix') . 'items AS items, ' . \F3::get('db_prefix') . 'sources AS sources + FROM ' . $this->configuration->dbPrefix . 'items AS items, ' . $this->configuration->dbPrefix . 'sources AS sources WHERE items.source=sources.id AND (' . $stmt::isTrue('unread') . ' OR ' . $stmt::isTrue('starred') . @@ -439,7 +444,7 @@ public function lowestIdOfInterest() { $stmt = static::$stmt; $lowest = $stmt::ensureRowTypes( $this->database->exec( - 'SELECT id FROM ' . \F3::get('db_prefix') . 'items AS items + 'SELECT id FROM ' . $this->configuration->dbPrefix . 'items AS items WHERE ' . $stmt::isTrue('unread') . ' OR ' . $stmt::isTrue('starred') . ' ORDER BY id LIMIT 1'), @@ -461,7 +466,7 @@ public function lastId() { $stmt = static::$stmt; $lastId = $stmt::ensureRowTypes( $this->database->exec( - 'SELECT id FROM ' . \F3::get('db_prefix') . 'items AS items + 'SELECT id FROM ' . $this->configuration->dbPrefix . 'items AS items ORDER BY id DESC LIMIT 1'), ['id' => \daos\PARAM_INT] ); @@ -480,7 +485,7 @@ public function lastId() { public function getThumbnails() { $thumbnails = []; $result = $this->database->exec('SELECT thumbnail - FROM ' . \F3::get('db_prefix') . 'items + FROM ' . $this->configuration->dbPrefix . 'items WHERE thumbnail!=""'); foreach ($result as $thumb) { $thumbnails[] = $thumb['thumbnail']; @@ -497,7 +502,7 @@ public function getThumbnails() { public function getIcons() { $icons = []; $result = $this->database->exec('SELECT icon - FROM ' . \F3::get('db_prefix') . 'items + FROM ' . $this->configuration->dbPrefix . 'items WHERE icon!=""'); foreach ($result as $icon) { $icons[] = $icon['icon']; @@ -515,7 +520,7 @@ public function getIcons() { */ public function hasThumbnail($thumbnail) { $res = $this->database->exec('SELECT count(*) AS amount - FROM ' . \F3::get('db_prefix') . 'items + FROM ' . $this->configuration->dbPrefix . 'items WHERE thumbnail=:thumbnail', [':thumbnail' => $thumbnail]); $amount = $res[0]['amount']; @@ -535,7 +540,7 @@ public function hasThumbnail($thumbnail) { */ public function hasIcon($icon) { $res = $this->database->exec('SELECT count(*) AS amount - FROM ' . \F3::get('db_prefix') . 'items + FROM ' . $this->configuration->dbPrefix . 'items WHERE icon=:icon', [':icon' => $icon]); @@ -584,7 +589,7 @@ public function getLastIcon($sourceid) { return null; } - $res = $this->database->exec('SELECT icon FROM ' . \F3::get('db_prefix') . 'items WHERE source=:sourceid AND icon!=\'\' AND icon IS NOT NULL ORDER BY ID DESC LIMIT 1', + $res = $this->database->exec('SELECT icon FROM ' . $this->configuration->dbPrefix . 'items WHERE source=:sourceid AND icon!=\'\' AND icon IS NOT NULL ORDER BY ID DESC LIMIT 1', [':sourceid' => $sourceid]); if (count($res) === 1) { return $res[0]['icon']; @@ -601,7 +606,7 @@ public function getLastIcon($sourceid) { public function numberOfUnread() { $stmt = static::$stmt; $res = $this->database->exec('SELECT count(*) AS amount - FROM ' . \F3::get('db_prefix') . 'items + FROM ' . $this->configuration->dbPrefix . 'items WHERE ' . $stmt::isTrue('unread')); return $res[0]['amount']; @@ -618,7 +623,7 @@ public function stats() { COUNT(*) AS total, ' . $stmt::sumBool('unread') . ' AS unread, ' . $stmt::sumBool('starred') . ' AS starred - FROM ' . \F3::get('db_prefix') . 'items;'); + FROM ' . $this->configuration->dbPrefix . 'items;'); $res = $stmt::ensureRowTypes($res, [ 'total' => \daos\PARAM_INT, 'unread' => \daos\PARAM_INT, @@ -636,7 +641,7 @@ public function stats() { public function lastUpdate() { $res = $this->database->exec('SELECT MAX(updatetime) AS last_update_time - FROM ' . \F3::get('db_prefix') . 'items;'); + FROM ' . $this->configuration->dbPrefix . 'items;'); return $res[0]['last_update_time']; } @@ -651,8 +656,8 @@ public function lastUpdate() { public function statuses(DateTime $since) { $stmt = static::$stmt; $res = $this->database->exec('SELECT id, unread, starred - FROM ' . \F3::get('db_prefix') . 'items - WHERE ' . \F3::get('db_prefix') . 'items.updatetime > :since;', + FROM ' . $this->configuration->dbPrefix . 'items + WHERE ' . $this->configuration->dbPrefix . 'items.updatetime > :since;', [':since' => [$since->format('Y-m-d H:i:s'), \PDO::PARAM_STR]]); $res = $stmt::ensureRowTypes($res, [ 'id' => \daos\PARAM_INT, @@ -736,7 +741,7 @@ public function bulkStatusUpdate(array $statuses) { ':statusUpdate' => [$q['datetime'], \PDO::PARAM_STR] ]; $updated = $this->database->exec( - 'UPDATE ' . \F3::get('db_prefix') . 'items + 'UPDATE ' . $this->configuration->dbPrefix . 'items SET ' . implode(', ', array_values($q['updates'])) . ' WHERE id = :id AND updatetime < :statusUpdate', $params); if ($updated == 0) { @@ -744,7 +749,7 @@ public function bulkStatusUpdate(array $statuses) { // be updated to ensure client side consistency of // statuses. $this->database->exec( - 'UPDATE ' . \F3::get('db_prefix') . 'items + 'UPDATE ' . $this->configuration->dbPrefix . 'items SET ' . $stmt::rowTouch('updatetime') . ' WHERE id = :id', [':id' => [$id, \PDO::PARAM_INT]]); } diff --git a/src/daos/mysql/Sources.php b/src/daos/mysql/Sources.php index 88f362d8b2..274c3218ea 100644 --- a/src/daos/mysql/Sources.php +++ b/src/daos/mysql/Sources.php @@ -2,6 +2,8 @@ namespace daos\mysql; +use helpers\Configuration; + /** * Class for accessing persistent saved sources -- mysql * @@ -13,10 +15,14 @@ class Sources implements \daos\SourcesInterface { /** @var class-string SQL helper */ protected static $stmt = Statements::class; + /** @var Configuration configuration */ + private $configuration; + /** @var \daos\Database database connection */ protected $database; - public function __construct(\daos\Database $database) { + public function __construct(Configuration $configuration, \daos\Database $database) { + $this->configuration = $configuration; $this->database = $database; } @@ -34,7 +40,7 @@ public function __construct(\daos\Database $database) { public function add($title, array $tags, $filter, $spout, array $params) { $stmt = static::$stmt; - return $this->database->insert('INSERT INTO ' . \F3::get('db_prefix') . 'sources (title, tags, filter, spout, params) VALUES (:title, :tags, :filter, :spout, :params)', [ + return $this->database->insert('INSERT INTO ' . $this->configuration->dbPrefix . 'sources (title, tags, filter, spout, params) VALUES (:title, :tags, :filter, :spout, :params)', [ ':title' => trim($title), ':tags' => $stmt::csvRow($tags), ':filter' => $filter, @@ -57,7 +63,7 @@ public function add($title, array $tags, $filter, $spout, array $params) { */ public function edit($id, $title, array $tags, $filter, $spout, array $params) { $stmt = static::$stmt; - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'sources SET title=:title, tags=:tags, filter=:filter, spout=:spout, params=:params WHERE id=:id', [ + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET title=:title, tags=:tags, filter=:filter, spout=:spout, params=:params WHERE id=:id', [ ':title' => trim($title), ':tags' => $stmt::csvRow($tags), ':filter' => $filter, @@ -75,10 +81,10 @@ public function edit($id, $title, array $tags, $filter, $spout, array $params) { * @return void */ public function delete($id) { - $this->database->exec('DELETE FROM ' . \F3::get('db_prefix') . 'sources WHERE id=:id', [':id' => $id]); + $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]); // delete items of this source - $this->database->exec('DELETE FROM ' . \F3::get('db_prefix') . 'items WHERE source=:id', [':id' => $id]); + $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'items WHERE source=:id', [':id' => $id]); } /** @@ -103,7 +109,7 @@ public function error($id, $error) { $setarg = ':error'; } - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'sources SET error=' . $setarg . ' WHERE id=:id', $arr); + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET error=' . $setarg . ' WHERE id=:id', $arr); } /** @@ -115,14 +121,14 @@ public function error($id, $error) { * @return void */ public function saveLastUpdate($id, $lastEntry) { - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'sources SET lastupdate=:lastupdate WHERE id=:id', + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET lastupdate=:lastupdate WHERE id=:id', [ ':id' => $id, ':lastupdate' => time() ]); if ($lastEntry !== null) { - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'sources SET lastentry=:lastentry WHERE id=:id', + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'sources SET lastentry=:lastentry WHERE id=:id', [ ':id' => $id, ':lastentry' => $lastEntry @@ -136,7 +142,7 @@ public function saveLastUpdate($id, $lastEntry) { * @return mixed all sources */ public function getByLastUpdate() { - $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . \F3::get('db_prefix') . 'sources ORDER BY lastupdate ASC'); + $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources ORDER BY lastupdate ASC'); return $ret; } @@ -153,7 +159,7 @@ public function get($id = null) { $stmt = static::$stmt; // select source by id if specified or return all sources if (isset($id)) { - $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . \F3::get('db_prefix') . 'sources WHERE id=:id', [':id' => $id]); + $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]); $ret = $stmt::ensureRowTypes($ret, ['id' => \daos\PARAM_INT]); if (isset($ret[0])) { $ret = $ret[0]; @@ -161,7 +167,7 @@ public function get($id = null) { $ret = null; } } else { - $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . \F3::get('db_prefix') . 'sources ORDER BY error DESC, lower(title) ASC'); + $ret = $this->database->exec('SELECT id, title, tags, spout, params, filter, error, lastupdate, lastentry FROM ' . $this->configuration->dbPrefix . 'sources ORDER BY error DESC, lower(title) ASC'); $ret = $stmt::ensureRowTypes($ret, [ 'id' => \daos\PARAM_INT, 'tags' => \daos\PARAM_CSV @@ -180,8 +186,8 @@ public function getWithUnread() { $stmt = static::$stmt; $ret = $this->database->exec('SELECT sources.id, sources.title, COUNT(items.id) AS unread - FROM ' . \F3::get('db_prefix') . 'sources AS sources - LEFT OUTER JOIN ' . \F3::get('db_prefix') . 'items AS items + FROM ' . $this->configuration->dbPrefix . 'sources AS sources + LEFT OUTER JOIN ' . $this->configuration->dbPrefix . 'items AS items ON (items.source=sources.id AND ' . $stmt::isTrue('items.unread') . ') GROUP BY sources.id, sources.title ORDER BY lower(sources.title) ASC'); @@ -203,12 +209,12 @@ public function getWithIcon() { sources.id, sources.title, sources.tags, sources.spout, sources.params, sources.filter, sources.error, sources.lastentry, sourceicons.icon AS icon - FROM ' . \F3::get('db_prefix') . 'sources AS sources + FROM ' . $this->configuration->dbPrefix . 'sources AS sources LEFT OUTER JOIN (SELECT items.source, icon - FROM ' . \F3::get('db_prefix') . 'items AS items, + FROM ' . $this->configuration->dbPrefix . 'items AS items, (SELECT source, MAX(id) as maxid - FROM ' . \F3::get('db_prefix') . 'items AS items + FROM ' . $this->configuration->dbPrefix . 'items AS items WHERE icon IS NOT NULL AND icon != \'\' GROUP BY items.source) AS icons WHERE items.id=icons.maxid AND items.source=icons.source @@ -248,7 +254,7 @@ public function isValid($name, $value) { * @return mixed all sources */ public function getAllTags() { - $result = $this->database->exec('SELECT tags FROM ' . \F3::get('db_prefix') . 'sources'); + $result = $this->database->exec('SELECT tags FROM ' . $this->configuration->dbPrefix . 'sources'); $tags = []; foreach ($result as $res) { $tags = array_merge($tags, explode(',', $res['tags'])); @@ -266,7 +272,7 @@ public function getAllTags() { * @return mixed tags of a source */ public function getTags($id) { - $result = $this->database->exec('SELECT tags FROM ' . \F3::get('db_prefix') . 'sources WHERE id=:id', [':id' => $id]); + $result = $this->database->exec('SELECT tags FROM ' . $this->configuration->dbPrefix . 'sources WHERE id=:id', [':id' => $id]); $tags = []; $tags = array_merge($tags, explode(',', $result[0]['tags'])); $tags = array_unique($tags); @@ -286,7 +292,7 @@ public function getTags($id) { */ public function checkIfExists($title, $spout, array $params) { // Check if a entry exists with same title, spout and params - $result = $this->database->exec('SELECT id FROM ' . \F3::get('db_prefix') . 'sources WHERE title=:title AND spout=:spout AND params=:params', [ + $result = $this->database->exec('SELECT id FROM ' . $this->configuration->dbPrefix . 'sources WHERE title=:title AND spout=:spout AND params=:params', [ ':title' => trim($title), ':spout' => $spout, ':params' => htmlentities(json_encode($params)) diff --git a/src/daos/mysql/Tags.php b/src/daos/mysql/Tags.php index 8e1bd513fb..df2ab3a87d 100644 --- a/src/daos/mysql/Tags.php +++ b/src/daos/mysql/Tags.php @@ -2,6 +2,8 @@ namespace daos\mysql; +use helpers\Configuration; + /** * Class for accessing persistent saved sources -- mysql * @@ -13,10 +15,14 @@ class Tags implements \daos\TagsInterface { /** @var class-string SQL helper */ protected static $stmt = Statements::class; + /** @var Configuration configuration */ + private $configuration; + /** @var \daos\Database database connection */ protected $database; - public function __construct(\daos\Database $database) { + public function __construct(Configuration $configuration, \daos\Database $database) { + $this->configuration = $configuration; $this->database = $database; } @@ -30,12 +36,12 @@ public function __construct(\daos\Database $database) { */ public function saveTagColor($tag, $color) { if ($this->hasTag($tag) === true) { - $this->database->exec('UPDATE ' . \F3::get('db_prefix') . 'tags SET color=:color WHERE tag=:tag', [ + $this->database->exec('UPDATE ' . $this->configuration->dbPrefix . 'tags SET color=:color WHERE tag=:tag', [ ':tag' => $tag, ':color' => $color ]); } else { - $this->database->exec('INSERT INTO ' . \F3::get('db_prefix') . 'tags (tag, color) VALUES (:tag, :color)', [ + $this->database->exec('INSERT INTO ' . $this->configuration->dbPrefix . 'tags (tag, color) VALUES (:tag, :color)', [ ':tag' => $tag, ':color' => $color, ]); @@ -78,7 +84,7 @@ public function autocolorTag($tag) { public function get() { return $this->database->exec('SELECT tag, color - FROM ' . \F3::get('db_prefix') . 'tags + FROM ' . $this->configuration->dbPrefix . 'tags ORDER BY LOWER(tag);'); } @@ -90,9 +96,9 @@ public function get() { public function getWithUnread() { $stmt = static::$stmt; $select = 'SELECT tag, color, COUNT(items.id) AS unread - FROM ' . \F3::get('db_prefix') . 'tags AS tags, - ' . \F3::get('db_prefix') . 'sources AS sources - LEFT OUTER JOIN ' . \F3::get('db_prefix') . 'items AS items + FROM ' . $this->configuration->dbPrefix . 'tags AS tags, + ' . $this->configuration->dbPrefix . 'sources AS sources + LEFT OUTER JOIN ' . $this->configuration->dbPrefix . 'items AS items ON (items.source=sources.id AND ' . $stmt::isTrue('items.unread') . ') WHERE ' . $stmt::csvRowMatches('sources.tags', 'tags.tag') . ' GROUP BY tags.tag, tags.color @@ -125,7 +131,7 @@ public function cleanup(array $tags) { * @return bool true if color is used by an tag */ private function isColorUsed($color) { - $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . \F3::get('db_prefix') . 'tags WHERE color=:color', [':color' => $color]); + $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . $this->configuration->dbPrefix . 'tags WHERE color=:color', [':color' => $color]); return $res[0]['amount'] > 0; } @@ -138,12 +144,12 @@ private function isColorUsed($color) { * @return bool true if color is used by an tag */ public function hasTag($tag) { - if (\F3::get('db_type') === 'mysql') { + if ($this->configuration->dbType === 'mysql') { $where = 'WHERE tag = _utf8mb4 :tag COLLATE utf8mb4_general_ci'; } else { $where = 'WHERE tag=:tag'; } - $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . \F3::get('db_prefix') . 'tags ' . $where, [':tag' => $tag]); + $res = $this->database->exec('SELECT COUNT(*) AS amount FROM ' . $this->configuration->dbPrefix . 'tags ' . $where, [':tag' => $tag]); return $res[0]['amount'] > 0; } @@ -156,6 +162,6 @@ public function hasTag($tag) { * @return void */ public function delete($tag) { - $this->database->exec('DELETE FROM ' . \F3::get('db_prefix') . 'tags WHERE tag=:tag', [':tag' => $tag]); + $this->database->exec('DELETE FROM ' . $this->configuration->dbPrefix . 'tags WHERE tag=:tag', [':tag' => $tag]); } } diff --git a/src/daos/pgsql/Database.php b/src/daos/pgsql/Database.php index 99ae67c120..2126354f8e 100644 --- a/src/daos/pgsql/Database.php +++ b/src/daos/pgsql/Database.php @@ -222,7 +222,7 @@ public function __construct(\DB\SQL $connection, Logger $logger) { if (strnatcmp($version, '13') < 0) { $this->exec([ "UPDATE sources SET spout = 'spouts\\rss\\fulltextrss' WHERE spout = 'spouts\\rss\\instapaper'", - 'INSERT INTO ' . \F3::get('db_prefix') . 'version (version) VALUES (13)' + 'INSERT INTO version (version) VALUES (13)' ]); } } diff --git a/src/daos/sqlite/Database.php b/src/daos/sqlite/Database.php index 72546835eb..9092ce6b98 100644 --- a/src/daos/sqlite/Database.php +++ b/src/daos/sqlite/Database.php @@ -185,7 +185,7 @@ public function __construct(\DB\SQL $connection, Logger $logger) { } if (strnatcmp($version, '9') < 0) { $this->exec(' - ALTER TABLE ' . \F3::get('db_prefix') . 'items ADD shared BOOL; + ALTER TABLE items ADD shared BOOL; '); $this->exec(' INSERT INTO version (version) VALUES (9); diff --git a/src/helpers/Anonymizer.php b/src/helpers/Anonymizer.php deleted file mode 100644 index d647b81355..0000000000 --- a/src/helpers/Anonymizer.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ -class Anonymizer { - /** - * @return bool whether or not we should anonymize urls - */ - private static function shouldAnonymize() { - return true; - } - - /** - * @return string the anonymizer string if we should anonymize otherwise blank - */ - public static function getAnonymizer() { - return self::shouldAnonymize() ? trim(\F3::get('anonymizer')) : ''; - } -} diff --git a/src/helpers/Authentication.php b/src/helpers/Authentication.php index 13026f5445..c8d2866e18 100644 --- a/src/helpers/Authentication.php +++ b/src/helpers/Authentication.php @@ -12,6 +12,9 @@ * @author Tobias Zeising */ class Authentication { + /** @var Configuration configuration */ + private $configuration; + /** @var bool loggedin */ private $loggedin = false; @@ -21,14 +24,15 @@ class Authentication { /** * start session and check login */ - public function __construct(Logger $logger) { + public function __construct(Configuration $configuration, Logger $logger, View $view) { + $this->configuration = $configuration; $this->logger = $logger; if ($this->enabled() === false) { return; } - $base_url = parse_url(\helpers\View::getBaseUrl()); + $base_url = parse_url($view->getBaseUrl()); // session cookie will be valid for one month. $cookie_expire = 3600 * 24 * 30; @@ -70,7 +74,7 @@ public function __construct(Logger $logger) { * @return bool */ public function enabled() { - return strlen(trim(\F3::get('username'))) != 0 && strlen(trim(\F3::get('password'))) != 0; + return strlen($this->configuration->username) != 0 && strlen($this->configuration->password) != 0; } /** @@ -83,13 +87,13 @@ public function enabled() { */ public function login($username, $password) { if ($this->enabled()) { - $usernameCorrect = $username === \F3::get('username'); - $hashedPassword = \F3::get('password'); + $usernameCorrect = $username === $this->configuration->username; + $hashedPassword = $this->configuration->password; // Passwords hashed with password_hash start with $, otherwise use the legacy path. $passwordCorrect = $hashedPassword !== '' && $hashedPassword[0] === '$' ? password_verify($password, $hashedPassword) - : hash('sha512', \F3::get('salt') . $password) === $hashedPassword; + : hash('sha512', $this->configuration->salt . $password) === $hashedPassword; $credentialsCorrect = $usernameCorrect && $passwordCorrect; if ($credentialsCorrect) { @@ -148,7 +152,7 @@ public function logout() { * @return void */ public function needsLoggedInOrPublicMode() { - if ($this->isLoggedin() !== true && \F3::get('public') != 1) { + if ($this->isLoggedin() !== true && !$this->configuration->public) { \F3::error(403); } } @@ -177,6 +181,6 @@ public function allowedToUpdate() { return $this->isLoggedin() === true || $_SERVER['REMOTE_ADDR'] === $_SERVER['SERVER_ADDR'] || $_SERVER['REMOTE_ADDR'] === '127.0.0.1' - || \F3::get('allow_public_update_access') == 1; + || $this->configuration->allowPublicUpdateAccess; } } diff --git a/src/helpers/Configuration.php b/src/helpers/Configuration.php new file mode 100644 index 0000000000..ef8c35b22c --- /dev/null +++ b/src/helpers/Configuration.php @@ -0,0 +1,229 @@ + $environment + */ + public function __construct($configPath = null, $environment = []) { + // read config.ini, if it exists + if ($configPath !== null && file_exists($configPath)) { + $config = parse_ini_file($configPath); + if ($config === false) { + throw new Exception('Error loading config.ini'); + } + } else { + $config = []; + } + + // overwrite config with ENV variables + if (isset($config['env_prefix'])) { + $this->envPrefix = $config['env_prefix']; + } + + $reflection = new ReflectionClass(self::class); + foreach ($reflection->getProperties() as $property) { + $configKey = strtolower(preg_replace('([[:upper:]]+)', '_$0', $property->getName())); + + if (isset($environment[strtoupper($this->envPrefix . $configKey)])) { + // Prefer the value from environment variable if present. + $value = $environment[strtoupper($this->envPrefix . $configKey)]; + } elseif (isset($environment[$this->envPrefix . $configKey])) { + // Also try lowercase spelling. + $value = $environment[$this->envPrefix . $configKey]; + } elseif (isset($config[$configKey])) { + // Finally, try the value from config.ini. + $value = $config[$configKey]; + } else { + // Otherwise, just leave the default value. + continue; + } + + $value = trim($value); + + preg_match('(@var (?P\??)(?P[a-z]+))', $property->getDocComment(), $matches); + if ($matches['nullable'] === '?' && $value === '') { + // Keep the default value for empty nullables. + continue; + } + + $propertyName = $property->getName(); + $propertyType = $matches['type']; + if ($propertyType === 'bool') { + $value = (bool) $value; + } elseif ($propertyType === 'int') { + $value = (int) $value; + } elseif ($propertyType === 'string') { + // Should already be a string. + } else { + throw new Exception("Unknown type “${propertyType}” for property “${propertyName}”.", 1); + } + + $this->{$propertyName} = $value; + } + + // Interpolate variables in the config values. + $datadir = $this->datadir; + foreach (self::INTERPOLATED_PROPERTIES as $property) { + $value = $this->{$property}; + $this->{$property} = str_replace('%datadir%', $datadir, $value); + } + } +} diff --git a/src/helpers/ContentLoader.php b/src/helpers/ContentLoader.php index a2bd3d37a1..dd2615f460 100644 --- a/src/helpers/ContentLoader.php +++ b/src/helpers/ContentLoader.php @@ -12,6 +12,9 @@ * @author Tobias Zeising */ class ContentLoader { + /** @var Configuration configuration */ + private $configuration; + /** @var \daos\Database database for optimization */ private $database; @@ -45,7 +48,8 @@ class ContentLoader { /** * ctor */ - public function __construct(\daos\Database $database, IconStore $iconStore, Image $imageHelper, \daos\Items $itemsDao, Logger $logger, \daos\Sources $sourcesDao, SpoutLoader $spoutLoader, ThumbnailStore $thumbnailStore, WebClient $webClient) { + public function __construct(Configuration $configuration, \daos\Database $database, IconStore $iconStore, Image $imageHelper, \daos\Items $itemsDao, Logger $logger, \daos\Sources $sourcesDao, SpoutLoader $spoutLoader, ThumbnailStore $thumbnailStore, WebClient $webClient) { + $this->configuration = $configuration; $this->database = $database; $this->iconStore = $iconStore; $this->imageHelper = $imageHelper; @@ -143,7 +147,7 @@ public function fetch($source) { // current date $minDate = new \DateTime(); - $minDate->sub(new \DateInterval('P' . \F3::get('items_lifetime') . 'D')); + $minDate->sub(new \DateInterval('P' . $this->configuration->itemsLifetime . 'D')); $this->logger->debug('minimum date: ' . $minDate->format('Y-m-d H:i:s')); // insert new items in database @@ -173,7 +177,7 @@ public function fetch($source) { $itemDate = new \DateTime(); } if ($itemDate < $minDate) { - $this->logger->debug('item "' . $item->getTitle() . '" (' . $item->getDate() . ') older than ' . \F3::get('items_lifetime') . ' days'); + $this->logger->debug('item "' . $item->getTitle() . '" (' . $item->getDate() . ') older than ' . $this->configuration->itemsLifetime . ' days'); continue; } @@ -484,7 +488,7 @@ public function fetchTitle($data) { public function cleanup() { // cleanup orphaned and old items $this->logger->debug('cleanup orphaned and old items'); - $this->itemsDao->cleanup((int) \F3::get('items_lifetime')); + $this->itemsDao->cleanup($this->configuration->itemsLifetime); $this->logger->debug('cleanup orphaned and old items finished'); // delete orphaned thumbnails diff --git a/src/helpers/View.php b/src/helpers/View.php index 3bd286d758..77eb0a015d 100644 --- a/src/helpers/View.php +++ b/src/helpers/View.php @@ -16,10 +16,14 @@ class View { /** @var string current base url */ public $base = ''; + /** @var Configuration configuration */ + private $configuration; + /** * set global view vars */ - public function __construct() { + public function __construct(Configuration $configuration) { + $this->configuration = $configuration; $this->base = $this->getBaseUrl(); } @@ -28,12 +32,12 @@ public function __construct() { * config.ini this will be used. Otherwise base url will be generated by * globale server variables ($_SERVER). */ - public static function getBaseUrl() { + public function getBaseUrl() { $base = ''; // base url in config.ini file - if (strlen(trim(\F3::get('base_url'))) > 0) { - $base = \F3::get('base_url'); + if (strlen($this->configuration->baseUrl) > 0) { + $base = $this->configuration->baseUrl; $length = strlen($base); if ($length > 0 && substr($base, $length - 1, 1) !== '/') { $base .= '/'; diff --git a/src/helpers/ViewHelper.php b/src/helpers/ViewHelper.php index ee1fb56434..fb6af6e7a3 100644 --- a/src/helpers/ViewHelper.php +++ b/src/helpers/ViewHelper.php @@ -10,6 +10,13 @@ * @author Tobias Zeising */ class ViewHelper { + /** @var Configuration configuration */ + private $configuration; + + public function __construct(Configuration $configuration) { + $this->configuration = $configuration; + } + /** * Enclose all searchWords with $word * for later highlighing with CSS @@ -96,12 +103,12 @@ public static function date_iso8601($datestr) { * * @return string item content */ - public static function camoflauge($content) { + public function camoflauge($content) { if (empty($content)) { return $content; } - $camo = new \WillWashburn\Phpamo\Phpamo(\F3::get('camo_key'), \F3::get('camo_domain')); + $camo = new \WillWashburn\Phpamo\Phpamo($this->configuration->camoKey, $this->configuration->camoDomain); return preg_replace_callback("//i", function($matches) use ($camo) { return ''; @@ -118,7 +125,7 @@ public static function camoflauge($content) { * * @return array modified item */ - public static function preprocessEntry(array $item, \controllers\Tags $tagsController, array $tags = null, $search = null) { + public function preprocessEntry(array $item, \controllers\Tags $tagsController, array $tags = null, $search = null) { // parse tags and assign tag colors $item['tags'] = $tagsController->tagsAddColors($item['tags'], $tags); @@ -131,8 +138,8 @@ public static function preprocessEntry(array $item, \controllers\Tags $tagsContr $item['content'] = ViewHelper::highlight($item['content'], $search); } - if (\F3::get('camo_key') != '') { - $item['content'] = ViewHelper::camoflauge($item['content']); + if ($this->configuration->camoKey != '') { + $item['content'] = $this->camoflauge($item['content']); } $item['title'] = ViewHelper::lazyimg($item['title']); diff --git a/src/helpers/WebClient.php b/src/helpers/WebClient.php index 69e0b2b2ec..1c1bd06f5d 100644 --- a/src/helpers/WebClient.php +++ b/src/helpers/WebClient.php @@ -16,13 +16,17 @@ * @author Alexandre Rossi */ class WebClient { + /** @var Configuration configuration */ + private $configuration; + /** @var GuzzleHttp\Client */ private $httpClient; /** @var Logger */ private $logger; - public function __construct(Logger $logger) { + public function __construct(Configuration $configuration, Logger $logger) { + $this->configuration = $configuration; $this->logger = $logger; } @@ -36,10 +40,10 @@ public function getHttpClient() { $stack = HandlerStack::create(); $stack->push(new GuzzleTranscoder()); - if (\F3::get('logger_level') === 'DEBUG') { - if (\F3::get('DEBUG') == 0) { + if ($this->configuration->loggerLevel === 'DEBUG') { + if ($this->configuration->debug === 0) { $logFormat = GuzzleHttp\MessageFormatter::SHORT; - } elseif (\F3::get('DEBUG') == 1) { + } elseif ($this->configuration->debug === 1) { $logFormat = ">>>>>>>>\n{req_headers}\n<<<<<<<<\n{res_headers}\n--------\n{error}"; } else { $logFormat = GuzzleHttp\MessageFormatter::DEBUG; @@ -78,7 +82,7 @@ public function getHttpClient() { * @return string the user agent string for this spout */ public function getUserAgent($agentInfo = null) { - $userAgent = 'Selfoss/' . \F3::get('version'); + $userAgent = 'Selfoss/' . SELFOSS_VERSION; if ($agentInfo === null) { $agentInfo = []; diff --git a/src/spouts/rss/fulltextrss.php b/src/spouts/rss/fulltextrss.php index ebf672e5c1..4af026792a 100644 --- a/src/spouts/rss/fulltextrss.php +++ b/src/spouts/rss/fulltextrss.php @@ -3,6 +3,7 @@ namespace spouts\rss; use Graby\Graby; +use helpers\Configuration; use helpers\FeedReader; use helpers\Image; use helpers\WebClient; @@ -37,6 +38,9 @@ class fulltextrss extends feed { /** @var string tag for logger */ private static $loggerTag = 'selfoss.graby'; + /** @var Configuration configuration */ + private $configuration; + /** @var Graby */ private $graby; @@ -46,9 +50,10 @@ class fulltextrss extends feed { /** @var WebClient */ private $webClient; - public function __construct(FeedReader $feed, Image $imageHelper, Logger $logger, WebClient $webClient) { + public function __construct(Configuration $configuration, FeedReader $feed, Image $imageHelper, Logger $logger, WebClient $webClient) { parent::__construct($feed, $imageHelper, $logger); + $this->configuration = $configuration; $this->imageHelper = $imageHelper; $this->logger = $logger; $this->webClient = $webClient; @@ -61,7 +66,7 @@ public function getContent() { $this->graby = new Graby([ 'extractor' => [ 'config_builder' => [ - 'site_config' => [\F3::get('ftrss_custom_data_dir')], + 'site_config' => [$this->configuration->ftrssCustomDataDir], ], ], ], new GuzzleAdapter($this->webClient->getHttpClient())); diff --git a/utils/bump-version.js b/utils/bump-version.js index 7f699e8ea7..a3c1fe684e 100755 --- a/utils/bump-version.js +++ b/utils/bump-version.js @@ -35,8 +35,8 @@ const replacements = [ // rule for src/common.php { - from: /'version', '\d+\.\d+(\-SNAPSHOT|\-[0-9a-f]+)?'/, - to: "'version', '" + newVersion + "'" + from: /SELFOSS_VERSION = '\d+\.\d+(\-SNAPSHOT|\-[0-9a-f]+)?'/, + to: "SELFOSS_VERSION = '" + newVersion + "'" }, // rule for docs/config.toml diff --git a/utils/create-zipball.py b/utils/create-zipball.py index 59f41b0f2b..10d170ebc8 100755 --- a/utils/create-zipball.py +++ b/utils/create-zipball.py @@ -107,6 +107,9 @@ def file(self, name): logger.info('Building asset bundles…') subprocess.check_call(['npm', 'run', 'build']) + logger.info('Generating defaults.ini…') + subprocess.check_call(['php', 'utils/generate-defaults-ini.php']) + logger.info('Optimizing PHP dependencies…') subprocess.check_call(['composer', 'install', '--no-dev', '--optimize-autoloader']) diff --git a/utils/generate-defaults-ini.php b/utils/generate-defaults-ini.php new file mode 100644 index 0000000000..df3f54a420 --- /dev/null +++ b/utils/generate-defaults-ini.php @@ -0,0 +1,31 @@ +getProperties() as $property) { + if (strpos($property->getDocComment(), '@internal') !== false) { + continue; + } + + $propertyName = $property->getName(); + $configKey = strtolower(preg_replace('([[:upper:]]+)', '_$0', $propertyName)); + $defaultValue = $property->getDeclaringClass()->getDefaultProperties()[$propertyName]; + + if ($defaultValue === true) { + $defaultValue = '1'; + } elseif ($defaultValue === false) { + $defaultValue = '0'; + } + + $defaults .= $configKey . '=' . $defaultValue . PHP_EOL; +} + +file_put_contents(__DIR__ . '/../defaults.ini', $defaults);