diff --git a/client/components/prompt/Confirm.vue b/client/components/prompt/Confirm.vue
index 032190cf..4e11ce45 100644
--- a/client/components/prompt/Confirm.vue
+++ b/client/components/prompt/Confirm.vue
@@ -7,6 +7,14 @@
+
{{ $strings.ButtonCancel }}
@@ -25,7 +33,8 @@ export default {
return {
el: null,
content: null,
- checkboxValue: false
+ checkboxValue: false,
+ formData: {}
}
},
watch: {
@@ -61,6 +70,9 @@ export default {
persistent() {
return !!this.confirmPromptOptions.persistent
},
+ formFields() {
+ return this.confirmPromptOptions.formFields || []
+ },
checkboxLabel() {
return this.confirmPromptOptions.checkboxLabel
},
@@ -100,11 +112,31 @@ export default {
this.show = false
},
confirm() {
- if (this.callback) this.callback(true, this.checkboxValue)
+ if (this.callback) {
+ if (this.formFields.length) {
+ const formFieldData = {
+ ...this.formData
+ }
+
+ this.callback(true, formFieldData)
+ } else {
+ this.callback(true, this.checkboxValue)
+ }
+ }
this.show = false
},
setShow() {
this.checkboxValue = this.checkboxDefaultValue
+
+ if (this.formFields.length) {
+ this.formFields.forEach((field) => {
+ let defaultValue = ''
+ if (field.type === 'boolean') defaultValue = false
+ if (field.type === 'select') defaultValue = field.options[0].value
+ this.$set(this.formData, field.name, defaultValue)
+ })
+ }
+
this.$eventBus.$emit('showing-prompt', true)
document.body.appendChild(this.el)
setTimeout(() => {
diff --git a/client/pages/config/plugins/_id.vue b/client/pages/config/plugins/_id.vue
index dda1094f..e376abb0 100644
--- a/client/pages/config/plugins/_id.vue
+++ b/client/pages/config/plugins/_id.vue
@@ -12,10 +12,14 @@
help_outline
+
+
+
+
Sourceopen_in_new
-
{{ configDescription }}
+
{{ configDescription }}
diff --git a/client/pages/item/_id/index.vue b/client/pages/item/_id/index.vue
index b4dc4e44..e5301cfe 100644
--- a/client/pages/item/_id/index.vue
+++ b/client/pages/item/_id/index.vue
@@ -786,13 +786,35 @@ export default {
}
},
onPluginAction(pluginId, pluginAction) {
+ const plugin = this.pluginExtensions.find((p) => p.id === pluginId)
+ const extension = plugin.extensions.find((ext) => ext.name === pluginAction)
+
+ if (extension.prompt) {
+ const payload = {
+ message: extension.prompt.message,
+ formFields: extension.prompt.formFields || [],
+ callback: (confirmed, promptData) => {
+ if (confirmed) {
+ this.sendPluginAction(pluginId, pluginAction, promptData)
+ }
+ },
+ type: 'yesNo'
+ }
+ this.$store.commit('globals/setConfirmPrompt', payload)
+ } else {
+ this.sendPluginAction(pluginId, pluginAction)
+ }
+ },
+ sendPluginAction(pluginId, pluginAction, promptData = null) {
this.$axios
.$post(`/api/plugins/${pluginId}/action`, {
pluginAction,
target: 'item.detail.actions',
data: {
entityId: this.libraryItemId,
- entityType: 'libraryItem'
+ entityType: 'libraryItem',
+ userId: this.$store.state.user.user.id,
+ promptData
}
})
.then((data) => {
diff --git a/server/Auth.js b/server/Auth.js
index 11fd0608..c11b3372 100644
--- a/server/Auth.js
+++ b/server/Auth.js
@@ -940,7 +940,17 @@ class Auth {
userDefaultLibraryId: user.getDefaultLibraryId(libraryIds),
serverSettings: Database.serverSettings.toJSONForBrowser(),
ereaderDevices: Database.emailSettings.getEReaderDevices(user),
- plugins: this.pluginManifests,
+ // TODO: Should be better handled by the PluginManager
+ // restrict plugin extensions that are not allowed for the user type
+ plugins: this.pluginManifests.map((manifest) => {
+ const manifestExtensions = (manifest.extensions || []).filter((ext) => {
+ if (ext.restrictToAccountTypes?.length) {
+ return ext.restrictToAccountTypes.includes(user.type)
+ }
+ return true
+ })
+ return { ...manifest, extensions: manifestExtensions }
+ }),
Source: global.Source
}
}
diff --git a/server/Server.js b/server/Server.js
index ebb65040..87f22fb2 100644
--- a/server/Server.js
+++ b/server/Server.js
@@ -28,7 +28,6 @@ const AbMergeManager = require('./managers/AbMergeManager')
const CacheManager = require('./managers/CacheManager')
const BackupManager = require('./managers/BackupManager')
const PlaybackSessionManager = require('./managers/PlaybackSessionManager')
-const PodcastManager = require('./managers/PodcastManager')
const AudioMetadataMangaer = require('./managers/AudioMetadataManager')
const RssFeedManager = require('./managers/RssFeedManager')
const CronManager = require('./managers/CronManager')
@@ -70,9 +69,8 @@ class Server {
this.backupManager = new BackupManager()
this.abMergeManager = new AbMergeManager()
this.playbackSessionManager = new PlaybackSessionManager()
- this.podcastManager = new PodcastManager()
this.audioMetadataManager = new AudioMetadataMangaer()
- this.cronManager = new CronManager(this.podcastManager, this.playbackSessionManager)
+ this.cronManager = new CronManager(this.playbackSessionManager)
this.apiCacheManager = new ApiCacheManager()
this.binaryManager = new BinaryManager()
diff --git a/server/controllers/LibraryController.js b/server/controllers/LibraryController.js
index f42a023d..6bc06fc8 100644
--- a/server/controllers/LibraryController.js
+++ b/server/controllers/LibraryController.js
@@ -19,6 +19,7 @@ const Scanner = require('../scanner/Scanner')
const Database = require('../Database')
const Watcher = require('../Watcher')
const RssFeedManager = require('../managers/RssFeedManager')
+const PodcastManager = require('../managers/PodcastManager')
const libraryFilters = require('../utils/queries/libraryFilters')
const libraryItemsPodcastFilters = require('../utils/queries/libraryItemsPodcastFilters')
@@ -219,7 +220,7 @@ class LibraryController {
* @param {Response} res
*/
async getEpisodeDownloadQueue(req, res) {
- const libraryDownloadQueueDetails = this.podcastManager.getDownloadQueueDetails(req.library.id)
+ const libraryDownloadQueueDetails = PodcastManager.getDownloadQueueDetails(req.library.id)
res.json(libraryDownloadQueueDetails)
}
@@ -1288,7 +1289,7 @@ class LibraryController {
}
})
- const opmlText = this.podcastManager.generateOPMLFileText(podcasts)
+ const opmlText = PodcastManager.generateOPMLFileText(podcasts)
res.type('application/xml')
res.send(opmlText)
}
diff --git a/server/controllers/LibraryItemController.js b/server/controllers/LibraryItemController.js
index 17c7be83..f929b6b5 100644
--- a/server/controllers/LibraryItemController.js
+++ b/server/controllers/LibraryItemController.js
@@ -18,6 +18,7 @@ const RssFeedManager = require('../managers/RssFeedManager')
const CacheManager = require('../managers/CacheManager')
const CoverManager = require('../managers/CoverManager')
const ShareManager = require('../managers/ShareManager')
+const PodcastManager = require('../managers/PodcastManager')
/**
* @typedef RequestUserObject
@@ -59,10 +60,10 @@ class LibraryItemController {
}
if (item.mediaType === 'podcast' && includeEntities.includes('downloads')) {
- const downloadsInQueue = this.podcastManager.getEpisodeDownloadsInQueue(req.libraryItem.id)
+ const downloadsInQueue = PodcastManager.getEpisodeDownloadsInQueue(req.libraryItem.id)
item.episodeDownloadsQueued = downloadsInQueue.map((d) => d.toJSONForClient())
- if (this.podcastManager.currentDownload?.libraryItemId === req.libraryItem.id) {
- item.episodesDownloading = [this.podcastManager.currentDownload.toJSONForClient()]
+ if (PodcastManager.currentDownload?.libraryItemId === req.libraryItem.id) {
+ item.episodesDownloading = [PodcastManager.currentDownload.toJSONForClient()]
}
}
diff --git a/server/controllers/PodcastController.js b/server/controllers/PodcastController.js
index 3610c2ea..742bf0e0 100644
--- a/server/controllers/PodcastController.js
+++ b/server/controllers/PodcastController.js
@@ -11,6 +11,7 @@ const { validateUrl } = require('../utils/index')
const Scanner = require('../scanner/Scanner')
const CoverManager = require('../managers/CoverManager')
+const PodcastManager = require('../managers/PodcastManager')
const LibraryItem = require('../objects/LibraryItem')
@@ -114,7 +115,7 @@ class PodcastController {
if (payload.episodesToDownload?.length) {
Logger.info(`[PodcastController] Podcast created now starting ${payload.episodesToDownload.length} episode downloads`)
- this.podcastManager.downloadPodcastEpisodes(libraryItem, payload.episodesToDownload)
+ PodcastManager.downloadPodcastEpisodes(libraryItem, payload.episodesToDownload)
}
// Turn on podcast auto download cron if not already on
@@ -169,7 +170,7 @@ class PodcastController {
}
res.json({
- feeds: this.podcastManager.getParsedOPMLFileFeeds(req.body.opmlText)
+ feeds: PodcastManager.getParsedOPMLFileFeeds(req.body.opmlText)
})
}
@@ -203,7 +204,7 @@ class PodcastController {
return res.status(404).send('Folder not found')
}
const autoDownloadEpisodes = !!req.body.autoDownloadEpisodes
- this.podcastManager.createPodcastsFromFeedUrls(rssFeeds, folder, autoDownloadEpisodes, this.cronManager)
+ PodcastManager.createPodcastsFromFeedUrls(rssFeeds, folder, autoDownloadEpisodes, this.cronManager)
res.sendStatus(200)
}
@@ -230,7 +231,7 @@ class PodcastController {
const maxEpisodesToDownload = !isNaN(req.query.limit) ? Number(req.query.limit) : 3
- var newEpisodes = await this.podcastManager.checkAndDownloadNewEpisodes(libraryItem, maxEpisodesToDownload)
+ var newEpisodes = await PodcastManager.checkAndDownloadNewEpisodes(libraryItem, maxEpisodesToDownload)
res.json({
episodes: newEpisodes || []
})
@@ -239,8 +240,6 @@ class PodcastController {
/**
* GET: /api/podcasts/:id/clear-queue
*
- * @this {import('../routers/ApiRouter')}
- *
* @param {RequestWithUser} req
* @param {Response} res
*/
@@ -249,22 +248,20 @@ class PodcastController {
Logger.error(`[PodcastController] Non-admin user "${req.user.username}" attempting to clear download queue`)
return res.sendStatus(403)
}
- this.podcastManager.clearDownloadQueue(req.params.id)
+ PodcastManager.clearDownloadQueue(req.params.id)
res.sendStatus(200)
}
/**
* GET: /api/podcasts/:id/downloads
*
- * @this {import('../routers/ApiRouter')}
- *
* @param {RequestWithUser} req
* @param {Response} res
*/
getEpisodeDownloads(req, res) {
var libraryItem = req.libraryItem
- var downloadsInQueue = this.podcastManager.getEpisodeDownloadsInQueue(libraryItem.id)
+ var downloadsInQueue = PodcastManager.getEpisodeDownloadsInQueue(libraryItem.id)
res.json({
downloads: downloadsInQueue.map((d) => d.toJSONForClient())
})
@@ -290,8 +287,6 @@ class PodcastController {
/**
* POST: /api/podcasts/:id/download-episodes
*
- * @this {import('../routers/ApiRouter')}
- *
* @param {RequestWithUser} req
* @param {Response} res
*/
@@ -306,7 +301,7 @@ class PodcastController {
return res.sendStatus(400)
}
- this.podcastManager.downloadPodcastEpisodes(libraryItem, episodes)
+ PodcastManager.downloadPodcastEpisodes(libraryItem, episodes)
res.sendStatus(200)
}
diff --git a/server/managers/CronManager.js b/server/managers/CronManager.js
index 7a8c9bd0..7771b0d9 100644
--- a/server/managers/CronManager.js
+++ b/server/managers/CronManager.js
@@ -5,11 +5,10 @@ const Database = require('../Database')
const LibraryScanner = require('../scanner/LibraryScanner')
const ShareManager = require('./ShareManager')
+const PodcastManager = require('./PodcastManager')
class CronManager {
- constructor(podcastManager, playbackSessionManager) {
- /** @type {import('./PodcastManager')} */
- this.podcastManager = podcastManager
+ constructor(playbackSessionManager) {
/** @type {import('./PlaybackSessionManager')} */
this.playbackSessionManager = playbackSessionManager
@@ -163,7 +162,7 @@ class CronManager {
task
})
} catch (error) {
- Logger.error(`[PodcastManager] Failed to schedule podcast cron ${this.serverSettings.podcastEpisodeSchedule}`, error)
+ Logger.error(`[CronManager] Failed to schedule podcast cron ${this.serverSettings.podcastEpisodeSchedule}`, error)
}
}
@@ -192,7 +191,7 @@ class CronManager {
// Run episode checks
for (const libraryItem of libraryItems) {
- const keepAutoDownloading = await this.podcastManager.runEpisodeCheck(libraryItem)
+ const keepAutoDownloading = await PodcastManager.runEpisodeCheck(libraryItem)
if (!keepAutoDownloading) {
// auto download was disabled
podcastCron.libraryItemIds = podcastCron.libraryItemIds.filter((lid) => lid !== libraryItem.id) // Filter it out
diff --git a/server/managers/PluginManager.js b/server/managers/PluginManager.js
index d85f4f5d..356b1cd8 100644
--- a/server/managers/PluginManager.js
+++ b/server/managers/PluginManager.js
@@ -3,6 +3,9 @@ const Logger = require('../Logger')
const Database = require('../Database')
const SocketAuthority = require('../SocketAuthority')
const TaskManager = require('../managers/TaskManager')
+const ShareManager = require('../managers/ShareManager')
+const RssFeedManager = require('../managers/RssFeedManager')
+const PodcastManager = require('../managers/PodcastManager')
const fsExtra = require('../libs/fsExtra')
const { isUUID, parseSemverStrict } = require('../utils')
@@ -13,6 +16,9 @@ const { isUUID, parseSemverStrict } = require('../utils')
* @property {import('../SocketAuthority')} SocketAuthority
* @property {import('../managers/TaskManager')} TaskManager
* @property {import('../models/Plugin')} pluginInstance
+ * @property {import('../managers/ShareManager')} ShareManager
+ * @property {import('../managers/RssFeedManager')} RssFeedManager
+ * @property {import('../managers/PodcastManager')} PodcastManager
*/
/**
@@ -50,7 +56,10 @@ class PluginManager {
Database,
SocketAuthority,
TaskManager,
- pluginInstance
+ pluginInstance,
+ ShareManager,
+ RssFeedManager,
+ PodcastManager
}
}
diff --git a/server/managers/PodcastManager.js b/server/managers/PodcastManager.js
index 456927c8..1f451182 100644
--- a/server/managers/PodcastManager.js
+++ b/server/managers/PodcastManager.js
@@ -586,4 +586,4 @@ class PodcastManager {
Logger.info(`[PodcastManager] createPodcastsFromFeedUrls: Finished OPML import. Created ${numPodcastsAdded} podcasts out of ${rssFeedUrls.length} RSS feed URLs`)
}
}
-module.exports = PodcastManager
+module.exports = new PodcastManager()
diff --git a/server/routers/ApiRouter.js b/server/routers/ApiRouter.js
index f7e31e5c..d84f9ae7 100644
--- a/server/routers/ApiRouter.js
+++ b/server/routers/ApiRouter.js
@@ -47,8 +47,6 @@ class ApiRouter {
this.abMergeManager = Server.abMergeManager
/** @type {import('../managers/BackupManager')} */
this.backupManager = Server.backupManager
- /** @type {import('../managers/PodcastManager')} */
- this.podcastManager = Server.podcastManager
/** @type {import('../managers/AudioMetadataManager')} */
this.audioMetadataManager = Server.audioMetadataManager
/** @type {import('../managers/CronManager')} */
diff --git a/test/server/managers/plugins/Example/manifest.json b/test/server/managers/plugins/Example/manifest.json
index 319299ed..11d261cd 100644
--- a/test/server/managers/plugins/Example/manifest.json
+++ b/test/server/managers/plugins/Example/manifest.json
@@ -6,6 +6,7 @@
"repositoryUrl": "https://github.com/example/example-plugin",
"documentationUrl": "https://example.com",
"description": "This is an example plugin",
+ "descriptionKey": "ExamplePluginDescription",
"extensions": [
{
"target": "item.detail.actions",
@@ -22,8 +23,7 @@
"name": "requestAddress",
"label": "Request Address",
"labelKey": "LabelRequestAddress",
- "type": "text",
- "required": true
+ "type": "text"
},
{
"name": "enable",
@@ -34,7 +34,8 @@
]
},
"localization": {
- "en-us": {
+ "de": {
+ "ExamplePluginDescription": "Dies ist ein Beispiel-Plugin",
"ItemExampleAction": "Item Example Action",
"LabelEnable": "Enable",
"ExamplePluginConfigurationDescription": "This is a description on how to configure the plugin",
@@ -46,7 +47,7 @@
"version": "1.0.0",
"changelog": "Initial release",
"timestamp": "2022-01-01T00:00:00Z",
- "sourceUrl": ""
+ "downloadUrl": ""
}
]
}