mirror of
https://github.com/laurent22/joplin.git
synced 2025-02-01 19:15:01 +02:00
Desktop: Install default plugins on first app start (#6585)
This commit is contained in:
parent
1069d7d6fb
commit
01f4bb0591
@ -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.d.ts
|
||||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
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/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.d.ts
|
||||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
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.d.ts
|
||||||
packages/lib/services/plugins/api/types.js
|
packages/lib/services/plugins/api/types.js
|
||||||
packages/lib/services/plugins/api/types.js.map
|
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.d.ts
|
||||||
packages/lib/services/plugins/reducer.js
|
packages/lib/services/plugins/reducer.js
|
||||||
packages/lib/services/plugins/reducer.js.map
|
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.d.ts
|
||||||
packages/app-cli/tests/services/plugins/api/JoplinWorkspace.js
|
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/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.d.ts
|
||||||
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
packages/app-cli/tests/services/plugins/sandboxProxy.js
|
||||||
packages/app-cli/tests/services/plugins/sandboxProxy.js.map
|
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.d.ts
|
||||||
packages/lib/services/plugins/api/types.js
|
packages/lib/services/plugins/api/types.js
|
||||||
packages/lib/services/plugins/api/types.js.map
|
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.d.ts
|
||||||
packages/lib/services/plugins/reducer.js
|
packages/lib/services/plugins/reducer.js
|
||||||
packages/lib/services/plugins/reducer.js.map
|
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 appCommands from './commands/index';
|
||||||
import libCommands from '@joplin/lib/commands/index';
|
import libCommands from '@joplin/lib/commands/index';
|
||||||
import { homedir } from 'os';
|
import { homedir } from 'os';
|
||||||
|
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||||
const electronContextMenu = require('./services/electron-context-menu');
|
const electronContextMenu = require('./services/electron-context-menu');
|
||||||
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
|
// import populateDatabase from '@joplin/lib/services/debug/populateDatabase';
|
||||||
|
|
||||||
@ -63,6 +64,8 @@ import checkForUpdates from './checkForUpdates';
|
|||||||
import { AppState } from './app.reducer';
|
import { AppState } from './app.reducer';
|
||||||
import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
|
import syncDebugLog from '@joplin/lib/services/synchronizer/syncDebugLog';
|
||||||
import eventManager from '@joplin/lib/eventManager';
|
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';
|
// import { runIntegrationTests } from '@joplin/lib/services/e2ee/ppkTestUtils';
|
||||||
|
|
||||||
const pluginClasses = [
|
const pluginClasses = [
|
||||||
@ -260,9 +263,9 @@ class Application extends BaseApplication {
|
|||||||
const pluginRunner = new PluginRunner();
|
const pluginRunner = new PluginRunner();
|
||||||
service.initialize(packageInfo.version, PlatformImplementation.instance(), pluginRunner, this.store());
|
service.initialize(packageInfo.version, PlatformImplementation.instance(), pluginRunner, this.store());
|
||||||
service.isSafeMode = Setting.value('isSafeMode');
|
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
|
// Users can add and remove plugins from the config screen at any
|
||||||
// time, however we only effectively uninstall the plugin the next
|
// time, however we only effectively uninstall the plugin the next
|
||||||
@ -272,7 +275,11 @@ class Application extends BaseApplication {
|
|||||||
Setting.setValue('plugins.states', newSettings);
|
Setting.setValue('plugins.states', newSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkPreInstalledDefaultPlugins(defaultPluginsId, pluginSettings);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const pluginsDir = path.join(bridge().buildDir(), 'defaultPlugins');
|
||||||
|
pluginSettings = await installDefaultPlugins(service, pluginsDir, defaultPluginsId, pluginSettings);
|
||||||
if (await shim.fsDriver().exists(Setting.value('pluginDir'))) {
|
if (await shim.fsDriver().exists(Setting.value('pluginDir'))) {
|
||||||
await service.loadAndRunPlugins(Setting.value('pluginDir'), pluginSettings);
|
await service.loadAndRunPlugins(Setting.value('pluginDir'), pluginSettings);
|
||||||
}
|
}
|
||||||
@ -320,6 +327,7 @@ class Application extends BaseApplication {
|
|||||||
type: 'STARTUP_PLUGINS_LOADED',
|
type: 'STARTUP_PLUGINS_LOADED',
|
||||||
value: true,
|
value: true,
|
||||||
});
|
});
|
||||||
|
setSettingsForDefaultPlugins(getDefaultPluginsInfo());
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 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');
|
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||||
import ClipperConfigScreen from '../ClipperConfigScreen';
|
import ClipperConfigScreen from '../ClipperConfigScreen';
|
||||||
import restart from '../../services/restart';
|
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 { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||||
|
|
||||||
const settingKeyToControl: any = {
|
const settingKeyToControl: any = {
|
||||||
@ -66,6 +69,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
|||||||
this.switchSection(this.props.defaultSection);
|
this.switchSection(this.props.defaultSection);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
updateDefaultPluginsInstallState(getDefaultPluginsInstallState(PluginService.instance(), Object.keys(getDefaultPluginsInfo())), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleSettingButton(key: string) {
|
private async handleSettingButton(key: string) {
|
||||||
|
@ -31,7 +31,8 @@
|
|||||||
"afterSign": "./tools/notarizeMacApp.js",
|
"afterSign": "./tools/notarizeMacApp.js",
|
||||||
"extraResources": [
|
"extraResources": [
|
||||||
"build/icons/**",
|
"build/icons/**",
|
||||||
"build/images/**"
|
"build/images/**",
|
||||||
|
"build/defaultPlugins/**"
|
||||||
],
|
],
|
||||||
"afterAllArtifactBuild": "./generateSha512.js",
|
"afterAllArtifactBuild": "./generateSha512.js",
|
||||||
"asar": true,
|
"asar": true,
|
||||||
|
@ -1576,6 +1576,13 @@ class Setting extends BaseModel {
|
|||||||
public: false,
|
public: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
installedDefaultPlugins: {
|
||||||
|
value: [],
|
||||||
|
type: SettingItemType.Array,
|
||||||
|
public: false,
|
||||||
|
storage: SettingStorage.Database,
|
||||||
|
},
|
||||||
|
|
||||||
// 'featureFlag.syncAccurateTimestamps': {
|
// 'featureFlag.syncAccurateTimestamps': {
|
||||||
// value: false,
|
// value: false,
|
||||||
// type: SettingItemType.Bool,
|
// type: SettingItemType.Bool,
|
||||||
@ -1954,6 +1961,17 @@ class Setting extends BaseModel {
|
|||||||
return this.setValue(key, !this.value(key));
|
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) {
|
static objectValue(settingKey: string, objectKey: string, defaultValue: any = null) {
|
||||||
const o = this.value(settingKey);
|
const o = this.value(settingKey);
|
||||||
if (!o || !(objectKey in o)) return defaultValue;
|
if (!o || !(objectKey in o)) return defaultValue;
|
||||||
|
@ -28,6 +28,19 @@ export interface Plugins {
|
|||||||
[key: string]: Plugin;
|
[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 {
|
export interface PluginSetting {
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
@ -416,7 +429,7 @@ export default class PluginService extends BaseService {
|
|||||||
return this.installPluginFromRepo(repoApi, pluginId);
|
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}"`);
|
logger.info(`Installing plugin: "${jplPath}"`);
|
||||||
|
|
||||||
// Before moving the plugin to the profile directory, we load it
|
// 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);
|
await shim.fsDriver().copy(jplPath, destPath);
|
||||||
|
|
||||||
// Now load it from the profile directory
|
// Now load it from the profile directory
|
||||||
|
if (loadPlugin) {
|
||||||
const plugin = await this.loadPluginFromPath(destPath);
|
const plugin = await this.loadPluginFromPath(destPath);
|
||||||
if (!this.plugins_[plugin.id]) this.setPluginAt(plugin.id, plugin);
|
if (!this.plugins_[plugin.id]) this.setPluginAt(plugin.id, plugin);
|
||||||
return plugin;
|
return plugin;
|
||||||
|
} else { return null; }
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pluginPath(pluginId: string) {
|
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,
|
permissions: permissions,
|
||||||
|
|
||||||
_recommended: getBoolean('_recommended', false, false),
|
_recommended: getBoolean('_recommended', false, false),
|
||||||
|
_built_in: getBoolean('_built_in', false, false),
|
||||||
};
|
};
|
||||||
|
|
||||||
validatePluginId(manifest.id);
|
validatePluginId(manifest.id);
|
||||||
|
@ -29,4 +29,5 @@ export interface PluginManifest {
|
|||||||
_npm_package_name?: string;
|
_npm_package_name?: string;
|
||||||
_obsolete?: boolean;
|
_obsolete?: boolean;
|
||||||
_recommended?: boolean;
|
_recommended?: boolean;
|
||||||
|
_built_in?: boolean;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user