Explorar o código

Recherche côté SQL avec LIKE

Premier essai de recherche côté base de données (à améliorer)
https://github.com/marienfressinaud/FreshRSS/issues/204

Pour l'instant fait avec du LIKE et pas d'indexation texte complet.

* Suppression de EntriesGetter car le code est devenu plus simple grâce
au filtrage côté SQL
* Uniformisation de get_c à une lettre ('all' devient 'a','favoris'
devient 's' - pour "starred") pour simplifier le code
* low_to_high par DESC, high_to_low par ASC
* Réduction du nombre de créations de *DAO dans indexController
* Refactorisation de checkAndProcessType()

Pas encore trop testé...
Alexandre Alapetite %!s(int64=12) %!d(string=hai) anos
pai
achega
37ce14c093

+ 0 - 1
app/App_FrontController.php

@@ -33,7 +33,6 @@ class App_FrontController extends FrontController {
 		include (APP_PATH . '/models/Category.php');
 		include (APP_PATH . '/models/Feed.php');
 		include (APP_PATH . '/models/Entry.php');
-		include (APP_PATH . '/models/EntriesGetter.php');
 		include (APP_PATH . '/models/RSSPaginator.php');
 		include (APP_PATH . '/models/Log_Model.php');
 	}

+ 2 - 2
app/controllers/configureController.php

