1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +02:00
This commit is contained in:
Laurent Cozic 2023-07-31 19:00:21 +01:00
parent 6210d85192
commit 6001e07cbc
24 changed files with 16016 additions and 64 deletions

View File

@ -76,6 +76,7 @@ plugin_types/
readme/
packages/react-native-vosk/lib/
packages/lib/countable/Countable.js
packages/app-mobile/plugins
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
packages/app-cli/app/LinkSelector.js
@ -457,6 +458,7 @@ packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/markupLanguageUtils.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/types.js

1
.gitignore vendored
View File

@ -442,6 +442,7 @@ packages/app-mobile/utils/checkPermissions.js
packages/app-mobile/utils/createRootStyle.js
packages/app-mobile/utils/debounce.js
packages/app-mobile/utils/fs-driver-rn.js
packages/app-mobile/utils/markupLanguageUtils.js
packages/app-mobile/utils/setupNotifications.js
packages/app-mobile/utils/shareHandler.js
packages/app-mobile/utils/types.js

View File

@ -2,7 +2,7 @@ import { useEffect, useState, useMemo, useRef } from 'react';
import shim from '@joplin/lib/shim';
import Setting from '@joplin/lib/models/Setting';
const { themeStyle } = require('../../global-style.js');
import markupLanguageUtils from '@joplin/lib/markupLanguageUtils';
import markupLanguageUtils from '../../../utils/markupLanguageUtils';
import Logger from '@joplin/utils/Logger';
const { assetsToHeaders } = require('@joplin/renderer');

View File

