From 217c191a1ba3ac03b847d261a32e19975380fcad Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Mon, 11 May 2015 22:42:41 +0200 Subject: More SQLite compatibility Additional changes to add compatibility with SQLite for the new hash/lastSeen mode of updating articles. --- app/Models/EntryDAOSQLite.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index ffe0f037c..ff049d813 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -2,6 +2,21 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { + protected function autoAddColumn($errorInfo) { + if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR + if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { + $showCreate = $tableInfo->fetchColumn(); + Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoAddColumn: ' . $showCreate); + foreach (array('lastSeen', 'hash') as $column) { + if (stripos($showCreate, $column) === false) { + return $this->addColumn($column); + } + } + } + } + return false; + } + protected function sqlConcat($s1, $s2) { return $s1 . '||' . $s2; } -- cgit v1.2.3 From 3e1b2e75e01dfc9ac2414940adaf4d62251075e9 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 2 Aug 2016 23:13:46 +0200 Subject: Forgotten method name update for SQLite https://github.com/FreshRSS/FreshRSS/pull/1183 https://github.com/FreshRSS/FreshRSS/issues/1153 --- app/Models/EntryDAOSQLite.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index ff049d813..19b97fd3a 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -2,11 +2,12 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { - protected function autoAddColumn($errorInfo) { + protected function autoUpdateDb($errorInfo) { if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR + //autoAddColumn if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { $showCreate = $tableInfo->fetchColumn(); - Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoAddColumn: ' . $showCreate); + Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoUpdateDb: ' . $showCreate); foreach (array('lastSeen', 'hash') as $column) { if (stripos($showCreate, $column) === false) { return $this->addColumn($column); -- cgit v1.2.3 From 6afa36e7e101236b4ac08f8c267f4e2bd78f4ea6 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 19 Aug 2016 11:05:24 +0200 Subject: SQLite fix for mark search as read https://github.com/FreshRSS/FreshRSS/issues/1220 https://github.com/FreshRSS/FreshRSS/pull/1218 https://github.com/FreshRSS/FreshRSS/issues/608 --- app/Models/EntryDAO.php | 2 +- app/Models/EntryDAOSQLite.php | 20 +++++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 3bb8a720c..769d2ae6c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -502,7 +502,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL } - private function sqlListEntriesWhere($filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { + protected function sqlListEntriesWhere($filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { $search = ' '; $values = array(); if ($state & FreshRSS_Entry::STATE_NOT_READ) { diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 19b97fd3a..de02616d6 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -119,7 +119,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { * @param integer $priorityMin * @return integer affected rows */ - public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0) { + public function markReadEntries($idMax = 0, $onlyFavorites = false, $priorityMin = 0, $filter = null, $state = 0) { if ($idMax == 0) { $idMax = time() . '000000'; Minz_Log::debug('Calling markReadEntries(0) is deprecated!'); @@ -132,8 +132,11 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $sql .= ' AND id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.priority > ' . intval($priorityMin) . ')'; } $values = array($idMax); - $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute($values))) { + + list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + + $stm = $this->bd->prepare($sql . $search); + if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::error('SQL error markReadEntries: ' . $info[2]); return false; @@ -156,7 +159,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { * @param integer $idMax fail safe article ID * @return integer affected rows */ - public function markReadCat($id, $idMax = 0) { + public function markReadCat($id, $idMax = 0, $filter = null, $state = 0) { if ($idMax == 0) { $idMax = time() . '000000'; Minz_Log::debug('Calling markReadCat(0) is deprecated!'); @@ -166,9 +169,12 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { . 'SET is_read=1 ' . 'WHERE is_read=0 AND id <= ? AND ' . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)'; - $values = array($idMax, $id); - $stm = $this->bd->prepare($sql); - if (!($stm && $stm->execute($values))) { + $values = array($id, $idMax); + + list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + + $stm = $this->bd->prepare($sql . $search); + if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); Minz_Log::error('SQL error markReadCat: ' . $info[2]); return false; -- cgit v1.2.3 From edd4516178c49f966248d832e63776f5f2f90236 Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 19 Aug 2016 14:33:08 +0200 Subject: More SQLite fix for mark search as read --- app/Models/EntryDAO.php | 40 ++++++++++++++++++++-------------------- app/Models/EntryDAOSQLite.php | 6 +++--- 2 files changed, 23 insertions(+), 23 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 769d2ae6c..8f64098e5 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -360,7 +360,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } $values = array($idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -397,7 +397,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE f.category=? AND e.is_read=0 AND e.id <= ?'; $values = array($id, $idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -435,7 +435,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'WHERE e.id_feed=? AND e.is_read=0 AND e.id <= ?'; $values = array($id_feed, $idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -502,24 +502,24 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL } - protected function sqlListEntriesWhere($filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { + protected function sqlListEntriesWhere($alias = '', $filter = null, $state = FreshRSS_Entry::STATE_ALL, $order = 'DESC', $firstId = '', $date_min = 0) { $search = ' '; $values = array(); if ($state & FreshRSS_Entry::STATE_NOT_READ) { if (!($state & FreshRSS_Entry::STATE_READ)) { - $search .= 'AND e.is_read=0 '; + $search .= 'AND ' . $alias . 'is_read=0 '; } } elseif ($state & FreshRSS_Entry::STATE_READ) { - $search .= 'AND e.is_read=1 '; + $search .= 'AND ' . $alias . 'is_read=1 '; } if ($state & FreshRSS_Entry::STATE_FAVORITE) { if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) { - $search .= 'AND e.is_favorite=1 '; + $search .= 'AND ' . $alias . 'is_favorite=1 '; } } elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) { - $search .= 'AND e.is_favorite=0 '; + $search .= 'AND ' . $alias . 'is_favorite=0 '; } switch ($order) { @@ -533,51 +533,51 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; //MySQL optimization. TODO: check if this is needed again, after the filtering for old articles has been removed in 0.9-dev }*/ if ($firstId !== '') { - $search .= 'AND e.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; + $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; } if ($date_min > 0) { - $search .= 'AND e.id >= ' . $date_min . '000000 '; + $search .= 'AND ' . $alias . 'id >= ' . $date_min . '000000 '; } if ($filter) { if ($filter->getIntitle()) { - $search .= 'AND e.title LIKE ? '; + $search .= 'AND ' . $alias . 'title LIKE ? '; $values[] = "%{$filter->getIntitle()}%"; } if ($filter->getInurl()) { - $search .= 'AND CONCAT(e.link, e.guid) LIKE ? '; + $search .= 'AND CONCAT(' . $alias . 'link, ' . $alias . 'guid) LIKE ? '; $values[] = "%{$filter->getInurl()}%"; } if ($filter->getAuthor()) { - $search .= 'AND e.author LIKE ? '; + $search .= 'AND ' . $alias . 'author LIKE ? '; $values[] = "%{$filter->getAuthor()}%"; } if ($filter->getMinDate()) { - $search .= 'AND e.id >= ? '; + $search .= 'AND ' . $alias . 'id >= ? '; $values[] = "{$filter->getMinDate()}000000"; } if ($filter->getMaxDate()) { - $search .= 'AND e.id <= ? '; + $search .= 'AND ' . $alias . 'id <= ? '; $values[] = "{$filter->getMaxDate()}000000"; } if ($filter->getMinPubdate()) { - $search .= 'AND e.date >= ? '; + $search .= 'AND ' . $alias . 'date >= ? '; $values[] = $filter->getMinPubdate(); } if ($filter->getMaxPubdate()) { - $search .= 'AND e.date <= ? '; + $search .= 'AND ' . $alias . 'date <= ? '; $values[] = $filter->getMaxPubdate(); } if ($filter->getTags()) { $tags = $filter->getTags(); foreach ($tags as $tag) { - $search .= 'AND e.tags LIKE ? '; + $search .= 'AND ' . $alias . 'tags LIKE ? '; $values[] = "%{$tag}%"; } } if ($filter->getSearch()) { $search_values = $filter->getSearch(); foreach ($search_values as $search_value) { - $search .= 'AND ' . $this->sqlconcat('e.title', $this->isCompressed() ? 'UNCOMPRESS(content_bin)' : 'content') . ' LIKE ? '; + $search .= 'AND ' . $this->sqlconcat($alias . 'title', $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? '; $values[] = "%{$search_value}%"; } } @@ -616,7 +616,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!'); } - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state, $order, $firstId, $date_min); + list($searchValues, $search) = $this->sqlListEntriesWhere('e.', $filter, $state, $order, $firstId, $date_min); return array(array_merge($values, $searchValues), 'SELECT e.id FROM `' . $this->prefix . 'entry` e ' diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index de02616d6..dad34a93d 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -133,7 +133,7 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } $values = array($idMax); - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { @@ -169,9 +169,9 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { . 'SET is_read=1 ' . 'WHERE is_read=0 AND id <= ? AND ' . 'id_feed IN (SELECT f.id FROM `' . $this->prefix . 'feed` f WHERE f.category=?)'; - $values = array($id, $idMax); + $values = array($idMax, $id); - list($searchValues, $search) = $this->sqlListEntriesWhere($filter, $state); + list($searchValues, $search) = $this->sqlListEntriesWhere('', $filter, $state); $stm = $this->bd->prepare($sql . $search); if (!($stm && $stm->execute(array_merge($values, $searchValues)))) { -- cgit v1.2.3 From c25fdbcc0990b637e305665a456e52e1aa3dec0a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Tue, 23 Aug 2016 00:02:54 +0200 Subject: More PostgreSQL --- app/Models/EntryDAO.php | 75 ++++++++++++++--------- app/Models/EntryDAOPGSQL.php | 79 +++--------------------- app/Models/EntryDAOSQLite.php | 4 ++ app/SQL/install.sql.pgsql.php | 136 +++++++++++++++++------------------------- app/install.php | 26 ++------ lib/Minz/ModelPdo.php | 29 +++++---- 6 files changed, 138 insertions(+), 211 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 3a1dc1ba5..8d136cd6c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -10,6 +10,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return parent::$sharedDbType !== 'sqlite'; } + public function sqlHexDecode($x) { + return 'X' . $x; + } + + public function sqlHexEncode($x) { + return 'hex(' . $x . ')'; + } + protected function addColumn($name) { Minz_Log::warning('FreshRSS_EntryDAO::addColumn: ' . $name); $hasTransaction = false; @@ -106,31 +114,42 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $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?' : '?') - . ', ?, ?, ?, ?)'; + . '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']); + + if ($this->hasNativeHex()) { + $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']); + } else { + $valuesTmp['hashBin'] = pack('H*', $valuesTmp['hash']); + $this->addEntryPrepared->bindParam(':hash', $valuesTmp['hashBin']); // X'09AF' hexadecimal literals do not work with SQLite/PDO //hex2bin() is PHP5.4+ + } - $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->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(); @@ -156,7 +175,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . 'SET title=?, author=?, ' . ($this->isCompressed() ? 'content_bin=COMPRESS(?)' : 'content=?') . ', link=?, date=?, lastSeen=?, hash=' - . ($this->hasNativeHex() ? 'X?' : '?') + . ($this->hasNativeHex() ? 'X?' : '?') //TODO PostgreSQL . ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=?, ') . 'tags=? ' . 'WHERE id_feed=? AND guid=?'; @@ -430,12 +449,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)))) { @@ -658,7 +677,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 hexHash 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); diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index 95c12ff5d..b96a62ebc 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -1,82 +1,21 @@ bd->beginTransaction(); - - $sql = 'UPDATE "' . $this->prefix . 'entry" ' - . 'SET is_read=:is_read ' - . 'WHERE id_feed=:id_feed AND NOT is_read AND id <= :idmax'; - $values = array($id_feed, $idMax); - $stm = $this->bd->prepare($sql); - $stm->bindValue(':is_read', true, PDO::PARAM_BOOL); - $stm->bindValue(':id_feed', $id_feed); - $stm->bindValue(':idmax', $idMax); - - if (!($stm && $stm->execute())) { - $info = $stm == null ? array(2 => 'syntax error') : $stm->errorInfo(); - Minz_Log::error('SQL error markReadFeed: ' . $info[2]); - $this->bd->rollBack(); - return false; - } - $affected = $stm->rowCount(); - - $this->bd->commit(); - return $affected; + public function sqlHexEncode($x) { + return 'encode(' . $x . ", 'hex')"; } - public function listHashForFeedGuids($id_feed, $guids) { - if (count($guids) < 1) { - return array(); - } - $sql = 'SELECT guid, hash AS hexHash 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); - if ($stm && $stm->execute($values)) { - $result = array(); - $rows = $stm->fetchAll(PDO::FETCH_ASSOC); - foreach ($rows as $row) { - $result[$row['guid']] = $row['hexHash']; - } - return $result; - } else { - $info = $stm == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $stm->errorInfo(); - if ($this->autoAddColumn($info)) { - return $this->listHashForFeedGuids($id_feed, $guids); - } - Minz_Log::error('SQL error listHashForFeedGuids: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] - . ' while querying feed ' . $id_feed); - return false; - } + protected function autoUpdateDb($errorInfo) { + return false; } - public function optimizeTable() { - return null; + protected function addColumn($name) { + return false; } public function size($all = true) { diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index dad34a93d..80dbcca6b 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -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 diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index 0f243bdb5..04e35af68 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -1,91 +1,65 @@ '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')); @@ -444,11 +427,12 @@ function checkBD() { ); try { // on ouvre une connexion juste pour créer la base si elle n'existe pas - $str = 'pgsql:host=' . $_SESSION['bd_host'] . ';'; + $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 diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index b98a26d06..41a9f60bf 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -60,17 +60,17 @@ class Minz_ModelPdo { $string = 'mysql:host=' . $db['host'] . ';dbname=' . $db['base'] . ';charset=utf8mb4'; $driver_options[PDO::MYSQL_ATTR_INIT_COMMAND] = 'SET NAMES utf8mb4'; $this->prefix = $db['prefix'] . $currentUser . '_'; - $this->bd = new MinzPDO($string, $db['user'], $db['password'], $driver_options); + $this->bd = new MinzPDOMySql($string, $db['user'], $db['password'], $driver_options); //TODO Consider: $this->bd->exec("SET SESSION sql_mode = 'ANSI_QUOTES';"); break; case 'sqlite': $string = 'sqlite:' . join_path(DATA_PATH, 'users', $currentUser, 'db.sqlite'); $this->prefix = ''; - $this->bd = new MinzPDO($string, $db['user'], $db['password'], $driver_options); + $this->bd = new MinzPDOMSQLite($string, $db['user'], $db['password'], $driver_options); $this->bd->exec('PRAGMA foreign_keys = ON;'); break; case 'pgsql': - $string = 'pgsql:host=' . $db['host'] . ';dbname=' . $db['base'] . ';charset=utf8'; + $string = 'pgsql:host=' . $db['host'] . ';dbname=' . $db['base']; $this->prefix = $db['prefix'] . $currentUser . '_'; $this->bd = new MinzPDOPGSQL($string, $db['user'], $db['password'], $driver_options); $this->bd->exec("SET NAMES 'UTF8';"); @@ -125,33 +125,40 @@ class MinzPDO extends PDO { public function prepare($statement, $driver_options = array()) { MinzPDO::check($statement); - $statement = MinzPDO::compatibility($statement); + $statement = $this->compatibility($statement); return parent::prepare($statement, $driver_options); } public function exec($statement) { MinzPDO::check($statement); - $statement = MinzPDO::compatibility($statement); + $statement = $this->compatibility($statement); return parent::exec($statement); } public function query($statement) { MinzPDO::check($statement); - $statement = MinzPDO::compatibility($statement); + $statement = $this->compatibility($statement); return parent::query($statement); } +} +class MinzPDOMySql extends PDO { 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('`', " X'"), array('"', " E'\\x"), $statement); +class MinzPDOMSQLite extends PDO { + public function lastInsertId($name = null) { + return parent::lastInsertId(); //We discard the name, only used by PostgreSQL } +} - public function lastInsertId($name = null) { - return parent::lastInsertId($name); +class MinzPDOPGSQL extends MinzPDO { + protected function compatibility($statement) { + return str_replace( + array('`', 'lastUpdate', 'pathEntries', 'httpAuth', 'cache_nbEntries', 'cache_nbUnreads', 'lastSeen'), + array('"', '"lastUpdate"', '"pathEntries"', '"httpAuth"', '"cache_nbEntries"', '"cache_nbUnreads"', '"lastSeen"'), + $statement); } } -- cgit v1.2.3 From f66be86e41d89214688a28243b412ffa43ce500d Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Wed, 31 Aug 2016 21:47:12 +0200 Subject: Quoted upper-cases instead of string replace --- app/Models/CategoryDAO.php | 2 +- app/Models/EntryDAO.php | 14 +++++++------- app/Models/EntryDAOSQLite.php | 4 ++-- app/Models/FeedDAO.php | 22 +++++++++++----------- app/Models/FeedDAOSQLite.php | 4 ++-- lib/Minz/ModelPdo.php | 5 +---- 6 files changed, 24 insertions(+), 27 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index 3b1519c20..1dab60ea9 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -100,7 +100,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 ' diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 8d136cd6c..d39f0237c 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -28,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()) { @@ -113,7 +113,7 @@ 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) ' + . ', 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, ' @@ -174,7 +174,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { $sql = 'UPDATE `' . $this->prefix . 'entry` ' . 'SET title=?, author=?, ' . ($this->isCompressed() ? 'content_bin=COMPRESS(?)' : 'content=?') - . ', link=?, date=?, lastSeen=?, hash=' + . ', link=?, date=?, `lastSeen`=?, hash=' . ($this->hasNativeHex() ? 'X?' : '?') //TODO PostgreSQL . ', ' . ($valuesTmp['is_read'] === null ? '' : 'is_read=?, ') . 'tags=? ' @@ -265,7 +265,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) { @@ -328,7 +328,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); @@ -467,7 +467,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); @@ -703,7 +703,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); diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 80dbcca6b..fd5d25bf6 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -28,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'; @@ -86,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); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index ce65ed8b1..6e6d8857b 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -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( @@ -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); diff --git a/app/Models/FeedDAOSQLite.php b/app/Models/FeedDAOSQLite.php index 7599fda53..440ae74da 100644 --- a/app/Models/FeedDAOSQLite.php +++ b/app/Models/FeedDAOSQLite.php @@ -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(); diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index 93a22fc3d..78b44ea7f 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -156,9 +156,6 @@ class MinzPDOMSQLite extends MinzPDO { class MinzPDOPGSQL extends MinzPDO { protected function compatibility($statement) { - return str_replace( - array('`', 'lastUpdate', 'pathEntries', 'httpAuth', 'cache_nbEntries', 'cache_nbUnreads', 'lastSeen'), - array('"', '"lastUpdate"', '"pathEntries"', '"httpAuth"', '"cache_nbEntries"', '"cache_nbUnreads"', '"lastSeen"'), - $statement); + return str_replace('`', '"', $statement); } } -- cgit v1.2.3 From d184478fb4c98e035dca1ebef36a5946d43c6a3a Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Fri, 14 Oct 2016 23:05:05 +0200 Subject: PostgreSQL compatibility boolean https://github.com/FreshRSS/FreshRSS/issues/1311 --- app/Models/EntryDAO.php | 12 ++++++++---- app/Models/EntryDAOSQLite.php | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 6563b0f93..938d90886 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -273,15 +273,19 @@ 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) ' - . 'WHERE 1'; + . 'SET f.`cache_nbUnreads`=COALESCE(x.nbUnreads, 0)'; + $hasWhere = false; $values = array(); if ($feedId !== false) { - $sql .= ' AND f.id=?'; + $sql .= $hasWhere ? ' AND' : ' WHERE'; + $hasWhere = true; + $sql .= ' f.id=?'; $values[] = $id; } if ($catId !== false) { - $sql .= ' AND f.category=?'; + $sql .= $hasWhere ? ' AND' : ' WHERE'; + $hasWhere = true; + $sql .= ' f.category=?'; $values[] = $catId; } $stm = $this->bd->prepare($sql); diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index fd5d25bf6..34e854608 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -30,15 +30,19 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { $sql = 'UPDATE `' . $this->prefix . 'feed` ' . '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'; + . 'WHERE e.id_feed=`' . $this->prefix . 'feed`.id AND e.is_read=0)'; + $hasWhere = false; $values = array(); if ($feedId !== false) { - $sql .= ' AND id=?'; + $sql .= $hasWhere ? ' AND' : ' WHERE'; + $hasWhere = true; + $sql .= ' id=?'; $values[] = $feedId; } if ($catId !== false) { - $sql .= ' AND category=?'; + $sql .= $hasWhere ? ' AND' : ' WHERE'; + $hasWhere = true; + $sql .= ' category=?'; $values[] = $catId; } $stm = $this->bd->prepare($sql); -- cgit v1.2.3 From 22b41f3bfcbd5a54d59789c2cebfda6dc23b7dde Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Sun, 26 Mar 2017 00:01:11 +0100 Subject: Candidate implementation of defered insertion https://github.com/FreshRSS/FreshRSS/issues/530 --- app/Controllers/feedController.php | 8 +- app/Controllers/importExportController.php | 1 + app/Models/EntryDAO.php | 115 +++++++++++++++++++++-------- app/Models/EntryDAOPGSQL.php | 26 +++++++ app/Models/EntryDAOSQLite.php | 39 +++++++--- app/Models/UserDAO.php | 10 ++- app/SQL/install.sql.mysql.php | 23 ++++++ app/SQL/install.sql.pgsql.php | 24 +++++- app/SQL/install.sql.sqlite.php | 24 ++++++ app/install.php | 36 +++------ p/scripts/main.js | 1 + 11 files changed, 234 insertions(+), 73 deletions(-) (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index f71f26a4e..a2d9d5c35 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -226,7 +226,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } } - public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null, $isNewFeed = false) { + public static function actualizeFeed($feed_id, $feed_url, $force, $simplePiePush = null, $isNewFeed = false, $noCommit = false) { @set_time_limit(300); $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -434,6 +434,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { break; } } + if (!$noCommit) { + $entryDAO->commitNewEntries(); + } return array($updated_feeds, reset($feeds)); } @@ -452,8 +455,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $id = Minz_Request::param('id'); $url = Minz_Request::param('url'); $force = Minz_Request::param('force'); + $noCommit = Minz_Session::param('isLastFeed', 1) != 1; - list($updated_feeds, $feed) = self::actualizeFeed($id, $url, $force); + list($updated_feeds, $feed) = self::actualizeFeed($id, $url, $force, null, false, $noCommit); if (Minz_Request::param('ajax')) { // Most of the time, ajax request is for only one feed. But since diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 6ae89defb..ededfe506 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -475,6 +475,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { } } $this->entryDAO->commit(); + $entryDAO->commitNewEntries(); return !$error; } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index afcde3d7f..decae9307 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -88,6 +88,38 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return false; } + protected function createEntryTempTable() { + $ok = false; + $hadTransaction = $this->bd->inTransaction(); + if ($hadTransaction) { + $this->bd->commit(); + } + try { + $db = FreshRSS_Context::$system_conf->db; + require_once(APP_PATH . '/SQL/install.sql.' . $db['type'] . '.php'); + Minz_Log::warning('SQL CREATE TABLE entrytmp...'); + if (defined('SQL_CREATE_TABLE_ENTRYTMP')) { + $sql = sprintf(SQL_CREATE_TABLE_ENTRYTMP, $this->prefix); + $stm = $this->bd->prepare($sql); + $ok = $stm && $stm->execute(); + } else { + global $SQL_CREATE_TABLE_ENTRYTMP; + $ok = !empty($SQL_CREATE_TABLE_ENTRYTMP); + foreach ($SQL_CREATE_TABLE_ENTRYTMP as $instruction) { + $sql = sprintf($instruction, $this->prefix); + $stm = $this->bd->prepare($sql); + $ok &= $stm && $stm->execute(); + } + } + } catch (Exception $e) { + Minz_Log::error('FreshRSS_EntryDAO::createEntryTempTable error: ' . $e->getMessage()); + } + if ($hadTransaction) { + $this->bd->beginTransaction(); + } + return $ok; + } + protected function autoUpdateDb($errorInfo) { if (isset($errorInfo[0])) { if ($errorInfo[0] === '42S22') { //ER_BAD_FIELD_ERROR @@ -97,6 +129,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return $this->addColumn($column); } } + } elseif ($errorInfo[0] === '42S02' && stripos($errorInfo[2], 'entrytmp') !== false) { //ER_BAD_TABLE_ERROR + return $this->createEntryTempTable(); //v1.7 } } if (isset($errorInfo[1])) { @@ -110,8 +144,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { private $addEntryPrepared = null; public function addEntry($valuesTmp) { - if ($this->addEntryPrepared === null) { - $sql = 'INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, ' + if ($this->addEntryPrepared == null) { + $sql = 'INSERT INTO `' . $this->prefix . 'entrytmp` (id, guid, title, author, ' . ($this->isCompressed() ? 'content_bin' : 'content') . ', link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . 'VALUES(:id, :guid, :title, :author, ' @@ -121,41 +155,43 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { . ', :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); - $valuesTmp['guid'] = safe_ascii($valuesTmp['guid']); - $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); - $valuesTmp['link'] = safe_ascii($valuesTmp['link']); - $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']); - - 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->bindParam(':id', $valuesTmp['id']); + $valuesTmp['guid'] = substr($valuesTmp['guid'], 0, 760); + $valuesTmp['guid'] = safe_ascii($valuesTmp['guid']); + $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); + $valuesTmp['link'] = safe_ascii($valuesTmp['link']); + $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']); + + 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()) { return $this->bd->lastInsertId(); } else { $info = $this->addEntryPrepared == null ? array(0 => '', 1 => '', 2 => 'syntax error') : $this->addEntryPrepared->errorInfo(); if ($this->autoUpdateDb($info)) { + $this->addEntryPrepared = null; return $this->addEntry($valuesTmp); } elseif ((int)($info[0] / 1000) !== 23) { //Filter out "SQLSTATE Class code 23: Constraint Violation" because of expected duplicate entries Minz_Log::error('SQL error addEntry: ' . $info[0] . ': ' . $info[1] . ' ' . $info[2] @@ -165,6 +201,23 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } } + public function commitNewEntries() { + $sql = 'SET @rank=SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`; ' . //MySQL-specific + 'INSERT IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . + 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; ' . + 'DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= @rank;'; + $stm = $this->bd->prepare($sql); + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $stm ? $stm->execute() : false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + private $updateEntryPrepared = null; public function updateEntry($valuesTmp) { diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index b96a62ebc..b25993c47 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -11,6 +11,11 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { } protected function autoUpdateDb($errorInfo) { + if (isset($errorInfo[0])) { + if ($errorInfo[0] === '42P01' && stripos($errorInfo[2], 'entrytmp') !== false) { //undefined_table + return $this->createEntryTempTable(); + } + } return false; } @@ -18,6 +23,27 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { return false; } + public function commitNewEntries() { + $sql = 'DO $$ +DECLARE +maxrank bigint := (SELECT MAX(id) FROM `' . $this->prefix . 'entrytmp`); +rank bigint := (SELECT maxrank - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); +BEGIN + INSERT INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + (SELECT rank + row_number() OVER(ORDER BY date) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date); + DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= maxrank; +END $$;'; + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $this->bd->exec($sql) !== false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + public function size($all = true) { $db = FreshRSS_Context::$system_conf->db; $sql = 'SELECT pg_size_pretty(pg_database_size(?))'; diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 34e854608..ad7bcd865 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -7,21 +7,42 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } protected function autoUpdateDb($errorInfo) { - if (empty($errorInfo[0]) || $errorInfo[0] == '42S22') { //ER_BAD_FIELD_ERROR - //autoAddColumn - if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { - $showCreate = $tableInfo->fetchColumn(); - Minz_Log::debug('FreshRSS_EntryDAOSQLite::autoUpdateDb: ' . $showCreate); - foreach (array('lastSeen', 'hash') as $column) { - if (stripos($showCreate, $column) === false) { - return $this->addColumn($column); - } + Minz_Log::error('FreshRSS_EntryDAO::autoUpdateDb error: ' . print_r($errorInfo, true)); + if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entrytmp'")) { + $showCreate = $tableInfo->fetchColumn(); + if (stripos($showCreate, 'entrytmp') === false) { + return $this->createEntryTempTable(); + } + } + if ($tableInfo = $this->bd->query("SELECT sql FROM sqlite_master where name='entry'")) { + $showCreate = $tableInfo->fetchColumn(); + foreach (array('lastSeen', 'hash') as $column) { + if (stripos($showCreate, $column) === false) { + return $this->addColumn($column); } } } return false; } + public function commitNewEntries() { + $sql = ' +CREATE TEMP TABLE `tmp` AS SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; +INSERT OR IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) + SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `tmp` ORDER BY date; +DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); +DROP TABLE `tmp`;'; + $hadTransaction = $this->bd->inTransaction(); + if (!$hadTransaction) { + $this->bd->beginTransaction(); + } + $result = $this->bd->exec($sql) !== false; + if (!$hadTransaction) { + $this->bd->commit(); + } + return $result; + } + protected function sqlConcat($s1, $s2) { return $s1 . '||' . $s2; } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index a60caf395..310c7c096 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -14,21 +14,23 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { $ok = false; $bd_prefix_user = $db['prefix'] . $username . '_'; if (defined('SQL_CREATE_TABLES')) { //E.g. MySQL - $sql = sprintf(SQL_CREATE_TABLES, $bd_prefix_user, _t('gen.short.default_category')); + $sql = sprintf(SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP, $bd_prefix_user, _t('gen.short.default_category')); $stm = $userPDO->bd->prepare($sql); $ok = $stm && $stm->execute(); } else { //E.g. SQLite global $SQL_CREATE_TABLES; + global $SQL_CREATE_TABLE_ENTRYTMP; if (is_array($SQL_CREATE_TABLES)) { - $ok = true; - foreach ($SQL_CREATE_TABLES as $instruction) { + $instructions = array_merge($SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP); + $ok = !empty($instructions); + foreach ($instructions as $instruction) { $sql = sprintf($instruction, $bd_prefix_user, _t('gen.short.default_category')); $stm = $userPDO->bd->prepare($sql); $ok &= ($stm && $stm->execute()); } } } - if ($insertDefaultFeeds) { + if ($ok && $insertDefaultFeeds) { if (defined('SQL_INSERT_FEEDS')) { //E.g. MySQL $sql = sprintf(SQL_INSERT_FEEDS, $bd_prefix_user); $stm = $userPDO->bd->prepare($sql); diff --git a/app/SQL/install.sql.mysql.php b/app/SQL/install.sql.mysql.php index a454829d5..ceca07f93 100644 --- a/app/SQL/install.sql.mysql.php +++ b/app/SQL/install.sql.mysql.php @@ -61,6 +61,29 @@ ENGINE = INNODB; INSERT IGNORE INTO `%1$scategory` (id, name) VALUES(1, "%2$s"); '); +define('SQL_CREATE_TABLE_ENTRYTMP', ' +CREATE TABLE IF NOT EXISTS `%1$sentrytmp` ( -- v1.7 + `id` bigint NOT NULL, + `guid` varchar(760) CHARACTER SET latin1 NOT NULL, + `title` varchar(255) NOT NULL, + `author` varchar(255), + `content_bin` blob, + `link` varchar(1023) CHARACTER SET latin1 NOT NULL, + `date` int(11), + `lastSeen` INT(11) DEFAULT 0, + `hash` BINARY(16), + `is_read` boolean NOT NULL DEFAULT 0, + `is_favorite` boolean NOT NULL DEFAULT 0, + `id_feed` SMALLINT, + `tags` varchar(1023), + PRIMARY KEY (`id`), + FOREIGN KEY (`id_feed`) REFERENCES `%1$sfeed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + UNIQUE KEY (`id_feed`,`guid`), + INDEX (`date`), +) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci +ENGINE = INNODB; +'); + define('SQL_INSERT_FEEDS', ' INSERT IGNORE 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 IGNORE 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); diff --git a/app/SQL/install.sql.pgsql.php b/app/SQL/install.sql.pgsql.php index 9f4240b98..1a666cb8a 100644 --- a/app/SQL/install.sql.pgsql.php +++ b/app/SQL/install.sql.pgsql.php @@ -32,7 +32,7 @@ $SQL_CREATE_TABLES = array( 'CREATE TABLE IF NOT EXISTS "%1$sentry" ( "id" BIGINT NOT NULL PRIMARY KEY, - "guid" VARCHAR(760) UNIQUE NOT NULL, + "guid" VARCHAR(760) NOT NULL, "title" VARCHAR(255) NOT NULL, "author" VARCHAR(255), "content" TEXT, @@ -54,6 +54,28 @@ $SQL_CREATE_TABLES = array( 'INSERT INTO "%1$scategory" (name) SELECT \'%2$s\' WHERE NOT EXISTS (SELECT id FROM "%1$scategory" WHERE id = 1);', ); +global $SQL_CREATE_TABLE_ENTRYTMP; +$SQL_CREATE_TABLE_ENTRYTMP = array( +'CREATE TABLE IF NOT EXISTS "%1$sentrytmp" ( -- v1.7 + "id" BIGINT NOT NULL PRIMARY KEY, + "guid" VARCHAR(760) 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 %1$sentrytmp_date_index ON "%1$sentrytmp" ("date");', +); + global $SQL_INSERT_FEEDS; $SQL_INSERT_FEEDS = array( 'INSERT INTO "%1$sfeed" (url, category, name, website, description, ttl) SELECT \'http://freshrss.org/feeds/all.atom.xml\', 1, \'FreshRSS.org\', \'http://freshrss.org/\', \'FreshRSS, a free, self-hostable aggregator…\', 86400 WHERE NOT EXISTS (SELECT id FROM "%1$sfeed" WHERE url = \'http://freshrss.org/feeds/all.atom.xml\');', diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index 68d93ba92..ad7d525fd 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -57,6 +57,30 @@ $SQL_CREATE_TABLES = array( 'INSERT OR IGNORE INTO `category` (id, name) VALUES(1, "%2$s");', ); +global $SQL_CREATE_TABLE_ENTRYTMP; +$SQL_CREATE_TABLE_ENTRYTMP = array( +'CREATE TABLE IF NOT EXISTS `entrytmp` ( -- v1.7 + `id` bigint NOT NULL, + `guid` varchar(760) NOT NULL, + `title` varchar(255) NOT NULL, + `author` varchar(255), + `content` text, + `link` varchar(1023) NOT NULL, + `date` int(11), + `lastSeen` INT(11) DEFAULT 0, + `hash` BINARY(16), + `is_read` boolean NOT NULL DEFAULT 0, + `is_favorite` boolean NOT NULL DEFAULT 0, + `id_feed` SMALLINT, + `tags` varchar(1023), + PRIMARY KEY (`id`), + FOREIGN KEY (`id_feed`) REFERENCES `feed`(`id`) ON DELETE CASCADE ON UPDATE CASCADE, + UNIQUE (`id_feed`,`guid`) +);', + +'CREATE INDEX IF NOT EXISTS entrytmp_date_index ON `entrytmp`(`date`);', +); + global $SQL_INSERT_FEEDS; $SQL_INSERT_FEEDS = array( 'INSERT OR IGNORE INTO `feed` (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);', diff --git a/app/install.php b/app/install.php index 9a88e0f37..17037c384 100644 --- a/app/install.php +++ b/app/install.php @@ -342,35 +342,19 @@ function checkDbUser(&$dbOptions) { $driver_options = $dbOptions['options']; try { $c = new PDO($str, $dbOptions['user'], $dbOptions['password'], $driver_options); - if (defined('SQL_CREATE_TABLES')) { - $sql = sprintf(SQL_CREATE_TABLES, $dbOptions['prefix_user'], _t('gen.short.default_category')); - $stm = $c->prepare($sql); - $ok = $stm->execute(); - } else { - global $SQL_CREATE_TABLES; - if (is_array($SQL_CREATE_TABLES)) { - $ok = true; - foreach ($SQL_CREATE_TABLES as $instruction) { - $sql = sprintf($instruction, $dbOptions['prefix_user'], _t('gen.short.default_category')); - $stm = $c->prepare($sql); - $ok &= $stm->execute(); - } - } - } - - if (defined('SQL_INSERT_FEEDS')) { - $sql = sprintf(SQL_INSERT_FEEDS, $dbOptions['prefix_user']); + $sql = sprintf(SQL_CREATE_TABLES . SQL_CREATE_TABLE_ENTRYTMP . SQL_INSERT_FEEDS, + $dbOptions['prefix_user'], _t('gen.short.default_category')); $stm = $c->prepare($sql); - $ok &= $stm->execute(); + $ok = $stm && $stm->execute(); } else { - global $SQL_INSERT_FEEDS; - if (is_array($SQL_INSERT_FEEDS)) { - foreach ($SQL_INSERT_FEEDS as $instruction) { - $sql = sprintf($instruction, $dbOptions['prefix_user']); - $stm = $c->prepare($sql); - $ok &= $stm->execute(); - } + global $SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_INSERT_FEEDS; + $instructions = array_merge($SQL_CREATE_TABLES, $SQL_CREATE_TABLE_ENTRYTMP, $SQL_INSERT_FEEDS); + $ok = !empty($instructions); + foreach ($instructions as $instruction) { + $sql = sprintf($instruction, $dbOptions['prefix_user'], _t('gen.short.default_category')); + $stm = $c->prepare($sql); + $ok &= $stm && $stm->execute(); } } } catch (PDOException $e) { diff --git a/p/scripts/main.js b/p/scripts/main.js index 5dbb95c91..c2f60bf7f 100644 --- a/p/scripts/main.js +++ b/p/scripts/main.js @@ -809,6 +809,7 @@ function updateFeed(feeds, feeds_count) { url: feed.url, data : { _csrf: context.csrf, + isLastFeed: feeds.length <= 0 ? 1 : 0, }, }).always(function (data) { feed_processed++; -- cgit v1.2.3 From 4e174ed9dd84ce4f8de410baf6a6e0fde9160055 Mon Sep 17 00:00:00 2001 From: Frans de Jonge Date: Fri, 22 Sep 2017 12:13:46 +0200 Subject: [ci] Add Travis (#1619) * [ci] Add Travis * Exclude some libs * Semi-auto whitespace fixes * line length in SQLite * Exclude tests from line length * Feed.php line length * Feed.php: get rid of unnecessary concat * Feed.php: line length * bootstrap.php: no newline at end of file * Allow concatenating across multiple lines * Add Travis badge * do-install line length * update-or-create-user line length * cli/create-user line length * tests/app/Models/SearchTest.php fix indentation * tests/app/Models/UserQueryTest.php fix indentation * tests/app/Models/CategoryTest.php fix indentation * [fix] PHP 5.3 on precise * cli/do-install no spaces * cli/list-users line length * cli/reconfigure line length * empty catch statements * api/index line length nonsense * spaces before semicolon * app/Models/EntryDAO bunch of indentation * extra blank lines * spaces before comma in function call * testing tabwidth * increase to 10 * comment out tabwidth line * try older phpcs version 3.0.0RC4 * line length exception for app/install.php * proper spaces * stray spaces in i18n * Minz/ModelPdo line length * Minz whitespace * greader line length * greader elseif placement * app/Models/Feed.php spacing in function argument * ignore php 5.3 * app/Models/ConfigurationSetter.php stray whitespace * EntryDAOSQLite line length * I vote for higher max line length =P * ignore SQL * remove classname complaint * line length/more legible SQL * ignore line length nonsense * greader line length * feedController issues * uppercase TRUE, FALSE, NULL * revert * importExportController lowercase null * Share.php default value not necessary because ! is_array () a few lines down * CategoryDAO constants should be UPPERCASE * EntryDAO reduce line length * contentious autofix * Allow failures on all versions of PHP except 7.1 because reasons --- .travis.yml | 34 ++++ README.md | 5 + app/Controllers/categoryController.php | 4 +- app/Controllers/feedController.php | 10 +- app/Controllers/importExportController.php | 10 +- app/Controllers/indexController.php | 4 +- app/Exceptions/ZipException.php | 2 +- app/Models/CategoryDAO.php | 10 +- app/Models/ConfigurationSetter.php | 10 +- app/Models/Context.php | 8 +- app/Models/DatabaseDAO.php | 2 +- app/Models/DatabaseDAOPGSQL.php | 2 +- app/Models/DatabaseDAOSQLite.php | 2 +- app/Models/Entry.php | 1 + app/Models/EntryDAO.php | 100 +++++----- app/Models/EntryDAOPGSQL.php | 2 +- app/Models/EntryDAOSQLite.php | 57 +++++- app/Models/Feed.php | 26 ++- app/Models/FeedDAO.php | 20 +- app/Models/Share.php | 2 +- app/Models/Themes.php | 8 +- app/Models/UserDAO.php | 6 +- app/SQL/install.sql.sqlite.php | 38 +++- app/i18n/cz/conf.php | 2 +- app/i18n/cz/gen.php | 2 +- app/i18n/de/conf.php | 2 +- app/i18n/en/conf.php | 2 +- app/i18n/it/conf.php | 2 +- app/i18n/kr/conf.php | 2 +- app/i18n/nl/conf.php | 2 +- app/i18n/ru/conf.php | 2 +- app/i18n/tr/conf.php | 2 +- app/i18n/zh-cn/conf.php | 2 +- app/install.php | 4 +- cli/_update-or-create-user.php | 3 +- cli/create-user.php | 3 +- cli/do-install.php | 9 +- cli/list-users.php | 3 +- cli/reconfigure.php | 3 +- data/shares.php | 2 +- lib/Minz/ActionController.php | 6 +- lib/Minz/ActionException.php | 2 +- lib/Minz/Configuration.php | 4 +- .../ControllerNotActionControllerException.php | 2 +- lib/Minz/ControllerNotExistException.php | 2 +- lib/Minz/CurrentPagePaginationException.php | 2 +- lib/Minz/Dispatcher.php | 2 +- lib/Minz/FileNotExistException.php | 2 +- lib/Minz/FrontController.php | 4 +- lib/Minz/Helper.php | 4 +- lib/Minz/Model.php | 2 +- lib/Minz/ModelArray.php | 3 +- lib/Minz/ModelPdo.php | 2 +- lib/Minz/PDOConnectionException.php | 2 +- lib/Minz/Paginator.php | 8 +- lib/Minz/Translate.php | 4 +- lib/Minz/View.php | 2 - lib/favicons.php | 1 + lib/lib_date.php | 3 +- lib/lib_install.php | 3 +- lib/lib_rss.php | 2 + p/api/greader.php | 52 +++-- p/api/index.php | 3 +- phpcs.xml | 100 ++++++++++ tests/app/Models/CategoryTest.php | 12 +- tests/app/Models/SearchTest.php | 216 ++++++++++----------- tests/app/Models/UserQueryTest.php | 62 +++--- tests/bootstrap.php | 2 +- 68 files changed, 593 insertions(+), 326 deletions(-) create mode 100644 .travis.yml create mode 100644 phpcs.xml (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..5c43e5666 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,34 @@ +language: php +php: + - '5.4' + - '5.5' + - '5.6' + - '7.0' + - '7.1' + - hhvm + - nightly + +install: + # newest version without https://github.com/squizlabs/PHP_CodeSniffer/pull/1404 + - pear install PHP_CodeSniffer-3.0.0RC4 + +script: + - phpenv rehash + - phpcs . --standard=phpcs.xml --warning-severity=0 --extensions=php -p + +env: # important! otherwise no job will be allowed to fail +matrix: + # PHP 5.3 only runs on Ubuntu 12.04 (precise), not 14.04 (trusty) + include: + - php: "5.3" + dist: precise + fast_finish: true + allow_failures: + - php: "5.3" + dist: precise + - php: "5.4" + - php: "5.5" + - php: "5.6" + - php: "7.0" + - php: hhvm + - php: nightly diff --git a/README.md b/README.md index 8595a26ec..016794ffc 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![Build Status][travis-badge]][travis-link] + * [Version française](README.fr.md) # FreshRSS @@ -182,3 +184,6 @@ Any client supporting a Google Reader-like API. Selection: * [EasyRSS](https://github.com/Alkarex/EasyRSS) (Open source, F-Droid) * Linux * [FeedReader 2.0+](https://jangernert.github.io/FeedReader/) (Open source) + +[travis-badge]:https://travis-ci.org/FreshRSS/FreshRSS.svg?branch=master +[travis-link]:https://travis-ci.org/FreshRSS/FreshRSS diff --git a/app/Controllers/categoryController.php b/app/Controllers/categoryController.php index 922f92844..f3b35a323 100644 --- a/app/Controllers/categoryController.php +++ b/app/Controllers/categoryController.php @@ -127,11 +127,11 @@ class FreshRSS_category_Controller extends Minz_ActionController { Minz_Request::bad(_t('feedback.sub.category.no_id'), $url_redirect); } - if ($id === FreshRSS_CategoryDAO::defaultCategoryId) { + if ($id === FreshRSS_CategoryDAO::DEFAULTCATEGORYID) { Minz_Request::bad(_t('feedback.sub.category.not_delete_default'), $url_redirect); } - if ($feedDAO->changeCategory($id, FreshRSS_CategoryDAO::defaultCategoryId) === false) { + if ($feedDAO->changeCategory($id, FreshRSS_CategoryDAO::DEFAULTCATEGORYID) === false) { Minz_Request::bad(_t('feedback.sub.category.error'), $url_redirect); } diff --git a/app/Controllers/feedController.php b/app/Controllers/feedController.php index c9b6deaa7..66b1167a8 100755 --- a/app/Controllers/feedController.php +++ b/app/Controllers/feedController.php @@ -42,7 +42,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if ($cat == null) { $catDAO->checkDefault(); } - $cat_id = $cat == null ? FreshRSS_CategoryDAO::defaultCategoryId : $cat->id(); + $cat_id = $cat == null ? FreshRSS_CategoryDAO::DEFAULTCATEGORYID : $cat->id(); $feed = new FreshRSS_Feed($url); //Throws FreshRSS_BadUrl_Exception $feed->_httpAuth($http_auth); @@ -420,8 +420,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } } - } - elseif ($feed->url() !== $url) { // HTTP 301 Moved Permanently + } elseif ($feed->url() !== $url) { // HTTP 301 Moved Permanently Minz_Log::notice('Feed ' . $url . ' moved permanently to ' . $feed->url()); $feedDAO->updateFeed($feed->id(), array('url' => $feed->url())); } @@ -537,7 +536,7 @@ class FreshRSS_feed_Controller extends Minz_ActionController { } if ($cat_id <= 1) { $catDAO->checkDefault(); - $cat_id = FreshRSS_CategoryDAO::defaultCategoryId; + $cat_id = FreshRSS_CategoryDAO::DEFAULTCATEGORYID; } $feedDAO = FreshRSS_Factory::createFeedDao(); @@ -566,6 +565,9 @@ class FreshRSS_feed_Controller extends Minz_ActionController { if (self::moveFeed($feed_id, $cat_id)) { // TODO: return something useful + // Log a notice to prevent "Empty IF statement" warning in PHP_CodeSniffer + Minz_Log::notice('Moved feed `' . $feed_id . '` ' . + 'in the category `' . $cat_id . '`');; } else { Minz_Log::warning('Cannot move feed `' . $feed_id . '` ' . 'in the category `' . $cat_id . '`'); diff --git a/app/Controllers/importExportController.php b/app/Controllers/importExportController.php index 2bc68848c..a69490e70 100644 --- a/app/Controllers/importExportController.php +++ b/app/Controllers/importExportController.php @@ -152,8 +152,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { // And finally, we get import status and redirect to the home page Minz_Session::_param('actualize_feeds', true); - $content_notif = $error === true ? _t('feedback.import_export.feeds_imported_with_errors') : - _t('feedback.import_export.feeds_imported'); + $content_notif = $error === true ? _t('feedback.import_export.feeds_imported_with_errors') : _t('feedback.import_export.feeds_imported'); Minz_Request::good($content_notif); } @@ -439,8 +438,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { $feed_id = $article_to_feed[$item['id']]; $author = isset($item['author']) ? $item['author'] : ''; - $key_content = ($google_compliant && !isset($item['content'])) ? - 'summary' : 'content'; + $key_content = ($google_compliant && !isset($item['content'])) ? 'summary' : 'content'; $tags = $item['categories']; if ($google_compliant) { // Remove tags containing "/state/com.google" which are useless. @@ -501,7 +499,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { try { // Create a Feed object and add it in database. $feed = new FreshRSS_Feed($url); - $feed->_category(FreshRSS_CategoryDAO::defaultCategoryId); + $feed->_category(FreshRSS_CategoryDAO::DEFAULTCATEGORYID); $feed->_name($name); $feed->_website($website); @@ -640,7 +638,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController { * @param FreshRSS_Feed $feed feed of which we want to get entries. * @return string the JSON file content. */ - private function generateEntries($type, $feed = NULL, $maxFeedEntries = 50) { + private function generateEntries($type, $feed = null, $maxFeedEntries = 50) { $this->view->categories = $this->catDAO->listCategories(); if ($type == 'starred') { diff --git a/app/Controllers/indexController.php b/app/Controllers/indexController.php index 5ca147ff3..e8dde36fa 100755 --- a/app/Controllers/indexController.php +++ b/app/Controllers/indexController.php @@ -46,9 +46,7 @@ class FreshRSS_index_Controller extends Minz_ActionController { } $first_entry = $nb_entries > 0 ? $entries[0] : null; - FreshRSS_Context::$id_max = $first_entry === null ? - (time() - 1) . '000000' : - $first_entry->id(); + FreshRSS_Context::$id_max = $first_entry === null ? (time() - 1) . '000000' : $first_entry->id(); if (FreshRSS_Context::$order === 'ASC') { // In this case we do not know but we guess id_max $id_max = (time() - 1) . '000000'; diff --git a/app/Exceptions/ZipException.php b/app/Exceptions/ZipException.php index 8441daedf..ad01b87ea 100644 --- a/app/Exceptions/ZipException.php +++ b/app/Exceptions/ZipException.php @@ -2,7 +2,7 @@ class FreshRSS_Zip_Exception extends Exception { private $zipErrorCode = 0; - + public function __construct($zipErrorCode) { parent::__construct('ZIP error! ' . $url, 2141); $this->zipErrorCode = $zipErrorCode; diff --git a/app/Models/CategoryDAO.php b/app/Models/CategoryDAO.php index c2d57c241..f219c275f 100644 --- a/app/Models/CategoryDAO.php +++ b/app/Models/CategoryDAO.php @@ -2,7 +2,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { - const defaultCategoryId = 1; + const DEFAULTCATEGORYID = 1; public function addCategory($valuesTmp) { $sql = 'INSERT INTO `' . $this->prefix . 'category`(name) VALUES(?)'; @@ -53,7 +53,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function deleteCategory($id) { - if ($id <= self::defaultCategoryId) { + if ($id <= self::DEFAULTCATEGORYID) { return false; } $sql = 'DELETE FROM `' . $this->prefix . 'category` WHERE id=?'; @@ -123,7 +123,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } public function getDefault() { - $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=' . self::defaultCategoryId; + $sql = 'SELECT * FROM `' . $this->prefix . 'category` WHERE id=' . self::DEFAULTCATEGORYID; $stm = $this->bd->prepare($sql); $stm->execute(); @@ -137,11 +137,11 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable } } public function checkDefault() { - $def_cat = $this->searchById(self::defaultCategoryId); + $def_cat = $this->searchById(self::DEFAULTCATEGORYID); if ($def_cat == null) { $cat = new FreshRSS_Category(_t('gen.short.default_category')); - $cat->_id(self::defaultCategoryId); + $cat->_id(self::DEFAULTCATEGORYID); $values = array( 'id' => $cat->id(), diff --git a/app/Models/ConfigurationSetter.php b/app/Models/ConfigurationSetter.php index 70e1dea2e..ca4709903 100644 --- a/app/Models/ConfigurationSetter.php +++ b/app/Models/ConfigurationSetter.php @@ -56,8 +56,7 @@ class FreshRSS_ConfigurationSetter { switch ($value) { case 'all': $data['default_view'] = $value; - $data['default_state'] = (FreshRSS_Entry::STATE_READ + - FreshRSS_Entry::STATE_NOT_READ); + $data['default_state'] = (FreshRSS_Entry::STATE_READ + FreshRSS_Entry::STATE_NOT_READ); break; case 'adaptive': case 'unread': @@ -163,7 +162,7 @@ class FreshRSS_ConfigurationSetter { if (!in_array($value, array('global', 'normal', 'reader'))) { $value = 'normal'; } - $data['view_mode'] = $value; + $data['view_mode'] = $value; } /** @@ -326,7 +325,7 @@ class FreshRSS_ConfigurationSetter { if (!in_array($value, array('silent', 'development', 'production'))) { $value = 'production'; } - $data['environment'] = $value; + $data['environment'] = $value; } private function _limits(&$data, $values) { @@ -361,8 +360,7 @@ class FreshRSS_ConfigurationSetter { $value = intval($value); $limits = $limits_keys[$key]; - if ( - (!isset($limits['min']) || $value >= $limits['min']) && + if ((!isset($limits['min']) || $value >= $limits['min']) && (!isset($limits['max']) || $value <= $limits['max']) ) { $data['limits'][$key] = $value; diff --git a/app/Models/Context.php b/app/Models/Context.php index fd0e79fc1..2ca8f80b0 100644 --- a/app/Models/Context.php +++ b/app/Models/Context.php @@ -250,9 +250,7 @@ class FreshRSS_Context { } // If no feed have been found, next_get is the current category. - self::$next_get = empty($another_unread_id) ? - 'c_' . self::$current_get['category'] : - 'f_' . $another_unread_id; + self::$next_get = empty($another_unread_id) ? 'c_' . self::$current_get['category'] : 'f_' . $another_unread_id; break; case 'c': // We search the next category with at least one unread article. @@ -275,9 +273,7 @@ class FreshRSS_Context { } // No unread category? The main stream will be our destination! - self::$next_get = empty($another_unread_id) ? - 'a' : - 'c_' . $another_unread_id; + self::$next_get = empty($another_unread_id) ? 'a' : 'c_' . $another_unread_id; break; } } diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index 0d85718e3..6ba5bca3e 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -9,7 +9,7 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { $stm = $this->bd->prepare($sql); $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - + $tables = array( $this->prefix . 'category' => false, $this->prefix . 'feed' => false, diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php index a4edaa448..2a18db970 100644 --- a/app/Models/DatabaseDAOPGSQL.php +++ b/app/Models/DatabaseDAOPGSQL.php @@ -12,7 +12,7 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAO { $values = array($dbowner); $stm->execute($values); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - + $tables = array( $this->prefix . 'category' => false, $this->prefix . 'feed' => false, diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php index 7f53f967d..2e1df132e 100644 --- a/app/Models/DatabaseDAOSQLite.php +++ b/app/Models/DatabaseDAOSQLite.php @@ -9,7 +9,7 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { $stm = $this->bd->prepare($sql); $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_ASSOC); - + $tables = array( 'category' => false, 'feed' => false, diff --git a/app/Models/Entry.php b/app/Models/Entry.php index 26cd24797..df3d59bea 100644 --- a/app/Models/Entry.php +++ b/app/Models/Entry.php @@ -189,6 +189,7 @@ class FreshRSS_Entry extends Minz_Model { ); } catch (Exception $e) { // rien à faire, on garde l'ancien contenu(requête a échoué) + Minz_Log::warning($e->getMessage()); } } } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index 7e836097a..bebafe500 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -146,13 +146,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function addEntry($valuesTmp) { if ($this->addEntryPrepared == null) { $sql = 'INSERT INTO `' . $this->prefix . 'entrytmp` (id, guid, title, author, ' - . ($this->isCompressed() ? 'content_bin' : 'content') - . ', 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->isCompressed() ? 'content_bin' : 'content') + . ', 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); } if ($this->addEntryPrepared) { @@ -203,8 +203,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function commitNewEntries() { $sql = 'SET @rank=(SELECT MAX(id) - COUNT(*) FROM `' . $this->prefix . 'entrytmp`); ' . //MySQL-specific - 'INSERT IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) ' . - 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; ' . + 'INSERT IGNORE INTO `' . $this->prefix . 'entry` + ( + id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags + ) ' . + 'SELECT @rank:=@rank+1 AS id, guid, title, author, content_bin, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags + FROM `' . $this->prefix . 'entrytmp` + ORDER BY date; ' . 'DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= @rank;'; $hadTransaction = $this->bd->inTransaction(); if (!$hadTransaction) { @@ -226,13 +231,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($this->updateEntryPrepared === null) { $sql = 'UPDATE `' . $this->prefix . 'entry` ' - . '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'; + . '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); } @@ -295,8 +300,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { } FreshRSS_UserDAO::touch(); $sql = 'UPDATE `' . $this->prefix . 'entry` ' - . 'SET is_favorite=? ' - . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)'; + . 'SET is_favorite=? ' + . 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?)'; $values = array($is_favorite ? 1 : 0); $values = array_merge($values, $ids); $stm = $this->bd->prepare($sql); @@ -322,14 +327,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { */ protected function updateCacheUnreads($catId = false, $feedId = false) { $sql = 'UPDATE `' . $this->prefix . 'feed` f ' - . 'LEFT OUTER JOIN (' - . 'SELECT e.id_feed, ' - . 'COUNT(*) AS nbUnreads ' - . 'FROM `' . $this->prefix . 'entry` e ' - . '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)'; + . 'LEFT OUTER JOIN (' + . 'SELECT e.id_feed, ' + . 'COUNT(*) AS nbUnreads ' + . 'FROM `' . $this->prefix . 'entry` e ' + . '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)'; $hasWhere = false; $values = array(); if ($feedId !== false) { @@ -558,9 +563,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function searchByGuid($id_feed, $guid) { // un guid est unique pour un flux donné $sql = 'SELECT id, guid, title, author, ' - . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', link, date, is_read, is_favorite, id_feed, tags ' - . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' + . 'FROM `' . $this->prefix . 'entry` WHERE id_feed=? AND guid=?'; $stm = $this->bd->prepare($sql); $values = array( @@ -576,9 +581,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function searchById($id) { $sql = 'SELECT id, guid, title, author, ' - . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', link, date, is_read, is_favorite, id_feed, tags ' - . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', link, date, is_read, is_favorite, id_feed, tags ' + . 'FROM `' . $this->prefix . 'entry` WHERE id=?'; $stm = $this->bd->prepare($sql); $values = array($id); @@ -600,16 +605,14 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if (!($state & FreshRSS_Entry::STATE_READ)) { $search .= 'AND ' . $alias . 'is_read=0 '; } - } - elseif ($state & FreshRSS_Entry::STATE_READ) { + } elseif ($state & FreshRSS_Entry::STATE_READ) { $search .= 'AND ' . $alias . 'is_read=1 '; } if ($state & FreshRSS_Entry::STATE_FAVORITE) { if (!($state & FreshRSS_Entry::STATE_NOT_FAVORITE)) { $search .= 'AND ' . $alias . 'is_favorite=1 '; } - } - elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) { + } elseif ($state & FreshRSS_Entry::STATE_NOT_FAVORITE) { $search .= 'AND ' . $alias . 'is_favorite=0 '; } @@ -621,7 +624,8 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!'); } /*if ($firstId === '' && parent::$sharedDbType === 'mysql') { - $firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; //MySQL optimization. TODO: check if this is needed again, after the filtering for old articles has been removed in 0.9-dev + //MySQL optimization. TODO: check if this is needed again, after the filtering for old articles has been removed in 0.9-dev + $firstId = $order === 'DESC' ? '9000000000'. '000000' : '0'; }*/ if ($firstId !== '') { $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' '; @@ -759,13 +763,13 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filter, $date_min); $sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, ' - . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') - . ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags ' - . 'FROM `' . $this->prefix . 'entry` e0 ' - . 'INNER JOIN (' - . $sql - . ') e2 ON e2.id=e0.id ' - . 'ORDER BY e0.id ' . $order; + . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content') + . ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags ' + . 'FROM `' . $this->prefix . 'entry` e0 ' + . 'INNER JOIN (' + . $sql + . ') e2 ON e2.id=e0.id ' + . 'ORDER BY e0.id ' . $order; $stm = $this->bd->prepare($sql); $stm->execute($values); @@ -839,7 +843,7 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function countUnreadRead() { $sql = 'SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0' - . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0 AND is_read=0'; + . ' UNION SELECT COUNT(e.id) AS count FROM `' . $this->prefix . 'entry` e INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed=f.id WHERE priority > 0 AND is_read=0'; $stm = $this->bd->prepare($sql); $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); @@ -870,9 +874,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { public function countUnreadReadFavorites() { $sql = 'SELECT c FROM (' - . 'SELECT COUNT(id) AS c, 1 as o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 ' - . 'UNION SELECT COUNT(id) AS c, 2 AS o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read=0' - . ') u ORDER BY o'; + . 'SELECT COUNT(id) AS c, 1 as o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 ' + . 'UNION SELECT COUNT(id) AS c, 2 AS o FROM `' . $this->prefix . 'entry` WHERE is_favorite=1 AND is_read=0' + . ') u ORDER BY o'; $stm = $this->bd->prepare($sql); $stm->execute(); $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index 6e6f9e658..405774abf 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -11,7 +11,7 @@ class FreshRSS_EntryDAOPGSQL extends FreshRSS_EntryDAOSQLite { } protected function autoUpdateDb($errorInfo) { - if (isset($errorInfo[0])) { + if (isset($errorInfo[0])) { if ($errorInfo[0] === '42P01' && stripos($errorInfo[2], 'entrytmp') !== false) { //undefined_table return $this->createEntryTempTable(); } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index ad7bcd865..8dad54322 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -27,11 +27,58 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { public function commitNewEntries() { $sql = ' -CREATE TEMP TABLE `tmp` AS SELECT id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `' . $this->prefix . 'entrytmp` ORDER BY date; -INSERT OR IGNORE INTO `' . $this->prefix . 'entry` (id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags) - SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS id, guid, title, author, content, link, date, `lastSeen`, hash, is_read, is_favorite, id_feed, tags FROM `tmp` ORDER BY date; -DELETE FROM `' . $this->prefix . 'entrytmp` WHERE id <= (SELECT MAX(id) FROM `tmp`); -DROP TABLE `tmp`;'; + CREATE TEMP TABLE `tmp` AS + SELECT + id, + guid, + title, + author, + content, + link, + date, + `lastSeen`, + hash, is_read, + is_favorite, + id_feed, + tags + FROM `' . $this->prefix . 'entrytmp` + ORDER BY date; + INSERT OR IGNORE INTO `' . $this->prefix . 'entry` + ( + id, + guid, + title, + author, + content, + link, + date, + `lastSeen`, + hash, + is_read, + is_favorite, + id_feed, + tags + ) + SELECT rowid + (SELECT MAX(id) - COUNT(*) FROM `tmp`) AS + id, + guid, + title, + author, + content, + link, + date, + `lastSeen`, + hash, + is_read, + is_favorite, + id_feed, + tags + FROM `tmp` + ORDER BY date; + DELETE FROM `' . $this->prefix . 'entrytmp` + WHERE id <= (SELECT MAX(id) + FROM `tmp`); + DROP TABLE `tmp`;'; $hadTransaction = $this->bd->inTransaction(); if (!$hadTransaction) { $this->bd->beginTransaction(); diff --git a/app/Models/Feed.php b/app/Models/Feed.php index 4f11d32e9..88ff9bf18 100644 --- a/app/Models/Feed.php +++ b/app/Models/Feed.php @@ -22,7 +22,7 @@ class FreshRSS_Feed extends Minz_Model { private $hubUrl = ''; private $selfUrl = ''; - public function __construct($url, $validate=true) { + public function __construct($url, $validate = true) { if ($validate) { $this->_url($url); } else { @@ -165,7 +165,7 @@ class FreshRSS_Feed extends Minz_Model { public function _id($value) { $this->id = $value; } - public function _url($value, $validate=true) { + public function _url($value, $validate = true) { $this->hash = null; if ($validate) { $value = checkUrl($value); @@ -182,7 +182,7 @@ class FreshRSS_Feed extends Minz_Model { public function _name($value) { $this->name = $value === null ? '' : $value; } - public function _website($value, $validate=true) { + public function _website($value, $validate = true) { if ($validate) { $value = checkUrl($value); } @@ -254,7 +254,9 @@ class FreshRSS_Feed extends Minz_Model { if ((!$mtime) || $feed->error()) { $errorMessage = $feed->error(); - throw new FreshRSS_Feed_Exception(($errorMessage == '' ? 'Unknown error for feed' : $errorMessage) . ' [' . $url . ']'); + throw new FreshRSS_Feed_Exception( + ($errorMessage == '' ? 'Unknown error for feed' : $errorMessage) . ' [' . $url . ']' + ); } $links = $feed->get_links('self'); @@ -324,9 +326,11 @@ class FreshRSS_Feed extends Minz_Model { if (strpos($mime, 'image/') === 0) { $content .= '

'; } elseif (strpos($mime, 'audio/') === 0) { - $content .= '

💾

'; + $content .= '

💾

'; } elseif (strpos($mime, 'video/') === 0) { - $content .= '

💾

'; + $content .= '

💾

'; } elseif (strpos($mime, 'application/') === 0 || strpos($mime, 'text/') === 0) { $content .= '

💾

'; } else { @@ -481,10 +485,12 @@ class FreshRSS_Feed extends Minz_Model { CURLOPT_FOLLOWLOCATION => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'FreshRSS/' . FRESHRSS_VERSION . ' (' . PHP_OS . '; ' . FRESHRSS_WEBSITE . ')', - CURLOPT_POSTFIELDS => 'hub.verify=sync' - . '&hub.mode=' . ($state ? 'subscribe' : 'unsubscribe') - . '&hub.topic=' . urlencode($url) - . '&hub.callback=' . urlencode($callbackUrl) + CURLOPT_POSTFIELDS => http_build_query( + 'hub.verify' => 'sync', + 'hub.mode' => ($state ? 'subscribe' : 'unsubscribe'), + 'hub.topic' => urlencode($url), + 'hub.callback' => urlencode($callbackUrl), + ) ) ); $response = curl_exec($ch); diff --git a/app/Models/FeedDAO.php b/app/Models/FeedDAO.php index d278122e3..0de6d98be 100644 --- a/app/Models/FeedDAO.php +++ b/app/Models/FeedDAO.php @@ -2,7 +2,23 @@ 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); $valuesTmp['url'] = safe_ascii($valuesTmp['url']); @@ -380,7 +396,7 @@ class FreshRSS_FeedDAO extends Minz_ModelPdo implements FreshRSS_Searchable { if ($catID === null) { $category = isset($dao['category']) ? $dao['category'] : 0; } else { - $category = $catID ; + $category = $catID; } $myFeed = new FreshRSS_Feed(isset($dao['url']) ? $dao['url'] : '', false); diff --git a/app/Models/Share.php b/app/Models/Share.php index 1c8a7e767..86b1b9ed9 100644 --- a/app/Models/Share.php +++ b/app/Models/Share.php @@ -87,7 +87,7 @@ class FreshRSS_Share { * decentralized ones. * @param $help_url is an optional url to give help on this option. */ - private function __construct($type, $url_transform, $transform = array(), + private function __construct($type, $url_transform, $transform, $form_type, $help_url = '') { $this->type = $type; $this->name = _t('gen.share.' . $type); diff --git a/app/Models/Themes.php b/app/Models/Themes.php index 5a6ec0a05..8920fbf7e 100644 --- a/app/Models/Themes.php +++ b/app/Models/Themes.php @@ -25,7 +25,7 @@ class FreshRSS_Themes extends Minz_Model { } public static function get_infos($theme_id) { - $theme_dir = PUBLIC_PATH . self::$themesUrl . $theme_id ; + $theme_dir = PUBLIC_PATH . self::$themesUrl . $theme_id; if (is_dir($theme_dir)) { $json_filename = $theme_dir . '/metadata.json'; if (file_exists($json_filename)) { @@ -109,10 +109,8 @@ class FreshRSS_Themes extends Minz_Model { } $url = $name . '.svg'; - $url = isset(self::$themeIcons[$url]) ? (self::$themeIconsUrl . $url) : - (self::$defaultIconsUrl . $url); + $url = isset(self::$themeIcons[$url]) ? (self::$themeIconsUrl . $url) : (self::$defaultIconsUrl . $url); - return $urlOnly ? Minz_Url::display($url) : - '' . $alts[$name] . ''; + return $urlOnly ? Minz_Url::display($url) : '' . $alts[$name] . ''; } } diff --git a/app/Models/UserDAO.php b/app/Models/UserDAO.php index 310c7c096..c921d54c9 100644 --- a/app/Models/UserDAO.php +++ b/app/Models/UserDAO.php @@ -83,17 +83,17 @@ class FreshRSS_UserDAO extends Minz_ModelPdo { } public static function exist($username) { - return is_dir(join_path(DATA_PATH , 'users', $username)); + return is_dir(join_path(DATA_PATH, 'users', $username)); } public static function touch($username = '') { if (!FreshRSS_user_Controller::checkUsername($username)) { $username = Minz_Session::param('currentUser', '_'); } - return touch(join_path(DATA_PATH , 'users', $username, 'config.php')); + return touch(join_path(DATA_PATH, 'users', $username, 'config.php')); } public static function mtime($username) { - return @filemtime(join_path(DATA_PATH , 'users', $username, 'config.php')); + return @filemtime(join_path(DATA_PATH, 'users', $username, 'config.php')); } } diff --git a/app/SQL/install.sql.sqlite.php b/app/SQL/install.sql.sqlite.php index c4e4af006..d485e2120 100644 --- a/app/SQL/install.sql.sqlite.php +++ b/app/SQL/install.sql.sqlite.php @@ -82,8 +82,42 @@ $SQL_CREATE_TABLE_ENTRYTMP = array( global $SQL_INSERT_FEEDS; $SQL_INSERT_FEEDS = array( -'INSERT OR IGNORE INTO `feed` (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 OR IGNORE INTO `feed` (url, category, name, website, description, ttl) VALUES("https://github.com/FreshRSS/FreshRSS/releases.atom", 1, "FreshRSS releases", "https://github.com/FreshRSS/FreshRSS/", "FreshRSS releases @ GitHub", 86400);', +'INSERT OR IGNORE INTO `feed` + ( + 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 OR IGNORE INTO `feed` + ( + url, + category, + name, + website, + description, + ttl + ) + VALUES + ( + "https://github.com/FreshRSS/FreshRSS/releases.atom", + 1, + "FreshRSS releases", + "https://github.com/FreshRSS/FreshRSS/", + "FreshRSS releases @ GitHub", + 86400 + );', ); define('SQL_DROP_TABLES', 'DROP TABLE IF EXISTS `entrytmp`, `entry`, `feed`, `category`'); diff --git a/app/i18n/cz/conf.php b/app/i18n/cz/conf.php index fd414e246..9a4410679 100644 --- a/app/i18n/cz/conf.php +++ b/app/i18n/cz/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Označit článek jako přečtený…', ), 'show' => array( - '_' => 'Počet zobrazených článků', + '_' => 'Počet zobrazených článků', 'adaptive' => 'Vyberte zobrazení', 'all_articles' => 'Zobrazit všechny články', 'unread' => 'Zobrazit jen nepřečtené', diff --git a/app/i18n/cz/gen.php b/app/i18n/cz/gen.php index f891f3f1d..405c05de9 100644 --- a/app/i18n/cz/gen.php +++ b/app/i18n/cz/gen.php @@ -25,7 +25,7 @@ return array( 'keep_logged_in' => 'Zapamatovat přihlášení (%s dny)', 'login' => 'Login', 'logout' => 'Odhlášení', - 'password' => array( + 'password' => array( '_' => 'Heslo', 'format' => 'Alespoň 7 znaků', ), diff --git a/app/i18n/de/conf.php b/app/i18n/de/conf.php index bfc24d2d2..ac7c08e98 100644 --- a/app/i18n/de/conf.php +++ b/app/i18n/de/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Artikel als gelesen markieren…', ), 'show' => array( - '_' => 'Artikel zum Anzeigen', + '_' => 'Artikel zum Anzeigen', 'adaptive' => 'Anzeige anpassen', 'all_articles' => 'Alle Artikel zeigen', 'unread' => 'Nur ungelesene zeigen', diff --git a/app/i18n/en/conf.php b/app/i18n/en/conf.php index f4618a1ff..e4eeb74b7 100644 --- a/app/i18n/en/conf.php +++ b/app/i18n/en/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Mark article as read…', ), 'show' => array( - '_' => 'Articles to display', + '_' => 'Articles to display', 'adaptive' => 'Adjust showing', 'all_articles' => 'Show all articles', 'unread' => 'Show only unread', diff --git a/app/i18n/it/conf.php b/app/i18n/it/conf.php index e6ce86ef9..15837ae8a 100644 --- a/app/i18n/it/conf.php +++ b/app/i18n/it/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Segna articoli come letti…', ), 'show' => array( - '_' => 'Articoli da visualizzare', + '_' => 'Articoli da visualizzare', 'adaptive' => 'Adatta visualizzazione', 'all_articles' => 'Mostra tutti gli articoli', 'unread' => 'Mostra solo non letti', diff --git a/app/i18n/kr/conf.php b/app/i18n/kr/conf.php index e116a6456..35d412078 100644 --- a/app/i18n/kr/conf.php +++ b/app/i18n/kr/conf.php @@ -104,7 +104,7 @@ return array( 'when' => '읽음으로 표시…', ), 'show' => array( - '_' => '글 표시 방식', + '_' => '글 표시 방식', 'adaptive' => '읽지 않은 글이 없으면 모든 글 표시', 'all_articles' => '모든 글 표시', 'unread' => '읽지 않은 글만 표시', diff --git a/app/i18n/nl/conf.php b/app/i18n/nl/conf.php index 77f6307ed..e4db5ec3d 100644 --- a/app/i18n/nl/conf.php +++ b/app/i18n/nl/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Markeer artikel als gelezen…', ), 'show' => array( - '_' => 'Artikelen om te tonen', + '_' => 'Artikelen om te tonen', 'adaptive' => 'Pas weergave aan', 'all_articles' => 'Bekijk alle artikelen', 'unread' => 'Bekijk alleen ongelezen', diff --git a/app/i18n/ru/conf.php b/app/i18n/ru/conf.php index 3cf0a5ea9..9c61754ae 100644 --- a/app/i18n/ru/conf.php +++ b/app/i18n/ru/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Mark article as read…', ), 'show' => array( - '_' => 'Articles to display', + '_' => 'Articles to display', 'adaptive' => 'Adjust showing', 'all_articles' => 'Show all articles', 'unread' => 'Show only unread', diff --git a/app/i18n/tr/conf.php b/app/i18n/tr/conf.php index 9930c3637..e4c094be2 100644 --- a/app/i18n/tr/conf.php +++ b/app/i18n/tr/conf.php @@ -104,7 +104,7 @@ return array( 'when' => 'Makaleyi okundu olarak işaretle…', ), 'show' => array( - '_' => 'Gösterilecek makaleler', + '_' => 'Gösterilecek makaleler', 'adaptive' => 'Ayarlanmış gösterim', 'all_articles' => 'Tüm makaleleri göster', 'unread' => 'Sadece okunmamış makaleleri göster', diff --git a/app/i18n/zh-cn/conf.php b/app/i18n/zh-cn/conf.php index 6ac0bb5e7..fb62c48ef 100644 --- a/app/i18n/zh-cn/conf.php +++ b/app/i18n/zh-cn/conf.php @@ -104,7 +104,7 @@ return array( 'when' => '将文章设为已读…', ), 'show' => array( - '_' => '文章显示', + '_' => '文章显示', 'adaptive' => '智能显示', 'all_articles' => '显示所有文章', 'unread' => '只显示未读', diff --git a/app/install.php b/app/install.php index e77eaa6a5..870c93908 100644 --- a/app/install.php +++ b/app/install.php @@ -11,7 +11,7 @@ session_set_cookie_params(0, dirname(empty($_SERVER['REQUEST_URI']) ? '/' : dirn session_start(); if (isset($_GET['step'])) { - define('STEP',(int)$_GET['step']); + define('STEP', (int)$_GET['step']); } else { define('STEP', 0); } @@ -652,7 +652,7 @@ function printStep3() {
- +
diff --git a/cli/_update-or-create-user.php b/cli/_update-or-create-user.php index c7f1008cd..15397f1f6 100644 --- a/cli/_update-or-create-user.php +++ b/cli/_update-or-create-user.php @@ -23,7 +23,8 @@ if (!$isUpdate) { $options = getopt('', $params); if (empty($options['user'])) { - fail('Usage: ' . basename($_SERVER['SCRIPT_FILENAME']) . " --user username ( --password 'password' --api_password 'api_password'" . + fail('Usage: ' . basename($_SERVER['SCRIPT_FILENAME']) . + " --user username ( --password 'password' --api_password 'api_password'" . " --language en --email user@example.net --token 'longRandomString'" . ($isUpdate ? '' : '--no_default_feeds') . " --purge_after_months 3 --feed_min_articles_default 50 --feed_ttl_default 3600" . diff --git a/cli/create-user.php b/cli/create-user.php index add9c1b13..1b6be3153 100755 --- a/cli/create-user.php +++ b/cli/create-user.php @@ -5,7 +5,8 @@ require('_update-or-create-user.php'); $username = $options['user']; if (!FreshRSS_user_Controller::checkUsername($username)) { - fail('FreshRSS error: invalid username “' . $username . '”! Must be matching ' . FreshRSS_user_Controller::USERNAME_PATTERN); + fail('FreshRSS error: invalid username “' . $username . + '”! Must be matching ' . FreshRSS_user_Controller::USERNAME_PATTERN); } $usernames = listUsers(); diff --git a/cli/do-install.php b/cli/do-install.php index 54e3c3d4f..74bbdfcd4 100755 --- a/cli/do-install.php +++ b/cli/do-install.php @@ -81,14 +81,17 @@ if ($requirements['all'] !== 'ok') { } if (!FreshRSS_user_Controller::checkUsername($options['default_user'])) { - fail('FreshRSS error: invalid default username “' . $options['default_user'] . '”! Must be matching ' . FreshRSS_user_Controller::USERNAME_PATTERN); + fail('FreshRSS error: invalid default username “' . $options['default_user'] + . '”! Must be matching ' . FreshRSS_user_Controller::USERNAME_PATTERN); } if (isset($options['auth_type']) && !in_array($options['auth_type'], array('form', 'http_auth', 'none'))) { - fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }): ' . $options['auth_type']); + fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }): ' + . $options['auth_type']); } -if (file_put_contents(join_path(DATA_PATH, 'config.php'), "default_user !== '' && in_array(FreshRSS_Context::$system_conf->default_user, $users, true)) { +if (FreshRSS_Context::$system_conf->default_user !== '' + && in_array(FreshRSS_Context::$system_conf->default_user, $users, true)) { array_unshift($users, FreshRSS_Context::$system_conf->default_user); $users = array_unique($users); } diff --git a/cli/reconfigure.php b/cli/reconfigure.php index c6902da67..466d35373 100755 --- a/cli/reconfigure.php +++ b/cli/reconfigure.php @@ -51,7 +51,8 @@ if (!FreshRSS_user_Controller::checkUsername($config->default_user)) { } if (isset($config->auth_type) && !in_array($config->auth_type, array('form', 'http_auth', 'none'))) { - fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' . $config->auth_type); + fail('FreshRSS invalid authentication method (auth_type must be one of { form, http_auth, none }: ' + . $config->auth_type); } $config->save(); diff --git a/data/shares.php b/data/shares.php index 16e528d14..d73ae3826 100644 --- a/data/shares.php +++ b/data/shares.php @@ -4,7 +4,7 @@ * This is a configuration file. You shouldn't modify it unless you know what * you are doing. If you want to add a share type, this is where you need to do * it. - * + * * For each share there is different configuration options. Here is the description * of those options: * - url is a mandatory option. It is a string representing the share URL. It diff --git a/lib/Minz/ActionController.php b/lib/Minz/ActionController.php index b47c54554..232a4ef9b 100644 --- a/lib/Minz/ActionController.php +++ b/lib/Minz/ActionController.php @@ -1,5 +1,5 @@ */ @@ -24,7 +24,7 @@ class Minz_ActionController { public function view () { return $this->view; } - + /** * Méthodes à redéfinir (ou non) par héritage * firstAction est la première méthode exécutée par le Dispatcher @@ -34,5 +34,3 @@ class Minz_ActionController { public function firstAction () { } public function lastAction () { } } - - diff --git a/lib/Minz/ActionException.php b/lib/Minz/ActionException.php index c566a076f..f1f70c1bc 100644 --- a/lib/Minz/ActionException.php +++ b/lib/Minz/ActionException.php @@ -3,7 +3,7 @@ class Minz_ActionException extends Minz_Exception { public function __construct ($controller_name, $action_name, $code = self::ERROR) { $message = '`' . $action_name . '` cannot be invoked on `' . $controller_name . '`'; - + parent::__construct ($message, $code); } } diff --git a/lib/Minz/Configuration.php b/lib/Minz/Configuration.php index d695d4a53..5470dc85f 100644 --- a/lib/Minz/Configuration.php +++ b/lib/Minz/Configuration.php @@ -104,7 +104,7 @@ class Minz_Configuration { /** * Create a new Minz_Configuration object. - * + * * @param $namespace the name of the current configuration. * @param $config_filename the file containing configuration values. * @param $default_filename the file containing default values, null by default. @@ -145,7 +145,7 @@ class Minz_Configuration { /** * Return the value of the given param. - * + * * @param $key the name of the param. * @param $default default value to return if key does not exist. * @return the value corresponding to the key. diff --git a/lib/Minz/ControllerNotActionControllerException.php b/lib/Minz/ControllerNotActionControllerException.php index 535a1377e..1a8e0729c 100644 --- a/lib/Minz/ControllerNotActionControllerException.php +++ b/lib/Minz/ControllerNotActionControllerException.php @@ -3,7 +3,7 @@ class Minz_ControllerNotActionControllerException extends Minz_Exception { public function __construct ($controller_name, $code = self::ERROR) { $message = 'Controller `' . $controller_name . '` isn\'t instance of ActionController'; - + parent::__construct ($message, $code); } } diff --git a/lib/Minz/ControllerNotExistException.php b/lib/Minz/ControllerNotExistException.php index 523867d11..24a09a635 100644 --- a/lib/Minz/ControllerNotExistException.php +++ b/lib/Minz/ControllerNotExistException.php @@ -3,7 +3,7 @@ class Minz_ControllerNotExistException extends Minz_Exception { public function __construct ($controller_name, $code = self::ERROR) { $message = 'Controller `' . $controller_name . '` doesn\'t exist'; - + parent::__construct ($message, $code); } } diff --git a/lib/Minz/CurrentPagePaginationException.php b/lib/Minz/CurrentPagePaginationException.php index 74214d879..3e3d9d1b4 100644 --- a/lib/Minz/CurrentPagePaginationException.php +++ b/lib/Minz/CurrentPagePaginationException.php @@ -2,7 +2,7 @@ class Minz_CurrentPagePaginationException extends Minz_Exception { public function __construct ($page) { $message = 'Page number `' . $page . '` doesn\'t exist'; - + parent::__construct ($message, self::ERROR); } } diff --git a/lib/Minz/Dispatcher.php b/lib/Minz/Dispatcher.php index 125ce5757..bdb1c76f6 100644 --- a/lib/Minz/Dispatcher.php +++ b/lib/Minz/Dispatcher.php @@ -1,5 +1,5 @@ */ diff --git a/lib/Minz/FileNotExistException.php b/lib/Minz/FileNotExistException.php index f8dfbdf66..f97f161af 100644 --- a/lib/Minz/FileNotExistException.php +++ b/lib/Minz/FileNotExistException.php @@ -2,7 +2,7 @@ class Minz_FileNotExistException extends Minz_Exception { public function __construct ($file_name, $code = self::ERROR) { $message = 'File not found: `' . $file_name.'`'; - + parent::__construct ($message, $code); } } diff --git a/lib/Minz/FrontController.php b/lib/Minz/FrontController.php index 952d983c9..066278b7c 100644 --- a/lib/Minz/FrontController.php +++ b/lib/Minz/FrontController.php @@ -119,12 +119,12 @@ class Minz_FrontController { switch($conf->environment) { case 'production': error_reporting(E_ALL); - ini_set('display_errors','Off'); + ini_set('display_errors', 'Off'); ini_set('log_errors', 'On'); break; case 'development': error_reporting(E_ALL); - ini_set('display_errors','On'); + ini_set('display_errors', 'On'); ini_set('log_errors', 'On'); break; case 'silent': diff --git a/lib/Minz/Helper.php b/lib/Minz/Helper.php index f4a547c4e..c328d9e6b 100644 --- a/lib/Minz/Helper.php +++ b/lib/Minz/Helper.php @@ -1,5 +1,5 @@ */ @@ -13,7 +13,7 @@ class Minz_Helper { * @param $var variable à traiter (tableau ou simple variable) */ public static function stripslashes_r($var) { - if (is_array($var)){ + if (is_array($var)) { return array_map(array('Minz_Helper', 'stripslashes_r'), $var); } else { return stripslashes($var); diff --git a/lib/Minz/Model.php b/lib/Minz/Model.php index adbaba942..1310888cf 100644 --- a/lib/Minz/Model.php +++ b/lib/Minz/Model.php @@ -1,5 +1,5 @@ */ diff --git a/lib/Minz/ModelArray.php b/lib/Minz/ModelArray.php index ff23dbc83..1ac2b313d 100644 --- a/lib/Minz/ModelArray.php +++ b/lib/Minz/ModelArray.php @@ -25,8 +25,7 @@ class Minz_ModelArray { protected function loadArray() { if (!file_exists($this->filename)) { throw new Minz_FileNotExistException($this->filename, Minz_Exception::WARNING); - } - elseif (($handle = $this->getLock()) === false) { + } elseif (($handle = $this->getLock()) === false) { throw new Minz_PermissionDeniedException($this->filename); } else { $data = include($this->filename); diff --git a/lib/Minz/ModelPdo.php b/lib/Minz/ModelPdo.php index caab1d114..d769e0ff4 100644 --- a/lib/Minz/ModelPdo.php +++ b/lib/Minz/ModelPdo.php @@ -39,7 +39,7 @@ class Minz_ModelPdo { if ($currentUser === null) { $currentUser = Minz_Session::param('currentUser'); } - if (self::$useSharedBd && self::$sharedBd != null && + if (self::$useSharedBd && self::$sharedBd != null && ($currentUser == null || $currentUser === self::$sharedCurrentUser)) { $this->bd = self::$sharedBd; $this->prefix = self::$sharedPrefix; diff --git a/lib/Minz/PDOConnectionException.php b/lib/Minz/PDOConnectionException.php index faf2e0fe9..064748708 100644 --- a/lib/Minz/PDOConnectionException.php +++ b/lib/Minz/PDOConnectionException.php @@ -3,7 +3,7 @@ class Minz_PDOConnectionException extends Minz_Exception { public function __construct ($string_connection, $user, $code = self::ERROR) { $message = 'Access to database is denied for `' . $user . '`' . ' (`' . $string_connection . '`)'; - + parent::__construct ($message, $code); } } diff --git a/lib/Minz/Paginator.php b/lib/Minz/Paginator.php index 5858e76a5..795085a30 100644 --- a/lib/Minz/Paginator.php +++ b/lib/Minz/Paginator.php @@ -1,5 +1,5 @@ */ @@ -51,7 +51,7 @@ class Minz_Paginator { */ public function render ($view, $getteur) { $view = APP_PATH . '/views/helpers/'.$view; - + if (file_exists ($view)) { include ($view); } @@ -129,7 +129,7 @@ class Minz_Paginator { $begin = ($this->currentPage - 1) * $this->nbItemsPerPage; $counter = 0; $i = 0; - + foreach ($this->items as $key => $item) { if ($i >= $begin) { $array[$key] = $item; @@ -164,7 +164,7 @@ class Minz_Paginator { if (is_array ($items)) { $this->items = $items; } - + $this->_nbPage (); } public function _nbItemsPerPage ($nbItemsPerPage) { diff --git a/lib/Minz/Translate.php b/lib/Minz/Translate.php index baddcb424..d9cd59f7e 100644 --- a/lib/Minz/Translate.php +++ b/lib/Minz/Translate.php @@ -1,5 +1,5 @@ */ @@ -153,7 +153,7 @@ class Minz_Translate { * @param additional parameters for variable keys. * @return the value corresponding to the key. * If no value is found, return the key itself. - */ + */ public static function t($key) { $group = explode('.', $key); diff --git a/lib/Minz/View.php b/lib/Minz/View.php index 8c5230ab6..d6bf6ea2c 100644 --- a/lib/Minz/View.php +++ b/lib/Minz/View.php @@ -262,5 +262,3 @@ class Minz_View { } } } - - diff --git a/lib/favicons.php b/lib/favicons.php index 48f7c9fda..a7ed966a1 100644 --- a/lib/favicons.php +++ b/lib/favicons.php @@ -16,6 +16,7 @@ function isImgMime($content) { $isImage = strpos(finfo_buffer($fInfo, $content), 'image') !== false; finfo_close($fInfo); } catch (Exception $e) { + echo 'Caught exception: ', $e->getMessage(), "\n"; } return $isImage; } diff --git a/lib/lib_date.php b/lib/lib_date.php index 9533711e3..a3f7efb46 100644 --- a/lib/lib_date.php +++ b/lib/lib_date.php @@ -63,8 +63,7 @@ function _dateCeiling($isoDate) { } function _noDelimit($isoDate) { - return $isoDate === null || $isoDate === '' ? null : - str_replace(array('-', ':'), '', $isoDate); //FIXME: Bug with negative time zone + return $isoDate === null || $isoDate === '' ? null : str_replace(array('-', ':'), '', $isoDate); //FIXME: Bug with negative time zone } function _dateRelative($d1, $d2) { diff --git a/lib/lib_install.php b/lib/lib_install.php index cc0dc3128..7305d8e28 100644 --- a/lib/lib_install.php +++ b/lib/lib_install.php @@ -68,8 +68,7 @@ function checkRequirements($dbType = '') { 'http_referer' => $http_referer ? 'ok' : 'ko', 'message' => $message ?: 'ok', 'all' => $php && $minz && $curl && $pdo && $pcre && $ctype && $dom && $xml && - $data && $cache && $users && $favicons && $http_referer && $message == '' ? - 'ok' : 'ko' + $data && $cache && $users && $favicons && $http_referer && $message == '' ? 'ok' : 'ko' ); } diff --git a/lib/lib_rss.php b/lib/lib_rss.php index dcce8a02c..959cb064c 100644 --- a/lib/lib_rss.php +++ b/lib/lib_rss.php @@ -348,6 +348,7 @@ function get_user_configuration($username) { join_path(FRESHRSS_PATH, 'config-user.default.php')); } catch (Minz_ConfigurationNamespaceException $e) { // namespace already exists, do nothing. + Minz_Log::warning($e->getMessage()); } catch (Minz_FileNotExistException $e) { Minz_Log::warning($e->getMessage()); return null; @@ -366,6 +367,7 @@ function cryptAvailable() { $hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG'; return $hash === @crypt('password', $hash); } catch (Exception $e) { + Minz_Log::warning($e->getMessage()); } return false; } diff --git a/p/api/greader.php b/p/api/greader.php index 7f6f0b04f..e5275b3a3 100644 --- a/p/api/greader.php +++ b/p/api/greader.php @@ -94,7 +94,16 @@ function debugInfo() { } } global $ORIGINAL_INPUT; - return print_r(array('date' => date('c'), 'headers' => $ALL_HEADERS, '_SERVER' => $_SERVER, '_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE, 'INPUT' => $ORIGINAL_INPUT), true); + return print_r( + array( + 'date' => date('c'), + 'headers' => $ALL_HEADERS, + '_SERVER' => $_SERVER, + '_GET' => $_GET, + '_POST' => $_POST, + '_COOKIE' => $_COOKIE, + 'INPUT' => $ORIGINAL_INPUT + ), true); } function badRequest() { @@ -694,8 +703,8 @@ $pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_I $pathInfos = explode('/', $pathInfo); Minz_Configuration::register('system', - DATA_PATH . '/config.php', - FRESHRSS_PATH . '/config.default.php'); + DATA_PATH . '/config.php', + FRESHRSS_PATH . '/config.default.php'); FreshRSS_Context::$system_conf = Minz_Configuration::get('system'); if (!FreshRSS_Context::$system_conf->api_enabled) { serviceUnavailable(); @@ -715,24 +724,34 @@ Minz_Session::_param('currentUser', $user); if (count($pathInfos) < 3) { badRequest(); -} -elseif ($pathInfos[1] === 'accounts') { +} elseif ($pathInfos[1] === 'accounts') { if (($pathInfos[2] === 'ClientLogin') && isset($_REQUEST['Email']) && isset($_REQUEST['Passwd'])) { clientLogin($_REQUEST['Email'], $_REQUEST['Passwd']); } -} -elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfos[3]) && $pathInfos[3] === '0' && isset($pathInfos[4])) { +} elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfos[3]) && $pathInfos[3] === '0' && isset($pathInfos[4])) { if ($user == '') { unauthorized(); } $timestamp = isset($_GET['ck']) ? intval($_GET['ck']) : 0; //ck=[unix timestamp] : Use the current Unix time here, helps Google with caching. switch ($pathInfos[4]) { case 'stream': - $exclude_target = isset($_GET['xt']) ? $_GET['xt'] : ''; //xt=[exclude target] : Used to exclude certain items from the feed. For example, using xt=user/-/state/com.google/read will exclude items that the current user has marked as read, or xt=feed/[feedurl] will exclude items from a particular feed (obviously not useful in this request, but xt appears in other listing requests). + /* xt=[exclude target] : Used to exclude certain items from the feed. + * For example, using xt=user/-/state/com.google/read will exclude items + * that the current user has marked as read, or xt=feed/[feedurl] will + * exclude items from a particular feed (obviously not useful in this + * request, but xt appears in other listing requests). */ + $exclude_target = isset($_GET['xt']) ? $_GET['xt'] : ''; $count = isset($_GET['n']) ? intval($_GET['n']) : 20; //n=[integer] : The maximum number of results to return. $order = isset($_GET['r']) ? $_GET['r'] : 'd'; //r=[d|n|o] : Sort order of item results. d or n gives items in descending date order, o in ascending order. - $start_time = isset($_GET['ot']) ? intval($_GET['ot']) : 0; //ot=[unix timestamp] : The time from which you want to retrieve items. Only items that have been crawled by Google Reader after this time will be returned. - $continuation = isset($_GET['c']) ? $_GET['c'] : ''; //Continuation token. If a StreamContents response does not represent all items in a timestamp range, it will have a continuation attribute. The same request can be re-issued with the value of that attribute put in this parameter to get more items + /* ot=[unix timestamp] : The time from which you want to retrieve + * items. Only items that have been crawled by Google Reader after + * this time will be returned. */ + $start_time = isset($_GET['ot']) ? intval($_GET['ot']) : 0; + /* Continuation token. If a StreamContents response does not represent + * all items in a timestamp range, it will have a continuation attribute. + * The same request can be re-issued with the value of that attribute put + * in this parameter to get more items */ + $continuation = isset($_GET['c']) ? $_GET['c'] : ''; if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6])) { if (isset($pathInfos[7])) { if ($pathInfos[6] === 'feed') { @@ -757,7 +776,10 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo } } elseif ($pathInfos[5] === 'items') { if ($pathInfos[6] === 'ids' && isset($_GET['s'])) { - $streamId = $_GET['s']; //StreamId for which to fetch the item IDs. The parameter may be repeated to fetch the item IDs from multiple streams at once (more efficient from a backend perspective than multiple requests). + /* StreamId for which to fetch the item IDs. The parameter may + * be repeated to fetch the item IDs from multiple streams at once + * (more efficient from a backend perspective than multiple requests). */ + $streamId = $_GET['s']; streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target); } } @@ -779,8 +801,12 @@ elseif ($pathInfos[1] === 'reader' && $pathInfos[2] === 'api' && isset($pathInfo break; case 'edit': if (isset($_POST['s']) && isset($_POST['ac'])) { - $streamNames = multiplePosts('s'); //StreamId to operate on. The parameter may be repeated to edit multiple subscriptions at once - $titles = multiplePosts('t'); //Title to use for the subscription. For the `subscribe` action, if not specified then the feed's current title will be used. Can be used with the `edit` action to rename a subscription + //StreamId to operate on. The parameter may be repeated to edit multiple subscriptions at once + $streamNames = multiplePosts('s'); + /* Title to use for the subscription. For the `subscribe` action, + * if not specified then the feed's current title will be used. Can + * be used with the `edit` action to rename a subscription */ + $titles = multiplePosts('t'); $action = $_POST['ac']; //Action to perform on the given StreamId. Possible values are `subscribe`, `unsubscribe` and `edit` $add = isset($_POST['a']) ? $_POST['a'] : ''; //StreamId to add the subscription to (generally a user label) $remove = isset($_POST['r']) ? $_POST['r'] : ''; //StreamId to remove the subscription from (generally a user label) diff --git a/p/api/index.php b/p/api/index.php index 580c90255..08f7b6b7b 100644 --- a/p/api/index.php +++ b/p/api/index.php @@ -22,7 +22,8 @@ echo Minz_Url::display('/api/greader.php', 'html', true); diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 000000000..5743466ec --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,100 @@ + + + Created with the PHP Coding Standard Generator. http://edorian.github.com/php-coding-standard-generator/ + + + ./static + ./vendor + ./lib/SimplePie/ + ./lib/http-conditional.php + ./lib/JSON.php + ./lib/lib_phpQuery.php + ./lib/password_compat.php + + + + + + + + + + + + + + + ./app/i18n/ + + ./app/install.php + + ./tests/app/ + + ./app/SQL/install.sql.mysql.php + ./app/SQL/install.sql.pgsql.php + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/app/Models/CategoryTest.php b/tests/app/Models/CategoryTest.php index 2fd153aee..a1edb17b6 100644 --- a/tests/app/Models/CategoryTest.php +++ b/tests/app/Models/CategoryTest.php @@ -20,12 +20,12 @@ class FreshRSS_CategoryTest extends PHPUnit\Framework\TestCase { public function provideValidNames() { return array( - array('', ''), - array('this string does not need trimming', 'this string does not need trimming'), - array(' this string needs trimming on left', 'this string needs trimming on left'), - array('this string needs trimming on right ', 'this string needs trimming on right'), - array(' this string needs trimming on both ends ', 'this string needs trimming on both ends'), - array(str_repeat('This string needs to be shortened because its length is way too long. ', 4), str_repeat('This string needs to be shortened because its length is way too long. ', 3) . 'This string needs to be shortened because its'), + array('', ''), + array('this string does not need trimming', 'this string does not need trimming'), + array(' this string needs trimming on left', 'this string needs trimming on left'), + array('this string needs trimming on right ', 'this string needs trimming on right'), + array(' this string needs trimming on both ends ', 'this string needs trimming on both ends'), + array(str_repeat('This string needs to be shortened because its length is way too long. ', 4), str_repeat('This string needs to be shortened because its length is way too long. ', 3) . 'This string needs to be shortened because its'), ); } diff --git a/tests/app/Models/SearchTest.php b/tests/app/Models/SearchTest.php index 4a7afc6f9..5c0469a48 100644 --- a/tests/app/Models/SearchTest.php +++ b/tests/app/Models/SearchTest.php @@ -28,8 +28,8 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideEmptyInput() { return array( - array(''), - array(null), + array(''), + array(null), ); } @@ -50,22 +50,22 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideIntitleSearch() { return array( - array('intitle:word1', array('word1'), null), - array('intitle:word1 word2', array('word1'), array('word2')), - array('intitle:"word1 word2"', array('word1 word2'), null), - array("intitle:'word1 word2'", array('word1 word2'), null), - array('word1 intitle:word2', array('word2'), array('word1')), - array('word1 intitle:word2 word3', array('word2'), array('word1', 'word3')), - array('word1 intitle:"word2 word3"', array('word2 word3'), array('word1')), - array("word1 intitle:'word2 word3'", array('word2 word3'), array('word1')), - array('intitle:word1 intitle:word2', array('word1', 'word2'), null), - array('intitle: word1 word2', array(), array('word1', 'word2')), - array('intitle:123', array('123'), null), - array('intitle:"word1 word2" word3"', array('word1 word2'), array('word3"')), - array("intitle:'word1 word2' word3'", array('word1 word2'), array("word3'")), - array('intitle:"word1 word2\' word3"', array("word1 word2' word3"), null), - array("intitle:'word1 word2\" word3'", array('word1 word2" word3'), null), - array("intitle:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), + array('intitle:word1', array('word1'), null), + array('intitle:word1 word2', array('word1'), array('word2')), + array('intitle:"word1 word2"', array('word1 word2'), null), + array("intitle:'word1 word2'", array('word1 word2'), null), + array('word1 intitle:word2', array('word2'), array('word1')), + array('word1 intitle:word2 word3', array('word2'), array('word1', 'word3')), + array('word1 intitle:"word2 word3"', array('word2 word3'), array('word1')), + array("word1 intitle:'word2 word3'", array('word2 word3'), array('word1')), + array('intitle:word1 intitle:word2', array('word1', 'word2'), null), + array('intitle: word1 word2', array(), array('word1', 'word2')), + array('intitle:123', array('123'), null), + array('intitle:"word1 word2" word3"', array('word1 word2'), array('word3"')), + array("intitle:'word1 word2' word3'", array('word1 word2'), array("word3'")), + array('intitle:"word1 word2\' word3"', array("word1 word2' word3"), null), + array("intitle:'word1 word2\" word3'", array('word1 word2" word3'), null), + array("intitle:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), ); } @@ -86,22 +86,22 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideAuthorSearch() { return array( - array('author:word1', array('word1'), null), - array('author:word1 word2', array('word1'), array('word2')), - array('author:"word1 word2"', array('word1 word2'), null), - array("author:'word1 word2'", array('word1 word2'), null), - array('word1 author:word2', array('word2'), array('word1')), - array('word1 author:word2 word3', array('word2'), array('word1', 'word3')), - array('word1 author:"word2 word3"', array('word2 word3'), array('word1')), - array("word1 author:'word2 word3'", array('word2 word3'), array('word1')), - array('author:word1 author:word2', array('word1', 'word2'), null), - array('author: word1 word2', array(), array('word1', 'word2')), - array('author:123', array('123'), null), - array('author:"word1 word2" word3"', array('word1 word2'), array('word3"')), - array("author:'word1 word2' word3'", array('word1 word2'), array("word3'")), - array('author:"word1 word2\' word3"', array("word1 word2' word3"), null), - array("author:'word1 word2\" word3'", array('word1 word2" word3'), null), - array("author:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), + array('author:word1', array('word1'), null), + array('author:word1 word2', array('word1'), array('word2')), + array('author:"word1 word2"', array('word1 word2'), null), + array("author:'word1 word2'", array('word1 word2'), null), + array('word1 author:word2', array('word2'), array('word1')), + array('word1 author:word2 word3', array('word2'), array('word1', 'word3')), + array('word1 author:"word2 word3"', array('word2 word3'), array('word1')), + array("word1 author:'word2 word3'", array('word2 word3'), array('word1')), + array('author:word1 author:word2', array('word1', 'word2'), null), + array('author: word1 word2', array(), array('word1', 'word2')), + array('author:123', array('123'), null), + array('author:"word1 word2" word3"', array('word1 word2'), array('word3"')), + array("author:'word1 word2' word3'", array('word1 word2'), array("word3'")), + array('author:"word1 word2\' word3"', array("word1 word2' word3"), null), + array("author:'word1 word2\" word3'", array('word1 word2" word3'), null), + array("author:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), ); } @@ -122,13 +122,13 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideInurlSearch() { return array( - array('inurl:word1', array('word1'), null), - array('inurl: word1', array(), array('word1')), - array('inurl:123', array('123'), null), - array('inurl:word1 word2', array('word1'), array('word2')), - array('inurl:"word1 word2"', array('"word1'), array('word2"')), - array('inurl:word1 word2 inurl:word3', array('word1', 'word3'), array('word2')), - array("inurl:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), + array('inurl:word1', array('word1'), null), + array('inurl: word1', array(), array('word1')), + array('inurl:123', array('123'), null), + array('inurl:word1 word2', array('word1'), array('word2')), + array('inurl:"word1 word2"', array('"word1'), array('word2"')), + array('inurl:word1 word2 inurl:word3', array('word1', 'word3'), array('word2')), + array("inurl:word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), ); } @@ -149,12 +149,12 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideDateSearch() { return array( - array('date:2007-03-01T13:00:00Z/2008-05-11T15:30:00Z', '1172754000', '1210519800'), - array('date:2007-03-01T13:00:00Z/P1Y2M10DT2H30M', '1172754000', '1210516199'), - array('date:P1Y2M10DT2H30M/2008-05-11T15:30:00Z', '1172757601', '1210519800'), - array('date:2007-03-01/2008-05-11', strtotime('2007-03-01'), strtotime('2008-05-12') - 1), - array('date:2007-03-01/', strtotime('2007-03-01'), ''), - array('date:/2008-05-11', '', strtotime('2008-05-12') - 1), + array('date:2007-03-01T13:00:00Z/2008-05-11T15:30:00Z', '1172754000', '1210519800'), + array('date:2007-03-01T13:00:00Z/P1Y2M10DT2H30M', '1172754000', '1210516199'), + array('date:P1Y2M10DT2H30M/2008-05-11T15:30:00Z', '1172757601', '1210519800'), + array('date:2007-03-01/2008-05-11', strtotime('2007-03-01'), strtotime('2008-05-12') - 1), + array('date:2007-03-01/', strtotime('2007-03-01'), ''), + array('date:/2008-05-11', '', strtotime('2008-05-12') - 1), ); } @@ -175,12 +175,12 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function providePubdateSearch() { return array( - array('pubdate:2007-03-01T13:00:00Z/2008-05-11T15:30:00Z', '1172754000', '1210519800'), - array('pubdate:2007-03-01T13:00:00Z/P1Y2M10DT2H30M', '1172754000', '1210516199'), - array('pubdate:P1Y2M10DT2H30M/2008-05-11T15:30:00Z', '1172757601', '1210519800'), - array('pubdate:2007-03-01/2008-05-11', strtotime('2007-03-01'), strtotime('2008-05-12') - 1), - array('pubdate:2007-03-01/', strtotime('2007-03-01'), ''), - array('pubdate:/2008-05-11', '', strtotime('2008-05-12') - 1), + array('pubdate:2007-03-01T13:00:00Z/2008-05-11T15:30:00Z', '1172754000', '1210519800'), + array('pubdate:2007-03-01T13:00:00Z/P1Y2M10DT2H30M', '1172754000', '1210516199'), + array('pubdate:P1Y2M10DT2H30M/2008-05-11T15:30:00Z', '1172757601', '1210519800'), + array('pubdate:2007-03-01/2008-05-11', strtotime('2007-03-01'), strtotime('2008-05-12') - 1), + array('pubdate:2007-03-01/', strtotime('2007-03-01'), ''), + array('pubdate:/2008-05-11', '', strtotime('2008-05-12') - 1), ); } @@ -201,13 +201,13 @@ class SearchTest extends PHPUnit\Framework\TestCase { */ public function provideTagsSearch() { return array( - array('#word1', array('word1'), null), - array('# word1', array(), array('#', 'word1')), - array('#123', array('123'), null), - array('#word1 word2', array('word1'), array('word2')), - array('#"word1 word2"', array('"word1'), array('word2"')), - array('#word1 #word2', array('word1', 'word2'), null), - array("#word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), + array('#word1', array('word1'), null), + array('# word1', array(), array('#', 'word1')), + array('#123', array('123'), null), + array('#word1 word2', array('word1'), array('word2')), + array('#"word1 word2"', array('"word1'), array('word2"')), + array('#word1 #word2', array('word1', 'word2'), null), + array("#word1 'word2 word3' word4", array('word1'), array('word2 word3', 'word4')), ); } @@ -240,54 +240,54 @@ class SearchTest extends PHPUnit\Framework\TestCase { public function provideMultipleSearch() { return array( - array( - 'author:word1 date:2007-03-01/2008-05-11 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 #word5', - array('word1'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word2'), - array('word3'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word4', 'word5'), - null, - ), - array( - 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 date:2007-03-01/2008-05-11', - array('word1'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word2'), - array('word3'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word4', 'word5'), - array('word6'), - ), - array( - 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 word7 date:2007-03-01/2008-05-11', - array('word1'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word2'), - array('word3'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word4', 'word5'), - array('word6', 'word7'), - ), - array( - 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 "word7 word8" date:2007-03-01/2008-05-11', - array('word1'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word2'), - array('word3'), - strtotime('2007-03-01'), - strtotime('2008-05-12') - 1, - array('word4', 'word5'), - array('word7 word8', 'word6'), - ), + array( + 'author:word1 date:2007-03-01/2008-05-11 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 #word5', + array('word1'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word2'), + array('word3'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word4', 'word5'), + null, + ), + array( + 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 date:2007-03-01/2008-05-11', + array('word1'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word2'), + array('word3'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word4', 'word5'), + array('word6'), + ), + array( + 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 word7 date:2007-03-01/2008-05-11', + array('word1'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word2'), + array('word3'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word4', 'word5'), + array('word6', 'word7'), + ), + array( + 'word6 intitle:word2 inurl:word3 pubdate:2007-03-01/2008-05-11 #word4 author:word1 #word5 "word7 word8" date:2007-03-01/2008-05-11', + array('word1'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word2'), + array('word3'), + strtotime('2007-03-01'), + strtotime('2008-05-12') - 1, + array('word4', 'word5'), + array('word7 word8', 'word6'), + ), ); } diff --git a/tests/app/Models/UserQueryTest.php b/tests/app/Models/UserQueryTest.php index 5c12a12fc..1959fd492 100644 --- a/tests/app/Models/UserQueryTest.php +++ b/tests/app/Models/UserQueryTest.php @@ -33,14 +33,14 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { $category_name = 'some category name'; $cat = $this->getMock('FreshRSS_Category'); $cat->expects($this->atLeastOnce()) - ->method('name') - ->withAnyParameters() - ->willReturn($category_name); + ->method('name') + ->withAnyParameters() + ->willReturn($category_name); $cat_dao = $this->getMock('FreshRSS_Searchable'); $cat_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn($cat); + ->method('searchById') + ->withAnyParameters() + ->willReturn($cat); $query = array('get' => 'c_1'); $user_query = new FreshRSS_UserQuery($query, null, $cat_dao); $this->assertEquals($category_name, $user_query->getGetName()); @@ -61,14 +61,14 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { $feed_name = 'some feed name'; $feed = $this->getMock('FreshRSS_Feed', array(), array('', false)); $feed->expects($this->atLeastOnce()) - ->method('name') - ->withAnyParameters() - ->willReturn($feed_name); + ->method('name') + ->withAnyParameters() + ->willReturn($feed_name); $feed_dao = $this->getMock('FreshRSS_Searchable'); $feed_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn($feed); + ->method('searchById') + ->withAnyParameters() + ->willReturn($feed); $query = array('get' => 'f_1'); $user_query = new FreshRSS_UserQuery($query, $feed_dao, null); $this->assertEquals($feed_name, $user_query->getGetName()); @@ -118,12 +118,12 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { public function testToArray_whenData_returnsArray() { $query = array( - 'get' => 's', - 'name' => 'some name', - 'order' => 'some order', - 'search' => 'some search', - 'state' => 'some state', - 'url' => 'some url', + 'get' => 's', + 'name' => 'some name', + 'order' => 'some order', + 'search' => 'some search', + 'state' => 'some state', + 'url' => 'some url', ); $user_query = new FreshRSS_UserQuery($query); $this->assertInternalType('array', $user_query->toArray()); @@ -133,7 +133,7 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { public function testHasSearch_whenSearch_returnsTrue() { $query = array( - 'search' => 'some search', + 'search' => 'some search', ); $user_query = new FreshRSS_UserQuery($query); $this->assertTrue($user_query->hasSearch()); @@ -166,9 +166,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { $cat = $this->getMock('FreshRSS_Category'); $cat_dao = $this->getMock('FreshRSS_Searchable'); $cat_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn($cat); + ->method('searchById') + ->withAnyParameters() + ->willReturn($cat); $query = array('get' => 'c_1'); $user_query = new FreshRSS_UserQuery($query, null, $cat_dao); $this->assertFalse($user_query->isDeprecated()); @@ -177,9 +177,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { public function testIsDeprecated_whenCategoryDoesNotExist_returnTrue() { $cat_dao = $this->getMock('FreshRSS_Searchable'); $cat_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn(null); + ->method('searchById') + ->withAnyParameters() + ->willReturn(null); $query = array('get' => 'c_1'); $user_query = new FreshRSS_UserQuery($query, null, $cat_dao); $this->assertTrue($user_query->isDeprecated()); @@ -189,9 +189,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { $feed = $this->getMock('FreshRSS_Feed', array(), array('', false)); $feed_dao = $this->getMock('FreshRSS_Searchable'); $feed_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn($feed); + ->method('searchById') + ->withAnyParameters() + ->willReturn($feed); $query = array('get' => 'f_1'); $user_query = new FreshRSS_UserQuery($query, $feed_dao, null); $this->assertFalse($user_query->isDeprecated()); @@ -200,9 +200,9 @@ class UserQueryTest extends PHPUnit\Framework\TestCase { public function testIsDeprecated_whenFeedDoesNotExist_returnTrue() { $feed_dao = $this->getMock('FreshRSS_Searchable'); $feed_dao->expects($this->atLeastOnce()) - ->method('searchById') - ->withAnyParameters() - ->willReturn(null); + ->method('searchById') + ->withAnyParameters() + ->willReturn(null); $query = array('get' => 'f_1'); $user_query = new FreshRSS_UserQuery($query, $feed_dao, null); $this->assertTrue($user_query->isDeprecated()); diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 24340b15c..896929649 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -4,4 +4,4 @@ error_reporting(E_ALL); ini_set('display_errors', 1); require('../constants.php'); -require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader \ No newline at end of file +require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader -- cgit v1.2.3 From f632a346269100d6a93bef318ffa66c97f16f6fa Mon Sep 17 00:00:00 2001 From: Alexandre Alapetite Date: Thu, 12 Oct 2017 20:11:06 +0200 Subject: CLI optimize database (#1663) CLI optimize database https://github.com/FreshRSS/FreshRSS/issues/1583 And VACUUM in SQLite https://github.com/FreshRSS/FreshRSS/issues/918 Add VACUUM for PostgreSQL (Not tested yet) --- CHANGELOG.md | 4 ++++ app/Controllers/configureController.php | 6 +++-- app/Controllers/entryController.php | 4 ++-- app/Controllers/userController.php | 4 +++- app/Models/DatabaseDAO.php | 41 +++++++++++++++++++++++++++++++++ app/Models/DatabaseDAOPGSQL.php | 37 +++++++++++++++++++++++++++++ app/Models/DatabaseDAOSQLite.php | 13 +++++++++++ app/Models/EntryDAO.php | 22 ------------------ app/Models/EntryDAOPGSQL.php | 11 --------- app/Models/EntryDAOSQLite.php | 8 ------- cli/README.md | 3 +++ cli/db-optimize.php | 20 ++++++++++++++++ cli/user-info.php | 5 ++-- 13 files changed, 130 insertions(+), 48 deletions(-) create mode 100755 cli/db-optimize.php (limited to 'app/Models/EntryDAOSQLite.php') diff --git a/CHANGELOG.md b/CHANGELOG.md index 051b04a3e..97fcdcbed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ * Remove "SimplePie" name from HTTP User-Agent string [#1656](https://github.com/FreshRSS/FreshRSS/pull/1656) * Bug fixing * Work-around for `CURLOPT_FOLLOWLOCATION` `open_basedir` bug in favicons and PubSubHubbub [#1655](https://github.com/FreshRSS/FreshRSS/issues/1655) +* CLI + * New command `./cli/db-optimize.php` for database optimisation [#1583](https://github.com/FreshRSS/FreshRSS/issues/1583) +* SQL + * Perform `VACUUM` on SQLite and PostgreSQL databases when optimisation is requested [#918](https://github.com/FreshRSS/FreshRSS/issues/918) * Misc. * Translation validation tool [#1653](https://github.com/FreshRSS/FreshRSS/pull/1653) * Translation manipulation tool [#1658](https://github.com/FreshRSS/FreshRSS/pull/1658) diff --git a/app/Controllers/configureController.php b/app/Controllers/configureController.php index 155221d19..9d2ee450c 100755 --- a/app/Controllers/configureController.php +++ b/app/Controllers/configureController.php @@ -225,10 +225,12 @@ class FreshRSS_configure_Controller extends Minz_ActionController { $entryDAO = FreshRSS_Factory::createEntryDao(); $this->view->nb_total = $entryDAO->count(); - $this->view->size_user = $entryDAO->size(); + + $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); + $this->view->size_user = $databaseDAO->size(); if (FreshRSS_Auth::hasAccess('admin')) { - $this->view->size_total = $entryDAO->size(true); + $this->view->size_total = $databaseDAO->size(true); } } diff --git a/app/Controllers/entryController.php b/app/Controllers/entryController.php index c40588105..bd8b65b2b 100755 --- a/app/Controllers/entryController.php +++ b/app/Controllers/entryController.php @@ -147,8 +147,8 @@ class FreshRSS_entry_Controller extends Minz_ActionController { @set_time_limit(300); - $entryDAO = FreshRSS_Factory::createEntryDao(); - $entryDAO->optimizeTable(); + $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); + $databaseDAO->optimize(); $feedDAO = FreshRSS_Factory::createFeedDao(); $feedDAO->updateCachedValues(); diff --git a/app/Controllers/userController.php b/app/Controllers/userController.php index a58501186..2a1d43d9e 100644 --- a/app/Controllers/userController.php +++ b/app/Controllers/userController.php @@ -120,7 +120,9 @@ class FreshRSS_user_Controller extends Minz_ActionController { // Get information about the current user. $entryDAO = FreshRSS_Factory::createEntryDao($this->view->current_user); $this->view->nb_articles = $entryDAO->count(); - $this->view->size_user = $entryDAO->size(); + + $databaseDAO = FreshRSS_Factory::createDatabaseDAO(); + $this->view->size_user = $databaseDAO->size(); } public static function createUser($new_user_name, $passwordPlain, $apiPasswordPlain, $userConfig = array(), $insertDefaultFeeds = true) { diff --git a/app/Models/DatabaseDAO.php b/app/Models/DatabaseDAO.php index 6ba5bca3e..f5469f2b7 100644 --- a/app/Models/DatabaseDAO.php +++ b/app/Models/DatabaseDAO.php @@ -80,4 +80,45 @@ class FreshRSS_DatabaseDAO extends Minz_ModelPdo { return $list; } + + public function size($all = false) { + $db = FreshRSS_Context::$system_conf->db; + $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL + $values = array($db['base']); + if (!$all) { + $sql .= ' AND table_name LIKE ?'; + $values[] = $this->prefix . '%'; + } + $stm = $this->bd->prepare($sql); + $stm->execute($values); + $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); + return $res[0]; + } + + public function optimize() { + $ok = true; + + $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'feed`'; //MySQL + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'category`'; //MySQL + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + return $ok; + } } diff --git a/app/Models/DatabaseDAOPGSQL.php b/app/Models/DatabaseDAOPGSQL.php index 2a18db970..1b3f7408d 100644 --- a/app/Models/DatabaseDAOPGSQL.php +++ b/app/Models/DatabaseDAOPGSQL.php @@ -40,4 +40,41 @@ class FreshRSS_DatabaseDAOPGSQL extends FreshRSS_DatabaseDAO { 'default' => $dao['default'], ); } + + 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]; + } + + public function optimize() { + $ok = true; + + $sql = 'VACUUM `' . $this->prefix . 'entry`'; + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + $sql = 'VACUUM `' . $this->prefix . 'feed`'; + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + $sql = 'VACUUM `' . $this->prefix . 'category`'; + $stm = $this->bd->prepare($sql); + $ok &= $stm != false; + if ($stm) { + $ok &= $stm->execute(); + } + + return $ok; + } } diff --git a/app/Models/DatabaseDAOSQLite.php b/app/Models/DatabaseDAOSQLite.php index 2e1df132e..d3aedb3c0 100644 --- a/app/Models/DatabaseDAOSQLite.php +++ b/app/Models/DatabaseDAOSQLite.php @@ -45,4 +45,17 @@ class FreshRSS_DatabaseDAOSQLite extends FreshRSS_DatabaseDAO { 'default' => $dao['dflt_value'], ); } + + public function size($all = false) { + return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite')); + } + + public function optimize() { + $sql = 'VACUUM'; + $stm = $this->bd->prepare($sql); + if ($stm) { + return $stm->execute(); + } + return false; + } } diff --git a/app/Models/EntryDAO.php b/app/Models/EntryDAO.php index bebafe500..e8b6dcdae 100644 --- a/app/Models/EntryDAO.php +++ b/app/Models/EntryDAO.php @@ -885,28 +885,6 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable { return array('all' => $all, 'unread' => $unread, 'read' => $all - $unread); } - public function optimizeTable() { - $sql = 'OPTIMIZE TABLE `' . $this->prefix . 'entry`'; //MySQL - $stm = $this->bd->prepare($sql); - if ($stm) { - return $stm->execute(); - } - } - - public function size($all = false) { - $db = FreshRSS_Context::$system_conf->db; - $sql = 'SELECT SUM(data_length + index_length) FROM information_schema.TABLES WHERE table_schema=?'; //MySQL - $values = array($db['base']); - if (!$all) { - $sql .= ' AND table_name LIKE ?'; - $values[] = $this->prefix . '%'; - } - $stm = $this->bd->prepare($sql); - $stm->execute($values); - $res = $stm->fetchAll(PDO::FETCH_COLUMN, 0); - return $res[0]; - } - public static function daoToEntry($dao) { $entry = new FreshRSS_Entry( $dao['id_feed'], diff --git a/app/Models/EntryDAOPGSQL.php b/app/Models/EntryDAOPGSQL.php index 405774abf..f09fe8e75 100644 --- a/app/Models/EntryDAOPGSQL.php +++ b/app/Models/EntryDAOPGSQL.php @@ -46,15 +46,4 @@ END $$;'; } return $result; } - - 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]; - } - } diff --git a/app/Models/EntryDAOSQLite.php b/app/Models/EntryDAOSQLite.php index 8dad54322..0f57dc1ba 100644 --- a/app/Models/EntryDAOSQLite.php +++ b/app/Models/EntryDAOSQLite.php @@ -261,12 +261,4 @@ class FreshRSS_EntryDAOSQLite extends FreshRSS_EntryDAO { } return $affected; } - - public function optimizeTable() { - //TODO: Search for an equivalent in SQLite - } - - public function size($all = false) { - return @filesize(join_path(DATA_PATH, 'users', $this->current_user, 'db.sqlite')); - } } diff --git a/cli/README.md b/cli/README.md index ce1be10a7..6f907566c 100644 --- a/cli/README.md +++ b/cli/README.md @@ -69,6 +69,9 @@ cd /usr/share/FreshRSS # Returns: 1) a * iff the user is admin, 2) the name of the user, # 3) the date/time of last user action, 4) the size occupied, # and the number of: 5) categories, 6) feeds, 7) read articles, 8) unread articles, and 9) favourites + +./cli/db-optimize.php --user username +# Optimize database (reduces the size) for a given user (perform `OPTIMIZE TABLE` in MySQL, `VACUUM` in SQLite) ``` diff --git a/cli/db-optimize.php b/cli/db-optimize.php new file mode 100755 index 000000000..83123a669 --- /dev/null +++ b/cli/db-optimize.php @@ -0,0 +1,20 @@ +#!/usr/bin/php +optimize(); + +done($ok); diff --git a/cli/user-info.php b/cli/user-info.php index aa3e239b8..41b95cbf3 100755 --- a/cli/user-info.php +++ b/cli/user-info.php @@ -19,6 +19,7 @@ foreach ($users as $username) { $catDAO = new FreshRSS_CategoryDAO(); $feedDAO = FreshRSS_Factory::createFeedDao($username); $entryDAO = FreshRSS_Factory::createEntryDao($username); + $databaseDAO = FreshRSS_Factory::createDatabaseDAO($username); $nbEntries = $entryDAO->countUnreadRead(); $nbFavorites = $entryDAO->countUnreadReadFavorites(); @@ -27,7 +28,7 @@ foreach ($users as $username) { echo $username, "\t", date('c', FreshRSS_UserDAO::mtime($username)), "\t", - format_bytes($entryDAO->size()), "\t", + format_bytes($databaseDAO->size()), "\t", $catDAO->count(), " categories\t", count($feedDAO->listFeedsIds()), " feeds\t", $nbEntries['read'], " reads\t", @@ -38,7 +39,7 @@ foreach ($users as $username) { echo $username, "\t", FreshRSS_UserDAO::mtime($username), "\t", - $entryDAO->size(), "\t", + $databaseDAO->size(), "\t", $catDAO->count(), "\t", count($feedDAO->listFeedsIds()), "\t", $nbEntries['read'], "\t", -- cgit v1.2.3