You've already forked joplin
							
							
				mirror of
				https://github.com/laurent22/joplin.git
				synced 2025-10-31 00:07:48 +02:00 
			
		
		
		
	Desktop: Install default plugins on first app start (#6585)
This commit is contained in:
		| @@ -117,6 +117,9 @@ packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.d.ts | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.js | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.js.map | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.d.ts | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.js | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.js.map | ||||
| @@ -1683,6 +1686,12 @@ packages/lib/services/plugins/api/JoplinWorkspace.js.map | ||||
| packages/lib/services/plugins/api/types.d.ts | ||||
| packages/lib/services/plugins/api/types.js | ||||
| packages/lib/services/plugins/api/types.js.map | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.d.ts | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js.map | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.d.ts | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js.map | ||||
| packages/lib/services/plugins/reducer.d.ts | ||||
| packages/lib/services/plugins/reducer.js | ||||
| packages/lib/services/plugins/reducer.js.map | ||||
|   | ||||
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -105,6 +105,9 @@ packages/app-cli/tests/services/plugins/api/JoplinViewMenuItem.js.map | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.d.ts | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js | ||||
| packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js.map | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.d.ts | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.js | ||||
| packages/app-cli/tests/services/plugins/defaultPluginsUtils.js.map | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.d.ts | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.js | ||||
| packages/app-cli/tests/services/plugins/sandboxProxy.js.map | ||||
| @@ -1671,6 +1674,12 @@ packages/lib/services/plugins/api/JoplinWorkspace.js.map | ||||
| packages/lib/services/plugins/api/types.d.ts | ||||
| packages/lib/services/plugins/api/types.js | ||||
| packages/lib/services/plugins/api/types.js.map | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.d.ts | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js | ||||
| packages/lib/services/plugins/defaultPlugins/defaultPluginsUtils.js.map | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.d.ts | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js | ||||
| packages/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo.js.map | ||||
| packages/lib/services/plugins/reducer.d.ts | ||||
| packages/lib/services/plugins/reducer.js | ||||
| packages/lib/services/plugins/reducer.js.map | ||||
|   | ||||
							
								
								
									
										216
									
								
								packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								packages/app-cli/tests/services/plugins/defaultPluginsUtils.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| import { installDefaultPlugins, getDefaultPluginsInstallState, setSettingsForDefaultPlugins, checkPreInstalledDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils'; | ||||
| import PluginRunner from '../../../app/services/plugins/PluginRunner'; | ||||
| import { pathExists } from 'fs-extra'; | ||||
| import { setupDatabaseAndSynchronizer, supportDir, switchClient } from '@joplin/lib/testing/test-utils'; | ||||
| import PluginService, { defaultPluginSetting, DefaultPluginsInfo, PluginSettings } from '@joplin/lib/services/plugins/PluginService'; | ||||
| import Setting from '@joplin/lib/models/Setting'; | ||||
|  | ||||
| const testPluginDir = `${supportDir}/plugins`; | ||||
|  | ||||
| function newPluginService(appVersion: string = '1.4') { | ||||
| 	const runner = new PluginRunner(); | ||||
| 	const service = new PluginService(); | ||||
| 	service.initialize( | ||||
| 		appVersion, | ||||
| 		{ | ||||
| 			joplin: {}, | ||||
| 		}, | ||||
| 		runner, | ||||
| 		{ | ||||
| 			dispatch: () => {}, | ||||
| 			getState: () => {}, | ||||
| 		} | ||||
| 	); | ||||
| 	return service; | ||||
| } | ||||
|  | ||||
| describe('defaultPluginsUtils', function() { | ||||
|  | ||||
| 	const pluginsId = ['joplin.plugin.ambrt.backlinksToNote', 'org.joplinapp.plugins.ToggleSidebars']; | ||||
|  | ||||
| 	beforeEach(async (done) => { | ||||
| 		await setupDatabaseAndSynchronizer(1); | ||||
| 		await switchClient(1); | ||||
| 		done(); | ||||
| 	}); | ||||
|  | ||||
| 	it('should install default plugins with no previous default plugins installed', (async () => { | ||||
| 		const testPluginDir = `${supportDir}/pluginRepo/plugins`; | ||||
| 		Setting.setValue('installedDefaultPlugins', []); | ||||
|  | ||||
| 		const service = newPluginService('2.1'); | ||||
|  | ||||
| 		const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states')); | ||||
|  | ||||
| 		const newPluginsSettings = await installDefaultPlugins(service, testPluginDir, pluginsId, pluginSettings); | ||||
|  | ||||
| 		const installedPluginPath1 = `${Setting.value('pluginDir')}/${pluginsId[0]}.jpl`; | ||||
| 		const installedPluginPath2 = `${Setting.value('pluginDir')}/${pluginsId[1]}.jpl`; | ||||
|  | ||||
| 		expect(await pathExists(installedPluginPath1)).toBe(true); | ||||
| 		expect(await pathExists(installedPluginPath2)).toBe(true); | ||||
|  | ||||
| 		expect(newPluginsSettings[pluginsId[0]]).toMatchObject(defaultPluginSetting()); | ||||
| 		expect(newPluginsSettings[pluginsId[1]]).toMatchObject(defaultPluginSetting()); | ||||
|  | ||||
| 	})); | ||||
|  | ||||
| 	it('should install default plugins with previous default plugins installed', (async () => { | ||||
|  | ||||
| 		const testPluginDir = `${supportDir}/pluginRepo/plugins`; | ||||
| 		Setting.setValue('installedDefaultPlugins', ['org.joplinapp.plugins.ToggleSidebars']); | ||||
|  | ||||
| 		const service = newPluginService('2.1'); | ||||
|  | ||||
| 		const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states')); | ||||
|  | ||||
| 		const newPluginsSettings = await installDefaultPlugins(service, testPluginDir, pluginsId, pluginSettings); | ||||
|  | ||||
| 		const installedPluginPath1 = `${Setting.value('pluginDir')}/${pluginsId[0]}.jpl`; | ||||
| 		const installedPluginPath2 = `${Setting.value('pluginDir')}/${pluginsId[1]}.jpl`; | ||||
|  | ||||
| 		expect(await pathExists(installedPluginPath1)).toBe(true); | ||||
| 		expect(await pathExists(installedPluginPath2)).toBe(false); | ||||
|  | ||||
| 		expect(newPluginsSettings[pluginsId[0]]).toMatchObject(defaultPluginSetting()); | ||||
| 		expect(newPluginsSettings[pluginsId[1]]).toBeUndefined(); | ||||
| 	})); | ||||
|  | ||||
| 	it('should get default plugins install state', (async () => { | ||||
| 		const testCases = [ | ||||
| 			{ | ||||
| 				'installedDefaultPlugins': [''], | ||||
| 				'loadingPlugins': [`${testPluginDir}/simple`, `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`], | ||||
| 				'plugin1DefaultState': defaultPluginSetting(), | ||||
| 				'plugin2DefaultState': defaultPluginSetting(), | ||||
| 				'installedDefaultPlugins1': true, | ||||
| 				'installedDefaultPlugins2': true, | ||||
| 			}, | ||||
| 			{ | ||||
| 				'installedDefaultPlugins': [''], | ||||
| 				'loadingPlugins': [`${testPluginDir}/simple`], | ||||
| 				'plugin1DefaultState': defaultPluginSetting(), | ||||
| 				'plugin2DefaultState': undefined, | ||||
| 				'installedDefaultPlugins1': true, | ||||
| 				'installedDefaultPlugins2': false, | ||||
| 			}, | ||||
| 			{ | ||||
| 				'installedDefaultPlugins': ['org.joplinapp.plugins.Simple'], | ||||
| 				'loadingPlugins': [`${testPluginDir}/simple`, `${testPluginDir}/jpl_test/org.joplinapp.FirstJplPlugin.jpl`], | ||||
| 				'plugin1DefaultState': undefined, | ||||
| 				'plugin2DefaultState': defaultPluginSetting(), | ||||
| 				'installedDefaultPlugins1': true, | ||||
| 				'installedDefaultPlugins2': true, | ||||
| 			}, | ||||
| 			{ | ||||
| 				'installedDefaultPlugins': ['org.joplinapp.plugins.Simple'], | ||||
| 				'loadingPlugins': [`${testPluginDir}/simple`], | ||||
| 				'plugin1DefaultState': undefined, | ||||
| 				'plugin2DefaultState': undefined, | ||||
| 				'installedDefaultPlugins1': true, | ||||
| 				'installedDefaultPlugins2': false, | ||||
| 			}, | ||||
| 		]; | ||||
|  | ||||
| 		for (const testCase of testCases) { | ||||
| 			const service = newPluginService(); | ||||
| 			const pluginsId = ['org.joplinapp.plugins.Simple', 'org.joplinapp.FirstJplPlugin']; | ||||
|  | ||||
| 			Setting.setValue('installedDefaultPlugins', testCase.installedDefaultPlugins); | ||||
| 			await service.loadAndRunPlugins(testCase.loadingPlugins, {}); | ||||
|  | ||||
| 			// setting installedDefaultPlugins state | ||||
| 			const defaultInstallStates: PluginSettings = getDefaultPluginsInstallState(service, pluginsId); | ||||
|  | ||||
| 			expect(defaultInstallStates[pluginsId[0]]).toStrictEqual(testCase.plugin1DefaultState); | ||||
| 			expect(defaultInstallStates[pluginsId[1]]).toStrictEqual(testCase.plugin2DefaultState); | ||||
|  | ||||
|  | ||||
| 			const installedDefaultPlugins = Setting.value('installedDefaultPlugins'); | ||||
| 			expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(testCase.installedDefaultPlugins1); | ||||
| 			expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(testCase.installedDefaultPlugins2); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 	})); | ||||
|  | ||||
| 	it('should check pre-installed default plugins', (async () => { | ||||
| 		// with previous pre-installed default plugins | ||||
| 		Setting.setValue('installedDefaultPlugins', ['']); | ||||
| 		let pluginSettings, installedDefaultPlugins; | ||||
|  | ||||
| 		pluginSettings = { [pluginsId[0]]: defaultPluginSetting() }; | ||||
| 		checkPreInstalledDefaultPlugins(pluginsId, pluginSettings); | ||||
|  | ||||
| 		installedDefaultPlugins = Setting.value('installedDefaultPlugins'); | ||||
| 		expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(true); | ||||
| 		expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(false); | ||||
|  | ||||
|  | ||||
| 		// with no previous pre-installed default plugins | ||||
| 		Setting.setValue('installedDefaultPlugins', ['not-a-default-plugin']); | ||||
| 		pluginSettings = {}; | ||||
| 		checkPreInstalledDefaultPlugins(pluginsId, pluginSettings); | ||||
|  | ||||
| 		installedDefaultPlugins = Setting.value('installedDefaultPlugins'); | ||||
| 		expect(installedDefaultPlugins.includes(pluginsId[0])).toBe(false); | ||||
| 		expect(installedDefaultPlugins.includes(pluginsId[1])).toBe(false); | ||||
|  | ||||
| 	})); | ||||
|  | ||||
| 	it('should set initial settings for default plugins', async () => { | ||||
| 		const service = newPluginService(); | ||||
|  | ||||
| 		const pluginScript = ` | ||||
| 		/* joplin-manifest: | ||||
| 		{ | ||||
| 			"id": "io.github.jackgruber.backup", | ||||
| 			"manifest_version": 1, | ||||
| 			"app_min_version": "1.4", | ||||
| 			"name": "JS Bundle test", | ||||
| 			"version": "1.0.0" | ||||
| 		} | ||||
| 		*/ | ||||
| 		joplin.plugins.register({ | ||||
| 			onStart: async function() { | ||||
| 				await joplin.settings.registerSettings({ | ||||
| 					path: { | ||||
| 						value: "initial-path", | ||||
| 						type: 2, | ||||
| 						section: "backupSection", | ||||
| 						public: true, | ||||
| 						label: "Backup path", | ||||
| 					  }, | ||||
| 				}) | ||||
| 			}, | ||||
| 		});`; | ||||
|  | ||||
| 		const plugin = await service.loadPluginFromJsBundle('', pluginScript); | ||||
| 		await service.runPlugin(plugin); | ||||
|  | ||||
| 		const defaultPluginsInfo: DefaultPluginsInfo = { | ||||
| 			'io.github.jackgruber.backup': { | ||||
| 				version: '1.0.2', | ||||
| 				settings: { | ||||
| 					'path': `${Setting.value('profileDir')}`, | ||||
| 				}, | ||||
| 			}, | ||||
| 			'plugin.calebjohn.rich-markdown': { | ||||
| 				version: '0.8.3', | ||||
| 			}, | ||||
| 		}; | ||||
|  | ||||
| 		// with pre-installed default plugin | ||||
| 		Setting.setValue('installedDefaultPlugins', ['io.github.jackgruber.backup']); | ||||
| 		setSettingsForDefaultPlugins(defaultPluginsInfo); | ||||
| 		expect(Setting.value('plugin-io.github.jackgruber.backup.path')).toBe('initial-path'); | ||||
| 		await service.destroy(); | ||||
|  | ||||
| 		// with no pre-installed default plugin | ||||
| 		Setting.setValue('installedDefaultPlugins', ['']); | ||||
| 		setSettingsForDefaultPlugins(defaultPluginsInfo); | ||||
| 		expect(Setting.value('plugin-io.github.jackgruber.backup.path')).toBe(`${Setting.value('profileDir')}`); | ||||
| 		await service.destroy(); | ||||
| 	}); | ||||
|  | ||||
| }); | ||||
| @@ -43,6 +43,7 @@ import sidebarCommands from './gui/Sidebar/commands/index'; | ||||
| import appCommands from './commands/index'; | ||||
| import libCommands from '@joplin/lib/commands/index'; | ||||
| import { homedir } from 'os'; | ||||
| import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo'; | ||||
| const electronContextMenu = require('./services/electron-context-menu'); | ||||
| // import  populateDatabase from '@joplin/lib/services/debug/populateDatabase'; | ||||
|  | ||||
| @@ -63,6 +64,8 @@ import checkForUpdates from './checkForUpdates'; | ||||
| import { AppState } from './app.reducer'; | ||||
| import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog'; | ||||
| import eventManager from '@joplin/lib/eventManager'; | ||||
| import path = require('path'); | ||||
| import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils'; | ||||
| // import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils'; | ||||
|  | ||||
| const pluginClasses = [ | ||||
| @@ -260,9 +263,9 @@ class Application extends BaseApplication { | ||||
| 		const pluginRunner = new PluginRunner(); | ||||
| 		service.initialize(packageInfo.version, PlatformImplementation.instance(), pluginRunner, this.store()); | ||||
| 		service.isSafeMode = Setting.value('isSafeMode'); | ||||
| 		const defaultPluginsId = Object.keys(getDefaultPluginsInfo()); | ||||
|  | ||||
| 		const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states')); | ||||
|  | ||||
| 		let pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states')); | ||||
| 		{ | ||||
| 			// Users can add and remove plugins from the config screen at any | ||||
| 			// time, however we only effectively uninstall the plugin the next | ||||
| @@ -272,7 +275,11 @@ class Application extends BaseApplication { | ||||
| 			Setting.setValue('plugins.states', newSettings); | ||||
| 		} | ||||
|  | ||||
| 		checkPreInstalledDefaultPlugins(defaultPluginsId, pluginSettings); | ||||
|  | ||||
| 		try { | ||||
| 			const pluginsDir = path.join(bridge().buildDir(), 'defaultPlugins'); | ||||
| 			pluginSettings = await installDefaultPlugins(service, pluginsDir, defaultPluginsId, pluginSettings); | ||||
| 			if (await shim.fsDriver().exists(Setting.value('pluginDir'))) { | ||||
| 				await service.loadAndRunPlugins(Setting.value('pluginDir'), pluginSettings); | ||||
| 			} | ||||
| @@ -320,6 +327,7 @@ class Application extends BaseApplication { | ||||
| 					type: 'STARTUP_PLUGINS_LOADED', | ||||
| 					value: true, | ||||
| 				}); | ||||
| 				setSettingsForDefaultPlugins(getDefaultPluginsInfo()); | ||||
| 			} | ||||
| 		}, 500); | ||||
| 	} | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -15,6 +15,9 @@ import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry'; | ||||
| const shared = require('@joplin/lib/components/shared/config-shared.js'); | ||||
| import ClipperConfigScreen from '../ClipperConfigScreen'; | ||||
| import restart from '../../services/restart'; | ||||
| import PluginService from '@joplin/lib/services/plugins/PluginService'; | ||||
| import { getDefaultPluginsInstallState, updateDefaultPluginsInstallState } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils'; | ||||
| import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo'; | ||||
| const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen'); | ||||
|  | ||||
| const settingKeyToControl: any = { | ||||
| @@ -66,6 +69,7 @@ class ConfigScreenComponent extends React.Component<any, any> { | ||||
| 				this.switchSection(this.props.defaultSection); | ||||
| 			}); | ||||
| 		} | ||||
| 		updateDefaultPluginsInstallState(getDefaultPluginsInstallState(PluginService.instance(), Object.keys(getDefaultPluginsInfo())), this); | ||||
| 	} | ||||
|  | ||||
| 	private async handleSettingButton(key: string) { | ||||
|   | ||||
| @@ -31,7 +31,8 @@ | ||||
|     "afterSign": "./tools/notarizeMacApp.js", | ||||
|     "extraResources": [ | ||||
|       "build/icons/**", | ||||
|       "build/images/**" | ||||
|       "build/images/**", | ||||
|       "build/defaultPlugins/**" | ||||
|     ], | ||||
|     "afterAllArtifactBuild": "./generateSha512.js", | ||||
|     "asar": true, | ||||
|   | ||||
| @@ -1576,6 +1576,13 @@ class Setting extends BaseModel { | ||||
| 				public: false, | ||||
| 			}, | ||||
|  | ||||
| 			installedDefaultPlugins: { | ||||
| 				value: [], | ||||
| 				type: SettingItemType.Array, | ||||
| 				public: false, | ||||
| 				storage: SettingStorage.Database, | ||||
| 			}, | ||||
|  | ||||
| 			// 'featureFlag.syncAccurateTimestamps': { | ||||
| 			// 	value: false, | ||||
| 			// 	type: SettingItemType.Bool, | ||||
| @@ -1954,6 +1961,17 @@ class Setting extends BaseModel { | ||||
| 		return this.setValue(key, !this.value(key)); | ||||
| 	} | ||||
|  | ||||
| 	// this method checks if the 'value' passed is present in the Setting "Array" | ||||
| 	// If yes, then it just returns 'true'. If its not present then, it will | ||||
| 	// update it and return 'false' | ||||
| 	public static setArrayValue(settingName: string, value: string): boolean { | ||||
| 		const settingValue: Array<any> = this.value(settingName); | ||||
| 		if (settingValue.includes(value)) return true; | ||||
| 		settingValue.push(value); | ||||
| 		this.setValue(settingName, settingValue); | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	static objectValue(settingKey: string, objectKey: string, defaultValue: any = null) { | ||||
| 		const o = this.value(settingKey); | ||||
| 		if (!o || !(objectKey in o)) return defaultValue; | ||||
|   | ||||
| @@ -28,6 +28,19 @@ export interface Plugins { | ||||
| 	[key: string]: Plugin; | ||||
| } | ||||
|  | ||||
| export interface SettingAndValue { | ||||
| 	[settingName: string]: string; | ||||
| } | ||||
|  | ||||
| export interface DefaultPluginSettings { | ||||
| 	version: string; | ||||
| 	settings?: SettingAndValue; | ||||
| } | ||||
|  | ||||
| export interface DefaultPluginsInfo { | ||||
|     [pluginId: string]: DefaultPluginSettings; | ||||
| } | ||||
|  | ||||
| export interface PluginSetting { | ||||
| 	enabled: boolean; | ||||
| 	deleted: boolean; | ||||
| @@ -416,7 +429,7 @@ export default class PluginService extends BaseService { | ||||
| 		return this.installPluginFromRepo(repoApi, pluginId); | ||||
| 	} | ||||
|  | ||||
| 	public async installPlugin(jplPath: string): Promise<Plugin> { | ||||
| 	public async installPlugin(jplPath: string, loadPlugin: boolean = true): Promise<Plugin | null> { | ||||
| 		logger.info(`Installing plugin: "${jplPath}"`); | ||||
|  | ||||
| 		// Before moving the plugin to the profile directory, we load it | ||||
| @@ -429,9 +442,11 @@ export default class PluginService extends BaseService { | ||||
| 		await shim.fsDriver().copy(jplPath, destPath); | ||||
|  | ||||
| 		// Now load it from the profile directory | ||||
| 		const plugin = await this.loadPluginFromPath(destPath); | ||||
| 		if (!this.plugins_[plugin.id]) this.setPluginAt(plugin.id, plugin); | ||||
| 		return plugin; | ||||
| 		if (loadPlugin) { | ||||
| 			const plugin = await this.loadPluginFromPath(destPath); | ||||
| 			if (!this.plugins_[plugin.id]) this.setPluginAt(plugin.id, plugin); | ||||
| 			return plugin; | ||||
| 		} else { return null; } | ||||
| 	} | ||||
|  | ||||
| 	private async pluginPath(pluginId: string) { | ||||
|   | ||||
| @@ -0,0 +1,75 @@ | ||||
| import produce from 'immer'; | ||||
| import path = require('path'); | ||||
| import Setting from '../../../models/Setting'; | ||||
| import shim from '../../../shim'; | ||||
| import PluginService, { defaultPluginSetting, DefaultPluginsInfo, PluginSettings } from '../PluginService'; | ||||
| import * as React from 'react'; | ||||
| const shared = require('@joplin/lib/components/shared/config-shared.js'); | ||||
|  | ||||
| export function checkPreInstalledDefaultPlugins(defaultPluginsId: string[],pluginSettings: PluginSettings) { | ||||
| 	const installedDefaultPlugins: Array<string> = Setting.value('installedDefaultPlugins'); | ||||
| 	for (const pluginId of defaultPluginsId) { | ||||
| 		// if pluginId is present in pluginSettings and not in installedDefaultPlugins array, | ||||
| 		// then its either pre-installed by user or just uninstalled | ||||
| 		if (pluginSettings[pluginId] && !installedDefaultPlugins.includes(pluginId)) Setting.setArrayValue('installedDefaultPlugins', pluginId); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export async function installDefaultPlugins(service: PluginService, pluginsDir: string, defaultPluginsId: string[], pluginSettings: PluginSettings): Promise<PluginSettings> { | ||||
| 	const defaultPluginsPaths = await shim.fsDriver().readDirStats(pluginsDir); | ||||
| 	const installedPlugins = Setting.value('installedDefaultPlugins'); | ||||
|  | ||||
| 	for (let pluginId of defaultPluginsPaths) { | ||||
| 		pluginId = pluginId.path; | ||||
|  | ||||
| 		// if pluginId is present in 'installedDefaultPlugins' array or it doesn't have default plugin ID, then we won't install it again as default plugin | ||||
| 		if (installedPlugins.includes(pluginId) || !defaultPluginsId.includes(pluginId)) continue; | ||||
| 		const defaultPluginPath: string = path.join(pluginsDir, pluginId, 'plugin.jpl'); | ||||
| 		await service.installPlugin(defaultPluginPath, false); | ||||
|  | ||||
| 		pluginSettings = produce(pluginSettings, (draft: PluginSettings) => { | ||||
| 			draft[pluginId] = defaultPluginSetting(); | ||||
| 		}); | ||||
| 	} | ||||
| 	return pluginSettings; | ||||
| } | ||||
|  | ||||
| export function setSettingsForDefaultPlugins(defaultPluginsInfo: DefaultPluginsInfo) { | ||||
| 	const installedDefaultPlugins = Setting.value('installedDefaultPlugins'); | ||||
|  | ||||
| 	// only set initial settings if the plugin is not present in installedDefaultPlugins array | ||||
| 	for (const pluginId of Object.keys(defaultPluginsInfo)) { | ||||
| 		if (!defaultPluginsInfo[pluginId].settings) continue; | ||||
| 		for (const settingName of Object.keys(defaultPluginsInfo[pluginId].settings)) { | ||||
| 			if (!installedDefaultPlugins.includes(pluginId)) { | ||||
| 				Setting.setValue(`plugin-${pluginId}.${settingName}`, defaultPluginsInfo[pluginId].settings[settingName]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| export function getDefaultPluginsInstallState(service: PluginService, defaultPluginsId: string[]): PluginSettings { | ||||
| 	const settings: PluginSettings = {}; | ||||
| 	for (const pluginId of defaultPluginsId) { | ||||
| 		if (!service.pluginIds.includes(pluginId)) continue; | ||||
| 		if (!Setting.setArrayValue('installedDefaultPlugins', pluginId)) { | ||||
| 			settings[pluginId] = defaultPluginSetting(); | ||||
| 		} | ||||
| 	} | ||||
| 	return settings; | ||||
| } | ||||
|  | ||||
| export function updateDefaultPluginsInstallState(newPluginStates: PluginSettings, ConfigScreen: React.Component<any, any>) { | ||||
| 	if (Object.keys(newPluginStates).length === 0) return; | ||||
| 	const key = 'plugins.states'; | ||||
| 	const md = Setting.settingMetadata(key); | ||||
| 	let newValue = Setting.value('plugins.states'); | ||||
| 	newValue = { | ||||
| 		...newValue, ...newPluginStates, | ||||
| 	}; | ||||
| 	shared.updateSettingValue(ConfigScreen, key, newValue); | ||||
|  | ||||
| 	if (md.autoSave) { | ||||
| 		shared.scheduleSaveSettings(ConfigScreen); | ||||
| 	} | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| import { DefaultPluginsInfo } from '../PluginService'; | ||||
| import Setting from '../../../models/Setting'; | ||||
|  | ||||
| const getDefaultPluginsInfo = (): DefaultPluginsInfo => { | ||||
| 	const defaultPlugins = { | ||||
| 		'io.github.jackgruber.backup': { | ||||
| 			version: '1.0.2', | ||||
| 			settings: { | ||||
| 				'path': `${Setting.value('profileDir')}`, | ||||
| 			}, | ||||
| 		}, | ||||
| 		'plugin.calebjohn.rich-markdown': { | ||||
| 			version: '0.8.3', | ||||
| 		}, | ||||
| 	}; | ||||
| 	return defaultPlugins; | ||||
| }; | ||||
| export default getDefaultPluginsInfo; | ||||
| @@ -55,6 +55,7 @@ export default function manifestFromObject(o: any): PluginManifest { | ||||
| 		permissions: permissions, | ||||
|  | ||||
| 		_recommended: getBoolean('_recommended', false, false), | ||||
| 		_built_in: getBoolean('_built_in', false, false), | ||||
| 	}; | ||||
|  | ||||
| 	validatePluginId(manifest.id); | ||||
|   | ||||
| @@ -29,4 +29,5 @@ export interface PluginManifest { | ||||
| 	_npm_package_name?: string; | ||||
| 	_obsolete?: boolean; | ||||
| 	_recommended?: boolean; | ||||
| 	_built_in?: boolean; | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user