Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into pr/dont-overwrit…
Browse files Browse the repository at this point in the history
…e-home-dir
  • Loading branch information
personalizedrefrigerator committed Feb 19, 2024
2 parents a4fd675 + d9254e0 commit f607110
Show file tree
Hide file tree
Showing 11 changed files with 119 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/buildAndTest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: "16"
node-version: "20"
- name: Install dependencies
run: npm install
- name: Build
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## not released

- Renamed Plugin from `Simple Backup` to `Backup`

## v1.3.6 (2024-01-11)

- Add: Screenshots / icon for [https://joplinapp.org/plugins/](https://joplinapp.org/plugins/)
Expand Down
41 changes: 21 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Joplin Backup Plugin <img src=img/icon_32.png>
# Joplin Plugin: Backup <img src=img/icon_32.png>

A plugin to extend Joplin with a manual and automatic backup function.

Expand All @@ -21,11 +21,13 @@ A plugin to extend Joplin with a manual and automatic backup function.
- [Restore](#restore)
- [Settings](#settings)
- [Notes](#notes)
- [Restore a singel note](#restore-a-singel-note)
- [FAQ](#faq)
- [Internal Joplin links betwen notes are lost](#internal-joplin-links-betwen-notes-are-lost)
- [Combine multiple JEX Files to one](#combine-multiple-jex-files-to-one)
- [Open a JEX Backup file](#open-a-jex-backup-file)
- [Are Note History Revisions backed up?](#are-note-history-revisions-backed-up)
- [Are all Joplin profiles backed up?](#are-all-joplin-profiles-backed-up)
- [Changelog](#changelog)
- [Links](#links)

Expand All @@ -37,7 +39,7 @@ A plugin to extend Joplin with a manual and automatic backup function.
### Automatic

- Go to `Tools > Options > Plugins`
- Search for `Simple Backup`
- Search for `Backup`
- Click Install plugin
- Restart Joplin to enable the plugin

Expand All @@ -51,6 +53,7 @@ A plugin to extend Joplin with a manual and automatic backup function.
## Usage

First configure the Plugin under `Tools > Options > Backup`!
The plugin must be configured separately for each Joplin profile.

Backups can be created manually with the command `Tools > Create backup` or are created automatically based on the configured interval.
The backup started manually by `Create backup` respects all the settings except for the `Backups interval in hours`.
Expand All @@ -59,24 +62,6 @@ The backup started manually by `Create backup` respects all the settings except

Go to `Tools > Options > Backup`

| Option | Description | Default |
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- |
| `Backup path` | Where to save the backups to. <br>This path is exclusive for the Joplin backups, there should be no other data in it when you disable the `Create Subfolder` settings! | |
| `Keep x backups` | How many backups should be kept | `1` |
| `Backups interval in hours` | Create a backup every X hours | `24` |
| `Only on change` | Creates a backup at the specified backup interval only if there was a change to a `note`, `tag`, `resource` or `notebook` | `false` |
| `Password protected backups` | Protect the backups via encrypted Zip archive. | `false` |
| `Logfile` | Loglevel for backup.log | `error` |
| `Create zip archive` | Save backup data in a Zip archive | `No` |
| `Zip compression Level` | Compression level for zip archive archive | `Copy (no compression)` |
| `Temporary export path` | The data is first exported into this path before it is copied to the backup `Backup path`. | `` |
| `Backup set name` | Name of the backup set if multiple backups are to be keep. [Available moment tokens](https://momentjs.com/docs/#/displaying/format/), which can be used with `{<TOKEN>}` | `{YYYYMMDDHHmm}` |
| `Single JEX` | Create only one JEX file for all, this option is recommended to prevent the loss of internal note links or folder structure during a restore! | `true` |
| `Export format` | Selection of the export format of the notes. | `jex` |
| `Command on Backup finish` | Execute command when backup is finished. | |
| `Create Subfolder` | Create a sub folder `JoplinBackup` in the configured `Backup path`. Deactivate only if there is no other data in the `Backup path`! | `true` |
| `Backup plugins` | Backup the plugin folder from the Joplin profile with all installed plugin jpl files. | `true` |

## Keyboard Shortcuts

Under `Options > Keyboard Shortcuts` you can assign a keyboard shortcut for the following commands:
Expand Down Expand Up @@ -111,6 +96,17 @@ The notes are imported via `File > Import > JEX - Joplin Export File`.
The notes are imported additionally, no check for duplicates is performed.
If the notebook in which the note was located already exists in your Joplin, then a "(1)" will be appended to the folder name.

### Restore a singel note

1. Create a new profile in Joplin via `File > Switch profile > Create new Profile`
2. Joplin switches automatically to the newly created profile
3. Import the Backup via `File > Import > JEX - Joplin Export File`
4. Search for the desired note
5. In the note overview, click on the note on the right and select `Export > JEX - Joplin Export File`
6. Save the file on your computer
7. Switch back to your orginal Joplin profil via `File > Switch profile > Default`
8. Import the exported note via `File > Import > JEX - Joplin Export File` and select the file from step 6

## FAQ

### Internal Joplin links betwen notes are lost
Expand All @@ -136,6 +132,11 @@ The file names in the archive correspond to the Joplin internal IDs.

The note history and file versions (revisions) are not included in the backup.

### Are all Joplin profiles backed up?

No, the backup must be configured for each profile.
Profiles that are not active are not backed up, even if a backup has been configured.

## Changelog

See [CHANGELOG.md](CHANGELOG.md)
Expand Down
42 changes: 41 additions & 1 deletion __test__/backup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ let spyOnLogWarn = null;
let spyOnLogError = null;
let spyOnShowError = null;
let spyOnSaveBackupInfo = null;
let spyOnDataGet = null;

const spyOnsSettingsValue = jest.spyOn(joplin.settings, "value");
const spyOnGlobalValue = jest.spyOn(joplin.settings, "globalValue");
Expand All @@ -51,7 +52,10 @@ describe("Backup", function () {
when(spyOnsSettingsValue)
.mockImplementation(() => Promise.resolve("no mockImplementation"))
.calledWith("fileLogLevel").mockImplementation(() => Promise.resolve("error"))
.calledWith("path").mockImplementation(() => Promise.resolve(testPath.backupBasePath));
.calledWith("path").mockImplementation(() => Promise.resolve(testPath.backupBasePath))
.calledWith("zipArchive").mockImplementation(() => "no")
.calledWith("execFinishCmd").mockImplementation(() => "")
.calledWith("usePassword").mockImplementation(() => false);

/* prettier-ignore */
when(spyOnGlobalValue)
Expand All @@ -60,6 +64,13 @@ describe("Backup", function () {
.calledWith("locale").mockImplementation(() => Promise.resolve("en_US"))
.calledWith("templateDir").mockImplementation(() => Promise.resolve(testPath.templates));

spyOnDataGet = jest
.spyOn(joplin.data, "get")
.mockImplementation(async (_path, _query) => ({
items: [],
hasMore: false,
}));

await createTestStructure();
backup = new Backup() as any;
backup.backupStartTime = new Date();
Expand Down Expand Up @@ -93,6 +104,7 @@ describe("Backup", function () {
spyOnShowError.mockReset();
spyOnsSettingsValue.mockReset();
spyOnGlobalValue.mockReset();
spyOnDataGet.mockReset();
spyOnSaveBackupInfo.mockReset();
});

Expand Down Expand Up @@ -1014,4 +1026,32 @@ describe("Backup", function () {
expect(backup.log.warn).toHaveBeenCalledTimes(0);
});
});

describe("create backup readme", () => {
it.each([{ backupRetention: 1 }, { backupRetention: 2 }])(
"should create a README.md in the backup directory (case %#)",
async ({ backupRetention }) => {
when(spyOnsSettingsValue)
.calledWith("backupRetention")
.mockImplementation(async () => backupRetention)
.calledWith("backupInfo")
.mockImplementation(() => Promise.resolve("[]"));

backup.backupStartTime = null;
await backup.start();

// Should exist and be non-empty
const readmePath = path.join(
testPath.backupBasePath,
"JoplinBackup",
"README.md"
);
expect(await fs.pathExists(readmePath)).toBe(true);
expect(await fs.readFile(readmePath, "utf8")).not.toBe("");

// Prevent "open handle" errors
backup.stopTimer();
}
);
});
});
14 changes: 7 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"husky": "^6.0.0",
"jest": "^27.0.4",
"jest-when": "^3.3.1",
"joplinplugindevtools": "^1.0.15",
"joplinplugindevtools": "^1.0.16",
"lint-staged": "^11.0.0",
"mime": "^2.5.2",
"on-build-webpack": "^0.1.0",
Expand Down
11 changes: 11 additions & 0 deletions src/Backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ class Backup {

const backupDst = await this.makeBackupSet();

await this.writeReadme(this.backupBasePath);
await joplin.settings.setValue(
"lastBackup",
this.backupStartTime.getTime()
Expand Down Expand Up @@ -690,6 +691,16 @@ class Backup {
}
}

private async writeReadme(backupFolder: string) {
const readmePath = path.join(backupFolder, "README.md");
this.log.info("writeReadme to", readmePath);
const readmeText = i18n.__(
"backupReadme",
this.backupStartTime.toLocaleString()
);
await fs.writeFile(readmePath, readmeText, "utf8");
}

private async backupNotebooks() {
const notebooks = await this.selectNotebooks();

Expand Down
29 changes: 16 additions & 13 deletions src/locales/de_DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,28 @@
},
"settings": {
"path": {
"label": "Sicherungs Pfad"
"label": "Sicherungs Pfad",
"description": "Speicherort für die Backups. "
},
"exportPath": {
"label": "Temporärer Export Pfad",
"description": "Temporärer Pfad für die Notizen währen des Exports, bevor diese in den Sicherungs Pfad verschoben werden"
"description": "Temporärer Pfad für den Datenexport aus Joplin, bevor die Daten in den %s verschoben werden"
},
"backupRetention": {
"label": "Behalte x Sicherungen",
"description": "Wenn mehr als eine Version konfiguriert ist, werden die Ordner im Sicherungspfad entsprechend der Einstellung `Sicherungsset Namen` erstellt"
"description": "Wie viele Sicherungen aufbewahrt werden sollen. Wenn mehr als eine Version konfiguriert ist, werden die Ordner im Sicherungspfad entsprechend der Einstellung 'Sicherungsset Namen' erstellt"
},
"backupInterval": {
"label": "Sicherungsinterval in Stunden",
"description": "0 = Automatisches Sicherung ist deaktivert"
},
"onlyOnChange": {
"label": "Nur bei änderung",
"description": "Erstellt eine Sicherung im angegebenen Sicherungsintervall nur dann, wenn es eine Änderung in den Notizen gab"
"description": "Erstellt eine Sicherung im angegebenen Sicherungsintervall nur dann, wenn es eine Änderung in den Notizen, Tags, Dateien oder Notizbücher gab"
},
"usePassword": {
"label": "Passwort geschütztes Sicherung",
"description": "Die Sicherung wird mittels verschlüsseltem ZIP Archive geschützt"
"description": "Die Sicherung wird mittels verschlüsseltem Archive geschützt"
},
"password": {
"label": "Passwort",
Expand All @@ -51,41 +52,43 @@
"description": "Wiederholen Sie das Passwort, um dieses zu bestätigen"
},
"fileLogLevel": {
"label": "Protokollierungsebene"
"label": "Protokollierungsebene",
"description": "Protokollierungsebene für die Backup Logdatei"
},
"createSubfolder": {
"label": "Erstellen eines Unterordners",
"description": "Erstellt einen Unterordner im konfigurierten `Sicherungs Pfad`. Nur deaktivieren, wenn sich keine weiteren Daten im `Sicherungs Pfad` befinden!"
"description": "Erstellt einen Unterordner im konfigurierten %s. Nur deaktivieren, wenn sich keine weiteren Daten im %s befinden!"
},
"zipArchive": {
"label": "Erstelle ein ZIP-Archive",
"description": "Wenn ein Passwortschutz für die Sicherung eingestellt ist, wird immer ein Zip-Archiv erstellt"
"label": "Erstelle ein Archive",
"description": "Backup Daten in einem Archiv speichern, wenn ein Passwortschutz für die Sicherung eingestellt ist wird immer ein Archiv erstellt"
},
"compressionLevel": {
"label": "ZIP Komprimierungsgrad",
"description": "Komprimierungsgrad für das ZIP-Archiv"
"description": "Komprimierungsgrad für das Archiv"
},
"backupSetName": {
"label": "Sicherungsset Namen",
"description": "Name des Sicherungssatzes, wenn mehrere Sicherungen aufbewahrt werden sollen"
"description": "Name des Sicherungssatzes, wenn mehrere Sicherungen aufbewahrt werden sollen. Moment Token (https://momentjs.com/docs/#/displaying/format/) können mittels {TOKEN} verwendet werden"
},
"backupPlugins": {
"label": "Plugins sichern",
"description": "Plugin jpl Dateien mit sichern (Es werden keine Plugin Einstellungen gesichert!)"
},
"exportFormat": {
"label": "Export Format",
"description": "Sicherungsformat für die Notizen"
"description": "Joplin Datenexportformat während der Sicherung"
},
"singleJex": {
"label": "Eine JEX Datei",
"description": "Erstellen nur eine JEX-Datei für alle Notizbücher (empfohlen, um den Verlust von internen Notizverknüpfungen und der Ordnerstruktur zu vermeiden)"
"description": "Erstellt nur eine JEX Datei (Empfohlen, um den Verlust interner Notizverknüpfungen oder der Ordnerstruktur bei einer Wiederherstellung zu vermeiden!)"
},
"execFinishCmd": {
"label": "Befehl nach der Sicherung",
"description": "Befehl/Program nach der Sicherung ausführen"
}
},
"backupReadme": "# Joplin Sicherung\n\nDieser Ordner enthält eine oder mehrere Sicherungen aus der Joplin Note Anwendung.\n\nSiehe [Backup documentation](https://joplinapp.org/plugins/plugin/io.github.jackgruber.backup/#restore) für Informationen wie eine Sicherung wieder hergestellt werden kann.",
"command": {
"createBackup": "Backup erstellen"
}
Expand Down
Loading

0 comments on commit f607110

Please sign in to comment.