From 12462ce2b2c3ac2ac5770ae0276a3bb4ee8019d1 Mon Sep 17 00:00:00 2001
From: Jonas Heinrich <onny@project-insanity.org>
Date: Mon, 15 Mar 2021 11:21:12 +0100
Subject: [PATCH] beginning to implement export podcast subscriptions

---
 appinfo/routes.php                     |  1 +
 l10n/de.js                             | 80 +++++++++++++-------------
 l10n/de.json                           | 80 +++++++++++++-------------
 lib/Controller/ExportController.php    | 79 +++++++++++++++++++++++++
 lib/ExportResponse.php                 | 56 ++++++++++++++++++
 src/components/ItemGrid.vue            | 26 +++++++++
 src/views/Library.vue                  | 14 ++++-
 translationfiles/templates/podcast.pot |  2 +-
 8 files changed, 254 insertions(+), 84 deletions(-)
 create mode 100644 lib/Controller/ExportController.php
 create mode 100644 lib/ExportResponse.php

diff --git a/appinfo/routes.php b/appinfo/routes.php
index 63e76b9..d001648 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -25,6 +25,7 @@ return [
     'resources' => [
       'show' => ['url' => '/api/shows'],
       'episode' => ['url' => '/api/episodes'],
+      'export' => ['url' => '/export'],
   	],
     'routes' => [
 
diff --git a/l10n/de.js b/l10n/de.js
index 57532f8..0ec5301 100644
--- a/l10n/de.js
+++ b/l10n/de.js
@@ -1,47 +1,45 @@
 OC.L10N.register(
     "podcast",
     {
-    "Radio stations" : "Radio Stationen",
-    "Radio" : "Radio",
-    "Radio listening app" : "Online Radiosender hören",
-    "Listening to your favorite radio stations in Nextcloud" : "Höre deine Lieblings-Radiosender direkt in Nextcloud",
-    "Error fetching favorite stations" : "Favorisierte Stationen konnten nicht geladen werden",
-    "No favorites added yet!" : "Noch keine favorisierte Stationen vorhanden",
-    "Failed to fetch favorite radio stations" : "Favorisierte Stationen konnten nicht geladen werden",
-    "No favorites yet" : "Noch keine Favoriten",
-    "No recent stations yet" : "Noch keine zuletzt gespielten Stationen",
-    "No search results" : "Keine Suchergebnisse",
-    "Stations you mark as favorite will show up here" : "Radio Stationen die favorisiert wurden werden hier angezeigt",
-    "Stations you recently played will show up here" : "Radio Stationen die vor kurzem gespielt wurden, werden hier angezeigt",
-    "No stations were found matching your search term" : "Keine Stationen unter diesen Suchbegriff gefunden",
-    "No stations here" : "Keine Stationen verfügbar",
-    "Could not remove station from favorites" : "Radio station konnte nicht von den Favoriten entfernt werden",
-    "Could not favor station" : "Station konnte nicht favorisiert werden",
-    "Lost connection to radio station, retrying ..." : "Verbindung zur Radio Station unterbrochen, versuche wiederzuverbinden ...",
-    "Unable to count play on remote API" : "Wiedergabe konnte nicht gezählt werden von der entfernten API",
-    "Could not add station to recent list" : "Radio Station konnte nicht in die zuletzt gespielt Liste aufgenommen werden",
-    "Countries" : "Länder",
-    "States" : "Staaten",
-    "Languages" : "Sprachen",
-    "Tags" : "Stichwörter",
-    "Could not fetch stations from remote API" : "Radio Stationen konnten nicht von der API geladen werden",
-    "Unable to load favorites" : "Favoriten konnten nicht geladen werden",
-    "Top" : "Beliebteste",
-    "Recent" : "Zuletzt gehört",
-    "New" : "Neu",
-    "Favorites" : "Favoriten",
-    "Categories" : "Kategorien",
-    "Search" : "Suche",
-    "Stream URL" : "Stream URL",
-    "Copy link to clipboard" : "Adresse in die Zwischenablage kopieren",
-    "Homepage" : "Webseite",
-    "Country & Language" : "Land und Sprache",
-    "Codec & Bitrate" : "Codec & Bitrate",
-    "Link copied to clipboard" : "Adresse in die Zwischenablage kopiert",
-    "Error while copying link to clipboard" : "Fehler beim Speichern in die Zwischenablage",
+    "Podcast episodes" : "Podcast Episoden",
+    "Podcast" : "Podcast",
+    "🔊 Browse, manage and listen to podcasts" : "🔊 Browse, manage and listen to podcasts",
+    "**🔊 Browse, listen and subscribe to podcasts**\n\nFull featured podcatcher which uses the community index fyyd.de as a source.\n\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- ⭐ Support episode chapters\n- 👂 Smoth audio playback and transitions" : "**🔊 Browse, listen and subscribe to podcasts**\n\nFull featured podcatcher which uses the community index fyyd.de as a source.\n\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- ⭐ Support episode chapters\n- 👂 Smoth audio playback and transitions",
+    "Hide" : "Verstecken",
+    "Show" : "Anzeigen",
+    "episodes" : "Episoden",
+    "Error fetching favorite episodes" : "Fehler beim Laden der favorisierten Episoden",
+    "No episodes added yet!" : "Keine Episoden bis jetzt hinzugefügt!",
+    "Failed to fetch favorite podcast episodes" : "Fehler beim Laden der Podcast Episoden",
+    "Show all" : "Zeige alle",
+    "by" : "von",
+    "Show more" : "Zeige mehr",
+    "Show less" : "Zeige weniger",
+    "Unsubscribe" : "Abbestellen",
+    "Subscribe" : "Abonnieren",
+    "Listening" : "Aktuell höhrend",
+    "Library" : "Bibliothek",
+    "Browse" : "Durchsuchen",
+    "Search" : "Suchen",
     "Name" : "Name",
-    "Add to favorites" : "Zu den Favoriten hinzufügen",
-    "Remove from favorites" : "Von den Favoriten entfernen",
-    "Details" : "Weitere Informationen"
+    "Duration" : "Länge",
+    "Date" : "Datum",
+    "Go to episode" : "Gehe zu Episode",
+    "Go to show" : "Gehe zu Sendung",
+    "Download" : "Herunterladen",
+    "Share" : "Teilen",
+    "Remove from queue" : "Von Wiedergabeliste entfernen",
+    "Resume" : "Fortsetzen",
+    "Pause" : "Pausieren",
+    "Play" : "Abspielen",
+    "Hot podcasts" : "Zurzeit beliebte Podcasts",
+    "New podcasts" : "Neue Podcasts",
+    "Podcasts in" : "Podcasts in",
+    "Publication date" : "Veröffentlicht",
+    "Episode chapters" : "Kapitel",
+    "Pause episode" : "Episode pausieren",
+    "Resume episode" : "Episode fortsetzen",
+    "Play episode" : "Episode abspielen",
+    "Currently listening" : "Zuletzt gehört"
 },
 "nplurals=2; plural=(n != 1);");
diff --git a/l10n/de.json b/l10n/de.json
index ab8a13d..36c8308 100644
--- a/l10n/de.json
+++ b/l10n/de.json
@@ -1,45 +1,43 @@
 { "translations": {
-    "Radio stations" : "Radio Stationen",
-    "Radio" : "Radio",
-    "Radio listening app" : "Online Radiosender hören",
-    "Listening to your favorite radio stations in Nextcloud" : "Höre deine Lieblings-Radiosender direkt in Nextcloud",
-    "Error fetching favorite stations" : "Favorisierte Stationen konnten nicht geladen werden",
-    "No favorites added yet!" : "Noch keine favorisierte Stationen vorhanden",
-    "Failed to fetch favorite radio stations" : "Favorisierte Stationen konnten nicht geladen werden",
-    "No favorites yet" : "Noch keine Favoriten",
-    "No recent stations yet" : "Noch keine zuletzt gespielten Stationen",
-    "No search results" : "Keine Suchergebnisse",
-    "Stations you mark as favorite will show up here" : "Radio Stationen die favorisiert wurden werden hier angezeigt",
-    "Stations you recently played will show up here" : "Radio Stationen die vor kurzem gespielt wurden, werden hier angezeigt",
-    "No stations were found matching your search term" : "Keine Stationen unter diesen Suchbegriff gefunden",
-    "No stations here" : "Keine Stationen verfügbar",
-    "Could not remove station from favorites" : "Radio station konnte nicht von den Favoriten entfernt werden",
-    "Could not favor station" : "Station konnte nicht favorisiert werden",
-    "Lost connection to radio station, retrying ..." : "Verbindung zur Radio Station unterbrochen, versuche wiederzuverbinden ...",
-    "Unable to count play on remote API" : "Wiedergabe konnte nicht gezählt werden von der entfernten API",
-    "Could not add station to recent list" : "Radio Station konnte nicht in die zuletzt gespielt Liste aufgenommen werden",
-    "Countries" : "Länder",
-    "States" : "Staaten",
-    "Languages" : "Sprachen",
-    "Tags" : "Stichwörter",
-    "Could not fetch stations from remote API" : "Radio Stationen konnten nicht von der API geladen werden",
-    "Unable to load favorites" : "Favoriten konnten nicht geladen werden",
-    "Top" : "Beliebteste",
-    "Recent" : "Zuletzt gehört",
-    "New" : "Neu",
-    "Favorites" : "Favoriten",
-    "Categories" : "Kategorien",
-    "Search" : "Suche",
-    "Stream URL" : "Stream URL",
-    "Copy link to clipboard" : "Adresse in die Zwischenablage kopieren",
-    "Homepage" : "Webseite",
-    "Country & Language" : "Land und Sprache",
-    "Codec & Bitrate" : "Codec & Bitrate",
-    "Link copied to clipboard" : "Adresse in die Zwischenablage kopiert",
-    "Error while copying link to clipboard" : "Fehler beim Speichern in die Zwischenablage",
+    "Podcast episodes" : "Podcast Episoden",
+    "Podcast" : "Podcast",
+    "🔊 Browse, manage and listen to podcasts" : "🔊 Browse, manage and listen to podcasts",
+    "**🔊 Browse, listen and subscribe to podcasts**\n\nFull featured podcatcher which uses the community index fyyd.de as a source.\n\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- ⭐ Support episode chapters\n- 👂 Smoth audio playback and transitions" : "**🔊 Browse, listen and subscribe to podcasts**\n\nFull featured podcatcher which uses the community index fyyd.de as a source.\n\n- 🔍 Browse and subscribe huge collection of podcasts\n- 🔊 Listen to episodes directly in Nextcloud\n- ⭐ Support episode chapters\n- 👂 Smoth audio playback and transitions",
+    "Hide" : "Verstecken",
+    "Show" : "Anzeigen",
+    "episodes" : "Episoden",
+    "Error fetching favorite episodes" : "Fehler beim Laden der favorisierten Episoden",
+    "No episodes added yet!" : "Keine Episoden bis jetzt hinzugefügt!",
+    "Failed to fetch favorite podcast episodes" : "Fehler beim Laden der Podcast Episoden",
+    "Show all" : "Zeige alle",
+    "by" : "von",
+    "Show more" : "Zeige mehr",
+    "Show less" : "Zeige weniger",
+    "Unsubscribe" : "Abbestellen",
+    "Subscribe" : "Abonnieren",
+    "Listening" : "Aktuell höhrend",
+    "Library" : "Bibliothek",
+    "Browse" : "Durchsuchen",
+    "Search" : "Suchen",
     "Name" : "Name",
-    "Add to favorites" : "Zu den Favoriten hinzufügen",
-    "Remove from favorites" : "Von den Favoriten entfernen",
-    "Details" : "Weitere Informationen"
+    "Duration" : "Länge",
+    "Date" : "Datum",
+    "Go to episode" : "Gehe zu Episode",
+    "Go to show" : "Gehe zu Sendung",
+    "Download" : "Herunterladen",
+    "Share" : "Teilen",
+    "Remove from queue" : "Von Wiedergabeliste entfernen",
+    "Resume" : "Fortsetzen",
+    "Pause" : "Pausieren",
+    "Play" : "Abspielen",
+    "Hot podcasts" : "Zurzeit beliebte Podcasts",
+    "New podcasts" : "Neue Podcasts",
+    "Podcasts in" : "Podcasts in",
+    "Publication date" : "Veröffentlicht",
+    "Episode chapters" : "Kapitel",
+    "Pause episode" : "Episode pausieren",
+    "Resume episode" : "Episode fortsetzen",
+    "Play episode" : "Episode abspielen",
+    "Currently listening" : "Zuletzt gehört"
 },"pluralForm" :"nplurals=2; plural=(n != 1);"
 }
\ No newline at end of file
diff --git a/lib/Controller/ExportController.php b/lib/Controller/ExportController.php
new file mode 100644
index 0000000..11712d9
--- /dev/null
+++ b/lib/Controller/ExportController.php
@@ -0,0 +1,79 @@
+<?php
+
+/**
+ * Podcast App
+ *
+ * @author Jonas Heinrich
+ * @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Podcast\Controller;
+
+use OC;
+use OCA\Podcast\ExportResponse;
+use OCA\Podcast\AppInfo\Application;
+use OCA\Podcast\Service\ShowService;
+use OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\IRequest;
+use SimpleXMLElement;
+use DOMDocument;
+
+class ExportController extends Controller {
+	/** @var ShowService */
+	private $service;
+
+	/** @var string */
+	private $userId;
+
+	use Errors;
+
+	public function __construct(IRequest $request,
+								ShowService $service,
+								$userId) {
+		parent::__construct(Application::APP_ID, $request);
+		$this->service = $service;
+		$this->userId = $userId;
+	}
+
+	/**
+	 * @NoAdminRequired
+	 */
+	public function index() {
+
+		$xml = new SimpleXMLElement('<?xml version="1.0"?><opml version="1.0"></opml>');
+		$xml->addAttribute('encoding', 'UTF-8');
+		$trackList = $xml->addChild('body');
+		foreach($this->service->findAll($this->userId) as $show) {
+			$track = $trackList->addChild('outline');
+			$track->addAttribute('type', 'rss');
+			$track->addAttribute('text', $show->getTitle());
+			$track->addAttribute('title', $show->getTitle());
+      $track->addAttribute('xmlUrl', 'FIXME');
+      $track->addAttribute('htmlUrl', 'FIXME');
+		}
+
+		$dom = new DOMDocument("1.0");
+		$dom->preserveWhiteSpace = false;
+		$dom->formatOutput = true;
+		$dom->loadXML($xml->asXML());
+
+		return new ExportResponse($dom->saveXML());
+
+	}
+
+}
diff --git a/lib/ExportResponse.php b/lib/ExportResponse.php
new file mode 100644
index 0000000..a6b2bd8
--- /dev/null
+++ b/lib/ExportResponse.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * Podcast App
+ *
+ * @author Jonas Heinrich
+ * @copyright 2021 Jonas Heinrich <onny@project-insanity.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Podcast;
+
+use OC;
+use OC\HintException;
+use OCP\AppFramework\Http\Response;
+
+class ExportResponse extends Response {
+	private $returnstring;
+
+	public function __construct($returnstring) {
+		parent::__construct();
+
+		$user = OC::$server->getUserSession()->getUser();
+		if (is_null($user)) {
+			throw new HintException('User not logged in');
+		}
+
+		$userName = $user->getDisplayName();
+		$productName = OC::$server->getThemingDefaults()->getName();
+		$dateTime = OC::$server->getDateTimeFormatter();
+
+		$export_name = '"' . $productName . ' Podcast Subscriptions (' . $userName . ') (' . $dateTime->formatDate(time()) . ').opml"';
+		$this->addHeader("Cache-Control", "private");
+		$this->addHeader("Content-Type", " application/xml");
+		$this->addHeader("Content-Length", strlen($returnstring));
+		$this->addHeader("Content-Disposition", "attachment; filename=" . $export_name);
+		$this->returnstring = $returnstring;
+	}
+
+	public function render() {
+		return $this->returnstring;
+	}
+}
diff --git a/src/components/ItemGrid.vue b/src/components/ItemGrid.vue
index f053a18..170eed4 100644
--- a/src/components/ItemGrid.vue
+++ b/src/components/ItemGrid.vue
@@ -23,6 +23,20 @@
 	<div class="podcastSection">
 		<div class="podcastSectionHeader">
 			<h1>{{ title }}</h1>
+			<Actions v-show="showMenu">
+				<ActionButton
+					icon="icon-add"
+					:close-after-click="true"
+					@click="showModal()">
+					Add podcast feed
+				</ActionButton>
+				<ActionButton
+					icon="icon-download"
+					:close-after-click="true"
+					@click="$emit('doExport')">
+					Export subscriptions
+				</ActionButton>
+			</Actions>
 		</div>
 		<div class="grid">
 			<div
@@ -49,8 +63,16 @@
 </template>
 
 <script>
+
+import Actions from '@nextcloud/vue/dist/Components/Actions'
+import ActionButton from '@nextcloud/vue/dist/Components/ActionButton'
+
 export default {
 	name: 'ItemGrid',
+	components: {
+		Actions,
+		ActionButton,
+	},
 	props: {
 		title: {
 			type: String,
@@ -60,6 +82,10 @@ export default {
 			type: Array,
 			default() { return [] },
 		},
+		showMenu: {
+			type: Boolean,
+			default: false,
+		},
 	},
 }
 </script>
diff --git a/src/views/Library.vue b/src/views/Library.vue
index 7059fe0..17d3f97 100644
--- a/src/views/Library.vue
+++ b/src/views/Library.vue
@@ -23,7 +23,9 @@
 	<div class="mainContent">
 		<ItemGrid
 			:title="t('podcast', 'Library')"
-			:podcasts="podcasts" />
+			:show-menu="true"
+			:podcasts="podcasts"
+			@doExport="doExport" />
 	</div>
 </template>
 
@@ -31,6 +33,7 @@
 import ItemGrid from '../components/ItemGrid'
 import { mapGetters } from 'vuex'
 import { setBrowserTitle } from '../utils/misc.js'
+import { getRequestToken } from '@nextcloud/auth'
 
 export default {
 	name: 'Library',
@@ -51,6 +54,15 @@ export default {
 	mounted() {
 		setBrowserTitle(t('podcast', 'Library'))
 	},
+	methods: {
+
+		doExport() {
+			window.location
+				= 'export?requesttoken='
+					+ encodeURIComponent(getRequestToken())
+		},
+
+	},
 }
 </script>
 
diff --git a/translationfiles/templates/podcast.pot b/translationfiles/templates/podcast.pot
index 0649c79..04da818 100644
--- a/translationfiles/templates/podcast.pot
+++ b/translationfiles/templates/podcast.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: Nextcloud 3.14159\n"
 "Report-Msgid-Bugs-To: translations\\@example.com\n"
-"POT-Creation-Date: 2021-03-14 13:20+0100\n"
+"POT-Creation-Date: 2021-03-14 13:43+0100\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
-- 
GitLab