Skip to content

Commit 021085c

Browse files
committed
Merge branch 'develop'
2 parents 07fafb3 + cde1ec9 commit 021085c

27 files changed

+23209
-13986
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
## not released
44

5+
## v1.3.4 (2023-12-04)
6+
7+
- Fix: #61 On Jex backup, do not overwrite already exported notebooks if the notebooks name differs only in special characters
8+
- Fix: #62 Better error message if the Joplin profile directory is set as backup path
9+
- Update en_US translation by @montoner0
10+
511
## v1.3.3 (2023-07-08)
612

713
- Add: Workaround for bug #132 with `"` (double quotes) in the password where zip files with such a password can no longer be opened

GENERATOR_DOC.md

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# generator-joplin
1+
# Plugin development
22

3-
Scaffolds out a new Joplin plugin
3+
This documentation describes how to create a plugin, and how to work with the plugin builder framework and API.
44

55
## Installation
66

@@ -14,14 +14,9 @@ npm install -g generator-joplin
1414
Then generate your new project:
1515

1616
```bash
17-
yo joplin
17+
yo --node-package-manager npm joplin
1818
```
1919

20-
## Development
21-
22-
To test the generator for development purposes, follow the instructions there: https://yeoman.io/authoring/#running-the-generator
23-
This is a template to create a new Joplin plugin.
24-
2520
## Structure
2621

2722
The main two files you will want to look at are:
@@ -39,6 +34,10 @@ To build the plugin, simply run `npm run dist`.
3934

4035
The project is setup to use TypeScript, although you can change the configuration to use plain JavaScript.
4136

37+
## Updating the manifest version number
38+
39+
You can run `npm run updateVersion` to bump the patch part of the version number, so for example 1.0.3 will become 1.0.4. This script will update both the package.json and manifest.json version numbers so as to keep them in sync.
40+
4241
## Publishing the plugin
4342

4443
To publish the plugin, add it to npmjs.com by running `npm publish`. Later on, a script will pick up your plugin and add it automatically to the Joplin plugin repository as long as the package satisfies these conditions:
@@ -57,6 +56,13 @@ In general this command tries to do the right thing - in particular it's going t
5756

5857
The file that may cause problem is "webpack.config.js" because it's going to be overwritten. For that reason, if you want to change it, consider creating a separate JavaScript file and include it in webpack.config.js. That way, when you update, you only have to restore the line that include your file.
5958

59+
### Simple backup changes to `webpack.config.js`
60+
61+
To support including `7zip-bin` in the plugin's built `.jpl` file, the following changes are made:
62+
63+
- Added `node: { __dirname: 'mock' },` to the plugin `baseConfig`. This causes `7zip-bin` to return a more correct path to `7za` (e.g. `/linux/x64/7za` instead of `/tmp/.mount_Joplin/resources/app.asar/services/plugins/linux/x64/7za`).
64+
- Added a `new CopyPlugin({ ... })` to `pluginConfig`'s `plugins` object to copy `7zip-bin` from `node_modules` to the `dist` directory.
65+
6066
## External script files
6167

