diff --git a/CHANGELOG.md b/CHANGELOG.md index 1580caca..5aafdf97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### ✨ New features +- [#376](https://github.com/estruyf/vscode-front-matter/issues/376): Ability to run scripts after content was created - [#377](https://github.com/estruyf/vscode-front-matter/issues/377): Git sync actions added on panel and content dashboard (pull and push your changes to remote) ### 🎨 Enhancements diff --git a/package.json b/package.json index b4b077cd..f41be4fb 100644 --- a/package.json +++ b/package.json @@ -363,6 +363,10 @@ "items": { "type": "object", "properties": { + "id": { + "type": "string", + "description": "ID of the script." + }, "title": { "type": "string", "description": "Title you want to give to your script. Will be shown as the title of the button." @@ -419,6 +423,11 @@ ], "description": "The type of script you want to execute.", "default": "node" + }, + "hidden": { + "type": "boolean", + "description": "Hide the action from the UI", + "default": false } }, "additionalProperties": false, @@ -1123,6 +1132,11 @@ "type": "string", "default": "", "description": "An optional template that can be used for creating new content." + }, + "postScript": { + "type": "string", + "default": "", + "description": "An optional post script that can be used after new content creation." } }, "additionalProperties": false, diff --git a/src/dashboardWebView/components/ChoiceButton.tsx b/src/dashboardWebView/components/ChoiceButton.tsx index 323700e8..7d8d6bb2 100644 --- a/src/dashboardWebView/components/ChoiceButton.tsx +++ b/src/dashboardWebView/components/ChoiceButton.tsx @@ -12,11 +12,10 @@ export interface IChoiceButtonProps { onClick: () => void; }[]; disabled?: boolean; - isTemplatesEnabled?: boolean; onClick: () => void; } -export const ChoiceButton: React.FunctionComponent = ({onClick, disabled, choices, isTemplatesEnabled, title}: React.PropsWithChildren) => { +export const ChoiceButton: React.FunctionComponent = ({onClick, disabled, choices, title}: React.PropsWithChildren) => { return ( { - isTemplatesEnabled && ( + choices.length > 0 && ( = ({ }, [path]); const customScriptActions = React.useMemo(() => { - return (scripts || []).filter(script => (script.type === undefined || script.type === ScriptType.Content) && !script.bulk).map(script => ( + return (scripts || []).filter(script => (script.type === undefined || script.type === ScriptType.Content) && !script.bulk && !script.hidden).map(script => ( {script.title}} diff --git a/src/dashboardWebView/components/Header/Header.tsx b/src/dashboardWebView/components/Header/Header.tsx index 464aa5fe..d88b2c24 100644 --- a/src/dashboardWebView/components/Header/Header.tsx +++ b/src/dashboardWebView/components/Header/Header.tsx @@ -21,7 +21,7 @@ import { CustomScript } from '../../../models'; import { LightningBoltIcon, PlusIcon } from '@heroicons/react/outline'; import { useLocation, useNavigate } from 'react-router-dom'; import { routePaths } from '../..'; -import { useEffect } from 'react'; +import { useEffect, useMemo } from 'react'; import { SyncButton } from './SyncButton'; export interface IHeaderProps { @@ -73,6 +73,38 @@ export const Header: React.FunctionComponent = ({header, totalPage onClick: () => runBulkScript(s) })); + const choiceOptions = useMemo(() => { + const isEnabled = settings?.dashboardState?.contents?.templatesEnabled || false; + + if (isEnabled) { + return [ + { + title: ( +
+ + Create by content type +
+ ), + onClick: createByContentType, + disabled: !settings?.initialized + }, { + title: ( +
+ + Create by template +
+ ), + onClick: createByTemplate, + disabled: !settings?.initialized + }, + ...customActions + ]; + } + + return []; + + }, [settings?.dashboardState?.contents?.templatesEnabled]); + useEffect(() => { if (location.search) { const searchParams = new URLSearchParams(location.search); @@ -114,30 +146,8 @@ export const Header: React.FunctionComponent = ({header, totalPage - - Create by content type - - ), - onClick: createByContentType, - disabled: !settings?.initialized - }, { - title: ( -
- - Create by template -
- ), - onClick: createByTemplate, - disabled: !settings?.initialized - }, - ...customActions - ]} - onClick={createContent} - isTemplatesEnabled={settings?.dashboardState?.contents?.templatesEnabled || undefined} + choices={choiceOptions} + onClick={createContent} disabled={!settings?.initialized} /> diff --git a/src/dashboardWebView/components/Media/FolderCreation.tsx b/src/dashboardWebView/components/Media/FolderCreation.tsx index 2258ceb1..d6591620 100644 --- a/src/dashboardWebView/components/Media/FolderCreation.tsx +++ b/src/dashboardWebView/components/Media/FolderCreation.tsx @@ -23,7 +23,8 @@ export const FolderCreation: React.FunctionComponent = (pr Messenger.send(DashboardMessage.runCustomScript, {script, path: selectedFolder}); }; - const scripts = (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFolder); + const scripts = (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFolder && !script.hidden); + if (scripts.length > 0) { return (
diff --git a/src/dashboardWebView/components/Media/Item.tsx b/src/dashboardWebView/components/Media/Item.tsx index 7b5cbbe8..e674ae10 100644 --- a/src/dashboardWebView/components/Media/Item.tsx +++ b/src/dashboardWebView/components/Media/Item.tsx @@ -225,7 +225,7 @@ export const Item: React.FunctionComponent = ({media}: React.PropsWi }; const customScriptActions = () => { - return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile).map(script => ( + return (settings?.scripts || []).filter(script => script.type === ScriptType.MediaFile && !script.hidden).map(script => ( {script.title}
} @@ -259,7 +259,6 @@ export const Item: React.FunctionComponent = ({media}: React.PropsWi const extension = path.split('.').pop(); let icon = ; - console.log(media); if (isImageFile) { return ; diff --git a/src/helpers/ContentType.ts b/src/helpers/ContentType.ts index 5b1845db..f1b3cf1d 100644 --- a/src/helpers/ContentType.ts +++ b/src/helpers/ContentType.ts @@ -1,9 +1,9 @@ import { ModeListener } from './../listeners/general/ModeListener'; import { PagesListener } from './../listeners/dashboard'; -import { ArticleHelper, Settings } from "."; +import { ArticleHelper, CustomScript, Settings } from "."; import { FEATURE_FLAG, SETTING_CONTENT_DRAFT_FIELD, SETTING_DATE_FORMAT, SETTING_FRAMEWORK_ID, SETTING_TAXONOMY_CONTENT_TYPES, SETTING_TAXONOMY_FIELD_GROUPS, TelemetryEvent } from "../constants"; -import { ContentType as IContentType, DraftField, Field, FieldGroup, FieldType } from '../models'; -import { Uri, commands, window, ProgressLocation } from 'vscode'; +import { ContentType as IContentType, DraftField, Field, FieldGroup, FieldType, ScriptType } from '../models'; +import { Uri, commands, window, ProgressLocation, workspace } from 'vscode'; import { Folders } from "../commands/Folders"; import { Questions } from "./Questions"; import { existsSync, writeFileSync } from "fs"; @@ -543,6 +543,19 @@ export class ContentType { writeFileSync(newFilePath, content, { encoding: "utf8" }); + // Check if the content type has a post script to execute + if (contentType.postScript) { + const scripts = await CustomScript.getScripts(); + const script = scripts.find(s => s.id === contentType.postScript); + + if (script && (script.type === ScriptType.Content || !script?.type)) { + await CustomScript.run(script, newFilePath); + + const doc = await workspace.openTextDocument(Uri.file(newFilePath)); + await doc.save(); + } + } + await commands.executeCommand('vscode.open', Uri.file(newFilePath)); Notifications.info(`Your new content has been created.`); diff --git a/src/helpers/CustomScript.ts b/src/helpers/CustomScript.ts index ee4df28f..ee9f3e67 100644 --- a/src/helpers/CustomScript.ts +++ b/src/helpers/CustomScript.ts @@ -1,3 +1,4 @@ +import { Settings } from './SettingsHelper'; import { CommandType } from './../models/PanelSettings'; import { CustomScript as ICustomScript, ScriptType } from '../models/PanelSettings'; import { window, env as vscodeEnv, ProgressLocation } from 'vscode'; @@ -12,9 +13,24 @@ import { Dashboard } from '../commands/Dashboard'; import { DashboardCommand } from '../dashboardWebView/DashboardCommand'; import { ParsedFrontMatter } from '../parsers'; import { TelemetryEvent } from '../constants/TelemetryEvent'; +import { SETTING_CUSTOM_SCRIPTS } from '../constants'; export class CustomScript { + /** + * Retrieve all scripts + * @returns + */ + public static async getScripts(): Promise { + const scripts = Settings.get(SETTING_CUSTOM_SCRIPTS) || []; + return scripts; + } + + /** + * Run a script + * @param script + * @param path + */ public static async run(script: ICustomScript, path: string | null = null): Promise { const wsFolder = Folders.getWorkspaceFolder(); @@ -24,19 +40,19 @@ export class CustomScript { if (script.type === ScriptType.MediaFile || script.type === ScriptType.MediaFolder) { Telemetry.send(TelemetryEvent.runMediaScript); - CustomScript.runMediaScript(wsPath, path, script); + await CustomScript.runMediaScript(wsPath, path, script); } else { Telemetry.send(TelemetryEvent.runCustomScript); if (script.bulk) { // Run script on all files - CustomScript.bulkRun(wsPath, script); + await CustomScript.bulkRun(wsPath, script); } else if (path) { // Run script for provided path - CustomScript.singleRun(wsPath, script, path); + await CustomScript.singleRun(wsPath, script, path); } else { // Run script on current file. - CustomScript.singleRun(wsPath, script); + await CustomScript.singleRun(wsPath, script); } } } @@ -64,13 +80,13 @@ export class CustomScript { } if (articlePath && article) { - window.withProgress({ + return window.withProgress({ location: ProgressLocation.Notification, title: `Executing: ${script.title}`, cancellable: false }, async () => { const output = await CustomScript.runScript(wsPath, article, articlePath as string, script); - CustomScript.showOutput(output, script, articlePath); + await CustomScript.showOutput(output, script, articlePath); }); } else { Notifications.warning(`${script.title}: Article couldn't be retrieved.`); @@ -116,7 +132,7 @@ export class CustomScript { } } - CustomScript.showOutput(output.join(`\n`), script); + await CustomScript.showOutput(output.join(`\n`), script); }); } @@ -142,7 +158,7 @@ export class CustomScript { try { const output = await CustomScript.executeScript(script, wsPath, `"${wsPath}" "${path}"`); - CustomScript.showOutput(output, script); + await CustomScript.showOutput(output, script); Dashboard.postWebviewMessage({ command: DashboardCommand.mediaUpdate @@ -188,7 +204,7 @@ export class CustomScript { * @param output * @param script */ - private static showOutput(output: string | null, script: ICustomScript, articlePath?: string | null): void { + private static async showOutput(output: string | null, script: ICustomScript, articlePath?: string | null): Promise { if (output) { try { const data = JSON.parse(output); @@ -212,9 +228,9 @@ export class CustomScript { } if (articlePath) { - ArticleHelper.updateByPath(articlePath, article); + await ArticleHelper.updateByPath(articlePath, article); } else if (editor) { - ArticleHelper.update(editor, article); + await ArticleHelper.update(editor, article); } else { throw new Error(`Couldn't update article.`); } diff --git a/src/helpers/PanelSettings.ts b/src/helpers/PanelSettings.ts index 5f2ab9d4..cec43fe9 100644 --- a/src/helpers/PanelSettings.ts +++ b/src/helpers/PanelSettings.ts @@ -36,7 +36,7 @@ export class PanelSettings { categories: Settings.get(SETTING_TAXONOMY_CATEGORIES, true) || [], customTaxonomy: Settings.get(SETTING_TAXONOMY_CUSTOM, true) || [], freeform: Settings.get(SETTING_PANEL_FREEFORM), - scripts: (Settings.get(SETTING_CUSTOM_SCRIPTS) || []).filter(s => s.type === ScriptType.Content || !s.type), + scripts: (Settings.get(SETTING_CUSTOM_SCRIPTS) || []).filter(s => (s.type === ScriptType.Content || !s.type) && !s.hidden), isInitialized: await Template.isInitialized(), modifiedDateUpdate: Settings.get(SETTING_AUTO_UPDATE_DATE) || false, writingSettingsEnabled: this.isWritingSettingsEnabled() || false, diff --git a/src/models/PanelSettings.ts b/src/models/PanelSettings.ts index b9dfabb4..57379266 100644 --- a/src/models/PanelSettings.ts +++ b/src/models/PanelSettings.ts @@ -48,6 +48,7 @@ export interface ContentType { previewPath?: string | null; pageBundle?: boolean; template?: string; + postScript?: string; } export type FieldType = "string" | "number" | "datetime" | "boolean" | "image" | "choice" | "tags" | "categories" | "draft" | "taxonomy" | "fields" | "json" | "block" | "file" | "dataFile" | "list" | "slug"; @@ -112,6 +113,7 @@ export interface FileInfo extends FileStat { }; export interface CustomScript { + id?: string; title: string; script: string; nodeBin?: string; @@ -120,6 +122,7 @@ export interface CustomScript { outputType?: string; type?: ScriptType; command?: CommandType | string; + hidden?: boolean; } export interface PreviewSettings { @@ -135,7 +138,7 @@ export interface CustomTaxonomy { export enum ScriptType { Content = "content", MediaFolder = "mediaFolder", - MediaFile = "mediaFile" + MediaFile = "mediaFile", } export enum CommandType {