|
|
@@ -2,23 +2,27 @@
|
|
|
|
|
|
class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
|
|
|
|
|
|
- public function isCompressed(): bool {
|
|
|
+ public static function isCompressed(): bool {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- public function hasNativeHex(): bool {
|
|
|
+ public static function hasNativeHex(): bool {
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
- public function sqlHexDecode(string $x): string {
|
|
|
+ protected static function sqlConcat($s1, $s2) {
|
|
|
+ return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL
|
|
|
+ }
|
|
|
+
|
|
|
+ public static function sqlHexDecode(string $x): string {
|
|
|
return 'unhex(' . $x . ')';
|
|
|
}
|
|
|
|
|
|
- public function sqlHexEncode(string $x): string {
|
|
|
+ public static function sqlHexEncode(string $x): string {
|
|
|
return 'hex(' . $x . ')';
|
|
|
}
|
|
|
|
|
|
- public function sqlIgnoreConflict(string $sql): string {
|
|
|
+ public static function sqlIgnoreConflict(string $sql): string {
|
|
|
return str_replace('INSERT INTO ', 'INSERT IGNORE INTO ', $sql);
|
|
|
}
|
|
|
|
|
|
@@ -90,14 +94,14 @@ SQL;
|
|
|
|
|
|
public function addEntry(array $valuesTmp, bool $useTmpTable = true) {
|
|
|
if ($this->addEntryPrepared == null) {
|
|
|
- $sql = $this->sqlIgnoreConflict(
|
|
|
+ $sql = static::sqlIgnoreConflict(
|
|
|
'INSERT INTO `_' . ($useTmpTable ? 'entrytmp' : 'entry') . '` (id, guid, title, author, '
|
|
|
- . ($this->isCompressed() ? 'content_bin' : 'content')
|
|
|
+ . (static::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')
|
|
|
+ . (static::isCompressed() ? 'COMPRESS(:content)' : ':content')
|
|
|
. ', :link, :date, :last_seen, '
|
|
|
- . $this->sqlHexDecode(':hash')
|
|
|
+ . static::sqlHexDecode(':hash')
|
|
|
. ', :is_read, :is_favorite, :id_feed, :tags)');
|
|
|
$this->addEntryPrepared = $this->pdo->prepare($sql);
|
|
|
}
|
|
|
@@ -132,7 +136,7 @@ SQL;
|
|
|
$valuesTmp['tags'] = safe_utf8($valuesTmp['tags']);
|
|
|
$this->addEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
|
|
|
|
|
|
- if ($this->hasNativeHex()) {
|
|
|
+ if (static::hasNativeHex()) {
|
|
|
$this->addEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
|
|
|
} else {
|
|
|
$valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']);
|
|
|
@@ -189,9 +193,9 @@ SQL;
|
|
|
if ($this->updateEntryPrepared === null) {
|
|
|
$sql = 'UPDATE `_entry` '
|
|
|
. 'SET title=:title, author=:author, '
|
|
|
- . ($this->isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
|
|
|
+ . (static::isCompressed() ? 'content_bin=COMPRESS(:content)' : 'content=:content')
|
|
|
. ', link=:link, date=:date, `lastSeen`=:last_seen'
|
|
|
- . ', hash=' . $this->sqlHexDecode(':hash')
|
|
|
+ . ', hash=' . static::sqlHexDecode(':hash')
|
|
|
. ', is_read=COALESCE(:is_read, is_read)'
|
|
|
. ', tags=:tags '
|
|
|
. 'WHERE id_feed=:id_feed AND guid=:guid';
|
|
|
@@ -226,7 +230,7 @@ SQL;
|
|
|
$valuesTmp['tags'] = safe_utf8($valuesTmp['tags']);
|
|
|
$this->updateEntryPrepared->bindParam(':tags', $valuesTmp['tags']);
|
|
|
|
|
|
- if ($this->hasNativeHex()) {
|
|
|
+ if (static::hasNativeHex()) {
|
|
|
$this->updateEntryPrepared->bindParam(':hash', $valuesTmp['hash']);
|
|
|
} else {
|
|
|
$valuesTmp['hashBin'] = hex2bin($valuesTmp['hash']);
|
|
|
@@ -649,8 +653,8 @@ SQL;
|
|
|
|
|
|
public function selectAll() {
|
|
|
$sql = 'SELECT id, guid, title, author, '
|
|
|
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
- . ', link, date, `lastSeen`, ' . $this->sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags '
|
|
|
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
+ . ', link, date, `lastSeen`, ' . static::sqlHexEncode('hash') . ' AS hash, is_read, is_favorite, id_feed, tags '
|
|
|
. 'FROM `_entry`';
|
|
|
$stm = $this->pdo->query($sql);
|
|
|
while ($row = $stm->fetch(PDO::FETCH_ASSOC)) {
|
|
|
@@ -662,7 +666,7 @@ SQL;
|
|
|
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')
|
|
|
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
. ', link, date, is_read, is_favorite, id_feed, tags '
|
|
|
. 'FROM `_entry` WHERE id_feed=:id_feed AND guid=:guid';
|
|
|
$stm = $this->pdo->prepare($sql);
|
|
|
@@ -676,7 +680,7 @@ SQL;
|
|
|
/** @return FreshRSS_Entry|null */
|
|
|
public function searchById($id) {
|
|
|
$sql = 'SELECT id, guid, title, author, '
|
|
|
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
. ', link, date, is_read, is_favorite, id_feed, tags '
|
|
|
. 'FROM `_entry` WHERE id=:id';
|
|
|
$stm = $this->pdo->prepare($sql);
|
|
|
@@ -696,281 +700,301 @@ SQL;
|
|
|
return isset($res[0]) ? $res[0] : null;
|
|
|
}
|
|
|
|
|
|
- protected function sqlConcat($s1, $s2) {
|
|
|
- return 'CONCAT(' . $s1 . ',' . $s2 . ')'; //MySQL
|
|
|
- }
|
|
|
+ /** @param FreshRSS_BooleanSearch $filters */
|
|
|
+ public static function sqlBooleanSearch(string $alias, $filters, int $level = 0) {
|
|
|
+ $search = '';
|
|
|
+ $values = [];
|
|
|
|
|
|
- /**
|
|
|
- * @param FreshRSS_BooleanSearch|null $filters
|
|
|
- */
|
|
|
- protected function sqlListEntriesWhere(string $alias = '', $filters = null, int $state = FreshRSS_Entry::STATE_ALL,
|
|
|
- string $order = 'DESC', string $firstId = '', int $date_min = 0) {
|
|
|
- $search = ' ';
|
|
|
- $values = array();
|
|
|
- if ($state & FreshRSS_Entry::STATE_NOT_READ) {
|
|
|
- if (!($state & FreshRSS_Entry::STATE_READ)) {
|
|
|
- $search .= 'AND ' . $alias . 'is_read=0 ';
|
|
|
+ $isOpen = false;
|
|
|
+ foreach ($filters->searches() as $filter) {
|
|
|
+ if ($filter == null) {
|
|
|
+ continue;
|
|
|
}
|
|
|
- } 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) {
|
|
|
- $search .= 'AND ' . $alias . 'is_favorite=0 ';
|
|
|
- }
|
|
|
-
|
|
|
- switch ($order) {
|
|
|
- case 'DESC':
|
|
|
- case 'ASC':
|
|
|
- break;
|
|
|
- default:
|
|
|
- throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
|
|
|
- }
|
|
|
- if ($firstId !== '') {
|
|
|
- $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . ' ? ';
|
|
|
- $values[] = $firstId;
|
|
|
- }
|
|
|
- if ($date_min > 0) {
|
|
|
- $search .= 'AND ' . $alias . 'id >= ? ';
|
|
|
- $values[] = $date_min . '000000';
|
|
|
- }
|
|
|
- if ($filters && count($filters->searches()) > 0) {
|
|
|
- $isOpen = false;
|
|
|
- foreach ($filters->searches() as $filter) {
|
|
|
- if ($filter == null) {
|
|
|
- continue;
|
|
|
+ if ($filter instanceof FreshRSS_BooleanSearch) {
|
|
|
+ // BooleanSearches are combined by AND (default) or OR (special case) operator and are recursive
|
|
|
+ list($filterValues, $filterSearch) = self::sqlBooleanSearch($alias, $filter, $level + 1);
|
|
|
+ $filterSearch = trim($filterSearch);
|
|
|
+
|
|
|
+ if ($filterSearch !== '') {
|
|
|
+ if ($search !== '') {
|
|
|
+ $search .= $filter->operator();
|
|
|
+ }
|
|
|
+ $search .= ' (' . $filterSearch . ') ';
|
|
|
+ $values = array_merge($values, $filterValues);
|
|
|
}
|
|
|
- $sub_search = '';
|
|
|
-
|
|
|
- if ($filter->getEntryIds()) {
|
|
|
- foreach ($filter->getEntryIds() as $entry_ids) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id IN (';
|
|
|
- foreach ($entry_ids as $entry_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $entry_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ') ';
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // Searches are combined by OR and are not recursive
|
|
|
+ $sub_search = '';
|
|
|
+ if ($filter->getEntryIds()) {
|
|
|
+ foreach ($filter->getEntryIds() as $entry_ids) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id IN (';
|
|
|
+ foreach ($entry_ids as $entry_id) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $entry_id;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ') ';
|
|
|
}
|
|
|
- if ($filter->getNotEntryIds()) {
|
|
|
- foreach ($filter->getNotEntryIds() as $entry_ids) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id NOT IN (';
|
|
|
- foreach ($entry_ids as $entry_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $entry_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ') ';
|
|
|
+ }
|
|
|
+ if ($filter->getNotEntryIds()) {
|
|
|
+ foreach ($filter->getNotEntryIds() as $entry_ids) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (';
|
|
|
+ foreach ($entry_ids as $entry_id) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $entry_id;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ') ';
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getMinDate()) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id >= ? ';
|
|
|
- $values[] = "{$filter->getMinDate()}000000";
|
|
|
- }
|
|
|
- if ($filter->getMaxDate()) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id <= ? ';
|
|
|
- $values[] = "{$filter->getMaxDate()}000000";
|
|
|
- }
|
|
|
- if ($filter->getMinPubdate()) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'date >= ? ';
|
|
|
- $values[] = $filter->getMinPubdate();
|
|
|
- }
|
|
|
- if ($filter->getMaxPubdate()) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'date <= ? ';
|
|
|
- $values[] = $filter->getMaxPubdate();
|
|
|
- }
|
|
|
+ if ($filter->getMinDate()) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id >= ? ';
|
|
|
+ $values[] = "{$filter->getMinDate()}000000";
|
|
|
+ }
|
|
|
+ if ($filter->getMaxDate()) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id <= ? ';
|
|
|
+ $values[] = "{$filter->getMaxDate()}000000";
|
|
|
+ }
|
|
|
+ if ($filter->getMinPubdate()) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'date >= ? ';
|
|
|
+ $values[] = $filter->getMinPubdate();
|
|
|
+ }
|
|
|
+ if ($filter->getMaxPubdate()) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'date <= ? ';
|
|
|
+ $values[] = $filter->getMaxPubdate();
|
|
|
+ }
|
|
|
|
|
|
- //Negation of date intervals must be combined by OR
|
|
|
- if ($filter->getNotMinDate() || $filter->getNotMaxDate()) {
|
|
|
- $sub_search .= 'AND (';
|
|
|
- if ($filter->getNotMinDate()) {
|
|
|
- $sub_search .= $alias . 'id < ?';
|
|
|
- $values[] = "{$filter->getNotMinDate()}000000";
|
|
|
- if ($filter->getNotMaxDate()) {
|
|
|
- $sub_search .= ' OR ';
|
|
|
- }
|
|
|
- }
|
|
|
+ //Negation of date intervals must be combined by OR
|
|
|
+ if ($filter->getNotMinDate() || $filter->getNotMaxDate()) {
|
|
|
+ $sub_search .= 'AND (';
|
|
|
+ if ($filter->getNotMinDate()) {
|
|
|
+ $sub_search .= $alias . 'id < ?';
|
|
|
+ $values[] = "{$filter->getNotMinDate()}000000";
|
|
|
if ($filter->getNotMaxDate()) {
|
|
|
- $sub_search .= $alias . 'id > ?';
|
|
|
- $values[] = "{$filter->getNotMaxDate()}000000";
|
|
|
+ $sub_search .= ' OR ';
|
|
|
}
|
|
|
- $sub_search .= ') ';
|
|
|
}
|
|
|
- if ($filter->getNotMinPubdate() || $filter->getNotMaxPubdate()) {
|
|
|
- $sub_search .= 'AND (';
|
|
|
- if ($filter->getNotMinPubdate()) {
|
|
|
- $sub_search .= $alias . 'date < ?';
|
|
|
- $values[] = $filter->getNotMinPubdate();
|
|
|
- if ($filter->getNotMaxPubdate()) {
|
|
|
- $sub_search .= ' OR ';
|
|
|
- }
|
|
|
- }
|
|
|
- if ($filter->getNotMaxPubdate()) {
|
|
|
- $sub_search .= $alias . 'date > ?';
|
|
|
- $values[] = $filter->getNotMaxPubdate();
|
|
|
- }
|
|
|
- $sub_search .= ') ';
|
|
|
+ if ($filter->getNotMaxDate()) {
|
|
|
+ $sub_search .= $alias . 'id > ?';
|
|
|
+ $values[] = "{$filter->getNotMaxDate()}000000";
|
|
|
}
|
|
|
-
|
|
|
- if ($filter->getFeedIds()) {
|
|
|
- foreach ($filter->getFeedIds() as $feed_ids) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id_feed IN (';
|
|
|
- foreach ($feed_ids as $feed_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $feed_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ') ';
|
|
|
+ $sub_search .= ') ';
|
|
|
+ }
|
|
|
+ if ($filter->getNotMinPubdate() || $filter->getNotMaxPubdate()) {
|
|
|
+ $sub_search .= 'AND (';
|
|
|
+ if ($filter->getNotMinPubdate()) {
|
|
|
+ $sub_search .= $alias . 'date < ?';
|
|
|
+ $values[] = $filter->getNotMinPubdate();
|
|
|
+ if ($filter->getNotMaxPubdate()) {
|
|
|
+ $sub_search .= ' OR ';
|
|
|
}
|
|
|
}
|
|
|
- if ($filter->getNotFeedIds()) {
|
|
|
- foreach ($filter->getNotFeedIds() as $feed_ids) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id_feed NOT IN (';
|
|
|
- foreach ($feed_ids as $feed_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $feed_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ') ';
|
|
|
- }
|
|
|
+ if ($filter->getNotMaxPubdate()) {
|
|
|
+ $sub_search .= $alias . 'date > ?';
|
|
|
+ $values[] = $filter->getNotMaxPubdate();
|
|
|
}
|
|
|
+ $sub_search .= ') ';
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getLabelIds()) {
|
|
|
- foreach ($filter->getLabelIds() as $label_ids) {
|
|
|
- if ($label_ids === '*') {
|
|
|
- $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
|
|
|
- } else {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
|
|
|
- foreach ($label_ids as $label_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $label_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ')) ';
|
|
|
- }
|
|
|
+ if ($filter->getFeedIds()) {
|
|
|
+ foreach ($filter->getFeedIds() as $feed_ids) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id_feed IN (';
|
|
|
+ foreach ($feed_ids as $feed_id) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $feed_id;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ') ';
|
|
|
}
|
|
|
- if ($filter->getNotLabelIds()) {
|
|
|
- foreach ($filter->getNotLabelIds() as $label_ids) {
|
|
|
- if ($label_ids === '*') {
|
|
|
- $sub_search .= 'AND NOT EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
|
|
|
- } else {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
|
|
|
- foreach ($label_ids as $label_id) {
|
|
|
- $sub_search .= '?,';
|
|
|
- $values[] = $label_id;
|
|
|
- }
|
|
|
- $sub_search = rtrim($sub_search, ',');
|
|
|
- $sub_search .= ')) ';
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getNotFeedIds()) {
|
|
|
+ foreach ($filter->getNotFeedIds() as $feed_ids) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id_feed NOT IN (';
|
|
|
+ foreach ($feed_ids as $feed_id) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $feed_id;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ') ';
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getLabelNames()) {
|
|
|
- foreach ($filter->getLabelNames() as $label_names) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
|
|
|
- foreach ($label_names as $label_name) {
|
|
|
+ if ($filter->getLabelIds()) {
|
|
|
+ foreach ($filter->getLabelIds() as $label_ids) {
|
|
|
+ if ($label_ids === '*') {
|
|
|
+ $sub_search .= 'AND EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
|
|
|
+ } else {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
|
|
|
+ foreach ($label_ids as $label_id) {
|
|
|
$sub_search .= '?,';
|
|
|
- $values[] = $label_name;
|
|
|
+ $values[] = $label_id;
|
|
|
}
|
|
|
$sub_search = rtrim($sub_search, ',');
|
|
|
$sub_search .= ')) ';
|
|
|
}
|
|
|
}
|
|
|
- if ($filter->getNotLabelNames()) {
|
|
|
- foreach ($filter->getNotLabelNames() as $label_names) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
|
|
|
- foreach ($label_names as $label_name) {
|
|
|
+ }
|
|
|
+ if ($filter->getNotLabelIds()) {
|
|
|
+ foreach ($filter->getNotLabelIds() as $label_ids) {
|
|
|
+ if ($label_ids === '*') {
|
|
|
+ $sub_search .= 'AND NOT EXISTS (SELECT et.id_tag FROM `_entrytag` et WHERE et.id_entry = ' . $alias . 'id) ';
|
|
|
+ } else {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et WHERE et.id_tag IN (';
|
|
|
+ foreach ($label_ids as $label_id) {
|
|
|
$sub_search .= '?,';
|
|
|
- $values[] = $label_name;
|
|
|
+ $values[] = $label_id;
|
|
|
}
|
|
|
$sub_search = rtrim($sub_search, ',');
|
|
|
$sub_search .= ')) ';
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getAuthor()) {
|
|
|
- foreach ($filter->getAuthor() as $author) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'author LIKE ? ';
|
|
|
- $values[] = "%{$author}%";
|
|
|
+ if ($filter->getLabelNames()) {
|
|
|
+ foreach ($filter->getLabelNames() as $label_names) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
|
|
|
+ foreach ($label_names as $label_name) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $label_name;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ')) ';
|
|
|
}
|
|
|
- if ($filter->getIntitle()) {
|
|
|
- foreach ($filter->getIntitle() as $title) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'title LIKE ? ';
|
|
|
- $values[] = "%{$title}%";
|
|
|
+ }
|
|
|
+ if ($filter->getNotLabelNames()) {
|
|
|
+ foreach ($filter->getNotLabelNames() as $label_names) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'id NOT IN (SELECT et.id_entry FROM `_entrytag` et, `_tag` t WHERE et.id_tag = t.id AND t.name IN (';
|
|
|
+ foreach ($label_names as $label_name) {
|
|
|
+ $sub_search .= '?,';
|
|
|
+ $values[] = $label_name;
|
|
|
}
|
|
|
+ $sub_search = rtrim($sub_search, ',');
|
|
|
+ $sub_search .= ')) ';
|
|
|
}
|
|
|
- if ($filter->getTags()) {
|
|
|
- foreach ($filter->getTags() as $tag) {
|
|
|
- $sub_search .= 'AND ' . $alias . 'tags LIKE ? ';
|
|
|
- $values[] = "%{$tag}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($filter->getAuthor()) {
|
|
|
+ foreach ($filter->getAuthor() as $author) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'author LIKE ? ';
|
|
|
+ $values[] = "%{$author}%";
|
|
|
}
|
|
|
- if ($filter->getInurl()) {
|
|
|
- foreach ($filter->getInurl() as $url) {
|
|
|
- $sub_search .= 'AND ' . $this->sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ? ';
|
|
|
- $values[] = "%{$url}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getIntitle()) {
|
|
|
+ foreach ($filter->getIntitle() as $title) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'title LIKE ? ';
|
|
|
+ $values[] = "%{$title}%";
|
|
|
}
|
|
|
+ }
|
|
|
+ if ($filter->getTags()) {
|
|
|
+ foreach ($filter->getTags() as $tag) {
|
|
|
+ $sub_search .= 'AND ' . $alias . 'tags LIKE ? ';
|
|
|
+ $values[] = "%{$tag}%";
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($filter->getInurl()) {
|
|
|
+ foreach ($filter->getInurl() as $url) {
|
|
|
+ $sub_search .= 'AND ' . static::sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ? ';
|
|
|
+ $values[] = "%{$url}%";
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getNotAuthor()) {
|
|
|
- foreach ($filter->getNotAuthor() as $author) {
|
|
|
- $sub_search .= 'AND (NOT ' . $alias . 'author LIKE ?) ';
|
|
|
- $values[] = "%{$author}%";
|
|
|
- }
|
|
|
+ if ($filter->getNotAuthor()) {
|
|
|
+ foreach ($filter->getNotAuthor() as $author) {
|
|
|
+ $sub_search .= 'AND (NOT ' . $alias . 'author LIKE ?) ';
|
|
|
+ $values[] = "%{$author}%";
|
|
|
}
|
|
|
- if ($filter->getNotIntitle()) {
|
|
|
- foreach ($filter->getNotIntitle() as $title) {
|
|
|
- $sub_search .= 'AND (NOT ' . $alias . 'title LIKE ?) ';
|
|
|
- $values[] = "%{$title}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getNotIntitle()) {
|
|
|
+ foreach ($filter->getNotIntitle() as $title) {
|
|
|
+ $sub_search .= 'AND (NOT ' . $alias . 'title LIKE ?) ';
|
|
|
+ $values[] = "%{$title}%";
|
|
|
}
|
|
|
- if ($filter->getNotTags()) {
|
|
|
- foreach ($filter->getNotTags() as $tag) {
|
|
|
- $sub_search .= 'AND (NOT ' . $alias . 'tags LIKE ?) ';
|
|
|
- $values[] = "%{$tag}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getNotTags()) {
|
|
|
+ foreach ($filter->getNotTags() as $tag) {
|
|
|
+ $sub_search .= 'AND (NOT ' . $alias . 'tags LIKE ?) ';
|
|
|
+ $values[] = "%{$tag}%";
|
|
|
}
|
|
|
- if ($filter->getNotInurl()) {
|
|
|
- foreach ($filter->getNotInurl() as $url) {
|
|
|
- $sub_search .= 'AND (NOT ' . $this->sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ?) ';
|
|
|
- $values[] = "%{$url}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getNotInurl()) {
|
|
|
+ foreach ($filter->getNotInurl() as $url) {
|
|
|
+ $sub_search .= 'AND (NOT ' . static::sqlConcat($alias . 'link', $alias . 'guid') . ' LIKE ?) ';
|
|
|
+ $values[] = "%{$url}%";
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ($filter->getSearch()) {
|
|
|
- foreach ($filter->getSearch() as $search_value) {
|
|
|
- $sub_search .= 'AND ' . $this->sqlConcat($alias . 'title',
|
|
|
- $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? ';
|
|
|
- $values[] = "%{$search_value}%";
|
|
|
- }
|
|
|
+ if ($filter->getSearch()) {
|
|
|
+ foreach ($filter->getSearch() as $search_value) {
|
|
|
+ $sub_search .= 'AND ' . static::sqlConcat($alias . 'title',
|
|
|
+ static::isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ? ';
|
|
|
+ $values[] = "%{$search_value}%";
|
|
|
}
|
|
|
- if ($filter->getNotSearch()) {
|
|
|
- foreach ($filter->getNotSearch() as $search_value) {
|
|
|
- $sub_search .= 'AND (NOT ' . $this->sqlConcat($alias . 'title',
|
|
|
- $this->isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) ';
|
|
|
- $values[] = "%{$search_value}%";
|
|
|
- }
|
|
|
+ }
|
|
|
+ if ($filter->getNotSearch()) {
|
|
|
+ foreach ($filter->getNotSearch() as $search_value) {
|
|
|
+ $sub_search .= 'AND (NOT ' . static::sqlConcat($alias . 'title',
|
|
|
+ static::isCompressed() ? 'UNCOMPRESS(' . $alias . 'content_bin)' : '' . $alias . 'content') . ' LIKE ?) ';
|
|
|
+ $values[] = "%{$search_value}%";
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- if ($sub_search != '') {
|
|
|
- if ($isOpen) {
|
|
|
- $search .= 'OR ';
|
|
|
- } else {
|
|
|
- $search .= 'AND (';
|
|
|
- $isOpen = true;
|
|
|
- }
|
|
|
- $search .= '(' . substr($sub_search, 4) . ') ';
|
|
|
+ if ($sub_search != '') {
|
|
|
+ if ($isOpen) {
|
|
|
+ $search .= ' OR ';
|
|
|
+ } else {
|
|
|
+ $isOpen = true;
|
|
|
}
|
|
|
+ // Remove superfluous leading 'AND '
|
|
|
+ $search .= '(' . substr($sub_search, 4) . ')';
|
|
|
}
|
|
|
- if ($isOpen) {
|
|
|
- $search .= ') ';
|
|
|
+ }
|
|
|
+
|
|
|
+ return [ $values, $search ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /** @param FreshRSS_BooleanSearch|null $filters */
|
|
|
+ protected function sqlListEntriesWhere(string $alias = '', $filters = null, int $state = FreshRSS_Entry::STATE_ALL,
|
|
|
+ string $order = 'DESC', string $firstId = '', int $date_min = 0) {
|
|
|
+ $search = ' ';
|
|
|
+ $values = array();
|
|
|
+ if ($state & FreshRSS_Entry::STATE_NOT_READ) {
|
|
|
+ if (!($state & FreshRSS_Entry::STATE_READ)) {
|
|
|
+ $search .= 'AND ' . $alias . 'is_read=0 ';
|
|
|
+ }
|
|
|
+ } 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) {
|
|
|
+ $search .= 'AND ' . $alias . 'is_favorite=0 ';
|
|
|
+ }
|
|
|
+
|
|
|
+ switch ($order) {
|
|
|
+ case 'DESC':
|
|
|
+ case 'ASC':
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ throw new FreshRSS_EntriesGetter_Exception('Bad order in Entry->listByType: [' . $order . ']!');
|
|
|
+ }
|
|
|
+ if ($firstId !== '') {
|
|
|
+ $search .= 'AND ' . $alias . 'id ' . ($order === 'DESC' ? '<=' : '>=') . ' ? ';
|
|
|
+ $values[] = $firstId;
|
|
|
+ }
|
|
|
+ if ($date_min > 0) {
|
|
|
+ $search .= 'AND ' . $alias . 'id >= ? ';
|
|
|
+ $values[] = $date_min . '000000';
|
|
|
+ }
|
|
|
+ if ($filters && count($filters->searches()) > 0) {
|
|
|
+ list($filterValues, $filterSearch) = self::sqlBooleanSearch($alias, $filters);
|
|
|
+ $filterSearch = trim($filterSearch);
|
|
|
+ if ($filterSearch !== '') {
|
|
|
+ $search .= 'AND (' . $filterSearch . ') ';
|
|
|
+ $values = array_merge($values, $filterValues);
|
|
|
}
|
|
|
}
|
|
|
return array($values, $search);
|
|
|
@@ -1040,7 +1064,7 @@ SQL;
|
|
|
list($values, $sql) = $this->sqlListWhere($type, $id, $state, $order, $limit, $firstId, $filters, $date_min);
|
|
|
|
|
|
$sql = 'SELECT e0.id, e0.guid, e0.title, e0.author, '
|
|
|
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
. ', e0.link, e0.date, e0.is_read, e0.is_favorite, e0.id_feed, e0.tags '
|
|
|
. 'FROM `_entry` e0 '
|
|
|
. 'INNER JOIN ('
|
|
|
@@ -1085,7 +1109,7 @@ SQL;
|
|
|
}
|
|
|
|
|
|
$sql = 'SELECT id, guid, title, author, '
|
|
|
- . ($this->isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
+ . (static::isCompressed() ? 'UNCOMPRESS(content_bin) AS content' : 'content')
|
|
|
. ', link, date, is_read, is_favorite, id_feed, tags '
|
|
|
. 'FROM `_entry` '
|
|
|
. 'WHERE id IN (' . str_repeat('?,', count($ids) - 1). '?) '
|
|
|
@@ -1124,7 +1148,7 @@ SQL;
|
|
|
return $result;
|
|
|
}
|
|
|
$guids = array_unique($guids);
|
|
|
- $sql = 'SELECT guid, ' . $this->sqlHexEncode('hash') .
|
|
|
+ $sql = 'SELECT guid, ' . static::sqlHexEncode('hash') .
|
|
|
' AS hex_hash FROM `_entry` WHERE id_feed=? AND guid IN (' . str_repeat('?,', count($guids) - 1). '?)';
|
|
|
$stm = $this->pdo->prepare($sql);
|
|
|
$values = array($id_feed);
|