version 1 done

This commit is contained in:
Jay Nguyen
2022-04-10 16:45:09 +01:00
parent 3d2042ab98
commit 1648517c08
9 changed files with 204 additions and 158 deletions

View File

@@ -3,7 +3,7 @@ import process from "process";
import builtins from 'builtin-modules'
const banner =
`/*
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
@@ -15,7 +15,7 @@ esbuild.build({
banner: {
js: banner,
},
entryPoints: ['main.ts'],
entryPoints: ['src/main.ts'],
bundle: true,
external: [
'obsidian',

137
main.ts
View File

@@ -1,137 +0,0 @@
import { App, Editor, MarkdownView, Modal, Notice, Plugin, PluginSettingTab, Setting } from 'obsidian';
// Remember to rename these classes and interfaces!
interface MyPluginSettings {
mySetting: string;
}
const DEFAULT_SETTINGS: MyPluginSettings = {
mySetting: 'default'
}
export default class MyPlugin extends Plugin {
settings: MyPluginSettings;
async onload() {
await this.loadSettings();
// This creates an icon in the left ribbon.
const ribbonIconEl = this.addRibbonIcon('dice', 'Sample Plugin', (evt: MouseEvent) => {
// Called when the user clicks the icon.
new Notice('This is a notice!');
});
// Perform additional things with the ribbon
ribbonIconEl.addClass('my-plugin-ribbon-class');
// This adds a status bar item to the bottom of the app. Does not work on mobile apps.
const statusBarItemEl = this.addStatusBarItem();
statusBarItemEl.setText('Status Bar Text');
// This adds a simple command that can be triggered anywhere
this.addCommand({
id: 'open-sample-modal-simple',
name: 'Open sample modal (simple)',
callback: () => {
new SampleModal(this.app).open();
}
});
// This adds an editor command that can perform some operation on the current editor instance
this.addCommand({
id: 'sample-editor-command',
name: 'Sample editor command',
editorCallback: (editor: Editor, view: MarkdownView) => {
console.log(editor.getSelection());
editor.replaceSelection('Sample Editor Command');
}
});
// This adds a complex command that can check whether the current state of the app allows execution of the command
this.addCommand({
id: 'open-sample-modal-complex',
name: 'Open sample modal (complex)',
checkCallback: (checking: boolean) => {
// Conditions to check
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
if (markdownView) {
// If checking is true, we're simply "checking" if the command can be run.
// If checking is false, then we want to actually perform the operation.
if (!checking) {
new SampleModal(this.app).open();
}
// This command will only show up in Command Palette when the check function returns true
return true;
}
}
});
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SampleSettingTab(this.app, this));
// If the plugin hooks up any global DOM events (on parts of the app that doesn't belong to this plugin)
// Using this function will automatically remove the event listener when this plugin is disabled.
this.registerDomEvent(document, 'click', (evt: MouseEvent) => {
console.log('click', evt);
});
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
}
onunload() {
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
}
class SampleModal extends Modal {
constructor(app: App) {
super(app);
}
onOpen() {
const {contentEl} = this;
contentEl.setText('Woah!');
}
onClose() {
const {contentEl} = this;
contentEl.empty();
}
}
class SampleSettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const {containerEl} = this;
containerEl.empty();
containerEl.createEl('h2', {text: 'Settings for my awesome plugin.'});
new Setting(containerEl)
.setName('Setting #1')
.setDesc('It\'s a secret')
.addText(text => text
.setPlaceholder('Enter your secret')
.setValue(this.plugin.settings.mySetting)
.onChange(async (value) => {
console.log('Secret: ' + value);
this.plugin.settings.mySetting = value;
await this.plugin.saveSettings();
}));
}
}

View File

@@ -1,10 +1,10 @@
{
"id": "obsidian-sample-plugin",
"name": "Sample Plugin",
"version": "1.0.1",
"id": "obsidian-ghost-publish",
"name": "Obsidian Ghost Publish",
"version": "1.0.0",
"minAppVersion": "0.12.0",
"description": "This is a sample plugin for Obsidian. This plugin demonstrates some of the capabilities of the Obsidian API.",
"author": "Obsidian",
"authorUrl": "https://obsidian.md",
"description": "Single click to publish to Ghost",
"author": "@jaynguyens <jay@nguyens.co>",
"authorUrl": "https://nguyens.co",
"isDesktopOnly": false
}
}

View File

@@ -1,24 +1,32 @@
{
"name": "obsidian-sample-plugin",
"version": "1.0.1",
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
"main": "main.js",
"name": "obsidian-ghost-publish",
"version": "1.0.0",
"description": "Obsidian plugin for easy publish to ghost with a single click.",
"main": "src/main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
},
"keywords": [],
"author": "",
"author": {
"name": "Jay Nguyen",
"email": "jay@nguyens.co",
"url": "https://github.com/jaynguyens/obsidian-ghost-publish"
},
"license": "MIT",
"devDependencies": {
"@types/jsonwebtoken": "^8.5.8",
"@types/markdown-it": "^12.2.3",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "^5.2.0",
"@typescript-eslint/parser": "^5.2.0",
"builtin-modules": "^3.2.0",
"esbuild": "0.13.12",
"obsidian": "latest",
"obsidian": "^0.14.4",
"tslib": "2.3.1",
"typescript": "4.4.4"
"typescript": "4.4.4",
"jsonwebtoken": "^8.5.1",
"markdown-it": "^12.3.2"
}
}
}

45
src/main.ts Normal file
View File

@@ -0,0 +1,45 @@
import { MarkdownView, Plugin } from 'obsidian';
import { DEFAULT_SETTINGS, SettingsProp } from './types/index';
import { SettingTab } from './settingTab'
import { publishPost } from './methods/publishPost';
export default class MyPlugin extends Plugin {
settings: SettingsProp;
async onload() {
// load the settings first
await this.loadSettings();
// 2 ways to publish post:
console.log('this.settings', this.settings)
// 1. Click on the ghost icon on the left
this.addRibbonIcon('ghost', 'Publish Ghost', () => {
const view = this.app.workspace.getActiveViewOfType(MarkdownView);
publishPost(view, this.settings)
});
// 2. Run the by command + P
this.addCommand({
id: 'publish',
name: 'Publish current document',
editorCallback: (_, view: MarkdownView) => {
publishPost(view, this.settings)
}
});
// This adds a settings tab so the user can configure various aspects of the plugin
this.addSettingTab(new SettingTab(this.app, this));
// When registering intervals, this function will automatically clear the interval when the plugin is disabled.
this.registerInterval(window.setInterval(() => console.log('setInterval'), 5 * 60 * 1000));
}
onunload() { }
async loadSettings() { this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) }
async saveSettings() { await this.saveData(this.settings) }
}

View File

@@ -0,0 +1,71 @@
import { Notice, request } from "obsidian";
import { sign } from 'jsonwebtoken';
const MarkdownIt = require("markdown-it")
const md = new MarkdownIt()
export const publishPost = async (view: any, settings: any) => {
const content = {
title: view.file.basename,
data: view.data
}
const version = 'v4'
console.log('content', content)
console.log('settings', settings)
// Admin API key goes here
const key = settings.adminToken;
// Split the key into ID and SECRET
const [id, secret] = key.split(':');
// Create the token (including decoding secret)
const token = sign({}, Buffer.from(secret, 'hex'), {
keyid: id,
algorithm: 'HS256',
expiresIn: '5m',
audience: `/${version}/admin/`
});
const contentPost = (content: any, settings: any) => ({
"posts": [{
"title": content.title,
"html": md.render(content.data),
"status": settings.publishStatus
}]
})
const body = contentPost(content, settings);
const result = await request({
url: `${settings.url}/ghost/api/${version}/admin/posts/?source=html`,
method: "POST",
contentType: "application/json",
headers: {
'Access-Control-Allow-Origin': 'app://obsidian.md',
'Access-Control-Allow-Methods': 'POST',
'Content-Type': 'application/json;charset=utf-8',
'Authorization': `Ghost ${token}`
},
body: JSON.stringify(body)
})
const json = JSON.parse(result)
if (json?.errors) {
new Notice(`${json.errors[0].type}! ${json.errors[0].message}`)
}
if (json?.posts) {
new Notice(`${json.posts[0].settings.title} has been ${json.posts[0].settings.status} successful!`)
}
return result
}

51
src/settingTab/index.ts Normal file
View File

@@ -0,0 +1,51 @@
import { App, PluginSettingTab, Setting } from 'obsidian';
import MyPlugin from 'src/main';
export class SettingTab extends PluginSettingTab {
plugin: MyPlugin;
constructor(app: App, plugin: MyPlugin) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl('h2', { text: 'Obsidian Ghost Integration' });
new Setting(containerEl)
.setName('API URL')
.addText(text => text
.setPlaceholder('nguyens.co')
.setValue(this.plugin.settings.url)
.onChange(async (value) => {
console.log('Blog URL: ' + value);
this.plugin.settings.url = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Admin API Key')
.addText(text => text
.setPlaceholder('6251555c94ca6')
.setValue(this.plugin.settings.adminToken)
.onChange(async (value) => {
console.log('admin api key: ' + value);
this.plugin.settings.adminToken = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Post publish status')
.addDropdown(dropdown => dropdown
.addOption('draft', 'Draft')
.addOption('published', 'Publish')
.setValue(this.plugin.settings.publishStatus)
.onChange(async (value) => {
this.plugin.settings.publishStatus = value;
await this.plugin.saveSettings();
}))
}
}

12
src/types/index.ts Normal file
View File

@@ -0,0 +1,12 @@
export interface SettingsProp {
url: string,
adminToken: string,
publishStatus: string
}
export const DEFAULT_SETTINGS: SettingsProp = {
url: '',
adminToken: '',
publishStatus: 'draft'
}

View File

@@ -1,4 +0,0 @@
/* Sets all the text color to red! */
body {
color: red;
}