Przeglądaj źródła

Add category order (#2592)

* Add category order

Each category has a new 'priority' attribute. It is used to sort categories in
views. Categories with the same priority are sorted alphabetically. Categories
with no priority are displayed after those with one.

For example, if we have the following categories:
- A (priority: 2)
- B (no priority)
- C (priority: 1)
- D (priority: 2)
- E (no priority)
- F (priority: 1)

They will be displayed in the following order:
- C
- F
- A
- D
- B
- E

See #190

* Shorten help text

It took too much room and will not be so necessary once we have drag &
drop
Alexis Degrugillier 6 lat temu
rodzic
commit
f6e10579f2

+ 1 - 1
app/Controllers/indexController.php

@@ -173,7 +173,7 @@ class FreshRSS_index_Controller extends Minz_ActionController {
 	private function updateContext() {
 	private function updateContext() {
 		if (empty(FreshRSS_Context::$categories)) {
 		if (empty(FreshRSS_Context::$categories)) {
 			$catDAO = FreshRSS_Factory::createCategoryDao();
 			$catDAO = FreshRSS_Factory::createCategoryDao();
-			FreshRSS_Context::$categories = $catDAO->listCategories();
+			FreshRSS_Context::$categories = $catDAO->listSortedCategories();
 		}
 		}
 
 
 		// Update number of read / unread variables.
 		// Update number of read / unread variables.

+ 4 - 1
app/Controllers/subscriptionController.php

@@ -19,7 +19,7 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 
 
 		$catDAO->checkDefault();
 		$catDAO->checkDefault();
 		$feedDAO->updateTTL();
 		$feedDAO->updateTTL();
-		$this->view->categories = $catDAO->listCategories(false);
+		$this->view->categories = $catDAO->listSortedCategories(false);
 		$this->view->default_category = $catDAO->getDefault();
 		$this->view->default_category = $catDAO->getDefault();
 	}
 	}
 
 
@@ -216,6 +216,9 @@ class FreshRSS_subscription_Controller extends Minz_ActionController {
 				]);
 				]);
 			}
 			}
 
 
+			$position = Minz_Request::param('position');
+			$category->_attributes('position', '' === $position ? null : (int) $position);
+
 			$values = [
 			$values = [
 				'name' => Minz_Request::param('name', ''),
 				'name' => Minz_Request::param('name', ''),
 				'attributes' => $category->attributes(),
 				'attributes' => $category->attributes(),

+ 2 - 2
app/Models/Category.php

@@ -98,14 +98,14 @@ class FreshRSS_Category extends Minz_Model {
 	}
 	}
 
 
 	public function _attributes($key, $value) {
 	public function _attributes($key, $value) {
-		if ($key == '') {
+		if ('' == $key) {
 			if (is_string($value)) {
 			if (is_string($value)) {
 				$value = json_decode($value, true);
 				$value = json_decode($value, true);
 			}
 			}
 			if (is_array($value)) {
 			if (is_array($value)) {
 				$this->attributes = $value;
 				$this->attributes = $value;
 			}
 			}
-		} elseif ($value === null) {
+		} elseif (null === $value) {
 			unset($this->attributes[$key]);
 			unset($this->attributes[$key]);
 		} else {
 		} else {
 			$this->attributes[$key] = $value;
 			$this->attributes[$key] = $value;

+ 25 - 0
app/Models/CategoryDAO.php

@@ -201,6 +201,29 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 		}
 		}
 	}
 	}
 
 
+	public function listSortedCategories($prePopulateFeeds = true, $details = false) {
+		$categories = $this->listCategories($prePopulateFeeds, $details);
+
+		if (!is_array($categories)) {
+			return $categories;
+		}
+
+		usort($categories, function ($a, $b) {
+			$aPosition = $a->attributes('position');
+			$bPosition = $b->attributes('position');
+			if ($aPosition === $bPosition) {
+				return ($a->name() < $b->name()) ? -1 : 1;
+			} elseif (null === $aPosition) {
+				return 1;
+			} elseif (null === $bPosition) {
+				return -1;
+			}
+			return ($aPosition < $bPosition) ? -1 : 1;
+		});
+
+		return $categories;
+	}
+
 	public function listCategories($prePopulateFeeds = true, $details = false) {
 	public function listCategories($prePopulateFeeds = true, $details = false) {
 		if ($prePopulateFeeds) {
 		if ($prePopulateFeeds) {
 			$sql = 'SELECT c.id AS c_id, c.name AS c_name, c.attributes AS c_attributes, '
 			$sql = 'SELECT c.id AS c_id, c.name AS c_name, c.attributes AS c_attributes, '
@@ -343,6 +366,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 					$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
 					$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
 				);
 				);
 				$cat->_id($previousLine['c_id']);
 				$cat->_id($previousLine['c_id']);
+				$cat->_attributes('', $previousLine['c_attributes']);
 				$list[$previousLine['c_id']] = $cat;
 				$list[$previousLine['c_id']] = $cat;
 
 
 				$feedsDao = array();	//Prepare for next category
 				$feedsDao = array();	//Prepare for next category
@@ -359,6 +383,7 @@ class FreshRSS_CategoryDAO extends Minz_ModelPdo implements FreshRSS_Searchable
 				$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
 				$feedDao->daoToFeed($feedsDao, $previousLine['c_id'])
 			);
 			);
 			$cat->_id($previousLine['c_id']);
 			$cat->_id($previousLine['c_id']);
+			$cat->_attributes('', $previousLine['c_attributes']);
 			$list[$previousLine['c_id']] = $cat;
 			$list[$previousLine['c_id']] = $cat;
 		}
 		}
 
 