@@ -158,12 +158,12 @@ class configureController extends ActionController {
 			$language = Request::param ('language', 'en');
 			$nb = Request::param ('posts_per_page', 10);
 			$mode = Request::param ('view_mode', 'normal');
-			$view = Request::param ('default_view', 'all');
+			$view = Request::param ('default_view', 'a');
 			$auto_load_more = Request::param ('auto_load_more', 'no');
 			$display = Request::param ('display_posts', 'no');
 			$onread_jump_next = Request::param ('onread_jump_next', 'no');
 			$lazyload = Request::param ('lazyload', 'no');
-			$sort = Request::param ('sort_order', 'low_to_high');
+			$sort = Request::param ('sort_order', 'DESC');
 			$old = Request::param ('old_entries', 3);
 			$mail = Request::param ('mail_login', false);
 			$anon = Request::param ('anon_access', 'no');

+ 119 - 135
app/controllers/indexController.php

@@ -3,19 +3,29 @@
 class indexController extends ActionController {
 	private $get = false;
 	private $nb_not_read_cat = 0;
+	private $entryDAO;
+	private $feedDAO;
+	private $catDAO;
+
+	function __construct($router) {
+		parent::__construct($router);
+		$this->entryDAO = new EntryDAO ();
+		$this->feedDAO = new FeedDAO ();
+		$this->catDAO = new CategoryDAO ();
+	}
 
 	public function indexAction () {
 		$output = Request::param ('output');
 
 		$token = $this->view->conf->token();
 		$token_param = Request::param ('token', '');
-		$token_is_ok = ($token != '' && $token == $token_param);
+		$token_is_ok = ($token != '' && $token === $token_param);
 
 		// check if user is log in
 		if(login_is_conf ($this->view->conf) &&
 				!is_logged() &&
-				$this->view->conf->anonAccess() == 'no' &&
-				!($output == 'rss' && $token_is_ok)) {
+				$this->view->conf->anonAccess() === 'no' &&
+				!($output === 'rss' && $token_is_ok)) {
 			return;
 		}
 
@@ -26,7 +36,7 @@ class indexController extends ActionController {
 			$params['search'] = urlencode ($params['search']);
 		}
 		if (login_is_conf($this->view->conf) &&
-				$this->view->conf->anonAccess() == 'no' &&
+				$this->view->conf->anonAccess() === 'no' &&
 				$token != '') {
 			$params['token'] = $token;
 		}
@@ -38,32 +48,25 @@ class indexController extends ActionController {
 
 		$this->view->rss_title = View::title();
 
-		if ($output == 'rss') {
+		if ($output === 'rss') {
 			// no layout for RSS output
 			$this->view->_useLayout (false);
 			header('Content-Type: application/rss+xml; charset=utf-8');
 		} else {
 			View::appendScript (Url::display ('/scripts/shortcut.js?' . @filemtime(PUBLIC_PATH . '/scripts/shortcut.js')));
 
-			if ($output == 'global') {
+			if ($output === 'global') {
 				View::appendScript (Url::display ('/scripts/global_view.js?' . @filemtime(PUBLIC_PATH . '/scripts/global_view.js')));
 			}
 		}
 
-		$entryDAO = new EntryDAO ();
-		$feedDAO = new FeedDAO ();
-		$catDAO = new CategoryDAO ();
-
-		$this->view->cat_aside = $catDAO->listCategories ();
-		$this->view->nb_favorites = $entryDAO->countUnreadReadFavorites ();
+		$this->view->cat_aside = $this->catDAO->listCategories ();
+		$this->view->nb_favorites = $this->entryDAO->countUnreadReadFavorites ();
 		$this->view->currentName = '';
 
 		$this->view->get_c = '';
 		$this->view->get_f = '';
 
-		$type = $this->getType ();
-		$error = $this->checkAndProcessType ($type);
-
 		// mise à jour des titres
 		$this->view->nb_not_read = HelperCategory::CountUnreads($this->view->cat_aside, 1);
 		if ($this->view->nb_not_read > 0) {
@@ -77,63 +80,73 @@ class indexController extends ActionController {
 			($this->nb_not_read_cat > 0 ? ' (' . $this->nb_not_read_cat . ')' : '')
 		);
 
-		if (!$error) {
-			// On récupère les différents éléments de filtrage
-			$this->view->state = $state = Request::param ('state', $this->view->conf->defaultView ());
-			$filter = Request::param ('search', '');
-			$this->view->order = $order = Request::param ('order', $this->view->conf->sortOrder ());
-			$nb = Request::param ('nb', $this->view->conf->postsPerPage ());
-			$first = Request::param ('next', '');
-
-			if ($state === 'not_read') {	//Any unread article in this category at all?
-				switch ($type['type']) {
-					case 'all':
-						$hasUnread = $this->view->nb_not_read > 0;
-						break;
-					case 'favoris':
-						$hasUnread = $this->view->nb_favorites['unread'] > 0;
-						break;
-					case 'c':
-						$hasUnread = (!isset($this->view->cat_aside[$type['id']])) || ($this->view->cat_aside[$type['id']]->nbNotRead() > 0);
-						break;
-					case 'f':
-						$myFeed = HelperCategory::findFeed($this->view->cat_aside, $type['id']);
-						$hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0);
-						break;
-					default:
-						$hasUnread = true;
-						break;
-				}
-				if (!$hasUnread) {
-					$this->view->state = $state = 'all';
-				}
+		$get = Request::param ('get', 'a');
+		$getType = $get[0];
+		$getId = substr ($get, 2);
+		if (!$this->checkAndProcessType ($getType, $getId)) {
+			Minz_Log::record ('Not found [' . $getType . '][' . $getId . ']', Minz_Log::DEBUG);
+			Error::error (
+				404,
+				array ('error' => array (Translate::t ('page_not_found')))
+			);
+			return;
+		}
+
+		// On récupère les différents éléments de filtrage
+		$this->view->state = $state = Request::param ('state', $this->view->conf->defaultView ());
+		$filter = Request::param ('search', '');
+		if (!empty($filter)) {
+			$state = 'all';	//Search always in read and unread articles
+		}
+		$this->view->order = $order = Request::param ('order', $this->view->conf->sortOrder ());
+		$nb = Request::param ('nb', $this->view->conf->postsPerPage ());
+		$first = Request::param ('next', '');
+
+		if ($state === 'not_read') {	//Any unread article in this category at all?
+			switch ($getType) {
+				case 'a':
+					$hasUnread = $this->view->nb_not_read > 0;
+					break;
+				case 's':
+					$hasUnread = $this->view->nb_favorites['unread'] > 0;
+					break;
+				case 'c':
+					$hasUnread = (!isset($this->view->cat_aside[$getId])) || ($this->view->cat_aside[$getId]->nbNotRead() > 0);
+					break;
+				case 'f':
+					$myFeed = HelperCategory::findFeed($this->view->cat_aside, $getId);
+					$hasUnread = ($myFeed === null) || ($myFeed->nbNotRead() > 0);
+					break;
+				default:
+					$hasUnread = true;
+					break;
 			}
+			if (!$hasUnread) {
+				$this->view->state = $state = 'all';
+			}
+		}
 
-			try {
-				// EntriesGetter permet de déporter la complexité du filtrage
-				$getter = new EntriesGetter ($type, $state, $filter, $order, $nb, $first);
-				$getter->execute ();
-				$entries = $getter->getPaginator ();
-
-				// Si on a récupéré aucun article "non lus"
-				// on essaye de récupérer tous les articles
-				if ($state === 'not_read' && $entries->isEmpty ()) {	//TODO: Remove in v0.8
-					Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
-					$this->view->state = 'all';
-					$getter->_state ('all');
-					$getter->execute ();
-					$entries = $getter->getPaginator ();
-				}
+		try {
+			$entries = $this->entryDAO->listWhere($getType, $getId, $state, $order, $nb + 1, $first, $filter);
+
+			// Si on a récupéré aucun article "non lus"
+			// on essaye de récupérer tous les articles
+			if ($state === 'not_read' && empty($entries)) {	//TODO: Remove in v0.8
+				Minz_Log::record ('Conflicting information about nbNotRead!', Minz_Log::DEBUG);
+				$this->view->state = 'all';
+				$entries = $this->entryDAO->listWhere($getType, $getId, 'all', $order, $nb, $first, $filter);
+			}
 
-				$this->view->entryPaginator = $entries;
-			} catch (EntriesGetterException $e) {
-				Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE);
-				Error::error (
-					404,
-					array ('error' => array (Translate::t ('page_not_found')))
-				);
+			if (count($entries) <= $nb) {
+				$next = '';
+			} else {	//We have more elements for pagination
+				$lastEntry = array_pop($entries);
+				$next = $lastEntry->id();
 			}
-		} else {
+
+			$this->view->entryPaginator = new RSSPaginator ($entries, $next);
+		} catch (EntriesGetterException $e) {
+			Minz_Log::record ($e->getMessage (), Minz_Log::NOTICE);
 			Error::error (
 				404,
 				array ('error' => array (Translate::t ('page_not_found')))
@@ -141,79 +154,50 @@ class indexController extends ActionController {
 		}
 	}
 
-	/*
-	 * Détermine le type d'article à récupérer :
-	 * "tous", "favoris", "public", "catégorie" ou "flux"
-	 */
-	private function getType () {
-		$get = Request::param ('get', 'all');
-		$typeGet = $get[0];
-		$id = substr ($get, 2);
-
-		$type = null;
-		if ($get == 'all' || $get == 'favoris' || $get == 'public') {
-			$type = array (
-				'type' => $get,
-				'id' => $get
-			);
-		} elseif ($typeGet == 'f' || $typeGet == 'c') {
-			$type = array (
-				'type' => $typeGet,
-				'id' => $id
-			);
-		}
-
-		return $type;
-	}
 	/*
 	 * Vérifie que la catégorie / flux sélectionné existe
 	 * + Initialise correctement les variables de vue get_c et get_f
 	 * + Met à jour la variable $this->nb_not_read_cat
 	 */
-	private function checkAndProcessType ($type) {
-		if ($type['type'] == 'all') {
-			$this->view->currentName = Translate::t ('your_rss_feeds');
-			$this->view->get_c = $type['type'];
-			return false;
-		} elseif ($type['type'] == 'favoris') {
-			$this->view->currentName = Translate::t ('your_favorites');
-			$this->view->get_c = $type['type'];
-			return false;
-		} elseif ($type['type'] == 'public') {
-			$this->view->currentName = Translate::t ('public');
-			$this->view->get_c = $type['type'];
-			return false;
-		} elseif ($type['type'] == 'c') {
-			$cat = isset($this->view->cat_aside[$type['id']]) ? $this->view->cat_aside[$type['id']] : null;
-			if ($cat === null) {
-				$catDAO = new CategoryDAO ();
-				$cat = $catDAO->searchById ($type['id']);
-			}
-			if ($cat) {
-				$this->view->currentName = $cat->name ();
-				$this->nb_not_read_cat = $cat->nbNotRead ();
-				$this->view->get_c = $type['id'];
-				return false;
-			} else {
+	private function checkAndProcessType ($getType, $getId) {
+		switch ($getType) {
+			case 'a':
+				$this->view->currentName = Translate::t ('your_rss_feeds');
+				$this->view->get_c = $getType;
 				return true;
-			}
-		} elseif ($type['type'] == 'f') {
-			$feed = HelperCategory::findFeed($this->view->cat_aside, $type['id']);
-			if (empty($feed)) {
-				$feedDAO = new FeedDAO ();
-				$feed = $feedDAO->searchById ($type['id']);
-			}
-			if ($feed) {
-				$this->view->currentName = $feed->name ();
-				$this->nb_not_read_cat = $feed->nbNotRead ();
-				$this->view->get_f = $type['id'];
-				$this->view->get_c = $feed->category ();
-				return false;
-			} else {
+			case 's':
+				$this->view->currentName = Translate::t ('your_favorites');
+				$this->view->get_c = $getType;
 				return true;
-			}
-		} else {
-			return true;
+			case 'c':
+				$cat = isset($this->view->cat_aside[$getId]) ? $this->view->cat_aside[$getId] : null;
+				if ($cat === null) {
+					$cat = $this->catDAO->searchById ($getId);
+				}
+				if ($cat) {
+					$this->view->currentName = $cat->name ();
+					$this->nb_not_read_cat = $cat->nbNotRead ();
+					$this->view->get_c = $getId;
+					return true;
+				} else {
+					return false;
+				}
+			case 'f':
+				$feed = HelperCategory::findFeed($this->view->cat_aside, $getId);
+				if (empty($feed)) {
+					$feed = $this->feedDAO->searchById ($getId);
+				}
+				if ($feed) {
+					$this->view->currentName = $feed->name ();
+					$this->nb_not_read_cat = $feed->nbNotRead ();
+					$this->view->get_f = $getId;
+					$this->view->get_c = $feed->category ();
+					return true;
+				} else {
+					return false;
+				}
+			default:
+				return false;
 		}
 	}
 
@@ -270,7 +254,7 @@ class indexController extends ActionController {
 		curl_close ($ch);
 
 		$res = json_decode ($result, true);
-		if ($res['status'] == 'okay' && $res['email'] == $this->view->conf->mailLogin ()) {
+		if ($res['status'] === 'okay' && $res['email'] === $this->view->conf->mailLogin ()) {
 			Session::_param ('mail', $res['email']);
 			invalidateHttpCache();
 		} else {

+ 2 - 2
app/layout/aside_flux.phtml

@@ -15,7 +15,7 @@
 
 		<li>
 			<div class="category all">
-				<a data-unread="<?php echo $this->nb_not_read; ?>" class="btn<?php echo $this->get_c == 'all' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index'); ?>">
+				<a data-unread="<?php echo $this->nb_not_read; ?>" class="btn<?php echo $this->get_c == 'a' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index'); ?>">
 					<?php echo RSSThemes::icon('all'); ?>
 					<?php echo Translate::t ('all_feeds'); ?>
 				</a>
@@ -24,7 +24,7 @@
 
 		<li>
 			<div class="category favorites">
-				<a data-unread="<?php echo $this->nb_favorites['unread']; ?>" class="btn<?php echo $this->get_c == 'favoris' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 'favoris'); ?>">
+				<a data-unread="<?php echo $this->nb_favorites['unread']; ?>" class="btn<?php echo $this->get_c == 's' ? ' active' : ''; ?>" href="<?php echo _url ('index', 'index', 'get', 's'); ?>">
 					<?php echo RSSThemes::icon('bookmark'); ?>
 					<?php echo Translate::t ('favorite_feeds', $this->nb_favorites['all']); ?>
 				</a>

+ 6 - 7
app/layout/nav_menu.phtml

@@ -11,9 +11,8 @@
 			$get = 'f_' . $this->get_f;
 			$string_mark = Translate::t ('mark_feed_read');
 		} elseif ($this->get_c &&
-		          $this->get_c != 'all' &&
-		          $this->get_c != 'favoris' &&
-		          $this->get_c != 'public') {
+		          $this->get_c != 'a' &&
+		          $this->get_c != 's') {
 			$get = 'c_' . $this->get_c;
 			$string_mark = Translate::t ('mark_cat_read');
 		}
@@ -34,7 +33,7 @@
 						$anotherUnreadId = $cat->id ();
 						if ($foundCurrent) break;
 					}
-					$nextGet = empty ($anotherUnreadId) ? 'all' : 'c_' . $anotherUnreadId;
+					$nextGet = empty ($anotherUnreadId) ? 'a' : 'c_' . $anotherUnreadId;
 					break;
 				case 'f':
 					foreach ($this->cat_aside as $cat) {
@@ -149,15 +148,15 @@
 			<li class="item">
 				<?php
 					$url_order = $url;
-					if ($this->order == 'low_to_high') {
-						$url_order['params']['order'] = 'high_to_low';
+					if ($this->order === 'DESC') {
+						$url_order['params']['order'] = 'ASC';
 				?>
 				<a href="<?php echo Url::display ($url_order); ?>">
 					<?php echo Translate::t ('older_first'); ?>
 				</a>
 				<?php
 					} else {
-						$url_order['params']['order'] = 'low_to_high';
+						$url_order['params']['order'] = 'DESC';
 				?>
 				<a href="<?php echo Url::display ($url_order); ?>">
 					<?php echo Translate::t ('newer_first'); ?>

+ 0 - 148
app/models/EntriesGetter.php

@@ -1,148 +0,0 @@
-<?php
-
-class EntriesGetter {
-	private $type = array (
-		'type' => 'all',
-		'id' => 'all'
-	);
-	private $state = 'all';
-	private $filter = array (
-		'words' => array (),
-		'tags' => array (),
-	);
-	private $order = 'high_to_low';
-	private $entries = array ();
-
-	private $nb = 1;
-	private $first = '';
-	private $next = '';
-
-	public function __construct ($type, $state, $filter, $order, $nb, $first = '') {
-		$this->_type ($type);
-		$this->_state ($state);
-		$this->_filter ($filter);
-		$this->_order ($order);
-		$this->nb = $nb;
-		$this->first = $first;
-	}
-
-	public function type () {
-		return $this->type;
-	}
-	public function state () {
-		return $this->state;
-	}
-	public function filter () {
-		return $this->filter;
-	}
-	public function order () {
-		return $this->order;
-	}
-	public function entries () {
-		return $this->entries;
-	}
-
-	public function _type ($value) {
-		if (!is_array ($value) ||
-		    !isset ($value['type']) ||
-		    !isset ($value['id'])) {
-			throw new EntriesGetterException ('Bad type line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-
-		$type = $value['type'];
-		$id = $value['id'];
-
-		if ($type != 'all' && $type != 'favoris' && $type != 'public' && $type != 'c' && $type != 'f') {
-			throw new EntriesGetterException ('Bad type line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-
-		if (($type == 'all' || $type == 'favoris' || $type == 'public') &&
-		    ($type != $id)) {
-			throw new EntriesGetterException ('Bad type line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-
-		$this->type = $value;
-	}
-	public function _state ($value) {
-		if ($value != 'all' && $value != 'not_read' && $value != 'read') {
-			throw new EntriesGetterException ('Bad state line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-
-		$this->state = $value;
-	}
-	public function _filter ($value) {
-		$value = trim ($value);
-		$terms = explode (' ', $value);
-
-		foreach ($terms as $word) {
-			if (!empty ($word) && $word[0] == '#' && isset ($word[1])) {
-				$tag = substr ($word, 1);
-				$this->filter['tags'][$tag] = $tag;
-			} elseif (!empty ($word)) {
-				$this->filter['words'][$word] = $word;
-			}
-		}
-	}
-	public function _order ($value) {
-		if ($value != 'high_to_low' && $value != 'low_to_high') {
-			throw new EntriesGetterException ('Bad order line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-
-		$this->order = $value;
-	}
-
-	public function execute () {
-		$entryDAO = new EntryDAO ();
-
-		HelperEntry::$nb = $this->nb;	//TODO: Update: Now done in SQL
-		HelperEntry::$first = $this->first;	//TODO: Update: Now done in SQL
-		HelperEntry::$filter = $this->filter;
-
-		$sqlLimit = (empty ($this->filter['words']) && empty ($this->filter['tags'])) ? $this->nb : '';	//Disable SQL LIMIT optimisation during search	//TODO: Do better!
-
-		switch ($this->type['type']) {
-		case 'all':
-			list ($this->entries, $this->next) = $entryDAO->listEntries (
-				$this->state,
-				$this->order,
-				$this->first,
-				$sqlLimit
-			);
-			break;
-		case 'favoris':
-			list ($this->entries, $this->next) = $entryDAO->listFavorites (
-				$this->state,
-				$this->order,
-				$this->first,
-				$sqlLimit
-			);
-			break;
-		case 'c':
-			list ($this->entries, $this->next) = $entryDAO->listByCategory (
-				$this->type['id'],
-				$this->state,
-				$this->order,
-				$this->first,
-				$sqlLimit
-			);
-			break;
-		case 'f':
-			list ($this->entries, $this->next) = $entryDAO->listByFeed (
-				$this->type['id'],
-				$this->state,
-				$this->order,
-				$this->first,
-				$sqlLimit
-			);
-			break;
-		default:
-			throw new EntriesGetterException ('Bad type line ' . __LINE__ . ' in file ' . __FILE__);
-		}
-	}
-
-	public function getPaginator () {
-		$paginator = new RSSPaginator ($this->entries, $this->next);
-
-		return $paginator;
-	}
-}

+ 81 - 132
app/models/Entry.php

@@ -15,7 +15,7 @@ class Entry extends Model {
 	private $tags;
 
 	public function __construct ($feed = '', $guid = '', $title = '', $author = '', $content = '',
-	                             $link = '', $pubdate = 0, $is_read = false, $is_favorite = false) {
+	                             $link = '', $pubdate = 0, $is_read = false, $is_favorite = false, $tags = '') {
 		$this->_guid ($guid);
 		$this->_title ($title);
 		$this->_author ($author);
@@ -25,7 +25,7 @@ class Entry extends Model {
 		$this->_isRead ($is_read);
 		$this->_isFavorite ($is_favorite);
 		$this->_feed ($feed);
-		$this->_tags (array ());
+		$this->_tags (preg_split('/[\s#]/', $tags));
 	}
 
 	public function id () {
@@ -81,11 +81,7 @@ class Entry extends Model {
 	}
 	public function tags ($inString = false) {
 		if ($inString) {
-			if (!empty ($this->tags)) {
-				return '#' . implode(' #', $this->tags);
-			} else {
-				return '';
-			}
+			return empty ($this->tags) ? '' : '#' . implode(' #', $this->tags);
 		} else {
 			return $this->tags;
 		}
@@ -110,8 +106,8 @@ class Entry extends Model {
 		$this->link = $value;
 	}
 	public function _date ($value) {
-		if (is_int (intval ($value))) {
-			$this->date = $value;
+		if (is_int ($value)) {
+			$this->date = intval ($value);
 		} else {
 			$this->date = time ();
 		}
@@ -448,13 +444,8 @@ class EntryDAO extends Model_pdo {
 
 		$stm->execute ($values);
 		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		list ($entry, $next) = HelperEntry::daoToEntry ($res);
-
-		if (isset ($entry[0])) {
-			return $entry[0];
-		} else {
-			return false;
-		}
+		$entries = HelperEntry::daoToEntry ($res);
+		return isset ($entries[0]) ? $entries[0] : false;
 	}
 
 	public function searchById ($id) {
@@ -466,60 +457,79 @@ class EntryDAO extends Model_pdo {
 
 		$stm->execute ($values);
 		$res = $stm->fetchAll (PDO::FETCH_ASSOC);
-		list ($entry, $next) = HelperEntry::daoToEntry ($res);
-
-		if (isset ($entry[0])) {
-			return $entry[0];
-		} else {
-			return false;
+		$entries = HelperEntry::daoToEntry ($res);
+		return isset ($entries[0]) ? $entries[0] : false;
+	}
+
+	public function listWhere($type = 'a', $id = '', $state = 'all', $order = 'DESC', $limit = 1, $firstId = -1, $filter = '') {
+		$where = '';
+		$values = array();
+		switch ($type) {
+			case 'a':
+				$where .= 'priority > 0 ';
+				break;
+			case 's':
+				$where .= 'is_favorite = 1 ';
+				break;
+			case 'c':
+				$where .= 'category = ? ';
+				$values[] = intval($id);
+				break;
+			case 'f':
+				$where .= 'id_feed = ? ';
+				$values[] = intval($id);
+				break;
+			default:
+				throw new EntriesGetterException ('Bad type in Entry->listByType: [' . $type . ']!');
 		}
-	}
-
-	private function listWhere ($where, $state, $order, $limitFromId = '', $limitCount = '', $values = array ()) {
-		if ($state === 'not_read') {
-			$where .= ' AND is_read = 0';
-		} elseif ($state === 'read') {
-			$where .= ' AND is_read = 1';
+		switch ($state) {
+			case 'all':
+				break;
+			case 'not_read':
+				$where .= 'AND is_read = 0 ';
+				break;
+			case 'read':
+				$where .= 'AND is_read = 1 ';
+				break;
+			default:
+				throw new EntriesGetterException ('Bad state in Entry->listByType: [' . $state . ']!');
 		}
-		if (!empty($limitFromId)) {
-			$where .= ' AND e.id ' . ($order === 'low_to_high' ? '<=' : '>=') . $limitFromId;
+		switch ($order) {
+			case 'DESC':
+			case 'ASC':
+				break;
+			default:
+				throw new EntriesGetterException ('Bad order in Entry->listByType: [' . $order . ']!');
 		}
-
-		if ($order === 'low_to_high') {
-			$order = ' DESC';
-		} else {
-			$order = '';
+		if ($firstId > 0) {
+			$where .= 'AND e.id ' . ($order === 'DESC' ? '<=' : '>=') . $firstId . ' ';
+		}
+		$terms = explode(' ', trim($filter));
+		sort($terms);	//Put #tags first
+		foreach ($terms as $word) {
+			if (!empty($word)) {
+				if ($word[0] === '#' && isset($word[1])) {
+					$where .= 'AND tags LIKE "%' . $word . '%" ';
+				} elseif (!empty($word)) {
+					$where .= 'AND (e.title LIKE "%' . $word . '%" OR UNCOMPRESS(e.content_bin) LIKE "%' . $word . '%") ';
+				}
+			}
 		}
 
 		$sql = 'SELECT e.id, e.guid, e.title, e.author, UNCOMPRESS(e.content_bin) AS content, e.link, e.date, e.is_read, e.is_favorite, e.id_feed, e.tags '
 		     . 'FROM `' . $this->prefix . 'entry` e '
-		     . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id ' . $where
-		     . ' ORDER BY e.id' . $order;
+		     . 'INNER JOIN `' . $this->prefix . 'feed` f ON e.id_feed = f.id WHERE ' . $where
+		     . 'ORDER BY e.id ' . $order;
 
-		if (empty($limitCount)) {
-			$limitCount = 20000;	//TODO: FIXME: Hack temporaire en attendant la recherche côté base-de-données
+		if ($limit > 0) {
+			$sql .= ' LIMIT ' . $limit;	//TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
 		}
-		//if (!empty($limitCount)) {
-			$sql .= ' LIMIT ' . ($limitCount + 2);	//TODO: See http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/
-		//}
 
 		$stm = $this->bd->prepare ($sql);
 		$stm->execute ($values);
 
 		return HelperEntry::daoToEntry ($stm->fetchAll (PDO::FETCH_ASSOC));
 	}
-	public function listEntries ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') {
-		return $this->listWhere ('WHERE priority > 0', $state, $order, $limitFromId, $limitCount);
-	}
-	public function listFavorites ($state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') {
-		return $this->listWhere ('WHERE is_favorite = 1', $state, $order, $limitFromId, $limitCount);
-	}
-	public function listByCategory ($cat, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') {
-		return $this->listWhere ('WHERE category = ?', $state, $order, $limitFromId, $limitCount, array ($cat));
-	}
-	public function listByFeed ($feed, $state, $order = 'high_to_low', $limitFromId = '', $limitCount = '') {
-		return $this->listWhere ('WHERE id_feed = ?', $state, $order, $limitFromId, $limitCount, array ($feed));
-	}
 
 	public function listLastGuidsByFeed($id, $n) {
 		$sql = 'SELECT guid FROM `' . $this->prefix . 'entry` WHERE id_feed=? ORDER BY id DESC LIMIT ' . intval($n);
@@ -579,14 +589,6 @@ class EntryDAO extends Model_pdo {
 }
 
 class HelperEntry {
-	public static $nb = 1;
-	public static $first = '';
-
-	public static $filter = array (
-		'words' => array (),
-		'tags' => array (),
-	);
-
 	public static function daoToEntry ($listDAO) {
 		$list = array ();
 
@@ -594,80 +596,27 @@ class HelperEntry {
 			$listDAO = array ($listDAO);
 		}
 
-		$count = 0;
-		$first_is_found = false;
-		$break_after = false;
-		$next = '';
 		foreach ($listDAO as $key => $dao) {
-			$dao['tags'] = preg_split('/[\s#]/', $dao['tags']);
-
-			if (self::tagsMatchEntry ($dao) &&
-			    self::searchMatchEntry ($dao)) {
-				if ($break_after) {
-					$next = $dao['id'];
-					break;
-				}
-				if ($first_is_found || $dao['id'] == self::$first || self::$first == '') {
-					$list[$key] = self::createEntry ($dao);
-
-					$count++;
-					$first_is_found = true;	//TODO: Update: Now done in SQL
-				}
-				if ($count >= self::$nb) {
-					$break_after = true;
-				}
+			$entry = new Entry (
+				$dao['id_feed'],
+				$dao['guid'],
+				$dao['title'],
+				$dao['author'],
+				$dao['content'],
+				$dao['link'],
+				$dao['date'],
+				$dao['is_read'],
+				$dao['is_favorite'],
+				$dao['tags']
+			);
+			if (isset ($dao['id'])) {
+				$entry->_id ($dao['id']);
 			}
+			$list[] = $entry;
 		}
 
 		unset ($listDAO);
 
-		return array ($list, $next);
-	}
-
-	private static function createEntry ($dao) {
-		$entry = new Entry (
-			$dao['id_feed'],
-			$dao['guid'],
-			$dao['title'],
-			$dao['author'],
-			$dao['content'],
-			$dao['link'],
-			$dao['date'],
-			$dao['is_read'],
-			$dao['is_favorite']
-		);
-
-		$entry->_tags ($dao['tags']);
-
-		if (isset ($dao['id'])) {
-			$entry->_id ($dao['id']);
-		}
-
-		return $entry;
-	}
-
-	private static function tagsMatchEntry ($dao) {
-		$tags = self::$filter['tags'];
-		foreach ($tags as $tag) {
-			if (!in_array ($tag, $dao['tags'])) {
-				return false;
-			}
-		}
-
-		return true;
-	}
-	private static function searchMatchEntry ($dao) {
-		$words = self::$filter['words'];
-
-		foreach ($words as $word) {
-			$word = strtolower ($word);
-			if (strpos (strtolower ($dao['title']), $word) === false &&
-			    strpos (strtolower ($dao['content']), $word) === false &&
-			    strpos (strtolower ($dao['link']), $word) === false) {
-				return false;
-			}
-		}
-
-		return true;
+		return $list;
 	}
 }

+ 2 - 6
app/models/RSSConfiguration.php

@@ -213,11 +213,7 @@ class RSSConfiguration extends Model {
 		}
 	}
 	public function _sortOrder ($value) {
-		if ($value == 'high_to_low') {
-			$this->sort_order = 'high_to_low';
-		} else {
-			$this->sort_order = 'low_to_high';
-		}
+		$this->sort_order = $value === 'ASC' ? 'ASC' : 'DESC';
 	}
 	public function _oldEntries ($value) {
 		if (is_int (intval ($value)) && $value > 0) {
@@ -334,7 +330,7 @@ class RSSConfigurationDAO extends Model_array {
 	public $display_posts = 'no';
 	public $onread_jump_next = 'yes';
 	public $lazyload = 'yes';
-	public $sort_order = 'low_to_high';
+	public $sort_order = 'DESC';
 	public $old_entries = 3;
 	public $shortcuts = array (
 		'mark_read' => 'r',

+ 2 - 2
app/views/configure/display.phtml

@@ -73,8 +73,8 @@
 			<label class="group-name" for="sort_order"><?php echo Translate::t ('sort_order'); ?></label>
 			<div class="group-controls">
 				<select name="sort_order" id="sort_order">
-					<option value="low_to_high"<?php echo $this->conf->sortOrder () == 'low_to_high' ? ' selected="selected"' : ''; ?>><?php echo Translate::t ('newer_first'); ?></option>
-					<option value="high_to_low"<?php echo $this->conf->sortOrder () == 'high_to_low' ? ' selected="selected"' : ''; ?>><?php echo Translate::t ('older_first'); ?></option>
+					<option value="DESC"<?php echo $this->conf->sortOrder () === 'DESC' ? ' selected="selected"' : ''; ?>><?php echo Translate::t ('newer_first'); ?></option>
+					<option value="ASC"<?php echo $this->conf->sortOrder () === 'ASC' ? ' selected="selected"' : ''; ?>><?php echo Translate::t ('older_first'); ?></option>
 				</select>
 			</div>
 		</div>