Переглянути джерело

Export labels (#2217)

* Export labels

https://github.com/FreshRSS/FreshRSS/issues/2196

* Small fixes

* Backport code from 1.14.0

https://github.com/FreshRSS/FreshRSS/pull/2199/commits/4888f919f104b2d170302565e481a0b731eb4145

* More fixes
Alexandre Alapetite 7 роки тому
батько
коміт
743c1b740b

+ 21 - 15
app/Controllers/importExportController.php

@@ -650,7 +650,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 		return $return;
 	}
 
-	public function exportFile($export_opml = true, $export_starred = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) {
+	public function exportFile($export_opml = true, $export_starred = false, $export_labelled = false, $export_feeds = array(), $maxFeedEntries = 50, $username = null) {
 		require_once(LIB_PATH . '/lib_opml.php');
 
 		$this->catDAO = new FreshRSS_CategoryDAO($username);
@@ -674,8 +674,11 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			$export_files["feeds_${day}.opml.xml"] = $this->generateOpml();
 		}
 
-		if ($export_starred) {
-			$export_files["starred_${day}.json"] = $this->generateEntries('starred');
+		if ($export_starred || $export_labelled) {
+			$export_files["starred_${day}.json"] = $this->generateEntries(
+				($export_starred ? 'S' : '') .
+				($export_labelled ? 'T' : '')
+			);
 		}
 
 		foreach ($export_feeds as $feed_id) {
@@ -683,7 +686,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			if ($feed) {
 				$filename = "feed_${day}_" . $feed->category() . '_'
 				          . $feed->id() . '.json';
-				$export_files[$filename] = $this->generateEntries('feed', $feed, $maxFeedEntries);
+				$export_files[$filename] = $this->generateEntries('f', $feed, $maxFeedEntries);
 			}
 		}
 
@@ -725,6 +728,7 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 			$nb_files = $this->exportFile(
 					Minz_Request::param('export_opml', false),
 					Minz_Request::param('export_starred', false),
+					Minz_Request::param('export_labelled', false),
 					Minz_Request::param('export_feeds', array())
 				);
 		} catch (FreshRSS_ZipMissing_Exception $zme) {
@@ -758,27 +762,29 @@ class FreshRSS_importExport_Controller extends Minz_ActionController {
 	/**
 	 * This method returns a JSON file content.
 	 *
-	 * @param string $type must be "starred" or "feed"
+	 * @param string $type must be one of:
+	 * 	'S' (starred/favourite), 'f' (feed), 'T' (taggued/labelled), 'ST' (starred or labelled)
 	 * @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) {
 		$this->view->categories = $this->catDAO->listCategories();
+		$tagDAO = FreshRSS_Factory::createTagDao();
 
-		if ($type == 'starred') {
+		if ($type === 's' || $type === 'S' || $type === 'T' || $type === 'ST') {
 			$this->view->list_title = _t('sub.import_export.starred_list');
 			$this->view->type = 'starred';
-			$unread_fav = $this->entryDAO->countUnreadReadFavorites();
-			$this->view->entriesRaw = $this->entryDAO->listWhereRaw(
-				's', '', FreshRSS_Entry::STATE_ALL, 'ASC', $unread_fav['all']
-			);
-		} elseif ($type === 'feed' && $feed != null) {
+			$this->view->entriesId = $this->entryDAO->listIdsWhere($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1);
+			$this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId);
+			//The following is a streamable query, i.e. must be last
+			$this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, '', FreshRSS_Entry::STATE_ALL, 'ASC', -1);
+		} elseif ($type === 'f' && $feed != null) {
 			$this->view->list_title = _t('sub.import_export.feed_list', $feed->name());
 			$this->view->type = 'feed/' . $feed->id();
-			$this->view->entriesRaw = $this->entryDAO->listWhereRaw(
-				'f', $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC',
-				$maxFeedEntries
-			);
+			$this->view->entriesId = $this->entryDAO->listIdsWhere($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries);
+			$this->view->entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->view->entriesId);
+			//The following is a streamable query, i.e. must be last
+			$this->view->entriesRaw = $this->entryDAO->listWhereRaw($type, $feed->id(), FreshRSS_Entry::STATE_ALL, 'ASC', $maxFeedEntries);
 			$this->view->feed = $feed;
 		}
 

+ 6 - 0
app/Models/EntryDAO.php

@@ -839,6 +839,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 			$where .= 'f.priority >= ' . FreshRSS_Feed::PRIORITY_NORMAL . ' ';
 			$where .= 'AND e.is_favorite=1 ';
 			break;
+		case 'S':	//Starred
+			$where .= 'e.is_favorite=1 ';
+			break;
 		case 'c':	//Category
 			$where .= 'f.priority >= ' . FreshRSS_Feed::PRIORITY_NORMAL . ' ';
 			$where .= 'AND f.category=? ';
@@ -855,6 +858,9 @@ class FreshRSS_EntryDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		case 'T':	//Any tag
 			$where .= '1=1 ';
 			break;
+		case 'ST':	//Starred or tagged
+			$where .= 'e.is_favorite=1 OR EXISTS (SELECT et2.id_tag FROM `' . $this->prefix . 'entrytag` et2 WHERE et2.id_entry = e.id) ';
+			break;
 		default:
 			throw new FreshRSS_EntriesGetter_Exception('Bad type in Entry->listByType: [' . $type . ']!');
 		}

+ 12 - 2
app/Models/TagDAO.php

@@ -265,8 +265,18 @@ class FreshRSS_TagDAO extends Minz_ModelPdo implements FreshRSS_Searchable {
 		$values = array();
 		if (is_array($entries) && count($entries) > 0) {
 			$sql .= ' AND et.id_entry IN (' . str_repeat('?,', count($entries) - 1). '?)';
-			foreach ($entries as $entry) {
-				$values[] = is_array($entry) ? $entry['id'] : $entry->id();
+			if (is_array($entries[0])) {
+				foreach ($entries as $entry) {
+					$values[] = $entry['id'];
+				}
+			} elseif (is_object($entries[0])) {
+				foreach ($entries as $entry) {
+					$values[] = $entry->id();
+				}
+			} else {
+				foreach ($entries as $entry) {
+					$values[] = $entry;
+				}
 			}
 		}
 		$stm = $this->bd->prepare($sql);

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Export',
 		'export_opml' => 'Exportovat seznam kanálů (OPML)',
 		'export_starred' => 'Exportovat oblíbené',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Seznam %s článků',
 		'file_to_import' => 'Soubor k importu<br />(OPML, JSON nebo ZIP)',
 		'file_to_import_no_zip' => 'Soubor k importu<br />(OPML nebo JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Exportieren',
 		'export_opml' => 'Liste der Feeds exportieren (OPML)',
 		'export_starred' => 'Ihre Favoriten exportieren',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Liste von %s Artikeln',
 		'file_to_import' => 'Zu importierende Datei<br />(OPML, JSON oder ZIP)',
 		'file_to_import_no_zip' => 'Zu importierende Datei<br />(OPML oder JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Export',
 		'export_opml' => 'Export list of feeds (OPML)',
 		'export_starred' => 'Export your favourites',
+		'export_labelled' => 'Export your labelled articles',
 		'feed_list' => 'List of %s articles',
 		'file_to_import' => 'File to import<br />(OPML, JSON or ZIP)',
 		'file_to_import_no_zip' => 'File to import<br />(OPML or JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Exportar',
 		'export_opml' => 'Exportar la lista de fuentes (OPML)',
 		'export_starred' => 'Exportar tus favoritos',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Lista de %s artículos',
 		'file_to_import' => 'Archivo a importar<br />(OPML, JSON o ZIP)',
 		'file_to_import_no_zip' => 'Archivo a importar<br />(OPML o JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Exporter',
 		'export_opml' => 'Exporter la liste des flux (OPML)',
 		'export_starred' => 'Exporter les favoris',
+		'export_labelled' => 'Exporter les articles étiquetés',
 		'feed_list' => 'Liste des articles de %s',
 		'file_to_import' => 'Fichier à importer<br />(OPML, JSON ou ZIP)',
 		'file_to_import_no_zip' => 'Fichier à importer<br />(OPML ou JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'ייצוא',
 		'export_opml' => 'ייצוא רשימת הזנות (OPML)',
 		'export_starred' => 'ייצוא מועדפים',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'רשימה של %s מאמרים',
 		'file_to_import' => 'קובץ לייבוא<br />(OPML, Json or Zip)',
 		'file_to_import_no_zip' => 'קובץ לייבוא<br />(OPML or Json)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Esporta',
 		'export_opml' => 'Esporta tutta la lista dei feed (OPML)',
 		'export_starred' => 'Esporta i tuoi preferiti',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Elenco di %s articoli',
 		'file_to_import' => 'File da importare<br />(OPML, JSON o ZIP)',
 		'file_to_import_no_zip' => 'File da importare<br />(OPML o JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => '내보내기',
 		'export_opml' => '피드 목록 내보내기 (OPML)',
 		'export_starred' => '즐겨찾기 내보내기',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => '%s 개의 글 목록',
 		'file_to_import' => '불러올 파일<br />(OPML, JSON 또는 ZIP)',
 		'file_to_import_no_zip' => '불러올 파일<br />(OPML 또는 JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Exporteer',
 		'export_opml' => 'Exporteer lijst van feeds (OPML)',
 		'export_starred' => 'Exporteer je favorieten',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Lijst van %s artikelen',
 		'file_to_import' => 'Bestand om te importeren<br />(OPML, JSON of ZIP)',
 		'file_to_import_no_zip' => 'Bestand om te importeren<br />(OPML of JSON)',

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

@@ -71,6 +71,7 @@ return array(
 		'export' => 'Exportar',
 		'export_opml' => 'Exportar la lista de fluxes (OPML)',
 		'export_starred' => 'Exportar los favorits',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Lista dels %s articles',
 		'file_to_import' => 'Fichièr d’importar<br />(OPML, JSON o ZIP)',
 		'file_to_import_no_zip' => 'Fichièr d’importar<br />(OPML o JSON)',

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

@@ -68,6 +68,7 @@ return array(
 		'export' => 'Exportar',
 		'export_opml' => 'Exporta a lista dos feeds (OPML)',
 		'export_starred' => 'Exportar seus favoritos',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'Lista dos %s artigos',
 		'file_to_import' => 'Arquivo para importar<br />(OPML, JSON or ZIP)',
 		'file_to_import_no_zip' => 'Arquivo para importar<br />(OPML or JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Export',	//TODO - Translation
 		'export_opml' => 'Export list of feeds (OPML)',	//TODO - Translation
 		'export_starred' => 'Export your favourites',	//TODO - Translation
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => 'List of %s articles',	//TODO - Translation
 		'file_to_import' => 'File to import<br />(OPML, JSON or ZIP)',	//TODO - Translation
 		'file_to_import_no_zip' => 'File to import<br />(OPML or JSON)',	//TODO - Translation

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

@@ -72,6 +72,7 @@ return array(
 		'export' => 'Dışa aktar',
 		'export_opml' => 'Akış listesini dışarı aktar (OPML)',
 		'export_starred' => 'Favorileri dışarı aktar',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => '%s makalenin listesi',
 		'file_to_import' => 'Dosyadan içe aktar<br />(OPML, JSON or ZIP)',
 		'file_to_import_no_zip' => 'Dosyadan içe aktar<br />(OPML or JSON)',

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

@@ -72,6 +72,7 @@ return array(
 		'export' => '导出',
 		'export_opml' => '导出 RSS 源列表 (OPML)',
 		'export_starred' => '导出你的收藏',
+		'export_labelled' => 'Export your labelled articles',	//TODO
 		'feed_list' => '%s 文章列表',
 		'file_to_import' => '需要导入的文件<br />(OPML, JSON 或 ZIP)',
 		'file_to_import_no_zip' => '需要导入的文件<br />(OPML 或 JSON)',

+ 4 - 6
app/views/helpers/export/articles.phtml

@@ -16,14 +16,12 @@ $articles = array(
 echo rtrim(json_encode($articles, $options), " ]}\n\r\t"), "\n";
 $first = true;
 
-$tagDAO = FreshRSS_Factory::createTagDao();
-$entryIdsTagNames = $tagDAO->getEntryIdsTagNames($this->entriesRaw);
-if ($entryIdsTagNames == false) {
-	$entryIdsTagNames = array();
+if (empty($this->entryIdsTagNames)) {
+	$this->entryIdsTagNames = array();
 }
 
 foreach ($this->entriesRaw as $entryRaw) {
-	if (empty($entryRaw)) {
+	if ($entryRaw == null) {
 		continue;
 	}
 	$entry = FreshRSS_EntryDAO::daoToEntry($entryRaw);
@@ -61,7 +59,7 @@ foreach ($this->entriesRaw as $entryRaw) {
 	if ($entry->isFavorite()) {
 		$article['categories'][] = 'user/-/state/com.google/starred';
 	}
-	$tagNames = isset($entryIdsTagNames['e_' . $entry->id()]) ? $entryIdsTagNames['e_' . $entry->id()] : array();
+	$tagNames = isset($this->entryIdsTagNames['e_' . $entry->id()]) ? $this->entryIdsTagNames['e_' . $entry->id()] : array();
 	foreach ($tagNames as $tagName) {
 		$article['categories'][] = 'user/-/label/' . $tagName;
 	}

+ 5 - 0
app/views/importExport/index.phtml

@@ -33,6 +33,11 @@
 					<?php echo _t('sub.import_export.export_opml'); ?>
 				</label>
 
+				<label class="checkbox" for="export_labelled">
+					<input type="checkbox" name="export_labelled" id="export_labelled" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> />
+					<?php echo _t('sub.import_export.export_labelled'); ?>
+				</label>
+
 				<label class="checkbox" for="export_starred">
 					<input type="checkbox" name="export_starred" id="export_starred" value="1" <?php echo extension_loaded('zip') ? 'checked="checked"' : ''; ?> />
 					<?php echo _t('sub.import_export.export_starred'); ?>

+ 1 - 1
cli/export-opml-for-user.php

@@ -17,7 +17,7 @@ fwrite(STDERR, 'FreshRSS exporting OPML for user “' . $username . "”…\n");
 $importController = new FreshRSS_importExport_Controller();
 
 $ok = false;
-$ok = $importController->exportFile(true, false, array(), 0, $username);
+$ok = $importController->exportFile(true, false, false, array(), 0, $username);
 
 invalidateHttpCache($username);
 

+ 1 - 1
cli/export-zip-for-user.php

@@ -19,7 +19,7 @@ $importController = new FreshRSS_importExport_Controller();
 
 $ok = false;
 try {
-	$ok = $importController->exportFile(true, true, true,
+	$ok = $importController->exportFile(true, true, true, true,
 		empty($options['max-feed-entries']) ? 100 : intval($options['max-feed-entries']),
 		$username);
 } catch (FreshRSS_ZipMissing_Exception $zme) {