mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-19 13:54:44 -04:00
@@ -4,6 +4,8 @@
|
||||
|
||||
* API
|
||||
* Support for editing feeds and categories from client applications [#1254](https://github.com/FreshRSS/FreshRSS/issues/1254)
|
||||
* Compatibility:
|
||||
* Experimental support for PostgreSQL [#1195](https://github.com/FreshRSS/FreshRSS/pull/1195)
|
||||
* Features
|
||||
* Better control of number of entries per page or RSS feed [#1249](https://github.com/FreshRSS/FreshRSS/issues/1249)
|
||||
* Since X hours: `https://freshrss.example/i/?a=rss&hours=3`
|
||||
|
||||
23
README.fr.md
23
README.fr.md
@@ -33,9 +33,9 @@ Nous sommes une communauté amicale.
|
||||
* Fonctionne même sur un Raspberry Pi 1 avec des temps de réponse < 1s (testé sur 150 flux, 22k articles)
|
||||
* Serveur Web Apache2 (recommandé), ou nginx, lighttpd (non testé sur les autres)
|
||||
* PHP 5.3.3+ (PHP 5.4+ recommandé, et PHP 5.5+ pour les performances, et PHP 7+ pour d’encore meilleures performances)
|
||||
* Requis : [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl)
|
||||
* Requis : [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) ou [PDO_SQLite](http://php.net/pdo-sqlite) ou [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl)
|
||||
* Recommandés : [JSON](http://php.net/json), [GMP](http://php.net/gmp) (pour accès API sur plateformes < 64 bits), [IDN](http://php.net/intl.idn) (pour les noms de domaines internationalisés), [mbstring](http://php.net/mbstring) et/ou [iconv](http://php.net/iconv) (pour conversion d’encodages), [Zip](http://php.net/zip) (pour import/export), [zlib](http://php.net/zlib) (pour les flux compressés)
|
||||
* MySQL 5.5.3+ (recommandé) ou SQLite 3.7.4+
|
||||
* MySQL 5.5.3+ (recommandé), ou SQLite 3.7.4+, ou PostgreSQL (experimental)
|
||||
* Un navigateur Web récent tel Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari.
|
||||
* Fonctionne aussi sur mobile
|
||||
|
||||
@@ -56,13 +56,20 @@ Nous sommes une communauté amicale.
|
||||
```sh
|
||||
# Si vous utilisez le serveur Web Apache (sinon il faut un autre serveur Web)
|
||||
sudo apt-get install apache2
|
||||
sudo a2enmod headers expires rewrite ssl
|
||||
# (optionnel) Si vous voulez un serveur de base de données MySQL
|
||||
sudo apt-get install mysql-server mysql-client php5-mysql
|
||||
# Composants principaux (pour Ubuntu <= 15.10, Debian <= 8 Jessie)
|
||||
sudo a2enmod headers expires rewrite ssl #Modules Apache
|
||||
|
||||
# Pour Ubuntu <= 15.10, Debian <= 8 Jessie
|
||||
sudo apt-get install php5 php5-curl php5-gmp php5-intl php5-json php5-sqlite
|
||||
# Composants principaux (pour Ubuntu >= 16.04, Debian >= 9 Stretch)
|
||||
sudo apt install php libapache2-mod-php php-curl php-gmp php-intl php-mbstring php-sqlite3 php-xml php-zip
|
||||
sudo apt-get install libapache2-mod-php5 #Pour Apache
|
||||
sudo apt-get install mysql-server mysql-client php5-mysql #Base de données MySQL optionnelle
|
||||
sudo apt-get install postgresql php5-pgsql #Base de données PostgreSQL optionnelle
|
||||
|
||||
# Pour Ubuntu >= 16.04, Debian >= 9 Stretch
|
||||
sudo apt install php php-curl php-gmp php-intl php-mbstring php-sqlite3 php-xml php-zip
|
||||
sudo apt install libapache2-mod-php #Pour Apache
|
||||
sudo apt install mysql-server mysql-client php-mysql #Base de données MySQL optionnelle
|
||||
sudo apt install postgresql php-pgsql #Base de données PostgreSQL optionnelle
|
||||
|
||||
# Redémarrage du serveur Web
|
||||
sudo service apache2 restart
|
||||
|
||||
|
||||
23
README.md
23
README.md
@@ -33,9 +33,9 @@ We are a friendly community.
|
||||
* It even works on Raspberry Pi 1 with response time under a second (tested with 150 feeds, 22k articles)
|
||||
* A web server: Apache2 (recommended), nginx, lighttpd (not tested on others)
|
||||
* PHP 5.3.3+ (PHP 5.4+ recommended, and PHP 5.5+ for performance, and PHP 7 for even higher performance)
|
||||
* Required extensions: [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite), [cURL](http://php.net/curl)
|
||||
* Required extensions: [DOM](http://php.net/dom), [XML](http://php.net/xml), [PDO_MySQL](http://php.net/pdo-mysql) or [PDO_SQLite](http://php.net/pdo-sqlite) or [PDO_PGSQL](http://php.net/pdo-pgsql), [cURL](http://php.net/curl)
|
||||
* Recommended extensions: [JSON](http://php.net/json), [GMP](http://php.net/gmp) (for API access on platforms < 64 bits), [IDN](http://php.net/intl.idn) (for Internationalized Domain Names), [mbstring](http://php.net/mbstring) and/or [iconv](http://php.net/iconv) (for charset conversion), [Zip](http://php.net/zip) (for import/export), [zlib](http://php.net/zlib) (for compressed feeds)
|
||||
* MySQL 5.5.3+ (recommended) or SQLite 3.7.4+
|
||||
* MySQL 5.5.3+ (recommended), or SQLite 3.7.4+, or PostgreSQL (experimental)
|
||||
* A recent browser like Firefox, Internet Explorer 11 / Edge, Chrome, Opera, Safari.
|
||||
* Works on mobile
|
||||
|
||||
@@ -56,13 +56,20 @@ We are a friendly community.
|
||||
```sh
|
||||
# If you use an Apache Web server (otherwise you need another Web server)
|
||||
sudo apt-get install apache2
|
||||
sudo a2enmod headers expires rewrite ssl
|
||||
# (Optional) If you want a MySQL database server
|
||||
sudo apt-get install mysql-server mysql-client php5-mysql
|
||||
# Main components (for Ubuntu <= 15.10, Debian <= 8 Jessie)
|
||||
sudo a2enmod headers expires rewrite ssl #Apache modules
|
||||
|
||||
# For Ubuntu <= 15.10, Debian <= 8 Jessie
|
||||
sudo apt-get install php5 php5-curl php5-gmp php5-intl php5-json php5-sqlite
|
||||
# Main components (for Ubuntu >= 16.04, Debian >= 9 Stretch)
|
||||
sudo apt install php libapache2-mod-php php-curl php-gmp php-intl php-mbstring php-sqlite3 php-xml php-zip
|
||||
sudo apt-get install libapache2-mod-php5 #For Apache
|
||||
sudo apt-get install mysql-server mysql-client php5-mysql #Optional MySQL database
|
||||
sudo apt-get install postgresql php5-pgsql #Optional PostgreSQL database
|
||||
|
||||
# For Ubuntu >= 16.04, Debian >= 9 Stretch
|
||||
sudo apt install php php-curl php-gmp php-intl php-mbstring php-sqlite3 php-xml php-zip
|
||||
sudo apt install libapache2-mod-php #For Apache
|
||||
sudo apt install mysql-server mysql-client php-mysql #Optional MySQL database
|
||||
sudo apt install postgresql php-pgsql #Optional PostgreSQL database
|
||||
|
||||
# Restart Web server
|
||||
sudo service apache2 restart
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
return $this->bd->lastInsertId();
|
||||
return $this->bd->lastInsertId('"' . $this->prefix . 'category_id_seq"');
|
||||
} else {
|
||||
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error addCategory: ' . $info[2]);
|
||||
@@ -103,7 +103,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
|
||||
public function listCategories($prePopulateFeeds = true, $details = false) {
|
||||
if ($prePopulateFeeds) {
|
||||
$sql = 'SELECT c.id AS c_id, c.name AS c_name, '
|
||||
. ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.cache_nbEntries, f.cache_nbUnreads ')
|
||||
. ($details ? 'f.* ' : 'f.id, f.name, f.url, f.website, f.priority, f.error, f.`cache_nbEntries`, f.`cache_nbUnreads` ')
|
||||
. 'FROM `' . $this->prefix . 'category` c '
|
||||
. 'LEFT OUTER JOIN `' . $this->prefix . 'feed` f ON f.category=c.id '
|
||||
. 'GROUP BY f.id, c_id '
|
||||
@@ -210,12 +210,13 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
|
||||
|
||||
$previousLine = null;
|
||||
$feedsDao = array();
|
||||
$feedDao = FreshRSS_Factory::createFeedDAO();
|
||||
foreach ($listDAO as $line) {
|
||||
if ($previousLine['c_id'] != null && $line['c_id'] !== $previousLine['c_id']) {
|
||||
// End of the current category, we add it to the $list
|
||||
$cat = new FreshRSS_Category(
|
||||
$previousLine['c_name'],
|
||||
FreshRSS_FeedDAO::daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
);
|
||||
$cat->_id($previousLine['c_id']);
|
||||
$list[$previousLine['c_id']] = $cat;
|
||||
@@ -231,7 +232,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
|
||||
if ($previousLine != null) {
|
||||
$cat = new FreshRSS_Category(
|
||||
$previousLine['c_name'],
|
||||
FreshRSS_FeedDAO::daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
|
||||
);
|
||||
$cat->_id($previousLine['c_id']);
|
||||
$list[$previousLine['c_id']] = $cat;
|
||||
|
||||
@@ -282,6 +282,7 @@ class FreshRSS_ConfigurationSetter {
|
||||
|
||||
switch ($value['type']) {
|
||||
case 'mysql':
|
||||
case 'pgsql':
|
||||
if (empty($value['host']) ||
|
||||
empty($value['user']) ||
|
||||
empty($value['base']) ||
|
||||
|
||||
43
app/Models/DatabaseDAOPGSQL.php
Normal file
43
app/Models/DatabaseDAOPGSQL.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This class is used to test database is well-constructed.
|
||||
*/
|
||||
class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAO {
|
||||
public function tablesAreCorrect() {
|
||||
$db = FreshRSS_Context::$system_conf->db;
|
||||
$dbowner = $db['user'];
|
||||
$sql = 'SELECT * FROM pg_catalog.pg_tables where tableowner=?';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$values = array($dbowner);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$tables = array(
|
||||
$this->prefix . 'category' => false,
|
||||
$this->prefix . 'feed' => false,
|
||||
$this->prefix . 'entry' => false,
|
||||
);
|
||||
foreach ($res as $value) {
|
||||
$tables[array_pop($value)] = true;
|
||||
}
|
||||
|
||||
return count(array_keys($tables, true, true)) == count($tables);
|
||||
}
|
||||
|
||||
public function getSchema($table) {
|
||||
$sql = 'select column_name as field, data_type as type, column_default as default, is_nullable as null from INFORMATION_SCHEMA.COLUMNS where table_name = ?';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$stm->execute(array($this->prefix . $table));
|
||||
return $this->listDaoToSchema($stm->fetchAll(PDO::FETCH_ASSOC));
|
||||
}
|
||||
|
||||
public function daoToSchema($dao) {
|
||||
return array(
|
||||
'name' => $dao['field'],
|
||||
'type' => strtolower($dao['type']),
|
||||
'notnull' => (bool)$dao['null'],
|
||||
'default' => $dao['default'],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,21 @@
|
||||
class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
|
||||
public function isCompressed() {
|
||||
return parent::$sharedDbType !== 'sqlite';
|
||||
return parent::$sharedDbType === 'mysql';
|
||||
}
|
||||
|
||||
public function hasNativeHex() {
|
||||
return parent::$sharedDbType !== 'sqlite';
|
||||
}
|
||||
|
||||
public function sqlHexDecode($x) {
|
||||
return 'unhex(' . $x . ')';
|
||||
}
|
||||
|
||||
public function sqlHexEncode($x) {
|
||||
return 'hex(' . $x . ')';
|
||||
}
|
||||
|
||||
protected function addColumn($name) {
|
||||
Minz_Log::warning('FreshRSS_EntryDAO::addColumn: ' . $name);
|
||||
$hasTransaction = false;
|
||||
@@ -20,7 +28,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
$this->bd->beginTransaction();
|
||||
$hasTransaction = true;
|
||||
}
|
||||
$stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'entry` ADD COLUMN lastSeen INT(11) DEFAULT 0');
|
||||
$stm = $this->bd->prepare('ALTER TABLE `' . $this->prefix . 'entry` ADD COLUMN `lastSeen` INT(11) DEFAULT 0');
|
||||
if ($stm && $stm->execute()) {
|
||||
$stm = $this->bd->prepare('CREATE INDEX entry_lastSeen_index ON `' . $this->prefix . 'entry`(`lastSeen`);'); //"IF NOT EXISTS" does not exist in MySQL 5.7
|
||||
if ($stm && $stm->execute()) {
|
||||
@@ -105,32 +113,43 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
if ($this->addEntryPrepared === null) {
|
||||
$sql = 'INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, '
|
||||
. ($this->isCompressed() ? 'content_bin' : 'content')
|
||||
. ', link, date, lastSeen, hash, is_read, is_favorite, id_feed, tags) '
|
||||
. 'VALUES(?, ?, ?, ?, '
|
||||
. ($this->isCompressed() ? 'COMPRESS(?)' : '?')
|
||||
. ', ?, ?, ?, '
|
||||
. ($this->hasNativeHex() ? 'X?' : '?')
|
||||
. ', ?, ?, ?, ?)';
|
||||
. ', link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) '
|
||||
. 'VALUES(:id, :guid, :title, :author, '
|
||||
. ($this->isCompressed() ? 'COMPRESS(:content)' : ':content')
|
||||
. ', :link, :date, :last_seen, '
|
||||
. $this->sqlHexDecode(':hash')
|
||||
. ', :is_read, :is_favorite, :id_feed, :tags)';
|
||||
$this->addEntryPrepared = $this->bd->prepare($sql);
|
||||
}
|
||||
$this->addEntryPrepared->bindParam(':id', $valuesTmp['id']);
|
||||
$valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760);
|
||||
$this->addEntryPrepared->bindParam(':guid', $valuesTmp['guid']);
|
||||
$valuesTmp['title'] = substr($valuesTmp['title'], 0, 255);
|
||||
$this->addEntryPrepared->bindParam(':title', $valuesTmp['title']);
|
||||
$valuesTmp['author'] = substr($valuesTmp['author'], 0, 255);
|
||||
$this->addEntryPrepared->bindParam(':author', $valuesTmp['author']);
|
||||
$this->addEntryPrepared->bindParam(':content', $valuesTmp['content']);
|
||||
$valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023);
|
||||
$this->addEntryPrepared->bindParam(':link', $valuesTmp['link']);
|
||||
$this->addEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT);
|
||||
$valuesTmp['lastSeen'] = time();
|
||||
$this->addEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT);
|
||||
$valuesTmp['is_read'] = $valuesTmp['is_read'] ? 1 : 0;
|
||||
$this->addEntryPrepared->bindParam(':is_read', $valuesTmp['is_read'], PDO::PARAM_INT);
|
||||
$valuesTmp['is_favorite'] = $valuesTmp['is_favorite'] ? 1 : 0;
|
||||
$this->addEntryPrepared->bindParam(':is_favorite', $valuesTmp['is_favorite'], PDO::PARAM_INT);
|
||||
$this->addEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT);
|
||||
$valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023);
|
||||
$this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
|
||||
|
||||
$values = array(
|
||||
$valuesTmp['id'],
|
||||
substr($valuesTmp['guid'], 0, 760),
|
||||
substr($valuesTmp['title'], 0, 255),
|
||||
substr($valuesTmp['author'], 0, 255),
|
||||
$valuesTmp['content'],
|
||||
substr($valuesTmp['link'], 0, 1023),
|
||||
$valuesTmp['date'],
|
||||
time(),
|
||||
$this->hasNativeHex() ? $valuesTmp['hash'] : pack('H*', $valuesTmp['hash']), // X'09AF' hexadecimal literals do not work with SQLite/PDO //hex2bin() is PHP5.4+
|
||||
$valuesTmp['is_read'] ? 1 : 0,
|
||||
$valuesTmp['is_favorite'] ? 1 : 0,
|
||||
$valuesTmp['id_feed'],
|
||||
substr($valuesTmp['tags'], 0, 1023),
|
||||
);
|
||||
if ($this->hasNativeHex()) {
|
||||
$this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
|
||||
} else {
|
||||
$valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+
|
||||
$this->addEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']);
|
||||
}
|
||||
|
||||
if ($this->addEntryPrepared && $this->addEntryPrepared->execute($values)) {
|
||||
if ($this->addEntryPrepared && $this->addEntryPrepared->execute()) {
|
||||
return $this->bd->lastInsertId();
|
||||
} else {
|
||||
$info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo();
|
||||
@@ -153,33 +172,41 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
|
||||
if ($this->updateEntryPrepared === null) {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` '
|
||||
. 'SET title=?, author=?, '
|
||||
. ($this->isCompressed() ? 'content_bin=COMPRESS(?)' : 'content=?')
|
||||
. ', link=?, date=?, lastSeen=?, hash='
|
||||
. ($this->hasNativeHex() ? 'X?' : '?')
|
||||
. ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=?, ')
|
||||
. 'tags=? '
|
||||
. 'WHERE id_feed=? AND guid=?';
|
||||
. 'SET title=:title, author=:author, '
|
||||
. ($this->isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
|
||||
. ', link=:link, date=:date, `lastSeen`=:last_seen, '
|
||||
. 'hash=' . $this->sqlHexDecode(':hash')
|
||||
. ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=:is_read, ')
|
||||
. 'tags=:tags '
|
||||
. 'WHERE id_feed=:id_feed AND guid=:guid';
|
||||
$this->updateEntryPrepared = $this->bd->prepare($sql);
|
||||
}
|
||||
|
||||
$values = array(
|
||||
substr($valuesTmp['title'], 0, 255),
|
||||
substr($valuesTmp['author'], 0, 255),
|
||||
$valuesTmp['content'],
|
||||
substr($valuesTmp['link'], 0, 1023),
|
||||
$valuesTmp['date'],
|
||||
time(),
|
||||
$this->hasNativeHex() ? $valuesTmp['hash'] : pack('H*', $valuesTmp['hash']),
|
||||
);
|
||||
$valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760);
|
||||
$this->updateEntryPrepared->bindParam(':guid', $valuesTmp['guid']);
|
||||
$valuesTmp['title'] = substr($valuesTmp['title'], 0, 255);
|
||||
$this->updateEntryPrepared->bindParam(':title', $valuesTmp['title']);
|
||||
$valuesTmp['author'] = substr($valuesTmp['author'], 0, 255);
|
||||
$this->updateEntryPrepared->bindParam(':author', $valuesTmp['author']);
|
||||
$this->updateEntryPrepared->bindParam(':content', $valuesTmp['content']);
|
||||
$valuesTmp['link'] = substr($valuesTmp['link'], 0, 1023);
|
||||
$this->updateEntryPrepared->bindParam(':link', $valuesTmp['link']);
|
||||
$this->updateEntryPrepared->bindParam(':date', $valuesTmp['date'], PDO::PARAM_INT);
|
||||
$valuesTmp['lastSeen'] = time();
|
||||
$this->updateEntryPrepared->bindParam(':last_seen', $valuesTmp['lastSeen'], PDO::PARAM_INT);
|
||||
if ($valuesTmp['is_read'] !== null) {
|
||||
$values[] = $valuesTmp['is_read'] ? 1 : 0;
|
||||
$this->updateEntryPrepared->bindParam(':is_read', $valuesTmp['is_read'] ? 1 : 0, PDO::PARAM_INT);
|
||||
}
|
||||
$this->updateEntryPrepared->bindParam(':id_feed', $valuesTmp['id_feed'], PDO::PARAM_INT);
|
||||
$valuesTmp['tags'] = substr($valuesTmp['tags'], 0, 1023);
|
||||
$this->updateEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
|
||||
|
||||
if ($this->hasNativeHex()) {
|
||||
$this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
|
||||
} else {
|
||||
$valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); //hex2bin() is PHP5.4+
|
||||
$this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']);
|
||||
}
|
||||
$values = array_merge($values, array(
|
||||
substr($valuesTmp['tags'], 0, 1023),
|
||||
$valuesTmp['id_feed'],
|
||||
substr($valuesTmp['guid'], 0, 760),
|
||||
));
|
||||
|
||||
if ($this->updateEntryPrepared && $this->updateEntryPrepared->execute($values)) {
|
||||
return $this->bd->lastInsertId();
|
||||
@@ -246,7 +273,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
. 'WHERE e.is_read=0 '
|
||||
. 'GROUP BY e.id_feed'
|
||||
. ') x ON x.id_feed=f.id '
|
||||
. 'SET f.cache_nbUnreads=COALESCE(x.nbUnreads, 0) '
|
||||
. 'SET f.`cache_nbUnreads`=COALESCE(x.nbUnreads, 0) '
|
||||
. 'WHERE 1';
|
||||
$values = array();
|
||||
if ($feedId !== false) {
|
||||
@@ -309,7 +336,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
} else {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id '
|
||||
. 'SET e.is_read=?,'
|
||||
. 'f.cache_nbUnreads=f.cache_nbUnreads' . ($is_read ? '-' : '+') . '1 '
|
||||
. 'f.`cache_nbUnreads`=f.`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 '
|
||||
. 'WHERE e.id=? AND e.is_read=?';
|
||||
$values = array($is_read ? 1 : 0, $ids, $is_read ? 0 : 1);
|
||||
$stm = $this->bd->prepare($sql);
|
||||
@@ -430,12 +457,12 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
}
|
||||
$this->bd->beginTransaction();
|
||||
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` e '
|
||||
. 'SET e.is_read=1 '
|
||||
. 'WHERE e.id_feed=? AND e.is_read=0 AND e.id <= ?';
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` '
|
||||
. 'SET is_read=1 '
|
||||
. 'WHERE id_feed=? AND is_read=0 AND id <= ?';
|
||||
$values = array($id_feed, $idMax);
|
||||
|
||||
list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state);
|
||||
list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state);
|
||||
|
||||
$stm = $this->bd->prepare($sql . $search);
|
||||
if (!($stm && $stm->execute(array_merge($values, $searchValues)))) {
|
||||
@@ -448,7 +475,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
|
||||
if ($affected > 0) {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` '
|
||||
. 'SET cache_nbUnreads=cache_nbUnreads-' . $affected
|
||||
. 'SET `cache_nbUnreads`=`cache_nbUnreads`-' . $affected
|
||||
. ' WHERE id=?';
|
||||
$values = array($id_feed);
|
||||
$stm = $this->bd->prepare($sql);
|
||||
@@ -658,7 +685,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
if (count($guids) < 1) {
|
||||
return array();
|
||||
}
|
||||
$sql = 'SELECT guid, hex(hash) AS hexHash FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
|
||||
$sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') . ' AS hex_hash FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$values = array($id_feed);
|
||||
$values = array_merge($values, $guids);
|
||||
@@ -666,7 +693,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
$result = array();
|
||||
$rows = $stm->fetchAll(PDO::FETCH_ASSOC);
|
||||
foreach ($rows as $row) {
|
||||
$result[$row['guid']] = $row['hexHash'];
|
||||
$result[$row['guid']] = $row['hex_hash'];
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
@@ -684,7 +711,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
if (count($guids) < 1) {
|
||||
return 0;
|
||||
}
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` SET lastSeen=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
|
||||
$sql = 'UPDATE `' . $this->prefix . 'entry` SET `lastSeen`=? WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$values = array(time(), $id_feed);
|
||||
$values = array_merge($values, $guids);
|
||||
|
||||
31
app/Models/EntryDAOPGSQL.php
Normal file
31
app/Models/EntryDAOPGSQL.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite {
|
||||
|
||||
public function sqlHexDecode($x) {
|
||||
return 'decode(' . $x . ", 'hex')";
|
||||
}
|
||||
|
||||
public function sqlHexEncode($x) {
|
||||
return 'encode(' . $x . ", 'hex')";
|
||||
}
|
||||
|
||||
protected function autoUpdateDb($errorInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function addColumn($name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function size($all = true) {
|
||||
$db = FreshRSS_Context::$system_conf->db;
|
||||
$sql = 'SELECT pg_size_pretty(pg_database_size(?))';
|
||||
$values = array($db['base']);
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$stm->execute($values);
|
||||
$res = $stm->fetchAll(PDO::FETCH_COLUMN, 0);
|
||||
return $res[0];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
|
||||
|
||||
public function sqlHexDecode($x) {
|
||||
return $x;
|
||||
}
|
||||
|
||||
protected function autoUpdateDb($errorInfo) {
|
||||
if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR
|
||||
//autoAddColumn
|
||||
@@ -24,7 +28,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
|
||||
|
||||
protected function updateCacheUnreads($catId = false, $feedId = false) {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` '
|
||||
. 'SET cache_nbUnreads=('
|
||||
. 'SET `cache_nbUnreads`=('
|
||||
. 'SELECT COUNT(*) AS nbUnreads FROM `' . $this->prefix . 'entry` e '
|
||||
. 'WHERE e.id_feed=`' . $this->prefix . 'feed`.id AND e.is_read=0) '
|
||||
. 'WHERE 1';
|
||||
@@ -82,7 +86,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO {
|
||||
}
|
||||
$affected = $stm->rowCount();
|
||||
if ($affected > 0) {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` SET cache_nbUnreads=cache_nbUnreads' . ($is_read ? '-' : '+') . '1 '
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` SET `cache_nbUnreads`=`cache_nbUnreads`' . ($is_read ? '-' : '+') . '1 '
|
||||
. 'WHERE id=(SELECT e.id_feed FROM `' . $this->prefix . 'entry` e WHERE e.id=?)';
|
||||
$values = array($ids);
|
||||
$stm = $this->bd->prepare($sql);
|
||||
|
||||
@@ -4,37 +4,47 @@ class FreshRSS_Factory {
|
||||
|
||||
public static function createFeedDao($username = null) {
|
||||
$conf = Minz_Configuration::get('system');
|
||||
if ($conf->db['type'] === 'sqlite') {
|
||||
return new FreshRSS_FeedDAOSQLite($username);
|
||||
} else {
|
||||
return new FreshRSS_FeedDAO($username);
|
||||
switch ($conf->db['type']) {
|
||||
case 'sqlite':
|
||||
return new FreshRSS_FeedDAOSQLite($username);
|
||||
default:
|
||||
return new FreshRSS_FeedDAO($username);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createEntryDao($username = null) {
|
||||
$conf = Minz_Configuration::get('system');
|
||||
if ($conf->db['type'] === 'sqlite') {
|
||||
return new FreshRSS_EntryDAOSQLite($username);
|
||||
} else {
|
||||
return new FreshRSS_EntryDAO($username);
|
||||
switch ($conf->db['type']) {
|
||||
case 'sqlite':
|
||||
return new FreshRSS_EntryDAOSQLite($username);
|
||||
case 'pgsql':
|
||||
return new FreshRSS_EntryDAOPGSQL($username);
|
||||
default:
|
||||
return new FreshRSS_EntryDAO($username);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createStatsDAO($username = null) {
|
||||
$conf = Minz_Configuration::get('system');
|
||||
if ($conf->db['type'] === 'sqlite') {
|
||||
return new FreshRSS_StatsDAOSQLite($username);
|
||||
} else {
|
||||
return new FreshRSS_StatsDAO($username);
|
||||
switch ($conf->db['type']) {
|
||||
case 'sqlite':
|
||||
return new FreshRSS_StatsDAOSQLite($username);
|
||||
case 'pgsql':
|
||||
return new FreshRSS_StatsDAOPGSQL($username);
|
||||
default:
|
||||
return new FreshRSS_StatsDAO($username);
|
||||
}
|
||||
}
|
||||
|
||||
public static function createDatabaseDAO($username = null) {
|
||||
$conf = Minz_Configuration::get('system');
|
||||
if ($conf->db['type'] === 'sqlite') {
|
||||
return new FreshRSS_DatabaseDAOSQLite($username);
|
||||
} else {
|
||||
return new FreshRSS_DatabaseDAO($username);
|
||||
switch ($conf->db['type']) {
|
||||
case 'sqlite':
|
||||
return new FreshRSS_DatabaseDAOSQLite($username);
|
||||
case 'pgsql':
|
||||
return new FreshRSS_DatabaseDAOPGSQL($username);
|
||||
default:
|
||||
return new FreshRSS_DatabaseDAO($username);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
public function addFeed($valuesTmp) {
|
||||
$sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, lastUpdate, priority, httpAuth, error, keep_history, ttl) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2, -2)';
|
||||
$sql = 'INSERT INTO `' . $this->prefix . 'feed` (url, category, name, website, description, `lastUpdate`, priority, `httpAuth`, error, keep_history, ttl) VALUES(?, ?, ?, ?, ?, ?, 10, ?, 0, -2, -2)';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
|
||||
$values = array(
|
||||
@@ -16,7 +16,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
);
|
||||
|
||||
if ($stm && $stm->execute($values)) {
|
||||
return $this->bd->lastInsertId();
|
||||
return $this->bd->lastInsertId('"' . $this->prefix . 'feed_id_seq"');
|
||||
} else {
|
||||
$info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo();
|
||||
Minz_Log::error('SQL error addFeed: ' . $info[2]);
|
||||
@@ -85,13 +85,13 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
public function updateLastUpdate($id, $inError = 0, $updateCache = true) {
|
||||
if ($updateCache) {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` ' //2 sub-requests with FOREIGN KEY(e.id_feed), INDEX(e.is_read) faster than 1 request with GROUP BY or CASE
|
||||
. 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
|
||||
. 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),'
|
||||
. 'lastUpdate=?, error=? '
|
||||
. 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
|
||||
. '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0),'
|
||||
. '`lastUpdate`=?, error=? '
|
||||
. 'WHERE id=?';
|
||||
} else {
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` '
|
||||
. 'SET lastUpdate=?, error=? '
|
||||
. 'SET `lastUpdate`=?, error=? '
|
||||
. 'WHERE id=?';
|
||||
}
|
||||
|
||||
@@ -226,10 +226,10 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
if ($defaultCacheDuration < 0) {
|
||||
$defaultCacheDuration = 2147483647;
|
||||
}
|
||||
$sql = 'SELECT id, url, name, website, lastUpdate, pathEntries, httpAuth, keep_history, ttl '
|
||||
$sql = 'SELECT id, url, name, website, `lastUpdate`, `pathEntries`, `httpAuth`, keep_history, ttl '
|
||||
. 'FROM `' . $this->prefix . 'feed` '
|
||||
. 'WHERE ttl <> -1 AND lastUpdate < (' . (time() + 60) . '-(CASE WHEN ttl=-2 THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) '
|
||||
. 'ORDER BY lastUpdate';
|
||||
. 'WHERE ttl <> -1 AND `lastUpdate` < (' . (time() + 60) . '-(CASE WHEN ttl=-2 THEN ' . intval($defaultCacheDuration) . ' ELSE ttl END)) '
|
||||
. 'ORDER BY `lastUpdate`';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
if (!($stm && $stm->execute())) {
|
||||
$sql2 = 'ALTER TABLE `' . $this->prefix . 'feed` ADD COLUMN ttl INT NOT NULL DEFAULT -2'; //v0.7.3
|
||||
@@ -282,7 +282,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
. 'FROM `' . $this->prefix . 'entry` e '
|
||||
. 'GROUP BY e.id_feed'
|
||||
. ') x ON x.id_feed=f.id '
|
||||
. 'SET f.cache_nbEntries=x.nbEntries, f.cache_nbUnreads=x.nbUnreads';
|
||||
. 'SET f.`cache_nbEntries`=x.nbEntries, f.`cache_nbUnreads`=x.nbUnreads';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
|
||||
if ($stm && $stm->execute()) {
|
||||
@@ -308,7 +308,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
$affected = $stm->rowCount();
|
||||
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` '
|
||||
. 'SET cache_nbEntries=0, cache_nbUnreads=0 WHERE id=?';
|
||||
. 'SET `cache_nbEntries`=0, `cache_nbUnreads`=0 WHERE id=?';
|
||||
$values = array($id);
|
||||
$stm = $this->bd->prepare($sql);
|
||||
if (!($stm && $stm->execute($values))) {
|
||||
@@ -326,7 +326,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
||||
$sql = 'DELETE FROM `' . $this->prefix . 'entry` '
|
||||
. 'WHERE id_feed=:id_feed AND id<=:id_max '
|
||||
. 'AND is_favorite=0 ' //Do not remove favourites
|
||||
. 'AND lastSeen < (SELECT maxLastSeen FROM (SELECT (MAX(e3.lastSeen)-99) AS maxLastSeen FROM `' . $this->prefix . 'entry` e3 WHERE e3.id_feed=:id_feed) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance
|
||||
. 'AND `lastSeen` < (SELECT maxLastSeen FROM (SELECT (MAX(e3.`lastSeen`)-99) AS maxLastSeen FROM `' . $this->prefix . 'entry` e3 WHERE e3.id_feed=:id_feed) recent) ' //Do not remove the most newly seen articles, plus a few seconds of tolerance
|
||||
. 'AND id NOT IN (SELECT id FROM (SELECT e2.id FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=:id_feed ORDER BY id DESC LIMIT :keep) keep)'; //Double select: MySQL doesn't support 'LIMIT & IN/ALL/ANY/SOME subquery'
|
||||
$stm = $this->bd->prepare($sql);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ class FreshRSS_FeedDAOSQLite extends FreshRSS_FeedDAO {
|
||||
|
||||
public function updateCachedValues() { //For one single feed, call updateLastUpdate($id)
|
||||
$sql = 'UPDATE `' . $this->prefix . 'feed` '
|
||||
. 'SET cache_nbEntries=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
|
||||
. 'cache_nbUnreads=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)';
|
||||
. 'SET `cache_nbEntries`=(SELECT COUNT(e1.id) FROM `' . $this->prefix . 'entry` e1 WHERE e1.id_feed=`' . $this->prefix . 'feed`.id),'
|
||||
. '`cache_nbUnreads`=(SELECT COUNT(e2.id) FROM `' . $this->prefix . 'entry` e2 WHERE e2.id_feed=`' . $this->prefix . 'feed`.id AND e2.is_read=0)';
|
||||
$stm = $this->bd->prepare($sql);
|
||||
if ($stm && $stm->execute()) {
|
||||
return $stm->rowCount();
|
||||
|
||||
@@ -4,6 +4,10 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
|
||||
|
||||
const ENTRY_COUNT_PERIOD = 30;
|
||||
|
||||
protected function sqlFloor($s) {
|
||||
return "FLOOR($s)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates entry repartition for all feeds and for main stream.
|
||||
*
|
||||
@@ -37,12 +41,12 @@ class FreshRSS_StatsDAO extends Minz_ModelPdo {
|
||||
$filter .= "AND e.id_feed = {$feed}";
|
||||
}
|
||||
$sql = <<<SQL
|
||||
SELECT COUNT(1) AS `total`,
|
||||
COUNT(1) - SUM(e.is_read) AS `unread`,
|
||||
SUM(e.is_read) AS `read`,
|
||||
SUM(e.is_favorite) AS `favorite`
|
||||
FROM {$this->prefix}entry AS e
|
||||
, {$this->prefix}feed AS f
|
||||
SELECT COUNT(1) AS total,
|
||||
COUNT(1) - SUM(e.is_read) AS count_unreads,
|
||||
SUM(e.is_read) AS count_reads,
|
||||
SUM(e.is_favorite) AS count_favorites
|
||||
FROM `{$this->prefix}entry` AS e
|
||||
, `{$this->prefix}feed` AS f
|
||||
WHERE e.id_feed = f.id
|
||||
{$filter}
|
||||
SQL;
|
||||
@@ -65,10 +69,11 @@ SQL;
|
||||
$oldest = $midnight - (self::ENTRY_COUNT_PERIOD * 86400);
|
||||
|
||||
// Get stats per day for the last 30 days
|
||||
$sqlDay = $this->sqlFloor("(date - $midnight) / 86400");
|
||||
$sql = <<<SQL
|
||||
SELECT FLOOR((date - {$midnight}) / 86400) AS day,
|
||||
SELECT {$sqlDay} AS day,
|
||||
COUNT(*) as count
|
||||
FROM {$this->prefix}entry
|
||||
FROM `{$this->prefix}entry`
|
||||
WHERE date >= {$oldest} AND date < {$midnight}
|
||||
GROUP BY day
|
||||
ORDER BY day ASC
|
||||
@@ -138,7 +143,7 @@ SQL;
|
||||
$sql = <<<SQL
|
||||
SELECT DATE_FORMAT(FROM_UNIXTIME(e.date), '{$period}') AS period
|
||||
, COUNT(1) AS count
|
||||
FROM {$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}entry` AS e
|
||||
{$restrict}
|
||||
GROUP BY period
|
||||
ORDER BY period ASC
|
||||
@@ -202,7 +207,7 @@ SQL;
|
||||
SELECT COUNT(1) AS count
|
||||
, MIN(date) AS date_min
|
||||
, MAX(date) AS date_max
|
||||
FROM {$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}entry` AS e
|
||||
{$restrict}
|
||||
SQL;
|
||||
$stm = $this->bd->prepare($sql);
|
||||
@@ -246,8 +251,8 @@ SQL;
|
||||
$sql = <<<SQL
|
||||
SELECT c.name AS label
|
||||
, COUNT(f.id) AS data
|
||||
FROM {$this->prefix}category AS c,
|
||||
{$this->prefix}feed AS f
|
||||
FROM `{$this->prefix}category` AS c,
|
||||
`{$this->prefix}feed` AS f
|
||||
WHERE c.id = f.category
|
||||
GROUP BY label
|
||||
ORDER BY data DESC
|
||||
@@ -269,9 +274,9 @@ SQL;
|
||||
$sql = <<<SQL
|
||||
SELECT c.name AS label
|
||||
, COUNT(e.id) AS data
|
||||
FROM {$this->prefix}category AS c,
|
||||
{$this->prefix}feed AS f,
|
||||
{$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}category` AS c,
|
||||
`{$this->prefix}feed` AS f,
|
||||
`{$this->prefix}entry` AS e
|
||||
WHERE c.id = f.category
|
||||
AND f.id = e.id_feed
|
||||
GROUP BY label
|
||||
@@ -295,9 +300,9 @@ SELECT f.id AS id
|
||||
, MAX(f.name) AS name
|
||||
, MAX(c.name) AS category
|
||||
, COUNT(e.id) AS count
|
||||
FROM {$this->prefix}category AS c,
|
||||
{$this->prefix}feed AS f,
|
||||
{$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}category` AS c,
|
||||
`{$this->prefix}feed` AS f,
|
||||
`{$this->prefix}entry` AS e
|
||||
WHERE c.id = f.category
|
||||
AND f.id = e.id_feed
|
||||
GROUP BY f.id
|
||||
@@ -320,8 +325,8 @@ SELECT MAX(f.id) as id
|
||||
, MAX(f.name) AS name
|
||||
, MAX(date) AS last_date
|
||||
, COUNT(*) AS nb_articles
|
||||
FROM {$this->prefix}feed AS f,
|
||||
{$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}feed` AS f,
|
||||
`{$this->prefix}entry` AS e
|
||||
WHERE f.id = e.id_feed
|
||||
GROUP BY f.id
|
||||
ORDER BY name
|
||||
|
||||
67
app/Models/StatsDAOPGSQL.php
Normal file
67
app/Models/StatsDAOPGSQL.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
class FreshRSS_StatsDAOPGSQL extends FreshRSS_StatsDAO {
|
||||
|
||||
/**
|
||||
* Calculates the number of article per hour of the day per feed
|
||||
*
|
||||
* @param integer $feed id
|
||||
* @return string
|
||||
*/
|
||||
public function calculateEntryRepartitionPerFeedPerHour($feed = null) {
|
||||
return $this->calculateEntryRepartitionPerFeedPerPeriod('hour', $feed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of article per day of week per feed
|
||||
*
|
||||
* @param integer $feed id
|
||||
* @return string
|
||||
*/
|
||||
public function calculateEntryRepartitionPerFeedPerDayOfWeek($feed = null) {
|
||||
return $this->calculateEntryRepartitionPerFeedPerPeriod('day', $feed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of article per month per feed
|
||||
*
|
||||
* @param integer $feed
|
||||
* @return string
|
||||
*/
|
||||
public function calculateEntryRepartitionPerFeedPerMonth($feed = null) {
|
||||
return $this->calculateEntryRepartitionPerFeedPerPeriod('month', $feed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the number of article per period per feed
|
||||
*
|
||||
* @param string $period format string to use for grouping
|
||||
* @param integer $feed id
|
||||
* @return string
|
||||
*/
|
||||
protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
|
||||
$restrict = '';
|
||||
if ($feed) {
|
||||
$restrict = "WHERE e.id_feed = {$feed}";
|
||||
}
|
||||
$sql = <<<SQL
|
||||
SELECT extract( {$period} from to_timestamp(e.date)) AS period
|
||||
, COUNT(1) AS count
|
||||
FROM "{$this->prefix}entry" AS e
|
||||
{$restrict}
|
||||
GROUP BY period
|
||||
ORDER BY period ASC
|
||||
SQL;
|
||||
|
||||
$stm = $this->bd->prepare($sql);
|
||||
$stm->execute();
|
||||
$res = $stm->fetchAll(PDO::FETCH_NAMED);
|
||||
|
||||
foreach ($res as $value) {
|
||||
$repartition[(int) $value['period']] = (int) $value['count'];
|
||||
}
|
||||
|
||||
return $repartition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
|
||||
|
||||
protected function sqlFloor($s) {
|
||||
return "CAST(($s) AS INT)";
|
||||
}
|
||||
|
||||
protected function calculateEntryRepartitionPerFeedPerPeriod($period, $feed = null) {
|
||||
if ($feed) {
|
||||
$restrict = "WHERE e.id_feed = {$feed}";
|
||||
@@ -11,7 +15,7 @@ class FreshRSS_StatsDAOSQLite extends FreshRSS_StatsDAO {
|
||||
$sql = <<<SQL
|
||||
SELECT strftime('{$period}', e.date, 'unixepoch') AS period
|
||||
, COUNT(1) AS count
|
||||
FROM {$this->prefix}entry AS e
|
||||
FROM `{$this->prefix}entry` AS e
|
||||
{$restrict}
|
||||
GROUP BY period
|
||||
ORDER BY period ASC
|
||||
@@ -26,7 +30,7 @@ SQL;
|
||||
$repartition[(int) $value['period']] = (int) $value['count'];
|
||||
}
|
||||
|
||||
return $this->convertToSerie($repartition);
|
||||
return $repartition;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<?php
|
||||
define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
|
||||
define('SQL_CREATE_TABLES', '
|
||||
CREATE TABLE IF NOT EXISTS `%1$scategory` (
|
||||
`id` SMALLINT NOT NULL AUTO_INCREMENT, -- v0.7
|
||||
|
||||
59
app/SQL/install.sql.pgsql.php
Normal file
59
app/SQL/install.sql.pgsql.php
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
define('SQL_CREATE_DB', 'CREATE DATABASE %1$s ENCODING \'UTF8\';');
|
||||
|
||||
global $SQL_CREATE_TABLES;
|
||||
$SQL_CREATE_TABLES = array(
|
||||
'CREATE TABLE IF NOT EXISTS "%1$scategory" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"name" VARCHAR(255) UNIQUE NOT NULL
|
||||
);',
|
||||
|
||||
'CREATE TABLE IF NOT EXISTS "%1$sfeed" (
|
||||
"id" SERIAL PRIMARY KEY,
|
||||
"url" varchar(511) UNIQUE NOT NULL,
|
||||
"category" SMALLINT DEFAULT 0,
|
||||
"name" VARCHAR(255) NOT NULL,
|
||||
"website" VARCHAR(255),
|
||||
"description" text,
|
||||
"lastUpdate" INT DEFAULT 0,
|
||||
"priority" SMALLINT NOT NULL DEFAULT 10,
|
||||
"pathEntries" VARCHAR(511) DEFAULT NULL,
|
||||
"httpAuth" VARCHAR(511) DEFAULT NULL,
|
||||
"error" smallint DEFAULT 0,
|
||||
"keep_history" INT NOT NULL DEFAULT -2,
|
||||
"ttl" INT NOT NULL DEFAULT -2,
|
||||
"cache_nbEntries" INT DEFAULT 0,
|
||||
"cache_nbUnreads" INT DEFAULT 0,
|
||||
FOREIGN KEY ("category") REFERENCES "%1$scategory" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||
);',
|
||||
'CREATE INDEX name_index ON "%1$sfeed" ("name");',
|
||||
'CREATE INDEX priority_index ON "%1$sfeed" ("priority");',
|
||||
'CREATE INDEX keep_history_index ON "%1$sfeed" ("keep_history");',
|
||||
|
||||
'CREATE TABLE IF NOT EXISTS "%1$sentry" (
|
||||
"id" BIGINT NOT NULL PRIMARY KEY,
|
||||
"guid" VARCHAR(760) UNIQUE NOT NULL,
|
||||
"title" VARCHAR(255) NOT NULL,
|
||||
"author" VARCHAR(255),
|
||||
"content" TEXT,
|
||||
"link" VARCHAR(1023) NOT NULL,
|
||||
"date" INT,
|
||||
"lastSeen" INT DEFAULT 0,
|
||||
"hash" BYTEA,
|
||||
"is_read" SMALLINT NOT NULL DEFAULT 0,
|
||||
"is_favorite" SMALLINT NOT NULL DEFAULT 0,
|
||||
"id_feed" SMALLINT,
|
||||
"tags" VARCHAR(1023),
|
||||
FOREIGN KEY ("id_feed") REFERENCES "%1$sfeed" ("id") ON DELETE CASCADE ON UPDATE CASCADE,
|
||||
UNIQUE ("id_feed","guid")
|
||||
);',
|
||||
'CREATE INDEX is_favorite_index ON "%1$sentry" ("is_favorite");',
|
||||
'CREATE INDEX is_read_index ON "%1$sentry" ("is_read");',
|
||||
'CREATE INDEX entry_lastSeen_index ON "%1$sentry" ("lastSeen");',
|
||||
|
||||
'INSERT INTO "%1$scategory" (id, name) VALUES(1, \'%2$s\');',
|
||||
'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) VALUES(\'http://freshrss.org/feeds/all.atom.xml\', 1, \'FreshRSS.org\', \'http://freshrss.org/\', \'FreshRSS, a free, self-hostable aggregator…\', 86400);',
|
||||
'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) VALUES(\'https://github.com/FreshRSS/FreshRSS/releases.atom\', 1, \'FreshRSS @ GitHub\', \'https://github.com/FreshRSS/FreshRSS/\', \'FreshRSS releases @ GitHub\', 86400);',
|
||||
);
|
||||
|
||||
define('SQL_DROP_TABLES', 'DROP TABLES "%1$sentry", "%1$sfeed", "%1$scategory"');
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'Máte požadovanou knihovnu pro regulární výrazy (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Nemáte PDO nebo některý z podporovaných ovladačů (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Máte PDO a alespoň jeden z podporovaných ovladačů (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Nemáte PDO nebo některý z podporovaných ovladačů (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Máte PDO a alespoň jeden z podporovaných ovladačů (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP instalace',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'Máte požadovanou knihovnu pro regulární výrazy (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Nemáte PDO nebo některý z podporovaných ovladačů (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Máte PDO a alespoň jeden z podporovaných ovladačů (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Nemáte PDO nebo některý z podporovaných ovladačů (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Máte PDO a alespoň jeden z podporovaných ovladačů (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'Vaše verze PHP je %s, ale FreshRSS vyžaduje alespoň verzi %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'Sie haben die benötigte Bibliothek für reguläre Ausdrücke (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP-Installation',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'Sie haben die benötigte Bibliothek für reguläre Ausdrücke (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Ihnen fehlt PDO oder einer der unterstützten Treiber (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Sie haben PDO und mindestens einen der unterstützten Treiber (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'Ihre PHP-Version ist %s aber FreshRSS benötigt mindestens Version %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'You have the required library for regular expressions (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'You lack PDO or one of the supported drivers (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'You have PDO and at least one of the supported drivers (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'You lack PDO or one of the supported drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'You have PDO and at least one of the supported drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP installation',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'You have the required library for regular expressions (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'You lack PDO or one of the supported drivers (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'You have PDO and at least one of the supported drivers (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'You lack PDO or one of the supported drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'You have PDO and at least one of the supported drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'Your PHP version is %s but FreshRSS requires at least version %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'Vous disposez du nécessaire pour les expressions régulières (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Vous ne disposez pas de PDO ou d’un des drivers supportés (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Vous disposez de PDO et d’au moins un des drivers supportés (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Vous ne disposez pas de PDO ou d’un des drivers supportés (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Vous disposez de PDO et d’au moins un des drivers supportés (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'Installation de PHP',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'Vous disposez du nécessaire pour les expressions régulières (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Vous ne disposez pas de PDO ou d’un des drivers supportés (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'Vous disposez de PDO et d’au moins un des drivers supportés (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Vous ne disposez pas de PDO ou d’un des drivers supportés (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'Vous disposez de PDO et d’au moins un des drivers supportés (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'Votre version de PHP est la %s mais FreshRSS requiert au moins la version %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'Libreria richiesta per le regular expressions presente (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'Installazione PHP',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'Libreria richiesta per le regular expressions presente (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'Manca PDO o uno degli altri driver supportati (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'PDO e altri driver supportati (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'Installazione PHP',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'U hebt de benodigde bibliotheek voor regular expressions (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'U mist PDO of een van de ondersteunde drivers (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'U hebt PDO en ten minste één van de ondersteunde drivers (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'U mist PDO of een van de ondersteunde drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'U hebt PDO en ten minste één van de ondersteunde drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP installatie',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'U hebt de benodigde bibliotheek voor regular expressions (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'U mist PDO of één van de ondersteunde (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'U hebt PDO en ten minste één van de ondersteunde drivers (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'U mist PDO of één van de ondersteunde (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'U hebt PDO en ten minste één van de ondersteunde drivers (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'Uw PHP versie is %s maar FreshRSS benodigd tenminste versie %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'У вас установлена необходимая библиотека для работы с регулярными выражениями (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'У вас не установлен PDO или один из необходимых драйверов (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'У вас установлен PDO и как минимум один из поддерживаемых драйверов (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'У вас не установлен PDO или один из необходимых драйверов (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'У вас установлен PDO и как минимум один из поддерживаемых драйверов (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP installation',
|
||||
|
||||
@@ -69,8 +69,8 @@ return array(
|
||||
'ok' => 'У вас установлена необходимая библиотека для работы с регулярными выражениями (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'У вас не установлен PDO или один из необходимых драйверов (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'У вас установлен PDO и как минимум один из поддерживаемых драйверов (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'У вас не установлен PDO или один из необходимых драйверов (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'У вас установлен PDO и как минимум один из поддерживаемых драйверов (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'У вас установлен PHP версии %s, но FreshRSS необходима версия не ниже %s.',
|
||||
|
||||
@@ -71,8 +71,8 @@ return array(
|
||||
'ok' => 'Düzenli ifadeler kütüphanesi sorunsuz (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'PDO veya PDO destekli bir sürücü eksik (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'PDO sorunsuz (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'PDO veya PDO destekli bir sürücü eksik (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'PDO sorunsuz (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'_' => 'PHP kurulumu',
|
||||
|
||||
@@ -73,8 +73,8 @@ return array(
|
||||
'ok' => 'Düzenli ifadeler kütüphanesi sorunsuz (PCRE).',
|
||||
),
|
||||
'pdo' => array(
|
||||
'nok' => 'PDO veya PDO destekli bir sürücü eksik (pdo_mysql, pdo_sqlite).',
|
||||
'ok' => 'PDO sorunsuz (pdo_mysql, pdo_sqlite).',
|
||||
'nok' => 'PDO veya PDO destekli bir sürücü eksik (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
'ok' => 'PDO sorunsuz (pdo_mysql, pdo_sqlite, pdo_pgsql).',
|
||||
),
|
||||
'php' => array(
|
||||
'nok' => 'PHP versiyonunuz %s fakat FreshRSS için gerekli olan en düşük sürüm %s.',
|
||||
|
||||
@@ -19,8 +19,6 @@ if (isset($_GET['step'])) {
|
||||
define('STEP', 0);
|
||||
}
|
||||
|
||||
define('SQL_CREATE_DB', 'CREATE DATABASE IF NOT EXISTS %1$s DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;');
|
||||
|
||||
if (STEP === 3 && isset($_POST['type'])) {
|
||||
$_SESSION['bd_type'] = $_POST['type'];
|
||||
}
|
||||
@@ -33,6 +31,9 @@ if (isset($_SESSION['bd_type'])) {
|
||||
case 'sqlite':
|
||||
include(APP_PATH . '/SQL/install.sql.sqlite.php');
|
||||
break;
|
||||
case 'pgsql':
|
||||
include(APP_PATH . '/SQL/install.sql.pgsql.php');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,6 +200,9 @@ function saveStep3() {
|
||||
$_SESSION['bd_prefix'] = substr($_POST['prefix'], 0, 16);
|
||||
$_SESSION['bd_prefix_user'] = $_SESSION['bd_prefix'] . (empty($_SESSION['default_user']) ? '' : ($_SESSION['default_user'] . '_'));
|
||||
}
|
||||
if ($_SESSION['bd_type'] === 'pgsql') {
|
||||
$_SESSION['bd_base'] = strtolower($_SESSION['bd_base']);
|
||||
}
|
||||
|
||||
// We use dirname to remove the /i part
|
||||
$base_url = dirname(Minz_Request::guessBaseUrl());
|
||||
@@ -235,26 +239,6 @@ function saveStep3() {
|
||||
invalidateHttpCache();
|
||||
}
|
||||
|
||||
function newPdo() {
|
||||
switch ($_SESSION['bd_type']) {
|
||||
case 'mysql':
|
||||
$str = 'mysql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base'];
|
||||
$driver_options = array(
|
||||
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
|
||||
);
|
||||
break;
|
||||
case 'sqlite':
|
||||
$str = 'sqlite:' . join_path(USERS_PATH, $_SESSION['default_user'], 'db.sqlite');
|
||||
$driver_options = array(
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options);
|
||||
}
|
||||
|
||||
function deleteInstall() {
|
||||
$res = unlink(join_path(DATA_PATH, 'do-install.txt'));
|
||||
|
||||
@@ -301,7 +285,8 @@ function checkStep1() {
|
||||
$curl = extension_loaded('curl');
|
||||
$pdo_mysql = extension_loaded('pdo_mysql');
|
||||
$pdo_sqlite = extension_loaded('pdo_sqlite');
|
||||
$pdo = $pdo_mysql || $pdo_sqlite;
|
||||
$pdo_pgsql = extension_loaded('pdo_pgsql');
|
||||
$pdo = $pdo_mysql || $pdo_sqlite || $pdo_pgsql;
|
||||
$pcre = extension_loaded('pcre');
|
||||
$ctype = extension_loaded('ctype');
|
||||
$dom = class_exists('DOMDocument');
|
||||
@@ -319,6 +304,7 @@ function checkStep1() {
|
||||
'curl' => $curl ? 'ok' : 'ko',
|
||||
'pdo-mysql' => $pdo_mysql ? 'ok' : 'ko',
|
||||
'pdo-sqlite' => $pdo_sqlite ? 'ok' : 'ko',
|
||||
'pdo-pgsql' => $pdo_pgsql ? 'ok' : 'ko',
|
||||
'pdo' => $pdo ? 'ok' : 'ko',
|
||||
'pcre' => $pcre ? 'ok' : 'ko',
|
||||
'ctype' => $ctype ? 'ok' : 'ko',
|
||||
@@ -435,6 +421,23 @@ function checkBD() {
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
);
|
||||
break;
|
||||
case 'pgsql':
|
||||
$driver_options = array(
|
||||
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||
);
|
||||
|
||||
try { // on ouvre une connexion juste pour créer la base si elle n'existe pas
|
||||
$str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=postgres';
|
||||
$c = new PDO($str, $_SESSION['bd_user'], $_SESSION['bd_password'], $driver_options);
|
||||
$sql = sprintf(SQL_CREATE_DB, $_SESSION['bd_base']);
|
||||
$res = $c->query($sql);
|
||||
} catch (PDOException $e) {
|
||||
syslog(LOG_DEBUG, 'pgsql ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// on écrase la précédente connexion en sélectionnant la nouvelle BDD
|
||||
$str = 'pgsql:host=' . $_SESSION['bd_host'] . ';dbname=' . $_SESSION['bd_base'];
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -690,7 +693,7 @@ function printStep3() {
|
||||
<p class="alert alert-error"><span class="alert-head"><?php echo _t('gen.short.damn'); ?></span> <?php echo _t('install.bdd.conf.ko'),(empty($_SESSION['bd_error']) ? '' : ' : ' . $_SESSION['bd_error']); ?></p>
|
||||
<?php } ?>
|
||||
|
||||
<form action="index.php?step=3" method="post">
|
||||
<form action="index.php?step=3" method="post" autocomplete="off">
|
||||
<legend><?php echo _t('install.bdd.conf'); ?></legend>
|
||||
<div class="form-group">
|
||||
<label class="group-name" for="type"><?php echo _t('install.bdd.type'); ?></label>
|
||||
@@ -708,6 +711,12 @@ function printStep3() {
|
||||
SQLite
|
||||
</option>
|
||||
<?php }?>
|
||||
<?php if (extension_loaded('pdo_pgsql')) {?>
|
||||
<option value="pgsql"
|
||||
<?php echo(isset($_SESSION['bd_type']) && $_SESSION['bd_type'] === 'pgsql') ? 'selected="selected"' : ''; ?>>
|
||||
PostgreSQL (⚠️ experimental)
|
||||
</option>
|
||||
<?php }?>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -730,7 +739,7 @@ function printStep3() {
|
||||
<div class="form-group">
|
||||
<label class="group-name" for="pass"><?php echo _t('install.bdd.password'); ?></label>
|
||||
<div class="group-controls">
|
||||
<input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" tabindex="4" />
|
||||
<input type="password" id="pass" name="pass" value="<?php echo isset($_SESSION['bd_password']) ? $_SESSION['bd_password'] : ''; ?>" tabindex="4" autocomplete="off" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -23,18 +23,18 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo _t('admin.stats.status_read'); ?></th>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['read']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['read']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_reads']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_reads']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo _t('admin.stats.status_unread'); ?></th>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['unread']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['unread']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_unreads']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_unreads']); ?></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><?php echo _t('admin.stats.status_favorites'); ?></th>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['favorite']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['favorite']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['main_stream']['count_favorites']); ?></td>
|
||||
<td class="numeric"><?php echo format_number($this->repartition['all_feeds']['count_favorites']); ?></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
if (!empty($feeds)) {
|
||||
echo '<optgroup label="', $category->name(), '">';
|
||||
foreach ($feeds as $feed) {
|
||||
if ($this->feed && $feed->id() == $this->feed->id()){
|
||||
if ($this->feed && $feed->id() == $this->feed->id()) {
|
||||
echo '<option value="', $feed->id(), '" selected="selected" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
|
||||
} else {
|
||||
echo '<option value="', $feed->id(), '" data-url="', _url('stats', 'repartition', 'id', $feed->id()), '">', $feed->name(), '</option>';
|
||||
@@ -39,9 +39,9 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="numeric"><?php echo $this->repartition['total']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['read']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['unread']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['favorite']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['count_reads']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['count_unreads']; ?></td>
|
||||
<td class="numeric"><?php echo $this->repartition['count_favorites']; ?></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -56,39 +56,41 @@ class Minz_ModelPdo {
|
||||
$dbServer = parse_url('db://' . $db['host']);
|
||||
|
||||
try {
|
||||
$type = $db['type'];
|
||||
if ($type === 'mysql') {
|
||||
$string = 'mysql:host=' . $dbServer['host']
|
||||
. ';dbname=' . $db['base']
|
||||
. ';charset=utf8mb4';
|
||||
if (!empty($dbServer['port'])) {
|
||||
$string .= ';port=' . $dbServer['port'];
|
||||
}
|
||||
$driver_options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8mb4';
|
||||
$this->prefix = $db['prefix'] . $currentUser . '_';
|
||||
} elseif ($type === 'sqlite') {
|
||||
$string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite');
|
||||
//$driver_options[PDO::ATTR_ERRMODE] = PDO::ERRMODE_EXCEPTION;
|
||||
$this->prefix = '';
|
||||
} else {
|
||||
throw new Minz_PDOConnectionException(
|
||||
'Invalid database type!',
|
||||
$db['user'], Minz_Exception::ERROR
|
||||
);
|
||||
}
|
||||
self::$sharedDbType = $type;
|
||||
self::$sharedPrefix = $this->prefix;
|
||||
|
||||
$this->bd = new MinzPDO(
|
||||
$string,
|
||||
$db['user'],
|
||||
$db['password'],
|
||||
$driver_options
|
||||
);
|
||||
if ($type === 'sqlite') {
|
||||
$this->bd->exec('PRAGMA foreign_keys = ON;');
|
||||
switch ($db['type']) {
|
||||
case 'mysql':
|
||||
$string = 'mysql:host=' . $dbServer['host'] . ';dbname=' . $db['base'] . ';charset=utf8mb4';
|
||||
if (!empty($dbServer['port'])) {
|
||||
$string .= ';port=' . $dbServer['port'];
|
||||
}
|
||||
$driver_options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8mb4';
|
||||
$this->prefix = $db['prefix'] . $currentUser . '_';
|
||||
$this->bd = new MinzPDOMySql($string, $db['user'], $db['password'], $driver_options);
|
||||
break;
|
||||
case 'sqlite':
|
||||
$string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite');
|
||||
$this->prefix = '';
|
||||
$this->bd = new MinzPDOMSQLite($string, $db['user'], $db['password'], $driver_options);
|
||||
$this->bd->exec('PRAGMA foreign_keys = ON;');
|
||||
break;
|
||||
case 'pgsql':
|
||||
$string = 'pgsql:host=' . $dbServer['host'] . ';dbname=' . $db['base'];
|
||||
if (!empty($dbServer['port'])) {
|
||||
$string .= ';port=' . $dbServer['port'];
|
||||
}
|
||||
$this->prefix = $db['prefix'] . $currentUser . '_';
|
||||
$this->bd = new MinzPDOPGSQL($string, $db['user'], $db['password'], $driver_options);
|
||||
$this->bd->exec("SET NAMES 'UTF8';");
|
||||
break;
|
||||
default:
|
||||
throw new Minz_PDOConnectionException(
|
||||
'Invalid database type!',
|
||||
$db['user'], Minz_Exception::ERROR
|
||||
);
|
||||
break;
|
||||
}
|
||||
self::$sharedBd = $this->bd;
|
||||
self::$sharedDbType = $db['type'];
|
||||
self::$sharedPrefix = $this->prefix;
|
||||
} catch (Exception $e) {
|
||||
throw new Minz_PDOConnectionException(
|
||||
$string,
|
||||
@@ -123,18 +125,43 @@ class MinzPDO extends PDO {
|
||||
}
|
||||
}
|
||||
|
||||
protected function compatibility($statement) {
|
||||
return $statement;
|
||||
}
|
||||
|
||||
public function prepare($statement, $driver_options = array()) {
|
||||
MinzPDO::check($statement);
|
||||
$statement = $this->compatibility($statement);
|
||||
return parent::prepare($statement, $driver_options);
|
||||
}
|
||||
|
||||
public function exec($statement) {
|
||||
MinzPDO::check($statement);
|
||||
$statement = $this->compatibility($statement);
|
||||
return parent::exec($statement);
|
||||
}
|
||||
|
||||
public function query($statement) {
|
||||
MinzPDO::check($statement);
|
||||
$statement = $this->compatibility($statement);
|
||||
return parent::query($statement);
|
||||
}
|
||||
}
|
||||
|
||||
class MinzPDOMySql extends MinzPDO {
|
||||
public function lastInsertId($name = null) {
|
||||
return parent::lastInsertId(); //We discard the name, only used by PostgreSQL
|
||||
}
|
||||
}
|
||||
|
||||
class MinzPDOMSQLite extends MinzPDO {
|
||||
public function lastInsertId($name = null) {
|
||||
return parent::lastInsertId(); //We discard the name, only used by PostgreSQL
|
||||
}
|
||||
}
|
||||
|
||||
class MinzPDOPGSQL extends MinzPDO {
|
||||
protected function compatibility($statement) {
|
||||
return str_replace(array('`', ' LIKE '), array('"', ' ILIKE '), $statement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,13 +43,15 @@ if (auth_type) {
|
||||
function mySqlShowHide() {
|
||||
var mysql = document.getElementById('mysql');
|
||||
if (mysql) {
|
||||
mysql.style.display = document.getElementById('type').value === 'mysql' ? 'block' : 'none';
|
||||
if (document.getElementById('type').value !== 'mysql') {
|
||||
if (document.getElementById('type').value === 'sqlite') {
|
||||
document.getElementById('host').value = '';
|
||||
document.getElementById('user').value = '';
|
||||
document.getElementById('pass').value = '';
|
||||
document.getElementById('base').value = '';
|
||||
document.getElementById('prefix').value = '';
|
||||
mysql.style.display = 'none';
|
||||
} else {
|
||||
mysql.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user