@ -13,17 +13,17 @@
const path = require('path');
const localPackages = {
'@joplin/fork-htmlparser2': path.resolve(__dirname, '../fork-htmlparser2/'),
'@joplin/fork-sax': path.resolve(__dirname, '../fork-sax/'),
'@joplin/fork-uslug': path.resolve(__dirname, '../fork-uslug/'),
'@joplin/lib': path.resolve(__dirname, '../lib/'),
'@joplin/react-native-alarm-notification': path.resolve(__dirname, '../react-native-alarm-notification/'),
'@joplin/react-native-saf-x': path.resolve(__dirname, '../react-native-saf-x/'),
'@joplin/renderer': path.resolve(__dirname, '../renderer/'),
'@joplin/tools': path.resolve(__dirname, '../tools/'),
'@joplin/utils': path.resolve(__dirname, '../utils/'),
'@joplin/fork-htmlparser2': path.resolve(__dirname, '../fork-htmlparser2/'),
'@joplin/fork-uslug': path.resolve(__dirname, '../fork-uslug/'),
'@joplin/react-native-saf-x': path.resolve(__dirname, '../react-native-saf-x/'),
'@joplin/react-native-alarm-notification': path.resolve(__dirname, '../react-native-alarm-notification/'),
'@joplin/fork-sax': path.resolve(__dirname, '../fork-sax/'),
'@joplin/turndown': path.resolve(__dirname, '../turndown/'),
'@joplin/turndown-plugin-gfm': path.resolve(__dirname, '../turndown-plugin-gfm/'),
'@joplin/turndown': path.resolve(__dirname, '../turndown/'),
'@joplin/utils': path.resolve(__dirname, '../utils/'),
};
const remappedPackages = {

View File

@ -0,0 +1,7 @@
const initPlugin = joplin => {
!function(t){var e={};function o(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=0)}([function(t,e,o){"use strict";var n=this&&this.__awaiter||function(t,e,o,n){return new(o||(o=Promise))((function(r,i){function u(t){try{c(n.next(t))}catch(t){i(t)}}function l(t){try{c(n.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?r(t.value):(e=t.value,e instanceof o?e:new o((function(t){t(e)}))).then(u,l)}c((n=n.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0});const r=o(1),i=o(2);r.default.plugins.register({onStart:function(){return n(this,void 0,void 0,(function*(){yield r.default.contentScripts.register(i.ContentScriptType.MarkdownItPlugin,"todoTxtMd","./todoTxtMdRule.js"),yield r.default.contentScripts.register(i.ContentScriptType.CodeMirrorPlugin,"todoTxtMdCtrl","./todoTxtMdCtrl.js"),yield r.default.contentScripts.onMessage("todoTxtMd",t=>{r.default.commands.execute("editor.execCommand",{name:"todoTxtAction",args:[t]}),r.default.commands.execute("editor.focus")})}))}})},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=joplin},function(t,e,o){"use strict";var n;Object.defineProperty(e,"__esModule",{value:!0}),e.ContentScriptType=e.SettingStorage=e.AppType=e.SettingItemType=e.ToolbarButtonLocation=e.isContextMenuItemLocation=e.MenuItemLocation=e.ImportModuleOutputFormat=e.FileSystemItem=void 0,function(t){t.File="file",t.Directory="directory"}(e.FileSystemItem||(e.FileSystemItem={})),function(t){t.Markdown="md",t.Html="html"}(e.ImportModuleOutputFormat||(e.ImportModuleOutputFormat={})),function(t){t.File="file",t.Edit="edit",t.View="view",t.Note="note",t.Tools="tools",t.Help="help",t.Context="context",t.NoteListContextMenu="noteListContextMenu",t.EditorContextMenu="editorContextMenu",t.FolderContextMenu="folderContextMenu",t.TagContextMenu="tagContextMenu"}(n=e.MenuItemLocation||(e.MenuItemLocation={})),e.isContextMenuItemLocation=function(t){return[n.Context,n.NoteListContextMenu,n.EditorContextMenu,n.FolderContextMenu,n.TagContextMenu].includes(t)},function(t){t.NoteToolbar="noteToolbar",t.EditorToolbar="editorToolbar"}(e.ToolbarButtonLocation||(e.ToolbarButtonLocation={})),function(t){t[t.Int=1]="Int",t[t.String=2]="String",t[t.Bool=3]="Bool",t[t.Array=4]="Array",t[t.Object=5]="Object",t[t.Button=6]="Button"}(e.SettingItemType||(e.SettingItemType={})),function(t){t.Desktop="desktop",t.Mobile="mobile",t.Cli="cli"}(e.AppType||(e.AppType={})),function(t){t[t.Database=1]="Database",t[t.File=2]="File"}(e.SettingStorage||(e.SettingStorage={})),function(t){t.MarkdownItPlugin="markdownItPlugin",t.CodeMirrorPlugin="codeMirrorPlugin"}(e.ContentScriptType||(e.ContentScriptType={}))}]);
}
module.exports = { initPlugin };

View File

@ -0,0 +1,12 @@
{
"manifest_version": 1,
"id": "com.hieuthi.joplin.metis",
"app_min_version": "2.2",
"version": "0.1.5",
"name": "Metis",
"description": "A Simple Task Manager Plugin for Joplin based on Todo.txt Specification",
"author": "Hieu-Thi Luong",
"homepage_url": "https://github.com/hieuthi/joplin-plugin-metis",
"repository_url": "https://github.com/hieuthi/joplin-plugin-metis",
"keywords": ["joplin-plugin","todo","todotxt","task management"]
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,163 @@
ul.todotxt {
--color-default: #999;
--color-faded: #999;
--color-A: rgb(247, 210, 110); /* yellow* */
--color-B: rgb(152, 104, 1); /* red */
--color-C: rgb(80, 161, 79); /* green */
--color-D: rgb(21, 91, 218); /* blue */
--project-background-color: rgb(247, 210, 110);
--project-color: black;
--context-color: rgb(152, 104, 1);
--transition-duration: 0.15s;
}
ul.todotxt { list-style:none; }
ul.todotxt li {
list-style-type:none;
border-bottom: dashed 1px;
margin-bottom: 4px;
line-height: 1.2em;
}
ul.todotxt li.todotxt-header {
font-size: 10px;
}
.todo-priority {
display: inline-block;
width: 1.2em;
overflow: hidden;
color: var(--color-default);
vertical-align: text-bottom;
}
.todo-priority select {
appearance: none;
background-color: transparent;
border-color: transparent;
color: inherit;
font-size: inherit;
font-weight: 700;
cursor: pointer;
}
.todo-priority select:focus-visible {
outline: none;
}
.todo-priority select:hover {
text-decoration: underline;
}
.todo-panel {
display: block;
font-size: 12px;
line-height: 1em;
text-align: right;
margin: 2px 0 4px 0;
color: var(--color-faded);
}
.todo-completion, .todo-creation {
display: inline-block;
min-width: 6em;
}
.todo-project {
background-color: var(--project-background-color);
color: var(--project-color);
border-radius: 2px;
}
.todo-context {
color: var(--context-color);
}
.todo-count {
color: var(--color-faded);
}
.todo-count::before{
content: '/';
}
.todo-meta {
color: var(--color-faded);
}
.todo-select {
display: inline-block;
min-width: 3em;
text-decoration: none;
}
.todo-checkbox {
margin-left: -1.9em;
margin-right: 0.5em;
display: inline-block;
vertical-align: text-top;
position: relative;
cursor: pointer;
user-select: none;
}
.todo-checkbox input {
position: absolute;
display: none;
}
.todo-checkbox .todo-checkmark {
width : 1.2em;
height: 1.2em;
position: relative;
display : inline-block;
overflow: hidden;
text-align: center;
color: white;
border: solid 1px var(--color-default);
border-radius: 100%;
transition: background-color var(--transition-duration) ease-in;
}
.todo-checkbox input:checked ~ .todo-checkmark {
background-color: var(--color-default);
}
.todo-checkbox .todo-checkmark:after {
content: '×';
position: relative;
display: inline-block;
transition: opacity var(--transition-duration);
opacity: 0;
}
.todo-checkbox input:checked ~ .todo-checkmark:after {
opacity: 1.0;
}
/* Color scheme */
.prior-A .todo-priority {
color: var(--color-A);
}
.prior-A .todo-checkbox .todo-checkmark {
border-color: var(--color-A);
color: black;
}
.prior-A .todo-checkbox input:checked ~ .todo-checkmark {
background-color: var(--color-A);
}
.prior-B .todo-priority {
color: var(--color-B);
}
.prior-B .todo-checkbox .todo-checkmark {
border-color: var(--color-B);
}
.prior-B .todo-checkbox input:checked ~ .todo-checkmark {
background-color: var(--color-B);
}
.prior-C .todo-priority {
color: var(--color-C);
}
.prior-C .todo-checkbox .todo-checkmark {
border-color: var(--color-C);
}
.prior-C .todo-checkbox input:checked ~ .todo-checkmark {
background-color: var(--color-C);
}
.prior-D .todo-priority {
color: var(--color-D);
}
.prior-D .todo-checkbox .todo-checkmark {
border-color: var(--color-D);
}
.prior-D .todo-checkbox input:checked ~ .todo-checkmark {
background-color: var(--color-D);
}

View File

@ -0,0 +1,38 @@
document.addEventListener('joplin-noteDidUpdate', makeTodoViewActionable );
if (/WebKit/i.test(navigator.userAgent)) { // sniff
var _timer_todotxt = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
makeTodoViewActionable()
}
}, 10);
}
function makeTodoViewActionable() {
if (_timer_todotxt) clearInterval(_timer_todotxt);
const todoTxts = document.getElementsByClassName('todotxt');
for (var i=0; i<todoTxts.length; i++){
const todoTxt = todoTxts[i];
const todos = todoTxt.getElementsByClassName('todo');
for (var j=0; j<todos.length; j++){
const todo = todos[j];
const lineIdx = todo.getAttribute("data-lineIdx");
const checkbox = todo.getElementsByClassName('todo-checkbox')[0].getElementsByTagName('input')[0];
const priority = todo.getElementsByClassName('todo-priority')[0].getElementsByTagName('select')[0];
const selectButton = todo.getElementsByClassName('todo-select')[0];
checkbox.onclick = function (){
setTimeout(()=> {webviewApi.postMessage('todoTxtMd', `toggleStatus:${lineIdx}`)}, 170);
}
priority.onchange = function(option) {
var value = option.target.value;
webviewApi.postMessage('todoTxtMd', `changePriority:${lineIdx}:${value}`);
}
selectButton.onclick = function () {
webviewApi.postMessage('todoTxtMd', `selectLine:${lineIdx}`);
}
}
}
}

View File

@ -0,0 +1,17 @@
const initPlugin = joplin => {
joplin.plugins.register({
onStart: async function() {
await joplin.contentScripts.register(
'markdownItPlugin',
'abc_music_sheet',
'./markdownItPlugin.js'
);
},
});
//!function(t){var e={};function n(o){if(e[o])return e[o].exports;var r=e[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=t,n.c=e,n.d=function(t,e,o){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:o})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)n.d(o,r,function(e){return t[e]}.bind(null,r));return o},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=0)}([function(t,e,n){"use strict";var o=this&&this.__awaiter||function(t,e,n,o){return new(n||(n=Promise))((function(r,i){function u(t){try{c(o.next(t))}catch(t){i(t)}}function l(t){try{c(o.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?r(t.value):(e=t.value,e instanceof n?e:new n((function(t){t(e)}))).then(u,l)}c((o=o.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0});const r=n(1),i=n(2);r.default.plugins.register({onStart:function(){return o(this,void 0,void 0,(function*(){yield r.default.contentScripts.register(i.ContentScriptType.MarkdownItPlugin,"abc_music_sheet","./markdownItPlugin.js"),setInterval(()=>{console.log("AAAAAAAAAAAAAAA")},1e3)}))}})},function(t,e,n){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.default=joplin},function(t,e,n){"use strict";var o;Object.defineProperty(e,"__esModule",{value:!0}),e.ContentScriptType=e.SettingStorage=e.AppType=e.SettingItemType=e.ToolbarButtonLocation=e.isContextMenuItemLocation=e.MenuItemLocation=e.ImportModuleOutputFormat=e.FileSystemItem=void 0,function(t){t.File="file",t.Directory="directory"}(e.FileSystemItem||(e.FileSystemItem={})),function(t){t.Markdown="md",t.Html="html"}(e.ImportModuleOutputFormat||(e.ImportModuleOutputFormat={})),function(t){t.File="file",t.Edit="edit",t.View="view",t.Note="note",t.Tools="tools",t.Help="help",t.Context="context",t.NoteListContextMenu="noteListContextMenu",t.EditorContextMenu="editorContextMenu",t.FolderContextMenu="folderContextMenu",t.TagContextMenu="tagContextMenu"}(o=e.MenuItemLocation||(e.MenuItemLocation={})),e.isContextMenuItemLocation=function(t){return[o.Context,o.NoteListContextMenu,o.EditorContextMenu,o.FolderContextMenu,o.TagContextMenu].includes(t)},function(t){t.NoteToolbar="noteToolbar",t.EditorToolbar="editorToolbar"}(e.ToolbarButtonLocation||(e.ToolbarButtonLocation={})),function(t){t[t.Int=1]="Int",t[t.String=2]="String",t[t.Bool=3]="Bool",t[t.Array=4]="Array",t[t.Object=5]="Object",t[t.Button=6]="Button"}(e.SettingItemType||(e.SettingItemType={})),function(t){t.Desktop="desktop",t.Mobile="mobile",t.Cli="cli"}(e.AppType||(e.AppType={})),function(t){t[t.Database=1]="Database",t[t.File=2]="File"}(e.SettingStorage||(e.SettingStorage={})),function(t){t.MarkdownItPlugin="markdownItPlugin",t.CodeMirrorPlugin="codeMirrorPlugin"}(e.ContentScriptType||(e.ContentScriptType={}))}]);
}
module.exports = { initPlugin };

View File

@ -0,0 +1,16 @@
{
"manifest_version": 1,
"id": "org.joplinapp.plugins.AbcSheetMusic",
"app_min_version": "2.2",
"version": "1.0.2",
"name": "ABC Sheet Music Plugin",
"description": "Turns ABC text notation into sheet music",
"author": "Laurent Cozic",
"homepage_url": "https://github.com/joplin/plugin-abc-sheet-music",
"repository_url": "https://github.com/joplin/plugin-abc-sheet-music",
"keywords": [
"sheet music",
"abc",
"notation"
]
}

View File

@ -1,14 +1,4 @@
let script = null;
let joplin = {
plugins: {
register: s => {
script = s;
},
},
};
const setJoplin = v => joplin = v;
const initPlugin = joplin => {
joplin.plugins.register({
onStart: async function() {
@ -19,4 +9,6 @@ joplin.plugins.register({
},
});
module.exports = { script, setJoplin };
}
module.exports = { initPlugin };

View File

@ -1,14 +1,4 @@
let script = null;
let joplin = {
plugins: {
register: s => {
script = s;
},
},
};
const setJoplin = v => joplin = v;
const initPlugin = joplin => {
joplin.plugins.register({
onStart: async function() {
@ -19,4 +9,6 @@ joplin.plugins.register({
},
});
module.exports = { script, setJoplin };
}
module.exports = { initPlugin };

View File

@ -468,6 +468,41 @@ const simplePluginManifest2 = `{
]
}`;
// const abcPlugin = require('./plugins/org.joplinapp.plugins.AbcSheetMusic/index.js');
// const abcPluginManifest = `{
// "manifest_version": 1,
// "id": "org.joplinapp.plugins.AbcSheetMusic",
// "app_min_version": "2.2",
// "version": "1.0.2",
// "name": "ABC Sheet Music Plugin",
// "description": "Turns ABC text notation into sheet music",
// "author": "Laurent Cozic",
// "homepage_url": "https://github.com/joplin/plugin-abc-sheet-music",
// "repository_url": "https://github.com/joplin/plugin-abc-sheet-music",
// "keywords": [
// "sheet music",
// "abc",
// "notation"
// ]
// }`;
// const abcPluginContentScript = require('./plugins/org.joplinapp.plugins.AbcSheetMusic/markdownItPlugin.js');
const metisPlugin = require('./plugins/com.hieuthi.joplin.metis/index.js');
const metisManifest = `{
"manifest_version": 1,
"id": "com.hieuthi.joplin.metis",
"app_min_version": "2.2",
"version": "0.1.5",
"name": "Metis",
"description": "A Simple Task Manager Plugin for Joplin based on Todo.txt Specification",
"author": "Hieu-Thi Luong",
"homepage_url": "https://github.com/hieuthi/joplin-plugin-metis",
"repository_url": "https://github.com/hieuthi/joplin-plugin-metis",
"keywords": ["joplin-plugin","todo","todotxt","task management"]
}`;
const metisContentScript = require('./plugins/com.hieuthi.joplin.metis/todoTxtMdRule.js');
const initPluginService = async () => {
const service = PluginService.instance();
const runner = new PluginRunner();
@ -483,11 +518,17 @@ const initPluginService = async () => {
}
);
const plugin1 = await PluginService.instance().loadPluginFromModule(simplePlugin1, simplePluginManifest1);
const plugin1 = await PluginService.instance().loadPluginFromModule({ main: simplePlugin1 }, simplePluginManifest1);
await PluginService.instance().runPlugin(plugin1);
const plugin2 = await PluginService.instance().loadPluginFromModule(simplePlugin2, simplePluginManifest2);
const plugin2 = await PluginService.instance().loadPluginFromModule({ main: simplePlugin2 }, simplePluginManifest2);
await PluginService.instance().runPlugin(plugin2);
// const plugin3 = await PluginService.instance().loadPluginFromModule({ main: abcPlugin, contentScripts: { ['markdownItPlugin.js']: abcPluginContentScript } }, abcPluginManifest);
// await PluginService.instance().runPlugin(plugin3);
const plugin4 = await PluginService.instance().loadPluginFromModule({ main: metisPlugin, contentScripts: { ['todoTxtMdView.js']: metisContentScript } }, metisManifest);
await PluginService.instance().runPlugin(plugin4);
};
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied

View File

@ -1,12 +1,15 @@
import Global from '@joplin/lib/services/plugins/api/Global';
import BasePluginRunner from '@joplin/lib/services/plugins/BasePluginRunner';
import Plugin from '@joplin/lib/services/plugins/Plugin';
import Logger from '@joplin/utils/Logger';
const logger = Logger.create('PluginRunner');
export default class PluginRunner extends BasePluginRunner {
public async run(plugin: Plugin, sandbox: Global): Promise<void> {
plugin.module.setJoplin(sandbox.joplin);
await sandbox.joplin.plugins.register(plugin.module.script);
logger.info(`Run plugin: ${plugin.id}`);
plugin.module.main.initPlugin(sandbox.joplin);
}
}

View File

@ -256,12 +256,8 @@ export default class FsDriverRN extends FsDriverBase {
return output ? output : null;
}
public resolve(path: string) {
return resolve(path);
}
public resolveRelativePathWithinDir(_baseDir: string, relativePath: string) {
throw new Error(`Not implemented: resolveRelativePathWithinDir(): ${relativePath}`);
public resolve(...pathSegments: string[]) {
return resolve(...pathSegments);
}
public async md5File(path: string): Promise<string> {

View File

@ -0,0 +1,21 @@
import { MarkupLanguageUtils as BaseMarkupLanguageUtils } from '@joplin/lib/markupLanguageUtils';
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
import { contentScriptsToRendererRules } from '@joplin/lib/services/plugins/utils/loadContentScripts';
import { Options } from '@joplin/renderer/MarkupToHtml';
class MarkupLanguageUtils extends BaseMarkupLanguageUtils {
public newMarkupToHtml(plugins: PluginStates = null, options: Options = null) {
plugins = plugins || {};
return super.newMarkupToHtml(null, {
extraRendererRules: contentScriptsToRendererRules(plugins),
...options,
});
}
}
const markupLanguageUtils = new MarkupLanguageUtils();
export default markupLanguageUtils;

View File

@ -158,6 +158,20 @@ export default class FsDriverBase {
};
}
public resolve(..._pathSegments: string[]): string {
throw new Error('Not implemented');
}
// Resolves the provided relative path to an absolute path within baseDir. The function
// also checks that the absolute path is within baseDir, to avoid security issues.
// It is expected that baseDir is a safe path (not user-provided).
public resolveRelativePathWithinDir(baseDir: string, relativePath: string) {
const resolvedBaseDir = this.resolve(baseDir);
const resolvedPath = this.resolve(baseDir, relativePath);
if (resolvedPath.indexOf(resolvedBaseDir) !== 0) throw new Error(`Resolved path for relative path "${relativePath}" is not within base directory "${baseDir}" (Was resolved to ${resolvedPath})`);
return resolvedPath;
}
public async tarExtract(_options: any) {
throw new Error('Not implemented');
}

View File

@ -186,18 +186,8 @@ export default class FsDriverNode extends FsDriverBase {
throw new Error(`Unsupported encoding: ${encoding}`);
}
public resolve(path: string) {
return require('path').resolve(path);
}
// Resolves the provided relative path to an absolute path within baseDir. The function
// also checks that the absolute path is within baseDir, to avoid security issues.
// It is expected that baseDir is a safe path (not user-provided).
public resolveRelativePathWithinDir(baseDir: string, relativePath: string) {
const resolvedBaseDir = nodeResolve(baseDir);
const resolvedPath = nodeResolve(baseDir, relativePath);
if (resolvedPath.indexOf(resolvedBaseDir) !== 0) throw new Error(`Resolved path for relative path "${relativePath}" is not within base directory "${baseDir}" (Was resolved to ${resolvedPath})`);
return resolvedPath;
public resolve(...pathSegments: string[]) {
return nodeResolve(...pathSegments);
}
public async md5File(path: string): Promise<string> {

View File

@ -4,6 +4,7 @@ import shim from '../../shim';
import { ViewHandle } from './utils/createViewHandle';
import { ContentScriptType } from './api/types';
import Logger from '@joplin/utils/Logger';
import Joplin from './api/Joplin';
const EventEmitter = require('events');
const logger = Logger.create('Plugin');
@ -15,12 +16,20 @@ interface ViewControllers {
export interface ContentScript {
id: string;
path: string;
module?: any;
}
interface ContentScripts {
[type: string]: ContentScript[];
}
export interface Module {
main: {
initPlugin: (joplin: Joplin)=> void;
};
contentScripts?: Record<string, any>;
}
export default class Plugin {
private baseDir_: string;
@ -38,10 +47,10 @@ export default class Plugin {
private contentScriptMessageListeners_: Record<string, Function> = {};
private dataDir_: string;
private dataDirCreated_ = false;
private module_: any|null = null;
private module_: Module|null = null;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
public constructor(baseDir: string, manifest: PluginManifest, scriptText: string, dispatch: Function, dataDir: string, module: any|null = null) {
public constructor(baseDir: string, manifest: PluginManifest, scriptText: string, dispatch: Function, dataDir: string, module: Module|null = null) {
this.baseDir_ = baseDir ? shim.fsDriver().resolve(baseDir) : '';
this.manifest_ = manifest;
this.scriptText_ = scriptText;
@ -75,7 +84,7 @@ export default class Plugin {
return this.baseDir_;
}
public get module(): any|null {
public get module(): Module|null {
return this.module_;
}
@ -111,13 +120,25 @@ export default class Plugin {
public async registerContentScript(type: ContentScriptType, id: string, path: string) {
if (!this.contentScripts_[type]) this.contentScripts_[type] = [];
const absolutePath = shim.fsDriver().resolveRelativePathWithinDir(this.baseDir, path);
let absolutePath = '';
let scriptModule: any = null;
if (!(await shim.fsDriver().exists(absolutePath))) throw new Error(`Could not find content script at path ${absolutePath}`);
if (this.module) {
const normalizePath = (p: string) => {
if (p.startsWith('./')) return p.substring(2);
return p;
};
this.contentScripts_[type].push({ id, path: absolutePath });
const moduleKey = normalizePath(path);
scriptModule = this.module.contentScripts[moduleKey];
logger.debug(`"${this.id}": Registered content script: ${type}: ${id}: ${moduleKey}`);
} else {
absolutePath = shim.fsDriver().resolveRelativePathWithinDir(this.baseDir, path);
if (!(await shim.fsDriver().exists(absolutePath))) throw new Error(`Could not find content script at path ${absolutePath}`);
logger.debug(`"${this.id}": Registered content script: ${type}: ${id}: ${absolutePath}`);
}
logger.debug(`"${this.id}": Registered content script: ${type}: ${id}: ${absolutePath}`);
this.contentScripts_[type].push({ id, path: absolutePath, module: scriptModule });
this.dispatch_({
type: 'PLUGIN_CONTENT_SCRIPTS_ADD',
@ -126,6 +147,7 @@ export default class Plugin {
type: type,
id: id,
path: absolutePath,
module: scriptModule,
},
});
}

View File

@ -1,4 +1,4 @@
import Plugin from './Plugin';
import Plugin, { Module } from './Plugin';
import manifestFromObject from './utils/manifestFromObject';
import Global from './api/Global';
import BasePluginRunner from './BasePluginRunner';
@ -287,7 +287,7 @@ export default class PluginService extends BaseService {
}
}
public async loadPluginFromModule(module: any, manifestText: string): Promise<Plugin> {
public async loadPluginFromModule(module: Module, manifestText: string): Promise<Plugin> {
return this.loadPlugin(
'',
manifestText,
@ -297,7 +297,7 @@ export default class PluginService extends BaseService {
);
}
private async loadPlugin(baseDir: string, manifestText: string, scriptText: string, pluginIdIfNotSpecified: string, module: any|null = null): Promise<Plugin> {
private async loadPlugin(baseDir: string, manifestText: string, scriptText: string, pluginIdIfNotSpecified: string, module: Module|null = null): Promise<Plugin> {
baseDir = rtrimSlashes(baseDir);
const manifestObj = JSON.parse(manifestText);

View File

@ -17,6 +17,7 @@ interface PluginViewStates {
interface PluginContentScriptState {
id: string;
path: string;
module?: any;
}
interface PluginContentScriptStates {
@ -170,6 +171,7 @@ const reducer = (draftRoot: Draft<any>, action: any) => {
draft.plugins[action.pluginId].contentScripts[type].push({
id: action.contentScript.id,
path: action.contentScript.path,
module: action.contentScript.module,
});
break;
}