mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
Desktop: Do not allow installing plugins incompatible with current app version
This commit is contained in:
parent
351a05fb0d
commit
774be9cc0d
@ -26,6 +26,7 @@ interface Props {
|
||||
installState?: InstallState;
|
||||
updateState?: UpdateState;
|
||||
themeId: number;
|
||||
isCompatible: boolean;
|
||||
onToggle?: Function;
|
||||
onDelete?: Function;
|
||||
onInstall?: Function;
|
||||
@ -34,28 +35,20 @@ interface Props {
|
||||
|
||||
function manifestToItem(manifest: PluginManifest): PluginItem {
|
||||
return {
|
||||
id: manifest.id,
|
||||
name: manifest.name,
|
||||
version: manifest.version,
|
||||
description: manifest.description,
|
||||
manifest: manifest,
|
||||
enabled: true,
|
||||
deleted: false,
|
||||
devMode: false,
|
||||
hasBeenUpdated: false,
|
||||
homepage_url: manifest.homepage_url,
|
||||
};
|
||||
}
|
||||
|
||||
export interface PluginItem {
|
||||
id: string;
|
||||
name: string;
|
||||
version: string;
|
||||
description: string;
|
||||
manifest: PluginManifest;
|
||||
enabled: boolean;
|
||||
deleted: boolean;
|
||||
devMode: boolean;
|
||||
hasBeenUpdated: boolean;
|
||||
homepage_url: string;
|
||||
}
|
||||
|
||||
const CellRoot = styled.div`
|
||||
@ -71,6 +64,8 @@ const CellRoot = styled.div`
|
||||
margin-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 1px 1px 3px rgba(0,0,0,0.2);
|
||||
|
||||
opacity: ${props => props.isCompatible ? '1' : '0.6'};
|
||||
`;
|
||||
|
||||
const CellTop = styled.div`
|
||||
@ -91,6 +86,12 @@ const CellFooter = styled.div`
|
||||
flex-direction: row;
|
||||
`;
|
||||
|
||||
const NeedUpgradeMessage = styled.span`
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
color: ${props => props.theme.colorWarn};
|
||||
font-size: ${props => props.theme.fontSize}px;
|
||||
`;
|
||||
|
||||
const DevModeLabel = styled.div`
|
||||
border: 1px solid ${props => props.theme.color};
|
||||
border-radius: 4px;
|
||||
@ -132,8 +133,8 @@ export default function(props: Props) {
|
||||
const item = props.item ? props.item : manifestToItem(props.manifest);
|
||||
|
||||
const onNameClick = useCallback(() => {
|
||||
if (!props.item.homepage_url) return;
|
||||
bridge().openExternal(props.item.homepage_url);
|
||||
if (!props.item.manifest.homepage_url) return;
|
||||
bridge().openExternal(props.item.manifest.homepage_url);
|
||||
}, [props.item]);
|
||||
|
||||
// For plugins in dev mode things like enabling/disabling or
|
||||
@ -194,6 +195,16 @@ export default function(props: Props) {
|
||||
function renderFooter() {
|
||||
if (item.devMode) return null;
|
||||
|
||||
if (!props.isCompatible) {
|
||||
return (
|
||||
<CellFooter>
|
||||
<NeedUpgradeMessage>
|
||||
{_('Please upgrade Joplin to use this plugin')}
|
||||
</NeedUpgradeMessage>
|
||||
</CellFooter>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CellFooter>
|
||||
{renderDeleteButton()}
|
||||
@ -205,13 +216,13 @@ export default function(props: Props) {
|
||||
}
|
||||
|
||||
return (
|
||||
<CellRoot>
|
||||
<CellRoot isCompatible={props.isCompatible}>
|
||||
<CellTop>
|
||||
<StyledNameAndVersion mb={'5px'}><StyledName onClick={onNameClick} href="#" style={{ marginRight: 5 }}>{item.name} {item.deleted ? _('(%s)', 'Deleted') : ''}</StyledName><StyledVersion>v{item.version}</StyledVersion></StyledNameAndVersion>
|
||||
<StyledNameAndVersion mb={'5px'}><StyledName onClick={onNameClick} href="#" style={{ marginRight: 5 }}>{item.manifest.name} {item.deleted ? _('(%s)', 'Deleted') : ''}</StyledName><StyledVersion>v{item.manifest.version}</StyledVersion></StyledNameAndVersion>
|
||||
{renderToggleButton()}
|
||||
</CellTop>
|
||||
<CellContent>
|
||||
<StyledDescription>{item.description}</StyledDescription>
|
||||
<StyledDescription>{item.manifest.description}</StyledDescription>
|
||||
</CellContent>
|
||||
{renderFooter()}
|
||||
</CellRoot>
|
||||
|
@ -63,20 +63,16 @@ function usePluginItems(plugins: Plugins, settings: PluginSettings): PluginItem[
|
||||
};
|
||||
|
||||
output.push({
|
||||
id: pluginId,
|
||||
name: plugin.manifest.name,
|
||||
version: plugin.manifest.version,
|
||||
description: plugin.manifest.description,
|
||||
manifest: plugin.manifest,
|
||||
enabled: setting.enabled,
|
||||
deleted: setting.deleted,
|
||||
devMode: plugin.devMode,
|
||||
hasBeenUpdated: setting.hasBeenUpdated,
|
||||
homepage_url: plugin.manifest.homepage_url,
|
||||
});
|
||||
}
|
||||
|
||||
output.sort((a: PluginItem, b: PluginItem) => {
|
||||
return a.name < b.name ? -1 : +1;
|
||||
return a.manifest.name < b.manifest.name ? -1 : +1;
|
||||
});
|
||||
|
||||
return output;
|
||||
@ -134,12 +130,12 @@ export default function(props: Props) {
|
||||
|
||||
const onDelete = useCallback(async (event: any) => {
|
||||
const item: PluginItem = event.item;
|
||||
const confirm = await bridge().showConfirmMessageBox(_('Delete plugin "%s"?', item.name));
|
||||
const confirm = await bridge().showConfirmMessageBox(_('Delete plugin "%s"?', item.manifest.name));
|
||||
if (!confirm) return;
|
||||
|
||||
const newSettings = produce(pluginSettings, (draft: PluginSettings) => {
|
||||
if (!draft[item.id]) draft[item.id] = defaultPluginSetting();
|
||||
draft[item.id].deleted = true;
|
||||
if (!draft[item.manifest.id]) draft[item.manifest.id] = defaultPluginSetting();
|
||||
draft[item.manifest.id].deleted = true;
|
||||
});
|
||||
|
||||
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
|
||||
@ -149,8 +145,8 @@ export default function(props: Props) {
|
||||
const item: PluginItem = event.item;
|
||||
|
||||
const newSettings = produce(pluginSettings, (draft: PluginSettings) => {
|
||||
if (!draft[item.id]) draft[item.id] = defaultPluginSetting();
|
||||
draft[item.id].enabled = !draft[item.id].enabled;
|
||||
if (!draft[item.manifest.id]) draft[item.manifest.id] = defaultPluginSetting();
|
||||
draft[item.manifest.id].enabled = !draft[item.manifest.id].enabled;
|
||||
});
|
||||
|
||||
props.onChange({ value: pluginService.serializePluginSettings(newSettings) });
|
||||
@ -213,8 +209,8 @@ export default function(props: Props) {
|
||||
for (const item of items) {
|
||||
if (item.deleted) continue;
|
||||
|
||||
const isUpdating = updatingPluginsIds[item.id];
|
||||
const onUpdateHandler = canBeUpdatedPluginIds[item.id] ? onUpdate : null;
|
||||
const isUpdating = updatingPluginsIds[item.manifest.id];
|
||||
const onUpdateHandler = canBeUpdatedPluginIds[item.manifest.id] ? onUpdate : null;
|
||||
|
||||
let updateState = UpdateState.Idle;
|
||||
if (onUpdateHandler) updateState = UpdateState.CanUpdate;
|
||||
@ -222,10 +218,11 @@ export default function(props: Props) {
|
||||
if (item.hasBeenUpdated) updateState = UpdateState.HasBeenUpdated;
|
||||
|
||||
output.push(<PluginBox
|
||||
key={item.id}
|
||||
key={item.manifest.id}
|
||||
item={item}
|
||||
themeId={props.themeId}
|
||||
updateState={updateState}
|
||||
isCompatible={PluginService.instance().isCompatible(item.manifest.app_min_version)}
|
||||
onDelete={onDelete}
|
||||
onToggle={onToggle}
|
||||
onUpdate={onUpdateHandler}
|
||||
|
@ -6,7 +6,7 @@ import RepositoryApi from '@joplin/lib/services/plugins/RepositoryApi';
|
||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||
import { PluginManifest } from '@joplin/lib/services/plugins/utils/types';
|
||||
import PluginBox, { InstallState } from './PluginBox';
|
||||
import { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import useOnInstallHandler from './useOnInstallHandler';
|
||||
|
||||
@ -82,6 +82,7 @@ export default function(props: Props) {
|
||||
key={manifest.id}
|
||||
manifest={manifest}
|
||||
themeId={props.themeId}
|
||||
isCompatible={PluginService.instance().isCompatible(manifest.app_min_version)}
|
||||
onInstall={onInstall}
|
||||
installState={installState(manifest.id)}
|
||||
/>);
|
||||
|
@ -334,8 +334,12 @@ export default class PluginService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
public isCompatible(pluginVersion: string): boolean {
|
||||
return compareVersions(this.appVersion_, pluginVersion) >= 0;
|
||||
}
|
||||
|
||||
public async runPlugin(plugin: Plugin) {
|
||||
if (compareVersions(this.appVersion_, plugin.manifest.app_min_version) < 0) {
|
||||
if (!this.isCompatible(plugin.manifest.app_min_version)) {
|
||||
throw new Error(`Plugin "${plugin.id}" was disabled because it requires Joplin version ${plugin.manifest.app_min_version} and current version is ${this.appVersion_}.`);
|
||||
} else {
|
||||
this.store_.dispatch({
|
||||
|
Loading…
Reference in New Issue
Block a user