+ 2 - 0
app/i18n/cz/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Vyprázdit kategorii',
 		'empty' => 'Vyprázdit kategorii',
 		'information' => 'Informace',
 		'information' => 'Informace',
 		'new' => 'Nová kategorie',
 		'new' => 'Nová kategorie',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Název',
 		'title' => 'Název',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/de/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Leere Kategorie',
 		'empty' => 'Leere Kategorie',
 		'information' => 'Information',
 		'information' => 'Information',
 		'new' => 'Neue Kategorie',
 		'new' => 'Neue Kategorie',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Titel',
 		'title' => 'Titel',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/en/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Empty category',
 		'empty' => 'Empty category',
 		'information' => 'Information',
 		'information' => 'Information',
 		'new' => 'New category',
 		'new' => 'New category',
+		'position' => 'Display position',
+		'position_help' => 'To control category sort order',
 		'title' => 'Title',
 		'title' => 'Title',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/es/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Vaciar categoría',
 		'empty' => 'Vaciar categoría',
 		'information' => 'Información',
 		'information' => 'Información',
 		'new' => 'Nueva categoría',
 		'new' => 'Nueva categoría',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Título',
 		'title' => 'Título',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/fr/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Catégorie vide',
 		'empty' => 'Catégorie vide',
 		'information' => 'Informations',
 		'information' => 'Informations',
 		'new' => 'Nouvelle catégorie',
 		'new' => 'Nouvelle catégorie',
+		'position' => 'Position d’affichage',
+		'position_help' => 'Pour contrôler l’ordre de tri des catégories',
 		'title' => 'Titre',
 		'title' => 'Titre',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/he/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Empty category',	//TODO - Translation
 		'empty' => 'Empty category',	//TODO - Translation
 		'information' => 'מידע',
 		'information' => 'מידע',
 		'new' => 'קטגוריה חדשה',
 		'new' => 'קטגוריה חדשה',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'כותרת',
 		'title' => 'כותרת',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/it/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Categoria vuota',
 		'empty' => 'Categoria vuota',
 		'information' => 'Informazioni',
 		'information' => 'Informazioni',
 		'new' => 'Nuova categoria',
 		'new' => 'Nuova categoria',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Titolo',
 		'title' => 'Titolo',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/kr/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => '빈 카테고리',
 		'empty' => '빈 카테고리',
 		'information' => '정보',
 		'information' => '정보',
 		'new' => '새 카테고리',
 		'new' => '새 카테고리',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => '제목',
 		'title' => '제목',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/nl/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Lege categorie',
 		'empty' => 'Lege categorie',
 		'information' => 'Informatie',
 		'information' => 'Informatie',
 		'new' => 'Nieuwe categorie',
 		'new' => 'Nieuwe categorie',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Titel',
 		'title' => 'Titel',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/oc/sub.php