6268
By default, the compiler (webpack) is going to compile `src/index.ts` only (as well as any file it imports), and any other file will simply be copied to the plugin package. In some cases this is sufficient, however if you have [content scripts](https://joplinapp.org/api/references/plugin_api/classes/joplincontentscripts.html) or [webview scripts](https://joplinapp.org/api/references/plugin_api/classes/joplinviewspanels.html#addscript) you might want to compile them too, in particular in these two cases:
@@ -67,6 +73,13 @@ By default, the compiler (webpack) is going to compile `src/index.ts` only (as w
6773

6874
To get such an external script file to compile, you need to add it to the `extraScripts` array in `plugin.config.json`. The path you add should be relative to /src. For example, if you have a file in "/src/webviews/index.ts", the path should be set to "webviews/index.ts". Once compiled, the file will always be named with a .js extension. So you will get "webviews/index.js" in the plugin package, and that's the path you should use to reference the file.
6975

76+
## More information
77+
78+
- [Joplin Plugin API](https://joplinapp.org/api/references/plugin_api/classes/joplin.html)
79+
- [Joplin Data API](https://joplinapp.org/help/api/references/rest_api)
80+
- [Joplin Plugin Manifest](https://joplinapp.org/api/references/plugin_manifest/)
81+
- Ask for help on the [forum](https://discourse.joplinapp.org/) or our [Discord channel](https://discord.gg/VSj7AFHvpq)
82+
7083
## License
7184

7285
MIT © Laurent Cozic

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A plugin to extend Joplin with a manual and automatic backup function.
66
<!-- markdownlint-disable MD028 -->
77
<!-- markdownlint-disable MD007 -->
88

9-
<img src=img/main.jpg>
9+
<img src=img/main.png>
1010

1111
<!-- prettier-ignore-start -->
1212
<!-- TOC depthfrom:2 orderedlist:false -->

__test__/backup.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import * as path from "path";
44
import { when } from "jest-when";
55
import { sevenZip } from "../src/sevenZip";
66
import joplin from "api";
7-
import { I18n } from "i18n";
87

98
function getTestPaths(): any {
109
const testPath: any = {};
@@ -58,6 +57,7 @@ describe("Backup", function () {
5857
when(spyOnGlobalValue)
5958
.mockImplementation(() => Promise.resolve("no mockImplementation"))
6059
.calledWith("profileDir").mockImplementation(() => Promise.resolve(testPath.joplinProfile))
60+
.calledWith("locale").mockImplementation(() => Promise.resolve("en_US"))
6161
.calledWith("templateDir").mockImplementation(() => Promise.resolve(testPath.templates));
6262

6363
await createTestStructure();
@@ -81,6 +81,8 @@ describe("Backup", function () {
8181
spyOnShowError = jest
8282
.spyOn(backup, "showError")
8383
.mockImplementation(() => {});
84+
85+
await backup.confLocale(path.join(__dirname, "..", "src", "locales"));
8486
});
8587

8688
afterEach(async () => {

api/Joplin.d.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import JoplinSettings from './JoplinSettings';
1010
import JoplinContentScripts from './JoplinContentScripts';
1111
import JoplinClipboard from './JoplinClipboard';
1212
import JoplinWindow from './JoplinWindow';
13+
import BasePlatformImplementation from '../BasePlatformImplementation';
14+
import JoplinImaging from './JoplinImaging';
1315
/**
1416
* This is the main entry point to the Joplin API. You can access various services using the provided accessors.
1517
*
@@ -25,6 +27,7 @@ import JoplinWindow from './JoplinWindow';
2527
export default class Joplin {
2628
private data_;
2729
private plugins_;
30+
private imaging_;
2831
private workspace_;
2932
private filters_;
3033
private commands_;
@@ -34,9 +37,11 @@ export default class Joplin {
3437
private contentScripts_;
3538
private clipboard_;
3639
private window_;
37-
constructor(implementation: any, plugin: Plugin, store: any);
40+
private implementation_;
41+
constructor(implementation: BasePlatformImplementation, plugin: Plugin, store: any);
3842
get data(): JoplinData;
3943
get clipboard(): JoplinClipboard;
44+
get imaging(): JoplinImaging;
4045
get window(): JoplinWindow;
4146
get plugins(): JoplinPlugins;
4247
get workspace(): JoplinWorkspace;
@@ -52,7 +57,6 @@ export default class Joplin {
5257
get views(): JoplinViews;
5358
get interop(): JoplinInterop;
5459
get settings(): JoplinSettings;
55-
get versionInfo(): any;
5660
/**
5761
* It is not possible to bundle native packages with a plugin, because they
5862
* need to work cross-platforms. Instead access to certain useful native
@@ -66,4 +70,5 @@ export default class Joplin {
6670
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/nativeModule)
6771
*/
6872
require(_path: string): any;
73+
versionInfo(): Promise<import("./types").VersionInfo>;
6974
}

api/JoplinData.d.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ModelType } from '../../../BaseModel';
2+
import Plugin from '../Plugin';
23
import { Path } from './types';
34
/**
4-
* This module provides access to the Joplin data API: https://joplinapp.org/api/references/rest_api/
5+
* This module provides access to the Joplin data API: https://joplinapp.org/help/api/references/rest_api
56
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
67
* or to update them or delete them.
78
*
@@ -17,7 +18,7 @@ import { Path } from './types';
1718
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
1819
* * `files`: (Optional) Used to create new resources and associate them with files.
1920
*
20-
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/references/rest_api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
21+
* Please refer to the [Joplin API documentation](https://joplinapp.org/help/api/references/rest_api) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
2122
*
2223
* For example:
2324
*
@@ -39,6 +40,8 @@ import { Path } from './types';
3940
export default class JoplinData {
4041
private api_;
4142
private pathSegmentRegex_;
43+
private plugin;
44+
constructor(plugin: Plugin);
4245
private serializeApiBody;
4346
private pathToString;
4447
get(path: Path, query?: any): Promise<any>;
@@ -47,4 +50,24 @@ export default class JoplinData {
4750
delete(path: Path, query?: any): Promise<any>;
4851
itemType(itemId: string): Promise<ModelType>;
4952
resourcePath(resourceId: string): Promise<string>;
53+
/**
54+
* Gets an item user data. User data are key/value pairs. The `key` can be any
55+
* arbitrary string, while the `value` can be of any type supported by
56+
* [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description)
57+
*
58+
* User data is synchronised across devices, and each value wil be merged based on their timestamp:
59+
*
60+
* - If value is modified by client 1, then modified by client 2, it will take the value from client 2
61+
* - If value is modified by client 1, then deleted by client 2, the value will be deleted after merge
62+
* - If value is deleted by client 1, then updated by client 2, the value will be restored and set to the value from client 2 after merge
63+
*/
64+
userDataGet<T>(itemType: ModelType, itemId: string, key: string): Promise<T>;
65+
/**
66+
* Sets a note user data. See {@link JoplinData.userDataGet} for more details.
67+
*/
68+
userDataSet<T>(itemType: ModelType, itemId: string, key: string, value: T): Promise<void>;
69+
/**
70+
* Deletes a note user data. See {@link JoplinData.userDataGet} for more details.
71+
*/
72+
userDataDelete(itemType: ModelType, itemId: string, key: string): Promise<void>;
5073
}

api/JoplinImaging.d.ts

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Rectangle } from './types';
2+
export interface Implementation {
3+
nativeImage: any;
4+
}
5+
export interface CreateFromBufferOptions {
6+
width?: number;
7+
height?: number;
8+
scaleFactor?: number;
9+
}
10+
export interface ResizeOptions {
11+
width?: number;
12+
height?: number;
13+
quality?: 'good' | 'better' | 'best';
14+
}
15+
export type Handle = string;
16+
/**
17+
* Provides imaging functions to resize or process images. You create an image
18+
* using one of the `createFrom` functions, then use the other functions to
19+
* process the image.
20+
*
21+
* Images are associated with a handle which is what will be available to the
22+
* plugin. Once you are done with an image, free it using the `free()` function.
23+
*
24+
* [View the
25+
* example](https://github.com/laurent22/joplin/blob/dev/packages/app-cli/tests/support/plugins/imaging/src/index.ts)
26+
*
27+
*/
28+
export default class JoplinImaging {
29+
private implementation_;
30+
private images_;
31+
constructor(implementation: Implementation);
32+
private createImageHandle;
33+
private imageByHandle;
34+
private cacheImage;
35+
createFromPath(filePath: string): Promise<Handle>;
36+
createFromResource(resourceId: string): Promise<Handle>;
37+
getSize(handle: Handle): Promise<any>;
38+
resize(handle: Handle, options?: ResizeOptions): Promise<string>;
39+
crop(handle: Handle, rectange: Rectangle): Promise<string>;
40+
toPngFile(handle: Handle, filePath: string): Promise<void>;
41+
/**
42+
* Quality is between 0 and 100
43+
*/
44+
toJpgFile(handle: Handle, filePath: string, quality?: number): Promise<void>;
45+
private tempFilePath;
46+
/**
47+
* Creates a new Joplin resource from the image data. The image will be
48+
* first converted to a JPEG.
49+
*/
50+
toJpgResource(handle: Handle, resourceProps: any, quality?: number): Promise<import("../../database/types").ResourceEntity>;
51+
/**
52+
* Creates a new Joplin resource from the image data. The image will be
53+
* first converted to a PNG.
54+
*/
55+
toPngResource(handle: Handle, resourceProps: any): Promise<import("../../database/types").ResourceEntity>;
56+
/**
57+
* Image data is not automatically deleted by Joplin so make sure you call
58+
* this method on the handle once you are done.
59+
*/
60+
free(handle: Handle): Promise<void>;
61+
}

api/JoplinInterop.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { ExportModule, ImportModule } from './types';
99
*
1010
* See the documentation of the [[ExportModule]] and [[ImportModule]] for more information.
1111
*
12-
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
12+
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/help/api/references/rest_api
1313
*/
1414
export default class JoplinInterop {
1515
registerExportModule(module: ExportModule): Promise<void>;

api/JoplinSettings.d.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ export interface ChangeEvent {
66
*/
77
keys: string[];
88
}
9-
export declare type ChangeHandler = (event: ChangeEvent) => void;
9+
export type ChangeHandler = (event: ChangeEvent) => void;
10+
export declare const namespacedKey: (pluginId: string, key: string) => string;
1011
/**
1112
* This API allows registering new settings and setting sections, as well as getting and setting settings. Once a setting has been registered it will appear in the config screen and be editable by the user.
1213
*
@@ -19,8 +20,6 @@ export declare type ChangeHandler = (event: ChangeEvent) => void;
1920
export default class JoplinSettings {
2021
private plugin_;
2122
constructor(plugin: Plugin);
22-
private get keyPrefix();
23-
private namespacedKey;
2423
/**
2524
* Registers new settings.
2625
* Note that registering a setting item is dynamic and will be gone next time Joplin starts.

api/JoplinViews.d.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import JoplinViewsMenuItems from './JoplinViewsMenuItems';
44
import JoplinViewsMenus from './JoplinViewsMenus';
55
import JoplinViewsToolbarButtons from './JoplinViewsToolbarButtons';
66
import JoplinViewsPanels from './JoplinViewsPanels';
7+
import JoplinViewsNoteList from './JoplinViewsNoteList';
78
/**
89
* This namespace provides access to view-related services.
910
*
@@ -13,16 +14,18 @@ import JoplinViewsPanels from './JoplinViewsPanels';
1314
export default class JoplinViews {
1415
private store;
1516
private plugin;
16-
private dialogs_;
1717
private panels_;
1818
private menuItems_;
1919
private menus_;
2020
private toolbarButtons_;
21+
private dialogs_;
22+
private noteList_;
2123
private implementation_;
2224
constructor(implementation: any, plugin: Plugin, store: any);
2325
get dialogs(): JoplinViewsDialogs;
2426
get panels(): JoplinViewsPanels;
2527
get menuItems(): JoplinViewsMenuItems;
2628
get menus(): JoplinViewsMenus;
2729
get toolbarButtons(): JoplinViewsToolbarButtons;
30+
get noteList(): JoplinViewsNoteList;
2831
}

api/JoplinViewsDialogs.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ export default class JoplinViewsDialogs {
4343
* Displays a message box with OK/Cancel buttons. Returns the button index that was clicked - "0" for OK and "1" for "Cancel"
4444
*/
4545
showMessageBox(message: string): Promise<number>;
46+
/**
47+
* Displays a dialog to select a file or a directory. Same options and
48+
* output as
49+
* https://www.electronjs.org/docs/latest/api/dialog#dialogshowopendialogbrowserwindow-options
50+
*/
51+
showOpenDialog(options: any): Promise<any>;
4652
/**
4753
* Sets the dialog HTML content
4854
*/

api/JoplinViewsNoteList.d.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Store } from 'redux';
2+
import Plugin from '../Plugin';
3+
import { ListRenderer } from './noteListType';
4+
/**
5+
* This API allows you to customise how each note in the note list is rendered.
6+
* The renderer you implement follows a unidirectional data flow.
7+
*
8+
* The app provides the required dependencies whenever a note is updated - you
9+
* process these dependencies, and return some props, which are then passed to
10+
* your template and rendered. See [[[ListRenderer]]] for a detailed description
11+
* of each property of the renderer.
12+
*
13+
* [View the demo plugin](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/note_list_renderer)
14+
*
15+
* The default list renderer is implemented using the same API, so it worth checking it too:
16+
*
17+
* [Default list renderer](https://github.com/laurent22/joplin/tree/dev/packages/lib/services/noteList/defaultListRenderer.ts)
18+
*/
19+
export default class JoplinViewsNoteList {
20+
private plugin_;
21+
private store_;
22+
constructor(plugin: Plugin, store: Store);
23+
registerRenderer(renderer: ListRenderer): Promise<void>;
24+
}

api/JoplinWorkspace.d.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Disposable, MenuItem } from './types';
33
export interface EditContextMenuFilterObject {
44
items: MenuItem[];
55
}
6-
declare type FilterHandler<T> = (object: T) => Promise<void>;
6+
type FilterHandler<T> = (object: T) => Promise<void>;
77
declare enum ItemChangeEventType {
88
Create = 1,
99
Update = 2,
@@ -19,9 +19,9 @@ interface SyncStartEvent {
1919
interface ResourceChangeEvent {
2020
id: string;
2121
}
22-
declare type ItemChangeHandler = (event: ItemChangeEvent) => void;
23-
declare type SyncStartHandler = (event: SyncStartEvent) => void;
24-
declare type ResourceChangeHandler = (event: ResourceChangeEvent) => void;
22+
type ItemChangeHandler = (event: ItemChangeEvent) => void;
23+
type SyncStartHandler = (event: SyncStartEvent) => void;
24+
type ResourceChangeHandler = (event: ResourceChangeEvent) => void;
2525
/**
2626
* The workspace service provides access to all the parts of Joplin that
2727
* are being worked on - i.e. the currently selected notes or notebooks as

0 commit comments

Comments
 (0)