@@ -16,6 +16,8 @@ return array(
 		'empty' => 'Categoria voida',
 		'empty' => 'Categoria voida',
 		'information' => 'Informacions',
 		'information' => 'Informacions',
 		'new' => 'Nòva categoria',
 		'new' => 'Nòva categoria',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Títol',
 		'title' => 'Títol',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/pt-br/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Categoria vazia',
 		'empty' => 'Categoria vazia',
 		'information' => 'Informações',
 		'information' => 'Informações',
 		'new' => 'Nova categoria',
 		'new' => 'Nova categoria',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Título',
 		'title' => 'Título',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/ru/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Empty category',	//TODO - Translation
 		'empty' => 'Empty category',	//TODO - Translation
 		'information' => 'Information',	//TODO - Translation
 		'information' => 'Information',	//TODO - Translation
 		'new' => 'New category',	//TODO - Translation
 		'new' => 'New category',	//TODO - Translation
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Title',	//TODO - Translation
 		'title' => 'Title',	//TODO - Translation
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/sk/sub.php

@@ -16,6 +16,8 @@ return array(
 		'empty' => 'Prázdna kategória',
 		'empty' => 'Prázdna kategória',
 		'information' => 'Informácia',
 		'information' => 'Informácia',
 		'new' => 'Nová kategória',
 		'new' => 'Nová kategória',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Názov',
 		'title' => 'Názov',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/tr/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => 'Boş kategori',
 		'empty' => 'Boş kategori',
 		'information' => 'Bilgi',
 		'information' => 'Bilgi',
 		'new' => 'Yeni kategori',
 		'new' => 'Yeni kategori',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => 'Başlık',
 		'title' => 'Başlık',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 0
app/i18n/zh-cn/sub.php

@@ -17,6 +17,8 @@ return array(
 		'empty' => '空分类',
 		'empty' => '空分类',
 		'information' => '信息',
 		'information' => '信息',
 		'new' => '新分类',
 		'new' => '新分类',
+		'position' => 'Display position',	//TODO - Translation
+		'position_help' => 'To control category sort order',	//TODO - Translation
 		'title' => '标题',
 		'title' => '标题',
 	),
 	),
 	'feed' => array(
 	'feed' => array(

+ 2 - 1
app/layout/aside_feed.phtml

@@ -63,11 +63,12 @@
 		<?php
 		<?php
 			foreach ($this->categories as $cat) {
 			foreach ($this->categories as $cat) {
 				$feeds = $cat->feeds();
 				$feeds = $cat->feeds();
+				$position = $cat->attributes('position');
 				if (!empty($feeds)) {
 				if (!empty($feeds)) {
 					$c_active = FreshRSS_Context::isCurrentGet('c_' . $cat->id());
 					$c_active = FreshRSS_Context::isCurrentGet('c_' . $cat->id());
 					$c_show = $c_active || FreshRSS_Context::$user_conf->display_categories;
 					$c_show = $c_active || FreshRSS_Context::$user_conf->display_categories;
 		?>
 		?>
-		<li class="tree-folder category<?= $c_active ? ' active' : '' ?>" data-unread="<?= $cat->nbNotRead() ?>">
+		<li class="tree-folder category<?= $c_active ? ' active' : '' ?>"<?= null === $position ? '' : "data-position='$position'" ?> data-unread="<?= $cat->nbNotRead() ?>">
 			<div class="tree-folder-title">
 			<div class="tree-folder-title">
 				<a class="dropdown-toggle" href="#"><?= _i($c_show ? 'up' : 'down') ?></a>
 				<a class="dropdown-toggle" href="#"><?= _i($c_show ? 'up' : 'down') ?></a>
 				<a class="title<?= $cat->hasFeedsWithError() ? ' error' : '' ?>" data-unread="<?= format_number($cat->nbNotRead()) ?>" href="<?= _url('index', $actual_view, 'get', 'c_' . $cat->id()) ?>"><?= $cat->name() ?></a>
 				<a class="title<?= $cat->hasFeedsWithError() ? ' error' : '' ?>" data-unread="<?= format_number($cat->nbNotRead()) ?>" href="<?= _url('index', $actual_view, 'get', 'c_' . $cat->id()) ?>"><?= $cat->name() ?></a>

+ 7 - 0
app/views/helpers/category/update.phtml

@@ -17,6 +17,13 @@
 				?> />
 				?> />
 			</div>
 			</div>
 		</div>
 		</div>
+		<div class="form-group">
+			<label class="group-name" for="position"><?= _t('sub.category.position') ?></label>
+			<div class="group-controls">
+				<input type="number" name="position" id="position" min="1" value="<?= $this->category->attributes('position') ?>" />
+				<?= _i('help') ?> <?= _t('sub.category.position_help') ?>
+			</div>
+		</div>
 
 
 		<div class="form-group form-actions">
 		<div class="form-group form-actions">
 			<div class="group-controls">
 			<div class="group-controls">