You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-09-02 20:46:21 +02:00
Compare commits
73 Commits
safe-mode-
...
linux-tags
Author | SHA1 | Date | |
---|---|---|---|
|
ad51985a69 | ||
|
403d770b1d | ||
|
a481bf1b53 | ||
|
0d32570c9e | ||
|
f017e99b02 | ||
|
a89d64d435 | ||
|
3a27086534 | ||
|
413c1e41b5 | ||
|
8b879464b8 | ||
|
97c9bbc1fe | ||
|
e5bebef7b2 | ||
|
73752c4b3f | ||
|
dcf7c9838d | ||
|
f325e7694b | ||
|
75d204c9ca | ||
|
cf4008951d | ||
|
d67818d096 | ||
|
6aaea8ad4f | ||
|
de41278096 | ||
|
f01ab70907 | ||
|
bbdb221a67 | ||
|
7d053f8c79 | ||
|
fcad0bf3ca | ||
|
58f929f6b5 | ||
|
943198c56e | ||
|
2112ad4004 | ||
|
5995dc81f3 | ||
|
104e752634 | ||
|
fc335cd15d | ||
|
45923ba0d3 | ||
|
8fefa99d81 | ||
|
85d652cd67 | ||
|
88e41e9c7d | ||
|
26750488d0 | ||
|
a0f582b2b9 | ||
|
917b53bec2 | ||
|
e44a93422a | ||
|
e115ef4259 | ||
|
bcec699124 | ||
|
d23c728a1a | ||
|
0a2d507dec | ||
|
0c08617606 | ||
|
29fba45c33 | ||
|
1071a455b6 | ||
|
57e4b36fd7 | ||
|
f08fa92294 | ||
|
3a8d87d292 | ||
|
53302c9e90 | ||
|
28a24d8c03 | ||
|
3e52411bc4 | ||
|
1548ea18e1 | ||
|
f8cd1ba8e5 | ||
|
d18a4be31f | ||
|
c56f270ed6 | ||
|
2bca3d1032 | ||
|
9f81d69c5e | ||
|
815419260d | ||
|
6729a3d51f | ||
|
6d8ce280dd | ||
|
9e5b455065 | ||
|
09cbac3019 | ||
|
5354ad3934 | ||
|
7754048b80 | ||
|
ffeeff260f | ||
|
71ea74d273 | ||
|
3a744c79ae | ||
|
d9ba27a1ec | ||
|
0a3540049c | ||
|
ab50ca9bbd | ||
|
0bee793ab8 | ||
|
89fc5e19d9 | ||
|
6a3bf51084 | ||
|
df1e298c84 |
@@ -66,6 +66,7 @@ packages/lib/welcomeAssets.js
|
||||
packages/plugins/**/api
|
||||
packages/plugins/**/dist
|
||||
packages/server/dist/
|
||||
packages/utils/dist/
|
||||
packages/tools/node_modules
|
||||
packages/tools/PortableAppsLauncher
|
||||
packages/turndown-plugin-gfm/
|
||||
@@ -862,6 +863,7 @@ packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-sponsors.js
|
||||
packages/tools/updateMarkdownDoc.js
|
||||
packages/tools/utils/discourse.js
|
||||
packages/tools/utils/loadSponsors.js
|
||||
packages/tools/utils/translation.js
|
||||
packages/tools/website/build.js
|
||||
packages/tools/website/buildTranslations.js
|
||||
|
2
.github/workflows/build-android.yml
vendored
2
.github/workflows/build-android.yml
vendored
@@ -6,6 +6,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
@@ -16,6 +17,7 @@ jobs:
|
||||
concurrent_skipping: 'same_content_newer'
|
||||
|
||||
BuildAndroidDebug:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
|
1
.github/workflows/cla.yml
vendored
1
.github/workflows/cla.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
|
||||
jobs:
|
||||
CLAAssistant:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
|
1
.github/workflows/close-stale-issues.yml
vendored
1
.github/workflows/close-stale-issues.yml
vendored
@@ -6,6 +6,7 @@ permissions:
|
||||
issues: write
|
||||
jobs:
|
||||
ProcessStaleIssues:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
|
5
.github/workflows/github-actions-main.yml
vendored
5
.github/workflows/github-actions-main.yml
vendored
@@ -2,6 +2,7 @@ name: Joplin Continuous Integration
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
pre_job:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
@@ -14,7 +15,7 @@ jobs:
|
||||
Main:
|
||||
needs: pre_job
|
||||
# We always process server or desktop release tags, because they also publish the release
|
||||
if: needs.pre_job.outputs.should_skip != 'true' || startsWith(github.ref, 'refs/tags/server-v') || startsWith(github.ref, 'refs/tags/v')
|
||||
if: github.repository == 'laurent22/joplin' && (needs.pre_job.outputs.should_skip != 'true' || startsWith(github.ref, 'refs/tags/server-v') || startsWith(github.ref, 'refs/tags/v'))
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -129,7 +130,7 @@ jobs:
|
||||
|
||||
ServerDockerImage:
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
if: github.repository == 'laurent22/joplin' && needs.pre_job.outputs.should_skip != 'true'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -850,6 +850,7 @@ packages/tools/update-readme-download.js
|
||||
packages/tools/update-readme-sponsors.js
|
||||
packages/tools/updateMarkdownDoc.js
|
||||
packages/tools/utils/discourse.js
|
||||
packages/tools/utils/loadSponsors.js
|
||||
packages/tools/utils/translation.js
|
||||
packages/tools/website/build.js
|
||||
packages/tools/website/buildTranslations.js
|
||||
|
@@ -14,7 +14,8 @@
|
||||
"@joplin/turndown-plugin-gfm",
|
||||
"@joplin/tools",
|
||||
"@joplin/react-native-saf-x",
|
||||
"@joplin/react-native-alarm-notification"
|
||||
"@joplin/react-native-alarm-notification",
|
||||
"@joplin/utils"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
@@ -30,6 +30,7 @@ COPY packages/fork-uslug ./packages/fork-uslug
|
||||
COPY packages/htmlpack ./packages/htmlpack
|
||||
COPY packages/renderer ./packages/renderer
|
||||
COPY packages/tools ./packages/tools
|
||||
COPY packages/utils ./packages/utils
|
||||
COPY packages/lib ./packages/lib
|
||||
COPY packages/server ./packages/server
|
||||
|
||||
|
@@ -329,6 +329,7 @@
|
||||
"packages/renderer/MdToHtml/rules/sanitize_html.js": true,
|
||||
"packages/server/db-*.sqlite": true,
|
||||
"packages/server/dist/": true,
|
||||
"packages/utils/dist/": true,
|
||||
"packages/server/temp": true,
|
||||
"packages/server/test.pid": true,
|
||||
"phpunit.xml": true,
|
||||
|
@@ -74,12 +74,12 @@
|
||||
"eslint-plugin-jest": "27.2.1",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-react": "7.32.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"glob": "8.1.0",
|
||||
"gulp": "4.0.2",
|
||||
"husky": "3.1.0",
|
||||
"lerna": "3.22.1",
|
||||
"lint-staged": "13.1.2",
|
||||
"lint-staged": "13.2.0",
|
||||
"madge": "6.0.0",
|
||||
"npm-package-json-lint": "6.4.0",
|
||||
"typedoc": "0.17.8",
|
||||
|
@@ -8,7 +8,7 @@ const Resource = require('@joplin/lib/models/Resource').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const reducer = require('@joplin/lib/reducer').default;
|
||||
const { defaultState } = require('@joplin/lib/reducer');
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils.js');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
|
@@ -9,7 +9,8 @@ const Tag = require('@joplin/lib/models/Tag').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
const { fileExtension } = require('@joplin/lib/path-utils');
|
||||
const { splitCommandString, splitCommandBatch } = require('@joplin/lib/string-utils');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const { splitCommandBatch } = require('@joplin/lib/string-utils');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const fs = require('fs-extra');
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils.js');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const uuid = require('@joplin/lib/uuid').default;
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
|
@@ -30,22 +30,24 @@
|
||||
2019,
|
||||
2020,
|
||||
2021,
|
||||
2022
|
||||
2022,
|
||||
2023
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "2.10.3",
|
||||
"version": "2.11.0",
|
||||
"bin": "./main.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/renderer": "~2.10",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@joplin/utils": "~2.11",
|
||||
"aws-sdk": "2.1290.0",
|
||||
"chalk": "4.1.2",
|
||||
"compare-version": "0.1.2",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"html-entities": "1.4.0",
|
||||
"image-type": "3.1.0",
|
||||
"keytar": "7.9.0",
|
||||
@@ -57,7 +59,7 @@
|
||||
"server-destroy": "1.0.1",
|
||||
"sharp": "0.31.3",
|
||||
"sprintf-js": "1.1.2",
|
||||
"sqlite3": "5.1.4",
|
||||
"sqlite3": "5.1.6",
|
||||
"string-padding": "1.0.2",
|
||||
"strip-ansi": "6.0.1",
|
||||
"tcp-port-used": "1.0.2",
|
||||
@@ -68,7 +70,7 @@
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/jest": "29.2.6",
|
||||
"@types/node": "18.11.18",
|
||||
|
@@ -47,9 +47,11 @@ describe('services_plugins_RepositoryApi', () => {
|
||||
|
||||
it('should tell if a plugin can be updated', (async () => {
|
||||
const api = await newRepoApi();
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0')).toBe(true);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0')).toBe(false);
|
||||
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '3.0.0')).toBe(true);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '1.0.0')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2', '3.0.0')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0', '3.0.0')).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@@ -19,7 +19,7 @@
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import { homedir } from 'os';
|
||||
import { execCommand2 } from '@joplin/tools/tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { chdir } from 'process';
|
||||
|
||||
const minUserNum = 1;
|
||||
@@ -66,7 +66,7 @@ const processUser = async (userNum: number) => {
|
||||
|
||||
await chdir(cliDir);
|
||||
|
||||
await execCommand2(['yarn', 'run', 'start-no-build', '--', '--profile', profileDir, 'batch', commandFile]);
|
||||
await execCommand(['yarn', 'run', 'start-no-build', '--', '--profile', profileDir, 'batch', commandFile]);
|
||||
} catch (error) {
|
||||
console.error(`Could not process user ${userNum}:`, error);
|
||||
} finally {
|
||||
@@ -90,7 +90,7 @@ const main = async () => {
|
||||
|
||||
// Build the app once before starting, because we'll use start-no-build to
|
||||
// run the scripts (faster)
|
||||
await execCommand2(['yarn', 'run', 'build']);
|
||||
await execCommand(['yarn', 'run', 'build']);
|
||||
|
||||
const focusUserNum = 0;
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
|
@@ -143,7 +143,7 @@ export default function(props: Props) {
|
||||
let cancelled = false;
|
||||
|
||||
async function fetchPluginIds() {
|
||||
const pluginIds = await repoApi().canBeUpdatedPlugins(pluginItems.map(p => p.manifest));
|
||||
const pluginIds = await repoApi().canBeUpdatedPlugins(pluginItems.map(p => p.manifest), pluginService.appVersion);
|
||||
if (cancelled) return;
|
||||
const conv: Record<string, boolean> = {};
|
||||
pluginIds.forEach(id => conv[id] = true);
|
||||
@@ -155,7 +155,7 @@ export default function(props: Props) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [manifestsLoaded, pluginItems]);
|
||||
}, [manifestsLoaded, pluginItems, pluginService.appVersion]);
|
||||
|
||||
const onDelete = useCallback(async (event: ItemEvent) => {
|
||||
const item = event.item;
|
||||
|
@@ -489,7 +489,6 @@ function useMenu(props: Props) {
|
||||
function _showAbout() {
|
||||
const v = versionInfo(packageInfo, PluginService.instance().plugins);
|
||||
|
||||
|
||||
const copyToClipboard = bridge().showMessageBox(v.message, {
|
||||
icon: `${bridge().electronApp().buildDir()}/icons/128x128.png`,
|
||||
buttons: [_('Copy'), _('OK')],
|
||||
|
@@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react';
|
||||
import { ResourceInfos } from './types';
|
||||
import markupLanguageUtils from '../../../utils/markupLanguageUtils';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import shim from '@joplin/lib/shim';
|
||||
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
import Note from '@joplin/lib/models/Note';
|
||||
@@ -23,6 +24,7 @@ export interface MarkupToHtmlOptions {
|
||||
useCustomPdfViewer?: boolean;
|
||||
noteId?: string;
|
||||
vendorDir?: string;
|
||||
platformName?: string;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
@@ -40,6 +42,7 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
options = {
|
||||
replaceResourceInternalToExternalLinks: false,
|
||||
resourceInfos: {},
|
||||
platformName: shim.platformName(),
|
||||
...options,
|
||||
};
|
||||
|
||||
|
@@ -241,6 +241,7 @@ const NoteListComponent = (props: Props) => {
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
};
|
||||
|
||||
const renderItem = useCallback((item: any, index: number) => {
|
||||
|
@@ -12,9 +12,9 @@ const { connect } = require('react-redux');
|
||||
const styled = require('styled-components').default;
|
||||
|
||||
enum BaseBreakpoint {
|
||||
Sm = 160,
|
||||
Md = 190,
|
||||
Lg = 40,
|
||||
Sm = 75,
|
||||
Md = 80,
|
||||
Lg = 120,
|
||||
Xl = 474,
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ const StyledButton = styled(Button)`
|
||||
width: auto;
|
||||
height: 26px;
|
||||
min-height: 26px;
|
||||
min-width: 37px;
|
||||
max-width: none;
|
||||
white-space: nowrap;
|
||||
|
||||
.fa, .fas {
|
||||
font-size: 11px;
|
||||
|
@@ -48,6 +48,15 @@ const StyledFoldersHolder = styled.div`
|
||||
}}
|
||||
}
|
||||
`;
|
||||
const TagsHolder = styled.div`
|
||||
// linux bug: https://github.com/laurent22/joplin/issues/8000
|
||||
// solution ref: https://github.com/laurent22/joplin/issues/7506#issuecomment-1447101057
|
||||
& a.list-item {
|
||||
${shim.isLinux() && {
|
||||
opacity: 1,
|
||||
}}
|
||||
}
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
@@ -738,9 +747,9 @@ const SidebarComponent = (props: Props) => {
|
||||
tagItemsOrder_.current = result.order;
|
||||
|
||||
items.push(
|
||||
<div className="tags" key="tag_items" style={{ display: props.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
<TagsHolder className="tags" key="tag_items" style={{ display: props.tagHeaderIsExpanded ? 'block' : 'none' }}>
|
||||
{tagItems}
|
||||
</div>
|
||||
</TagsHolder>
|
||||
);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.10.10",
|
||||
"version": "2.11.1",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
@@ -107,7 +107,7 @@
|
||||
},
|
||||
"homepage": "https://github.com/laurent22/joplin#readme",
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@testing-library/react-hooks": "8.0.1",
|
||||
"@types/jest": "29.2.6",
|
||||
"@types/node": "18.11.18",
|
||||
@@ -136,9 +136,9 @@
|
||||
"@electron/remote": "2.0.9",
|
||||
"@fortawesome/fontawesome-free": "5.15.4",
|
||||
"@joeattardi/emoji-button": "4.6.4",
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/pdf-viewer": "~2.10",
|
||||
"@joplin/renderer": "~2.10",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/pdf-viewer": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"async-mutex": "0.4.0",
|
||||
"codemirror": "5.65.9",
|
||||
"color": "3.2.1",
|
||||
@@ -147,7 +147,7 @@
|
||||
"debounce": "1.2.1",
|
||||
"electron-window-state": "5.0.3",
|
||||
"formatcoords": "1.1.3",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"highlight.js": "11.7.0",
|
||||
"immer": "7.0.15",
|
||||
"keytar": "7.9.0",
|
||||
@@ -163,15 +163,15 @@
|
||||
"react-datetime": "3.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-redux": "8.0.5",
|
||||
"react-select": "5.7.0",
|
||||
"react-select": "5.7.1",
|
||||
"react-toggle-button": "2.2.0",
|
||||
"react-tooltip": "4.5.1",
|
||||
"redux": "4.2.1",
|
||||
"reselect": "4.1.7",
|
||||
"roboto-fontface": "0.10.0",
|
||||
"smalltalk": "2.5.1",
|
||||
"sqlite3": "5.1.4",
|
||||
"styled-components": "5.3.8",
|
||||
"sqlite3": "5.1.6",
|
||||
"styled-components": "5.3.9",
|
||||
"styled-system": "5.1.5",
|
||||
"taboverride": "4.0.3",
|
||||
"tinymce": "5.10.6"
|
||||
|
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
bracketSpacing: false,
|
||||
jsxBracketSameLine: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'all',
|
||||
};
|
@@ -150,8 +150,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097684
|
||||
versionName "2.10.8"
|
||||
versionCode 2097685
|
||||
versionName "2.11.0"
|
||||
// ndk {
|
||||
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
// }
|
||||
|
@@ -30,6 +30,7 @@ interface Props {
|
||||
initialSelection?: Selection;
|
||||
style: ViewStyle;
|
||||
contentStyle?: ViewStyle;
|
||||
toolbarEnabled: boolean;
|
||||
|
||||
onChange: ChangeEventHandler;
|
||||
onSelectionChange: SelectionChangeEventHandler;
|
||||
@@ -364,6 +365,19 @@ function NoteEditor(props: Props, ref: any) {
|
||||
console.error('NoteEditor: webview error');
|
||||
}, []);
|
||||
|
||||
const toolbar = <MarkdownToolbar
|
||||
style={{
|
||||
// Don't show the markdown toolbar if there isn't enough space
|
||||
// for it:
|
||||
flexShrink: 1,
|
||||
}}
|
||||
editorSettings={editorSettings}
|
||||
editorControl={editorControl}
|
||||
selectionState={selectionState}
|
||||
searchState={searchState}
|
||||
onAttach={props.onAttach}
|
||||
/>;
|
||||
|
||||
// - `scrollEnabled` prevents iOS from scrolling the document (has no effect on Android)
|
||||
// when an editable region (e.g. a the full-screen NoteEditor) is focused.
|
||||
return (
|
||||
@@ -401,18 +415,7 @@ function NoteEditor(props: Props, ref: any) {
|
||||
searchState={searchState}
|
||||
/>
|
||||
|
||||
<MarkdownToolbar
|
||||
style={{
|
||||
// Don't show the markdown toolbar if there isn't enough space
|
||||
// for it:
|
||||
flexShrink: 1,
|
||||
}}
|
||||
editorSettings={editorSettings}
|
||||
editorControl={editorControl}
|
||||
selectionState={selectionState}
|
||||
searchState={searchState}
|
||||
onAttach={props.onAttach}
|
||||
/>
|
||||
{props.toolbarEnabled ? toolbar : null}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
@@ -13,7 +13,11 @@ interface Props {
|
||||
}
|
||||
|
||||
export default (props: Props) => {
|
||||
const [initialPromptDone, setInitialPromptDone] = useState(Setting.value('security.biometricsInitialPromptDone'));
|
||||
// The initial prompt is there so that the user can choose to opt-in to
|
||||
// biometrics auth the first time the app is launched. However since it
|
||||
// doesn't work properly, we disable it. We only want the user to enable the
|
||||
// feature after they've read the description in the config screen.
|
||||
const [initialPromptDone, setInitialPromptDone] = useState(true); // useState(Setting.value('security.biometricsInitialPromptDone'));
|
||||
const [display, setDisplay] = useState(!!props.sensorInfo.supportedSensors && (props.sensorInfo.enabled || !initialPromptDone));
|
||||
const [tryBiometricsCheck, setTryBiometricsCheck] = useState(initialPromptDone);
|
||||
|
||||
|
@@ -8,6 +8,18 @@ export interface SensorInfo {
|
||||
}
|
||||
|
||||
export default async (): Promise<SensorInfo> => {
|
||||
// Early exit if the feature is disabled, so that we don't make any
|
||||
// FingerprintScanner scanner calls, since it seems they can fail and freeze
|
||||
// the app.
|
||||
|
||||
if (!Setting.value('security.biometricsEnabled')) {
|
||||
return {
|
||||
enabled: false,
|
||||
sensorsHaveChanged: false,
|
||||
supportedSensors: '',
|
||||
};
|
||||
}
|
||||
|
||||
let hasChanged = false;
|
||||
let supportedSensors = '';
|
||||
|
||||
|
@@ -1118,6 +1118,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
|
||||
bodyComponent = <NoteEditor
|
||||
ref={this.editorRef}
|
||||
toolbarEnabled={this.props.toolbarEnabled}
|
||||
themeId={this.props.themeId}
|
||||
initialText={note.body}
|
||||
initialSelection={this.selection}
|
||||
@@ -1231,6 +1232,7 @@ const NoteScreen = connect((state: any) => {
|
||||
themeId: state.settings.theme,
|
||||
editorFont: [state.settings['style.editor.fontFamily']],
|
||||
editorFontSize: state.settings['style.editor.fontSize'],
|
||||
toolbarEnabled: state.settings['editor.mobile.toolbarEnabled'],
|
||||
ftsEnabled: state.settings['db.ftsEnabled'],
|
||||
sharedData: state.sharedData,
|
||||
showSideMenu: state.showSideMenu,
|
||||
|
@@ -523,7 +523,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.10.5;
|
||||
MARKETING_VERSION = 12.11.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -551,7 +551,7 @@
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.10.5;
|
||||
MARKETING_VERSION = 12.11.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -705,7 +705,7 @@
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.10.5;
|
||||
MARKETING_VERSION = 12.11.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
@@ -736,7 +736,7 @@
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.10.5;
|
||||
MARKETING_VERSION = 12.11.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
@@ -306,7 +306,7 @@ PODS:
|
||||
- React-jsinspector (0.70.6)
|
||||
- React-logger (0.70.6):
|
||||
- glog
|
||||
- react-native-alarm-notification (2.10.0):
|
||||
- react-native-alarm-notification (2.11.0):
|
||||
- React
|
||||
- react-native-camera (4.2.1):
|
||||
- React-Core
|
||||
@@ -316,7 +316,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-camera/RN (4.2.1):
|
||||
- React-Core
|
||||
- react-native-document-picker (8.1.3):
|
||||
- react-native-document-picker (8.1.4):
|
||||
- React-Core
|
||||
- react-native-fingerprint-scanner (6.0.0):
|
||||
- React
|
||||
@@ -324,7 +324,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-get-random-values (1.8.0):
|
||||
- React-Core
|
||||
- react-native-image-picker (5.0.2):
|
||||
- react-native-image-picker (5.1.0):
|
||||
- React-Core
|
||||
- react-native-image-resizer (1.4.5):
|
||||
- React-Core
|
||||
@@ -332,7 +332,7 @@ PODS:
|
||||
- React-Core
|
||||
- react-native-rsa-native (2.0.5):
|
||||
- React
|
||||
- react-native-saf-x (2.10.2):
|
||||
- react-native-saf-x (2.11.0):
|
||||
- React-Core
|
||||
- react-native-safe-area-context (4.5.0):
|
||||
- RCT-Folly
|
||||
@@ -432,7 +432,7 @@ PODS:
|
||||
- React
|
||||
- RNSecureRandom (1.0.1):
|
||||
- React
|
||||
- RNShare (8.2.0):
|
||||
- RNShare (8.2.1):
|
||||
- React-Core
|
||||
- RNVectorIcons (9.2.0):
|
||||
- React-Core
|
||||
@@ -714,17 +714,17 @@ SPEC CHECKSUMS:
|
||||
React-jsiexecutor: b4a65947391c658450151275aa406f2b8263178f
|
||||
React-jsinspector: 60769e5a0a6d4b32294a2456077f59d0266f9a8b
|
||||
React-logger: 1623c216abaa88974afce404dc8f479406bbc3a0
|
||||
react-native-alarm-notification: 0f58eaa37a4188480536fd7ab62db9b1dfba392f
|
||||
react-native-alarm-notification: 26527410a6162d07a9dc57f4bbc62e94ff48e65d
|
||||
react-native-camera: 3eae183c1d111103963f3dd913b65d01aef8110f
|
||||
react-native-document-picker: 958e2bc82e128be69055be261aeac8d872c8d34c
|
||||
react-native-document-picker: a9bd26996d1b2e4f412dd186041714c79af381d0
|
||||
react-native-fingerprint-scanner: ac6656f18c8e45a7459302b84da41a44ad96dbbe
|
||||
react-native-geolocation: 69f4fd37650b8e7fee91816d395e62dd16f5ab8d
|
||||
react-native-get-random-values: a6ea6a8a65dc93e96e24a11105b1a9c8cfe1d72a
|
||||
react-native-image-picker: a5dddebb4d2955ac4712a4ed66b00a85f62a63ac
|
||||
react-native-image-picker: c33d4e79f0a14a2b66e5065e14946ae63749660b
|
||||
react-native-image-resizer: d9fb629a867335bdc13230ac2a58702bb8c8828f
|
||||
react-native-netinfo: 2517ad504b3d303e90d7a431b0fcaef76d207983
|
||||
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
||||
react-native-saf-x: db5a33862e7aec0f9f2d4cccfe7264b09b234e2e
|
||||
react-native-saf-x: 9bd5238d3b43d76bbec64aa82c173ac20a4bce9f
|
||||
react-native-safe-area-context: 39c2d8be3328df5d437ac1700f4f3a4f75716acc
|
||||
react-native-slider: 33b8d190b59d4f67a541061bb91775d53d617d9d
|
||||
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
|
||||
@@ -751,7 +751,7 @@ SPEC CHECKSUMS:
|
||||
RNFS: 4ac0f0ea233904cb798630b3c077808c06931688
|
||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
|
||||
RNShare: b089c33619bbfb0a32bc4069c858b9274e694187
|
||||
RNShare: eaee3dd5a06dad397c7d3b14762007035c5de405
|
||||
RNVectorIcons: fcc2f6cb32f5735b586e66d14103a74ce6ad61f8
|
||||
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
||||
Yoga: 99caf8d5ab45e9d637ee6e0174ec16fbbb01bcfc
|
||||
|
@@ -2,7 +2,7 @@
|
||||
"name": "@joplin/app-mobile",
|
||||
"description": "Joplin for Mobile",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "react-native start --reset-cache",
|
||||
@@ -18,10 +18,10 @@
|
||||
"postinstall": "jetify && yarn run build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/react-native-alarm-notification": "~2.10",
|
||||
"@joplin/react-native-saf-x": "~2.10",
|
||||
"@joplin/renderer": "~2.10",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/react-native-alarm-notification": "~2.11",
|
||||
"@joplin/react-native-saf-x": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@react-native-community/clipboard": "1.5.1",
|
||||
"@react-native-community/datetimepicker": "6.7.5",
|
||||
"@react-native-community/geolocation": "2.1.0",
|
||||
@@ -44,7 +44,7 @@
|
||||
"react-native-action-button": "2.8.5",
|
||||
"react-native-camera": "4.2.1",
|
||||
"react-native-dialogbox": "0.6.10",
|
||||
"react-native-document-picker": "8.1.3",
|
||||
"react-native-document-picker": "8.1.4",
|
||||
"react-native-dropdownalert": "4.5.1",
|
||||
"react-native-exit-app": "1.1.0",
|
||||
"react-native-file-viewer": "2.1.5",
|
||||
@@ -54,7 +54,7 @@
|
||||
"react-native-image-picker": "5.1.0",
|
||||
"react-native-image-resizer": "1.4.5",
|
||||
"react-native-modal-datetime-picker": "14.0.1",
|
||||
"react-native-paper": "5.2.0",
|
||||
"react-native-paper": "5.3.1",
|
||||
"react-native-popup-menu": "0.16.1",
|
||||
"react-native-quick-actions": "0.3.13",
|
||||
"react-native-rsa-native": "2.0.5",
|
||||
@@ -92,7 +92,7 @@
|
||||
"@codemirror/search": "6.2.3",
|
||||
"@codemirror/state": "6.1.4",
|
||||
"@codemirror/view": "6.7.1",
|
||||
"@joplin/tools": "~2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@lezer/highlight": "1.1.3",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/jest": "29.2.6",
|
||||
@@ -100,7 +100,7 @@
|
||||
"@types/react-redux": "7.1.25",
|
||||
"babel-plugin-module-resolver": "4.1.0",
|
||||
"execa": "4.1.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"gulp": "4.0.2",
|
||||
"jest": "29.4.3",
|
||||
"jest-environment-jsdom": "29.4.3",
|
||||
|
@@ -501,7 +501,7 @@ async function initialize(dispatch: Function) {
|
||||
if (Setting.value('env') === 'prod') {
|
||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
|
||||
} else {
|
||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile) });
|
||||
await db.open({ name: getDatabaseName(currentProfile, isSubProfile, '-1') });
|
||||
|
||||
// await db.clearForTesting();
|
||||
}
|
||||
|
@@ -24,9 +24,10 @@ export const getResourceDir = (profile: Profile, isSubProfile: boolean) => {
|
||||
return `${getProfilesRootDir()}/resources-${profile.id}`;
|
||||
};
|
||||
|
||||
export const getDatabaseName = (profile: Profile, isSubProfile: boolean) => {
|
||||
if (!isSubProfile) return 'joplin.sqlite';
|
||||
return `joplin-${profile.id}.sqlite`;
|
||||
// The suffix is for debugging only
|
||||
export const getDatabaseName = (profile: Profile, isSubProfile: boolean, suffix: string = '') => {
|
||||
if (!isSubProfile) return `joplin${suffix}.sqlite`;
|
||||
return `joplin-${profile.id}${suffix}.sqlite`;
|
||||
};
|
||||
|
||||
export const loadProfileConfig = async () => {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 1,
|
||||
"id": "<%= pluginId %>",
|
||||
"app_min_version": "2.10",
|
||||
"app_min_version": "2.11",
|
||||
"version": "1.0.0",
|
||||
"name": "<%= pluginName %>",
|
||||
"description": "<%= pluginDescription %>",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "generator-joplin",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"description": "Scaffolds out a new Joplin plugin",
|
||||
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
|
||||
"author": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/htmlpack",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "Pack an HTML file and all its linked resources into a single HTML file",
|
||||
"main": "dist/index.js",
|
||||
"types": "src/index.ts",
|
||||
@@ -17,7 +17,7 @@
|
||||
"@joplin/fork-htmlparser2": "^4.1.43",
|
||||
"css": "3.0.0",
|
||||
"datauri": "4.1.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"html-entities": "1.4.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@@ -20,7 +20,7 @@ import Folder from './models/Folder';
|
||||
import BaseItem from './models/BaseItem';
|
||||
import Note from './models/Note';
|
||||
import Tag from './models/Tag';
|
||||
const { splitCommandString } = require('./string-utils.js');
|
||||
import { splitCommandString } from '@joplin/utils';
|
||||
import { reg } from './registry';
|
||||
import time from './time';
|
||||
import BaseSyncTarget from './BaseSyncTarget';
|
||||
@@ -706,7 +706,7 @@ export default class BaseApplication {
|
||||
|
||||
flagContent = flagContent.trim();
|
||||
|
||||
let flags = splitCommandString(flagContent);
|
||||
let flags: any = splitCommandString(flagContent);
|
||||
flags.splice(0, 0, 'cmd');
|
||||
flags.splice(0, 0, 'node');
|
||||
|
||||
|
@@ -96,12 +96,14 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
let context = Setting.value(`sync.${this.syncTargetId()}.context`);
|
||||
context = context === '' ? null : JSON.parse(context);
|
||||
let accountProperties = context ? context.accountProperties : null;
|
||||
const api = this.api();
|
||||
|
||||
if (!accountProperties) {
|
||||
accountProperties = await this.api_.execAccountPropertiesRequest();
|
||||
accountProperties = await api.execAccountPropertiesRequest();
|
||||
context ? context.accountProperties = accountProperties : context = { accountProperties: accountProperties };
|
||||
Setting.setValue(`sync.${this.syncTargetId()}.context`, JSON.stringify(context));
|
||||
}
|
||||
this.api_.setAccountProperties(accountProperties);
|
||||
api.setAccountProperties(accountProperties);
|
||||
const appDir = await this.api().appDirectory();
|
||||
// the appDir might contain non-ASCII characters
|
||||
// /[^\u0021-\u00ff]/ is used in Node.js to detect the unescaped characters.
|
||||
|
@@ -1050,6 +1050,17 @@ class Setting extends BaseModel {
|
||||
isGlobal: true,
|
||||
},
|
||||
|
||||
'editor.mobile.toolbarEnabled': {
|
||||
value: true,
|
||||
type: SettingItemType.Bool,
|
||||
section: 'note',
|
||||
public: true,
|
||||
appTypes: [AppType.Mobile],
|
||||
label: () => _('Enable the Markdown toolbar'),
|
||||
storage: SettingStorage.File,
|
||||
isGlobal: true,
|
||||
},
|
||||
|
||||
// Works around a bug in which additional space is visible beneath the toolbar on some devices.
|
||||
// See https://github.com/laurent22/joplin/pull/6823
|
||||
'editor.mobile.removeSpaceBelowToolbar': {
|
||||
@@ -1634,10 +1645,17 @@ class Setting extends BaseModel {
|
||||
storage: SettingStorage.Database,
|
||||
},
|
||||
|
||||
// The biometrics feature is disabled by default and marked as beta
|
||||
// because it seems to cause a freeze or slow down startup on
|
||||
// certain devices. May be the reason for:
|
||||
//
|
||||
// - https://discourse.joplinapp.org/t/on-android-when-joplin-gets-started-offline/29951/1
|
||||
// - https://github.com/laurent22/joplin/issues/7956
|
||||
'security.biometricsEnabled': {
|
||||
value: false,
|
||||
type: SettingItemType.Bool,
|
||||
label: () => _('Use biometrics to secure access to the app'),
|
||||
label: () => `${_('Use biometrics to secure access to the app')} (Beta)`,
|
||||
description: () => 'Important: This is a beta feature and it is not compatible with certain devices. If the app no longer starts after enabling this or is very slow to start, please uninstall and reinstall the app.',
|
||||
public: true,
|
||||
appTypes: [AppType.Mobile],
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/lib",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "Joplin Core library",
|
||||
"author": "Laurent Cozic",
|
||||
"homepage": "",
|
||||
@@ -34,10 +34,11 @@
|
||||
"@joplin/fork-htmlparser2": "^4.1.43",
|
||||
"@joplin/fork-sax": "^1.2.47",
|
||||
"@joplin/fork-uslug": "^1.0.8",
|
||||
"@joplin/htmlpack": "^2.10.2",
|
||||
"@joplin/renderer": "^2.10.2",
|
||||
"@joplin/htmlpack": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@joplin/turndown": "^4.0.65",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.47",
|
||||
"@joplin/utils": "~2.11",
|
||||
"@types/nanoid": "3.0.0",
|
||||
"async-mutex": "0.4.0",
|
||||
"base-64": "1.0.0",
|
||||
@@ -53,7 +54,7 @@
|
||||
"fast-xml-parser": "3.21.1",
|
||||
"follow-redirects": "1.15.2",
|
||||
"form-data": "4.0.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"hpagent": "1.2.0",
|
||||
"html-entities": "1.4.0",
|
||||
"html-minifier": "4.0.0",
|
||||
@@ -82,7 +83,7 @@
|
||||
"reselect": "4.1.7",
|
||||
"server-destroy": "1.0.1",
|
||||
"sprintf-js": "1.1.2",
|
||||
"sqlite3": "5.1.4",
|
||||
"sqlite3": "5.1.6",
|
||||
"string-padding": "1.0.2",
|
||||
"string-to-stream": "3.0.1",
|
||||
"tar": "6.1.13",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
const { splitCommandString } = require('../../string-utils');
|
||||
import { splitCommandString } from '@joplin/utils';
|
||||
import { spawn } from 'child_process';
|
||||
import Logger from '../../Logger';
|
||||
import Setting from '../../models/Setting';
|
||||
|
@@ -108,6 +108,10 @@ export default class PluginService extends BaseService {
|
||||
return this.isSafeMode_;
|
||||
}
|
||||
|
||||
public get appVersion(): string {
|
||||
return this.appVersion_;
|
||||
}
|
||||
|
||||
public set isSafeMode(v: boolean) {
|
||||
this.isSafeMode_ = v;
|
||||
}
|
||||
|
@@ -215,21 +215,21 @@ export default class RepositoryApi {
|
||||
return this.manifests_;
|
||||
}
|
||||
|
||||
public async canBeUpdatedPlugins(installedManifests: PluginManifest[]): Promise<string[]> {
|
||||
public async canBeUpdatedPlugins(installedManifests: PluginManifest[], appVersion: string): Promise<string[]> {
|
||||
const output = [];
|
||||
|
||||
for (const manifest of installedManifests) {
|
||||
const canBe = await this.pluginCanBeUpdated(manifest.id, manifest.version);
|
||||
const canBe = await this.pluginCanBeUpdated(manifest.id, manifest.version, appVersion);
|
||||
if (canBe) output.push(manifest.id);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public async pluginCanBeUpdated(pluginId: string, installedVersion: string): Promise<boolean> {
|
||||
public async pluginCanBeUpdated(pluginId: string, installedVersion: string, appVersion: string): Promise<boolean> {
|
||||
const manifest = (await this.manifests()).find(m => m.id === pluginId);
|
||||
if (!manifest) return false;
|
||||
return compareVersions(installedVersion, manifest.version) < 0;
|
||||
return compareVersions(installedVersion, manifest.version) < 0 && compareVersions(appVersion, manifest.app_min_version) >= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -138,80 +138,6 @@ function commandArgumentsToString(args) {
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
function splitCommandString(command, options = null) {
|
||||
options = options || {};
|
||||
if (!('handleEscape' in options)) {
|
||||
options.handleEscape = true;
|
||||
}
|
||||
|
||||
const args = [];
|
||||
let state = 'start';
|
||||
let current = '';
|
||||
let quote = '"';
|
||||
let escapeNext = false;
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const c = command[i];
|
||||
|
||||
if (state === 'quotes') {
|
||||
if (c !== quote) {
|
||||
current += c;
|
||||
} else {
|
||||
args.push(current);
|
||||
current = '';
|
||||
state = 'start';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (escapeNext) {
|
||||
current += c;
|
||||
escapeNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === '\\' && options.handleEscape) {
|
||||
escapeNext = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === '"' || c === '\'') {
|
||||
state = 'quotes';
|
||||
quote = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state === 'arg') {
|
||||
if (c === ' ' || c === '\t') {
|
||||
args.push(current);
|
||||
current = '';
|
||||
state = 'start';
|
||||
} else {
|
||||
current += c;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c !== ' ' && c !== '\t') {
|
||||
state = 'arg';
|
||||
current += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (state === 'quotes') {
|
||||
throw new Error(`Unclosed quote in command line: ${command}`);
|
||||
}
|
||||
|
||||
if (current !== '') {
|
||||
args.push(current);
|
||||
}
|
||||
|
||||
if (args.length <= 0) {
|
||||
throw new Error('Empty command line');
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function splitCommandBatch(commandBatch) {
|
||||
const commandLines = [];
|
||||
const eol = '\n';
|
||||
@@ -368,4 +294,4 @@ function scriptType(s) {
|
||||
return 'en';
|
||||
}
|
||||
|
||||
module.exports = Object.assign({ formatCssSize, camelCaseToDash, removeDiacritics, substrWithEllipsis, nextWhitespaceIndex, escapeFilename, wrap, splitCommandString, splitCommandBatch, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType, commandArgumentsToString }, stringUtilsCommon);
|
||||
module.exports = Object.assign({ formatCssSize, camelCaseToDash, removeDiacritics, substrWithEllipsis, nextWhitespaceIndex, escapeFilename, wrap, splitCommandBatch, padLeft, toTitleCase, urlDecode, escapeHtml, surroundKeywords, scriptType, commandArgumentsToString }, stringUtilsCommon);
|
||||
|
@@ -2,6 +2,8 @@ import versionInfo from './versionInfo';
|
||||
import { reg } from './registry';
|
||||
import { Plugins } from './services/plugins/PluginService';
|
||||
import Plugin from './services/plugins/Plugin';
|
||||
import Setting from './models/Setting';
|
||||
import { PluginSettings } from './services/plugins/PluginService';
|
||||
|
||||
jest.mock('./registry');
|
||||
|
||||
@@ -64,6 +66,10 @@ describe('getPluginLists', () => {
|
||||
const plugins: Plugins = {};
|
||||
plugins[plugin.manifest.id] = plugin;
|
||||
|
||||
const pluginSettings: PluginSettings = {};
|
||||
pluginSettings[plugin.id] = { enabled: true, deleted: false, hasBeenUpdated: false };
|
||||
Setting.setValue('plugins.states', pluginSettings);
|
||||
|
||||
const v = versionInfo(packageInfo, plugins);
|
||||
expect(v.body).toMatch(/\n\nPlugin1: 1/);
|
||||
expect(v.message).toMatch(/\n\nPlugin1: 1/);
|
||||
@@ -86,6 +92,13 @@ describe('getPluginLists', () => {
|
||||
);
|
||||
plugins[plugin.manifest.id] = plugin;
|
||||
}
|
||||
|
||||
const pluginSettings: PluginSettings = {};
|
||||
for (const key of Object.keys(plugins)) {
|
||||
pluginSettings[key] = { enabled: true, deleted: false, hasBeenUpdated: false };
|
||||
}
|
||||
Setting.setValue('plugins.states', pluginSettings);
|
||||
|
||||
const v = versionInfo(packageInfo, plugins);
|
||||
|
||||
expect(v.body).toMatch(/\n\nPlugin1: 1\nPlugin2: 1\nPlugin3: 1/);
|
||||
@@ -110,6 +123,13 @@ describe('getPluginLists', () => {
|
||||
|
||||
plugins[plugin.manifest.id] = plugin;
|
||||
}
|
||||
|
||||
const pluginSettings: PluginSettings = {};
|
||||
for (const key of Object.keys(plugins)) {
|
||||
pluginSettings[key] = { enabled: true, deleted: false, hasBeenUpdated: false };
|
||||
}
|
||||
Setting.setValue('plugins.states', pluginSettings);
|
||||
|
||||
const v = versionInfo(packageInfo, plugins);
|
||||
|
||||
const body = '\n';
|
||||
|
@@ -2,6 +2,7 @@ import { _ } from './locale';
|
||||
import Setting from './models/Setting';
|
||||
import { reg } from './registry';
|
||||
import { Plugins } from './services/plugins/PluginService';
|
||||
import PluginService from './services/plugins/PluginService';
|
||||
|
||||
interface PluginList {
|
||||
completeList: string;
|
||||
@@ -10,12 +11,17 @@ interface PluginList {
|
||||
|
||||
function getPluginLists(plugins: Plugins): PluginList {
|
||||
const pluginList = [];
|
||||
if (Object.keys(plugins).length > 0) {
|
||||
for (const pluginId in plugins) {
|
||||
pluginList.push(`${plugins[pluginId].manifest.name}: ${plugins[pluginId].manifest.version}`);
|
||||
const pluginSettings = PluginService.instance().unserializePluginSettings(Setting.value('plugins.states'));
|
||||
const enabledPlugins = Object.fromEntries(Object.entries(plugins).filter((p) => pluginSettings[p[0]] && pluginSettings[p[0]].enabled === true));
|
||||
|
||||
if (Object.keys(enabledPlugins).length > 0) {
|
||||
for (const pluginId in enabledPlugins) {
|
||||
pluginList.push(`${enabledPlugins[pluginId].manifest.name}: ${enabledPlugins[pluginId].manifest.version}`);
|
||||
}
|
||||
}
|
||||
|
||||
pluginList.sort();
|
||||
|
||||
let completeList = '';
|
||||
let summary = '';
|
||||
if (pluginList.length > 0) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/pdf-viewer",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"description": "Provides embedded PDF viewers for Joplin",
|
||||
"main": "dist/main.js",
|
||||
"types": "src/main.ts",
|
||||
@@ -28,7 +28,7 @@
|
||||
"css-loader": "6.7.3",
|
||||
"jest": "29.4.3",
|
||||
"jest-environment-jsdom": "29.4.3",
|
||||
"style-loader": "3.3.1",
|
||||
"style-loader": "3.3.2",
|
||||
"ts-jest": "29.0.5",
|
||||
"ts-loader": "9.4.2",
|
||||
"typescript": "4.9.4",
|
||||
@@ -39,11 +39,11 @@
|
||||
"@fortawesome/fontawesome-svg-core": "6.1.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.1.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/lib": "~2.11",
|
||||
"async-mutex": "0.4.0",
|
||||
"pdfjs-dist": "2.16.105",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"styled-components": "5.3.8"
|
||||
"styled-components": "5.3.9"
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import * as path from 'path';
|
||||
import * as process from 'process';
|
||||
import validatePluginId from '@joplin/lib/services/plugins/utils/validatePluginId';
|
||||
import validatePluginVersion from '@joplin/lib/services/plugins/utils/validatePluginVersion';
|
||||
import { execCommand2, resolveRelativePathWithinDir, gitPullTry, gitRepoCleanTry, gitRepoClean } from '@joplin/tools/tool-utils.js';
|
||||
import { resolveRelativePathWithinDir, gitPullTry, gitRepoCleanTry, gitRepoClean } from '@joplin/tools/tool-utils.js';
|
||||
import checkIfPluginCanBeAdded from './lib/checkIfPluginCanBeAdded';
|
||||
import updateReadme from './lib/updateReadme';
|
||||
import { NpmPackage } from './lib/types';
|
||||
@@ -17,6 +17,7 @@ import gitCompareUrl from './lib/gitCompareUrl';
|
||||
import commandUpdateRelease from './commands/updateRelease';
|
||||
import { isJoplinPluginPackage, readJsonFile } from './lib/utils';
|
||||
import { applyManifestOverrides, getObsoleteManifests, readManifestOverrides } from './lib/overrideUtils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
|
||||
function pluginInfoFromSearchResults(results: any[]): NpmPackage[] {
|
||||
const output: NpmPackage[] = [];
|
||||
@@ -49,7 +50,7 @@ async function checkPluginRepository(dirPath: string, dryRun: boolean) {
|
||||
async function extractPluginFilesFromPackage(existingManifests: any, workDir: string, packageName: string, destDir: string): Promise<any> {
|
||||
const previousDir = chdir(workDir);
|
||||
|
||||
await execCommand2(`npm install ${packageName} --save --ignore-scripts`, { showStderr: false, showStdout: false });
|
||||
await execCommand(`npm install ${packageName} --save --ignore-scripts`, { showStderr: false, showStdout: false });
|
||||
|
||||
const pluginDir = resolveRelativePathWithinDir(workDir, 'node_modules', packageName, 'publish');
|
||||
|
||||
@@ -154,7 +155,7 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string, dryRun
|
||||
|
||||
await fs.mkdirp(packageTempDir);
|
||||
chdir(packageTempDir);
|
||||
await execCommand2('npm init --yes --loglevel silent', { quiet: true });
|
||||
await execCommand('npm init --yes --loglevel silent', { quiet: true });
|
||||
|
||||
let actionType: ProcessingActionType = ProcessingActionType.Update;
|
||||
let manifests: any = {};
|
||||
@@ -200,8 +201,8 @@ async function processNpmPackage(npmPackage: NpmPackage, repoDir: string, dryRun
|
||||
|
||||
if (!dryRun) {
|
||||
if (!(await gitRepoClean())) {
|
||||
await execCommand2('git add -A', { showStdout: false });
|
||||
await execCommand2(['git', 'commit', '-m', commitMessage(actionType, manifest, previousManifest, npmPackage, error)], { showStdout: false });
|
||||
await execCommand('git add -A', { showStdout: false });
|
||||
await execCommand(['git', 'commit', '-m', commitMessage(actionType, manifest, previousManifest, npmPackage, error)], { showStdout: false });
|
||||
} else {
|
||||
console.info('Nothing to commit');
|
||||
}
|
||||
@@ -227,14 +228,14 @@ async function commandBuild(args: CommandBuildArgs) {
|
||||
if (!dryRun) {
|
||||
if (!(await gitRepoClean())) {
|
||||
console.info('Updating README...');
|
||||
await execCommand2('git add -A');
|
||||
await execCommand2('git commit -m "Update README"');
|
||||
await execCommand('git add -A');
|
||||
await execCommand('git commit -m "Update README"');
|
||||
}
|
||||
}
|
||||
|
||||
chdir(previousDir);
|
||||
|
||||
const searchResults = (await execCommand2('npm search joplin-plugin --searchlimit 5000 --json', { showStdout: false, showStderr: false })).trim();
|
||||
const searchResults = (await execCommand('npm search joplin-plugin --searchlimit 5000 --json', { showStdout: false, showStderr: false })).trim();
|
||||
const npmPackages = pluginInfoFromSearchResults(JSON.parse(searchResults));
|
||||
|
||||
for (const npmPackage of npmPackages) {
|
||||
@@ -245,11 +246,11 @@ async function commandBuild(args: CommandBuildArgs) {
|
||||
await commandUpdateRelease(args);
|
||||
|
||||
if (!(await gitRepoClean())) {
|
||||
await execCommand2('git add -A');
|
||||
await execCommand2('git commit -m "Update stats"');
|
||||
await execCommand('git add -A');
|
||||
await execCommand('git commit -m "Update stats"');
|
||||
}
|
||||
|
||||
await execCommand2('git push');
|
||||
await execCommand('git push');
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/plugin-repo-cli",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": "./dist/index.js",
|
||||
@@ -18,9 +18,10 @@
|
||||
"author": "",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "^2.10.2",
|
||||
"@joplin/tools": "^2.10.2",
|
||||
"fs-extra": "11.1.0",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@joplin/utils": "~2.11",
|
||||
"fs-extra": "11.1.1",
|
||||
"gh-release-assets": "2.0.1",
|
||||
"node-fetch": "2.6.7",
|
||||
"source-map-support": "0.5.21",
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@joplin/react-native-alarm-notification",
|
||||
"title": "React Native Alarm Notification for Joplin. Forked from https://github.com/emekalites/react-native-alarm-notification",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"description": "schedule alarm with notification in react-native",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/react-native-saf-x",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "a module to help work with scoped storages on android easily",
|
||||
"main": "src/index",
|
||||
"react-native": "src/index",
|
||||
|
@@ -179,6 +179,8 @@ export interface RuleOptions {
|
||||
noteId?: string;
|
||||
vendorDir?: string;
|
||||
itemIdToUrl?: ItemIdToUrlHandler;
|
||||
|
||||
platformName?: string;
|
||||
}
|
||||
|
||||
export default class MdToHtml {
|
||||
|
@@ -1,6 +1,8 @@
|
||||
import { RuleOptions } from '../../MdToHtml';
|
||||
|
||||
export default {
|
||||
|
||||
assets: function() {
|
||||
assets: function(theme: any) {
|
||||
return [
|
||||
{ name: 'mermaid.min.js' },
|
||||
{ name: 'mermaid_render.js' },
|
||||
@@ -12,14 +14,27 @@ export default {
|
||||
text: '.mermaid { background-color: white; width: 640px; }',
|
||||
mime: 'text/css',
|
||||
},
|
||||
{
|
||||
inline: true,
|
||||
// Export button in mermaid graph should be shown only on hovering the mermaid graph
|
||||
// ref: https://github.com/laurent22/joplin/issues/6101
|
||||
text: `
|
||||
.mermaid-export-graph { visibility: hidden; }
|
||||
.joplin-editable:hover .mermaid-export-graph { visibility: visible; }
|
||||
.mermaid-export-graph:hover { background-color: ${theme.backgroundColorHover3} !important; }
|
||||
`.trim(),
|
||||
mime: 'text/css',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
plugin: function(markdownIt: any) {
|
||||
plugin: function(markdownIt: any, ruleOptions: RuleOptions) {
|
||||
const defaultRender: Function = markdownIt.renderer.rules.fence || function(tokens: any[], idx: number, options: any, env: any, self: any) {
|
||||
return self.renderToken(tokens, idx, options, env, self);
|
||||
};
|
||||
|
||||
const exportButtonMarkup = isDesktop(ruleOptions.platformName) ? exportGraphButton(ruleOptions) : '';
|
||||
|
||||
markdownIt.renderer.rules.fence = function(tokens: any[], idx: number, options: {}, env: any, self: any) {
|
||||
const token = tokens[idx];
|
||||
if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self);
|
||||
@@ -31,9 +46,49 @@ export default {
|
||||
return `
|
||||
<div class="joplin-editable">
|
||||
<pre class="joplin-source" data-joplin-language="mermaid" data-joplin-source-open="\`\`\`mermaid " data-joplin-source-close=" \`\`\` ">${contentHtml}</pre>
|
||||
${exportButtonMarkup}
|
||||
<pre class="mermaid">${contentHtml}</pre>
|
||||
</div>
|
||||
`;
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const exportGraphButton = (ruleOptions: RuleOptions) => {
|
||||
const theme = ruleOptions.theme;
|
||||
// Clicking on export button manually triggers a right click context menu event
|
||||
const onClickHandler = `
|
||||
const target = arguments[0].target;
|
||||
const button = target.closest("button.mermaid-export-graph");
|
||||
if (!button) return false;
|
||||
const $mermaid_elem = button.nextElementSibling;
|
||||
const rightClickEvent = new PointerEvent("contextmenu", {bubbles: true});
|
||||
rightClickEvent.target = $mermaid_elem;
|
||||
$mermaid_elem.dispatchEvent(rightClickEvent);
|
||||
return false;
|
||||
`.trim();
|
||||
const style = `
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
border-radius: ${theme.buttonStyle.borderRadius}px;
|
||||
font-size: ${theme.fontSize}px;
|
||||
color: ${theme.color};
|
||||
background: ${theme.buttonStyle.backgroundColor};
|
||||
border: ${theme.buttonStyle.border};
|
||||
`.trim();
|
||||
|
||||
return `<button class="mermaid-export-graph" onclick='${onClickHandler}' style="${style}" alt="Export mermaid graph">${downloadIcon()}</button>`;
|
||||
};
|
||||
|
||||
const downloadIcon = () => {
|
||||
// https://www.svgrepo.com/svg/505363/download
|
||||
return '<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" xmlns="http://www.w3.org/2000/svg"><g id="SVGRepo_bgCarrier" stroke-width="0"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g><g id="SVGRepo_iconCarrier"> <path d="M20 15V18C20 19.1046 19.1046 20 18 20H6C4.89543 20 4 19.1046 4 18L4 15M8 11L12 15M12 15L16 11M12 15V3" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>';
|
||||
};
|
||||
|
||||
const isDesktop = (platformName?: string) => {
|
||||
if (!platformName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ['darwin', 'linux', 'freebsd', 'win32'].includes(platformName);
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/renderer",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "The Joplin note renderer, used the mobile and desktop application",
|
||||
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/renderer",
|
||||
"main": "index.js",
|
||||
@@ -29,7 +29,7 @@
|
||||
"@joplin/fork-htmlparser2": "^4.1.43",
|
||||
"@joplin/fork-uslug": "^1.0.8",
|
||||
"font-awesome-filetypes": "2.1.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"highlight.js": "11.7.0",
|
||||
"html-entities": "1.4.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
@@ -43,7 +43,7 @@
|
||||
"markdown-it-footnote": "3.0.3",
|
||||
"markdown-it-ins": "3.0.1",
|
||||
"markdown-it-mark": "3.0.1",
|
||||
"markdown-it-multimd-table": "4.2.0",
|
||||
"markdown-it-multimd-table": "4.2.1",
|
||||
"markdown-it-sub": "1.0.0",
|
||||
"markdown-it-sup": "1.0.0",
|
||||
"markdown-it-toc-done-right": "4.2.0",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/server",
|
||||
"version": "2.10.10",
|
||||
"version": "2.11.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start-dev": "yarn run build && JOPLIN_IS_TESTING=1 nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",
|
||||
@@ -23,8 +23,8 @@
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.241.0",
|
||||
"@fortawesome/fontawesome-free": "5.15.4",
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/renderer": "~2.10",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@koa/cors": "3.1.0",
|
||||
"@types/uuid": "9.0.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
@@ -33,9 +33,9 @@
|
||||
"compare-versions": "3.6.0",
|
||||
"dayjs": "1.11.7",
|
||||
"formidable": "2.1.1",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"html-entities": "1.4.0",
|
||||
"jquery": "3.6.3",
|
||||
"jquery": "3.6.4",
|
||||
"knex": "2.4.2",
|
||||
"koa": "2.14.1",
|
||||
"markdown-it": "13.0.1",
|
||||
@@ -45,20 +45,20 @@
|
||||
"node-env-file": "0.1.8",
|
||||
"nodemailer": "6.9.1",
|
||||
"nodemon": "2.0.21",
|
||||
"pg": "8.9.0",
|
||||
"pg": "8.10.0",
|
||||
"pretty-bytes": "5.6.0",
|
||||
"prettycron": "0.10.0",
|
||||
"query-string": "7.1.3",
|
||||
"rate-limiter-flexible": "2.4.1",
|
||||
"raw-body": "2.5.2",
|
||||
"sqlite3": "5.1.4",
|
||||
"sqlite3": "5.1.6",
|
||||
"stripe": "8.222.0",
|
||||
"uuid": "9.0.0",
|
||||
"yargs": "17.7.1",
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@rmp135/sql-ts": "1.16.0",
|
||||
"@types/formidable": "2.0.5",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
@@ -75,7 +75,7 @@
|
||||
"jest": "29.4.3",
|
||||
"jest-expect-message": "1.1.3",
|
||||
"jsdom": "21.0.0",
|
||||
"node-mocks-http": "1.12.1",
|
||||
"node-mocks-http": "1.12.2",
|
||||
"source-map-support": "0.5.21",
|
||||
"typescript": "4.9.4"
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { execCommand2, rootDir } from './tool-utils';
|
||||
import { rootDir } from './tool-utils';
|
||||
import * as moment from 'moment';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
|
||||
interface Argv {
|
||||
dryRun?: boolean;
|
||||
@@ -35,7 +36,7 @@ async function main() {
|
||||
const buildDate = moment(new Date().getTime()).format('YYYY-MM-DDTHH:mm:ssZ');
|
||||
let revision = '';
|
||||
try {
|
||||
revision = await execCommand2('git rev-parse --short HEAD', { showStdout: false });
|
||||
revision = await execCommand('git rev-parse --short HEAD', { showStdout: false });
|
||||
} catch (error) {
|
||||
console.info('Could not get git commit: metadata revision field will be empty');
|
||||
}
|
||||
@@ -62,11 +63,11 @@ async function main() {
|
||||
return;
|
||||
}
|
||||
|
||||
await execCommand2(dockerCommand);
|
||||
await execCommand(dockerCommand);
|
||||
|
||||
for (const tag of dockerTags) {
|
||||
await execCommand2(`docker tag "${repository}:${imageVersion}" "${repository}:${tag}"`);
|
||||
if (pushImages) await execCommand2(`docker push ${repository}:${tag}`);
|
||||
await execCommand(`docker tag "${repository}:${imageVersion}" "${repository}:${tag}"`);
|
||||
if (pushImages) await execCommand(`docker push ${repository}:${tag}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,8 @@
|
||||
import { join } from 'path';
|
||||
import { execCommand2 } from './tool-utils';
|
||||
import { pathExists, mkdir, readFile, move, remove, writeFile } from 'fs-extra';
|
||||
import { DefaultPluginsInfo } from '@joplin/lib/services/plugins/PluginService';
|
||||
import getDefaultPluginsInfo from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
interface PluginAndVersion {
|
||||
@@ -41,7 +41,7 @@ async function downloadFile(url: string, outputPath: string) {
|
||||
|
||||
export async function extractPlugins(currentDir: string, defaultPluginDir: string, downloadedPluginsNames: PluginIdAndName): Promise<void> {
|
||||
for (const pluginId of Object.keys(downloadedPluginsNames)) {
|
||||
await execCommand2(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: true });
|
||||
await execCommand(`tar xzf ${currentDir}/${downloadedPluginsNames[pluginId]}`, { quiet: true });
|
||||
await move(`package/publish/${pluginId}.jpl`, `${defaultPluginDir}/${pluginId}/plugin.jpl`, { overwrite: true });
|
||||
await move(`package/publish/${pluginId}.json`, `${defaultPluginDir}/${pluginId}/manifest.json`, { overwrite: true });
|
||||
await remove(`${downloadedPluginsNames[pluginId]}`);
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { execCommand2, rootDir } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { rootDir } from './tool-utils';
|
||||
|
||||
const sqlts = require('@rmp135/sql-ts').default;
|
||||
const fs = require('fs-extra');
|
||||
@@ -6,7 +7,7 @@ const fs = require('fs-extra');
|
||||
async function main() {
|
||||
// Run the CLI app once so as to generate the database file
|
||||
process.chdir(`${rootDir}/packages/app-cli`);
|
||||
await execCommand2('yarn start version');
|
||||
await execCommand('yarn start version');
|
||||
|
||||
const sqlTsConfig = {
|
||||
'client': 'sqlite3',
|
||||
|
@@ -36,6 +36,7 @@ module.exports = {
|
||||
'packages/fork-sax/**',
|
||||
'packages/lib/plugin_types/**',
|
||||
'packages/server/**',
|
||||
'packages/utils/**',
|
||||
],
|
||||
}).filter(f => !f.endsWith('.d.ts'));
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { readdir, stat, writeFile } from 'fs-extra';
|
||||
import { chdir, cwd } from 'process';
|
||||
import { execCommand2, rootDir } from './tool-utils';
|
||||
import { rootDir } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import yargs = require('yargs');
|
||||
import { rtrimSlashes } from '@joplin/lib/path-utils';
|
||||
|
||||
@@ -13,7 +14,7 @@ interface LicenseInfo {
|
||||
const getLicenses = async (directory: string): Promise<Record<string, LicenseInfo>> => {
|
||||
const previousDir = cwd();
|
||||
await chdir(directory);
|
||||
const result = await execCommand2(['license-checker-rseidelsohn', '--production', '--json'], { quiet: true });
|
||||
const result = await execCommand(['license-checker-rseidelsohn', '--production', '--json'], { quiet: true });
|
||||
const info: Record<string, LicenseInfo> = JSON.parse(result);
|
||||
if (!info) throw new Error(`Could not parse JSON: ${directory}`);
|
||||
await chdir(previousDir);
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -91,12 +91,12 @@ msgstr "%d päivää"
|
||||
#: packages/lib/utils/joplinCloud.ts:136 packages/lib/utils/joplinCloud.ts:137
|
||||
#: packages/lib/utils/joplinCloud.ts:138
|
||||
msgid "%d GB"
|
||||
msgstr ""
|
||||
msgstr "%d Gt"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:133 packages/lib/utils/joplinCloud.ts:134
|
||||
#: packages/lib/utils/joplinCloud.ts:135
|
||||
msgid "%d GB storage space"
|
||||
msgstr ""
|
||||
msgstr "%d Gt tallennustila"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1345
|
||||
msgid "%d hour"
|
||||
@@ -109,13 +109,12 @@ msgstr "%d tuntia"
|
||||
#: packages/lib/utils/joplinCloud.ts:124 packages/lib/utils/joplinCloud.ts:125
|
||||
#: packages/lib/utils/joplinCloud.ts:126
|
||||
msgid "%d MB"
|
||||
msgstr ""
|
||||
msgstr "%d Mt"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:121 packages/lib/utils/joplinCloud.ts:122
|
||||
#: packages/lib/utils/joplinCloud.ts:123
|
||||
#, fuzzy
|
||||
msgid "%d MB per note or attachment"
|
||||
msgstr "Muistiinpanon liitteet"
|
||||
msgstr "%d Mt muistiinpanoa tai liitettä kohti"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1342 packages/lib/models/Setting.ts:1343
|
||||
#: packages/lib/models/Setting.ts:1344
|
||||
@@ -128,9 +127,8 @@ msgstr "%d muistiinpanot vastaavat tätä mallia. Poistetaanko ne?"
|
||||
|
||||
#: packages/app-desktop/gui/NoteListControls/NoteListControls.tsx:135
|
||||
#: packages/app-desktop/gui/NoteListControls/NoteListControls.tsx:144
|
||||
#, fuzzy
|
||||
msgid "%s"
|
||||
msgstr "(%s)"
|
||||
msgstr "%s"
|
||||
|
||||
#: packages/app-desktop/gui/utils/NoteListUtils.ts:61
|
||||
msgid "%s - Copy"
|
||||
@@ -323,6 +321,8 @@ msgstr "Lisätyökalut"
|
||||
msgid ""
|
||||
"All data, including notes, notebooks and tags will be permanently deleted."
|
||||
msgstr ""
|
||||
"Kaikki tiedot, mukaan lukien muistiinpanot, muistikirjat ja tunnisteet, "
|
||||
"poistetaan pysyvästi."
|
||||
|
||||
#: packages/app-desktop/gui/Sidebar/Sidebar.tsx:484
|
||||
#: packages/app-mobile/components/screens/Notes.tsx:203
|
||||
@@ -359,8 +359,8 @@ msgstr ""
|
||||
#: packages/app-cli/app/command-mkbook.ts:33
|
||||
#: packages/app-cli/app/command-mv.js:29
|
||||
msgid ""
|
||||
"Ambiguous notebook \"%s\". Please use short notebook id instead - press "
|
||||
"\"ti\" to see the short notebook id"
|
||||
"Ambiguous notebook \"%s\". Please use short notebook id instead - press \"ti"
|
||||
"\" to see the short notebook id"
|
||||
msgstr ""
|
||||
"Epäselvä muistikirja \"%s\". Käytä sen sijaan lyhyttä muistikirjan tunnusta "
|
||||
"- paina \"ti\" nähdäksesi lyhyen muistikirjan tunnuksen"
|
||||
@@ -390,6 +390,8 @@ msgid ""
|
||||
"Are you sure you want to return to the default layout? The current layout "
|
||||
"configuration will be lost."
|
||||
msgstr ""
|
||||
"Haluatko varmasti palata oletus ulkoasuun? Nykyinen ulkoasun määritys "
|
||||
"menetetään."
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx:517
|
||||
msgid "Arguments:"
|
||||
@@ -494,7 +496,7 @@ msgstr "Takaisin"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:294
|
||||
msgid "Basic"
|
||||
msgstr ""
|
||||
msgstr "Perus"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts:33
|
||||
#: packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.tsx:132
|
||||
@@ -598,9 +600,8 @@ msgid "Cannot find \"%s\"."
|
||||
msgstr "Ei löydy \"%s\"."
|
||||
|
||||
#: packages/app-cli/app/command-mkbook.ts:28
|
||||
#, fuzzy
|
||||
msgid "Cannot find: \"%s\""
|
||||
msgstr "Ei löydy \"%s\"."
|
||||
msgstr "Ei löydy: \"%s\""
|
||||
|
||||
#: packages/app-cli/app/command-sync.ts:164
|
||||
msgid "Cannot initialise synchroniser."
|
||||
@@ -743,9 +744,8 @@ msgid "Close"
|
||||
msgstr "Sulkea"
|
||||
|
||||
#: packages/app-mobile/components/Dropdown.tsx:166
|
||||
#, fuzzy
|
||||
msgid "Close dropdown"
|
||||
msgstr "Sulje ikkuna"
|
||||
msgstr "Sulje pudotusvalikko"
|
||||
|
||||
#: packages/app-desktop/gui/KeymapConfig/utils/getLabel.ts:24
|
||||
#: packages/app-desktop/gui/MenuBar.tsx:596
|
||||
@@ -857,7 +857,7 @@ msgstr "Ristiriidat (liitteet)"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:171
|
||||
msgid "Consolidated billing"
|
||||
msgstr ""
|
||||
msgstr "Yhdistetty laskutus"
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/SearchPlugins.tsx:110
|
||||
msgid "Content provided by %s"
|
||||
@@ -865,7 +865,7 @@ msgstr "Sisällön toimittaa %s"
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.tsx:64
|
||||
msgid "Continue"
|
||||
msgstr ""
|
||||
msgstr "Jatkaa"
|
||||
|
||||
#: packages/app-mobile/components/screens/Note.tsx:930
|
||||
msgid "Convert to note"
|
||||
@@ -970,9 +970,8 @@ msgstr ""
|
||||
"Virhe oli: \"%s\""
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.tsx:55
|
||||
#, fuzzy
|
||||
msgid "Could not switch profile: %s"
|
||||
msgstr "Laajennuksen asentaminen epäonnistui: %s"
|
||||
msgstr "Profiilia ei voitu vaihtaa: %s"
|
||||
|
||||
#: packages/lib/components/EncryptionConfigScreen/utils.ts:219
|
||||
msgid "Could not upgrade master key: %s"
|
||||
@@ -988,16 +987,15 @@ msgstr ""
|
||||
|
||||
#: packages/app-mobile/components/biometrics/BiometricPopup.tsx:29
|
||||
msgid "Could not verify your identify"
|
||||
msgstr ""
|
||||
msgstr "Henkilöllisyyttäsi ei voitu vahvistaa"
|
||||
|
||||
#: packages/app-desktop/gui/PromptDialog.tsx:260
|
||||
msgid "Create"
|
||||
msgstr "Luo"
|
||||
|
||||
#: packages/app-cli/app/command-mkbook.ts:19
|
||||
#, fuzzy
|
||||
msgid "Create a new notebook under a parent notebook."
|
||||
msgstr "Luo uuden muistikirjan."
|
||||
msgstr "Luo uusi muistikirja päämuistikirjan alle."
|
||||
|
||||
#: packages/app-mobile/components/note-list.js:101
|
||||
msgid "Create a notebook"
|
||||
@@ -1220,9 +1218,8 @@ msgid "Delete plugin \"%s\"?"
|
||||
msgstr "Poista laajennus \"%s\"?"
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.tsx:101
|
||||
#, fuzzy
|
||||
msgid "Delete profile \"%s\""
|
||||
msgstr "Poista muistiinpano \"%s\"?"
|
||||
msgstr "Poista profiili \"%s\""
|
||||
|
||||
#: packages/app-mobile/components/ScreenHeader.tsx:420
|
||||
msgid "Delete selected notes"
|
||||
@@ -1241,9 +1238,8 @@ msgstr ""
|
||||
"muistikirjaa."
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.tsx:97
|
||||
#, fuzzy
|
||||
msgid "Delete this profile?"
|
||||
msgstr "Poistetaanko nämä %d muistiinpanot?"
|
||||
msgstr "Poistetaanko tämä profiili?"
|
||||
|
||||
#: packages/lib/Synchronizer.ts:186
|
||||
msgid "Deleted local items: %d."
|
||||
@@ -1488,9 +1484,8 @@ msgid "Edit notebook"
|
||||
msgstr "Muokkaa muistikirjaa"
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileEditor.tsx:87
|
||||
#, fuzzy
|
||||
msgid "Edit profile"
|
||||
msgstr "Vie profiili"
|
||||
msgstr "Muokkaa profiilia"
|
||||
|
||||
#: packages/app-desktop/commands/editProfileConfig.ts:9
|
||||
msgid "Edit profile configuration..."
|
||||
@@ -1582,7 +1577,7 @@ msgstr "Ota käyttöön audiosoitin"
|
||||
|
||||
#: packages/app-mobile/components/biometrics/BiometricPopup.tsx:61
|
||||
msgid "Enable biometrics authentication?"
|
||||
msgstr ""
|
||||
msgstr "Otetaanko biometrinen todennus käyttöön?"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1146
|
||||
msgid "Enable deflist syntax"
|
||||
@@ -1635,7 +1630,7 @@ msgstr "Ota pehmeät tauot käyttöön"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1048
|
||||
msgid "Enable spellcheck in the text editor"
|
||||
msgstr ""
|
||||
msgstr "Ota oikeinkirjoituksen tarkistus käyttöön tekstieditorissa"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1143
|
||||
msgid "Enable table of contents extension"
|
||||
@@ -2298,7 +2293,7 @@ msgstr "Kohteet, joita ei voi synkronoida"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.tsx:792
|
||||
msgid "Join us on Twitter"
|
||||
msgstr ""
|
||||
msgstr "Liity meihin Twitterissä"
|
||||
|
||||
#: packages/app-desktop/gui/SyncWizard/Dialog.tsx:330
|
||||
msgid ""
|
||||
@@ -2547,14 +2542,12 @@ msgid "Manage master password..."
|
||||
msgstr "Pääsalasanan hallinta..."
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:165
|
||||
#, fuzzy
|
||||
msgid "Manage multiple users"
|
||||
msgstr "Pääsalasanan hallinta"
|
||||
msgstr "Hallitse useita käyttäjiä"
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:611
|
||||
#, fuzzy
|
||||
msgid "Manage profiles"
|
||||
msgstr "Päivitä profiili"
|
||||
msgstr "Hallitse profiileja"
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/controls/plugins/PluginsStates.tsx:320
|
||||
msgid "Manage your plugins"
|
||||
@@ -2621,9 +2614,8 @@ msgid "Max Item Size"
|
||||
msgstr "Kohteen enimmäiskoko"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:117
|
||||
#, fuzzy
|
||||
msgid "Max note or attachment size"
|
||||
msgstr "Muistiinpanon liitteet"
|
||||
msgstr "Muistiinpanon tai liitteen enimmäiskoko"
|
||||
|
||||
#: packages/server/src/routes/admin/users.ts:150
|
||||
msgid "Max Total Size"
|
||||
@@ -2642,9 +2634,8 @@ msgid "Missing required argument: %s"
|
||||
msgstr "Vaadittu argumentti puuttuu: %s"
|
||||
|
||||
#: packages/app-cli/app/cli-utils.js:135
|
||||
#, fuzzy
|
||||
msgid "Missing required flag value: %s"
|
||||
msgstr "Vaadittu argumentti puuttuu: %s"
|
||||
msgstr "Pakollinen lipun arvo puuttuu: %s"
|
||||
|
||||
#: packages/app-mobile/components/side-menu-content.tsx:457
|
||||
msgid "Mobile data - auto-sync disabled"
|
||||
@@ -2819,9 +2810,8 @@ msgid "Not generated"
|
||||
msgstr "Ei luotu"
|
||||
|
||||
#: packages/app-mobile/components/biometrics/BiometricPopup.tsx:70
|
||||
#, fuzzy
|
||||
msgid "Not now"
|
||||
msgstr "Hae se nyt"
|
||||
msgstr "Ei nyt"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteTitle/NoteTitleBar.tsx:110
|
||||
#: packages/server/src/models/UserModel.ts:215
|
||||
@@ -3084,7 +3074,7 @@ msgstr "Liitä"
|
||||
#: packages/app-desktop/gui/NoteEditor/commands/pasteAsText.ts:6
|
||||
#: packages/app-desktop/gui/NoteEditor/utils/contextMenu.ts:181
|
||||
msgid "Paste as text"
|
||||
msgstr ""
|
||||
msgstr "Liitä tekstinä"
|
||||
|
||||
#: packages/app-desktop/gui/ConfigScreen/ConfigScreen.tsx:541
|
||||
msgid "Path:"
|
||||
@@ -3097,7 +3087,7 @@ msgstr "PDF tiedosto"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:355
|
||||
msgid "Per user. Minimum of %d users."
|
||||
msgstr ""
|
||||
msgstr "Käyttäjää kohti. Vähintään %d käyttäjää."
|
||||
|
||||
#: packages/app-mobile/components/screens/Note.tsx:404
|
||||
msgid "Permission needed"
|
||||
@@ -3258,16 +3248,15 @@ msgstr "Tulosta"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:183
|
||||
msgid "Priority support"
|
||||
msgstr ""
|
||||
msgstr "Ensisijainen tuki"
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:703
|
||||
msgid "Privacy Policy"
|
||||
msgstr "Tietosuojakäytäntö"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:316
|
||||
#, fuzzy
|
||||
msgid "Pro"
|
||||
msgstr "Profiili"
|
||||
msgstr "Ammattilainen"
|
||||
|
||||
#: packages/server/src/services/TaskService.ts:24
|
||||
msgid "Process failed payment subscriptions"
|
||||
@@ -3286,9 +3275,8 @@ msgid "Profile"
|
||||
msgstr "Profiili"
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileEditor.tsx:95
|
||||
#, fuzzy
|
||||
msgid "Profile name"
|
||||
msgstr "Profiilin nimi:"
|
||||
msgstr "Profiilin nimi"
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/addProfile.ts:17
|
||||
msgid "Profile name:"
|
||||
@@ -3299,9 +3287,8 @@ msgid "Profile Version: %s"
|
||||
msgstr "Profiilin versio: %s"
|
||||
|
||||
#: packages/app-mobile/components/ProfileSwitcher/ProfileSwitcher.tsx:155
|
||||
#, fuzzy
|
||||
msgid "Profiles"
|
||||
msgstr "Profiili"
|
||||
msgstr "Profiilit"
|
||||
|
||||
#: packages/app-mobile/components/screens/Note.tsx:944
|
||||
msgid "Properties"
|
||||
@@ -3450,9 +3437,8 @@ msgid "Replace: "
|
||||
msgstr "Vaihda: "
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/resetLayout.ts:7
|
||||
#, fuzzy
|
||||
msgid "Reset application layout"
|
||||
msgstr "Sovelluksen asettelun muuttaminen"
|
||||
msgstr "Palauta sovelluksen asettelu"
|
||||
|
||||
#: packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx:219
|
||||
#: packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx:220
|
||||
@@ -3692,9 +3678,8 @@ msgid "Share"
|
||||
msgstr "Jaa"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:159
|
||||
#, fuzzy
|
||||
msgid "Share and collaborate on a notebook"
|
||||
msgstr "Muistiinpanoja voi luoda vain muistikirjaan."
|
||||
msgstr "Muistikirjan jakaminen ja yhteiskäyttö"
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx:339
|
||||
msgid "Share Notebook"
|
||||
@@ -3706,7 +3691,7 @@ msgstr "Muistikirjan jakaminen..."
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:177
|
||||
msgid "Sharing access control"
|
||||
msgstr ""
|
||||
msgstr "Käyttöoikeuksien hallinnan jakaminen"
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx:305
|
||||
msgid "Sharing notebook..."
|
||||
@@ -3932,7 +3917,7 @@ msgstr "Lopeta ulkoisen tekstieditorin muokkaus"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:129
|
||||
msgid "Storage space"
|
||||
msgstr ""
|
||||
msgstr "Tallennustila"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.ts:19
|
||||
msgid "Strikethrough"
|
||||
@@ -4000,7 +3985,7 @@ msgstr ""
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:147
|
||||
msgid "Sync as many devices as you want"
|
||||
msgstr ""
|
||||
msgstr "Synkronoi niin monta laitetta kuin haluat"
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:612
|
||||
msgid "Sync Status"
|
||||
@@ -4115,7 +4100,7 @@ msgstr "Tehtävät"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:338
|
||||
msgid "Teams"
|
||||
msgstr ""
|
||||
msgstr "Tiimit"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1366
|
||||
msgid "Text editor command"
|
||||
@@ -4130,6 +4115,8 @@ msgid ""
|
||||
"The active profile cannot be deleted. Switch to a different profile and try "
|
||||
"again."
|
||||
msgstr ""
|
||||
"Aktiivista profiilia ei voi poistaa. Vaihda toiseen profiiliin ja yritä "
|
||||
"uudelleen."
|
||||
|
||||
#: packages/app-desktop/bridge.ts:280
|
||||
msgid ""
|
||||
@@ -4195,7 +4182,7 @@ msgstr ""
|
||||
|
||||
#: packages/lib/services/profileConfig/index.ts:104
|
||||
msgid "The default profile cannot be deleted"
|
||||
msgstr ""
|
||||
msgstr "Oletusprofiilia ei voi poistaa"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1366
|
||||
msgid ""
|
||||
@@ -4343,6 +4330,8 @@ msgid ""
|
||||
"The WebDAV implementation of %s is incompatible with Joplin, and as such is "
|
||||
"no longer supported. Please use a different sync method."
|
||||
msgstr ""
|
||||
"%s WebDAV toteutus ei ole yhteensopiva Joplinin kanssa, joten sitä ei enää "
|
||||
"tueta. Käytä toista synkronointitapaa."
|
||||
|
||||
#: packages/lib/models/Setting.ts:827
|
||||
msgid "Theme"
|
||||
@@ -4356,7 +4345,7 @@ msgstr ""
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:341
|
||||
msgid "There are unsaved changes."
|
||||
msgstr ""
|
||||
msgstr "On tallentamattomia muutoksia."
|
||||
|
||||
#: packages/app-desktop/gui/NoteList/NoteList.tsx:512
|
||||
msgid ""
|
||||
@@ -4580,6 +4569,8 @@ msgid ""
|
||||
"To switch the profile, the app is going to close and you will need to "
|
||||
"restart it."
|
||||
msgstr ""
|
||||
"Jos haluat vaihtaa profiilia, sovellus suljetaan ja sinun on käynnistettävä "
|
||||
"se uudelleen."
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:651
|
||||
msgid ""
|
||||
@@ -4661,9 +4652,8 @@ msgstr "Yritä uudestaan"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:309 packages/lib/utils/joplinCloud.ts:331
|
||||
#: packages/lib/utils/joplinCloud.ts:353
|
||||
#, fuzzy
|
||||
msgid "Try it now"
|
||||
msgstr "Hae se nyt"
|
||||
msgstr "Kokeile nyt"
|
||||
|
||||
#: packages/app-cli/app/command-help.js:71
|
||||
msgid ""
|
||||
@@ -4822,7 +4812,7 @@ msgstr "Käyttö: %s"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1640
|
||||
msgid "Use biometrics to secure access to the app"
|
||||
msgstr ""
|
||||
msgstr "Käytä biometrisiä tunnisteita suojataksesi pääsyn sovellukseen"
|
||||
|
||||
#: packages/app-cli/app/command-ls.js:32 packages/app-cli/app/command-tag.js:18
|
||||
msgid ""
|
||||
@@ -4861,6 +4851,8 @@ msgid ""
|
||||
"Use your biometrics to secure access to your application. You can always set "
|
||||
"it up later in Settings."
|
||||
msgstr ""
|
||||
"Käytä biometrisiä tunnisteita suojataksesi pääsyn sovellukseen. Voit "
|
||||
"määrittää sen myöhemmin asetuksissa."
|
||||
|
||||
#: packages/lib/models/Setting.ts:1244
|
||||
msgid ""
|
||||
@@ -4896,7 +4888,7 @@ msgstr "Kelvollinen"
|
||||
|
||||
#: packages/app-mobile/components/biometrics/BiometricPopup.tsx:25
|
||||
msgid "Verify your identity"
|
||||
msgstr ""
|
||||
msgstr "Vahvista henkilöllisyytesi"
|
||||
|
||||
#: packages/app-desktop/gui/NoteList/NoteList.tsx:167
|
||||
msgid "View"
|
||||
|
@@ -653,7 +653,7 @@ msgstr ""
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/SearchPanel.tsx:328
|
||||
msgid "Case sensitive"
|
||||
msgstr ""
|
||||
msgstr "Razlikuje velika i mala slova"
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/commands/toggleLayoutMoveMode.ts:7
|
||||
msgid "Change application layout"
|
||||
@@ -1072,7 +1072,7 @@ msgstr "Stvaranje izvještaja …"
|
||||
|
||||
#: packages/app-desktop/checkForUpdates.ts:180
|
||||
msgid "Current version is up-to-date."
|
||||
msgstr "Trenutačna verzija je najnovija verzija."
|
||||
msgstr "Trenutačna verzija je aktualna."
|
||||
|
||||
#: packages/lib/models/Note.ts:38
|
||||
msgid "custom order"
|
||||
@@ -1174,7 +1174,7 @@ msgstr "Izbriši istekle sesije"
|
||||
|
||||
#: packages/server/src/services/TaskService.ts:20
|
||||
msgid "Delete expired tokens"
|
||||
msgstr "Izbriši istekle tokene"
|
||||
msgstr "Izbriši istekle ključeve"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/editorCommandDeclarations.ts:88
|
||||
msgid "Delete line"
|
||||
@@ -1392,7 +1392,7 @@ msgstr ""
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/EditLinkDialog.tsx:149
|
||||
msgid "Done"
|
||||
msgstr ""
|
||||
msgstr "Gotovo"
|
||||
|
||||
#: packages/app-desktop/checkForUpdates.ts:199
|
||||
msgid "Download"
|
||||
@@ -1582,7 +1582,7 @@ msgstr "Aktiviraj audio-player"
|
||||
|
||||
#: packages/app-mobile/components/biometrics/BiometricPopup.tsx:61
|
||||
msgid "Enable biometrics authentication?"
|
||||
msgstr ""
|
||||
msgstr "Aktivirati biometrijsku autentifikaciju?"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1146
|
||||
msgid "Enable deflist syntax"
|
||||
@@ -2228,7 +2228,7 @@ msgstr "Umetnuti kȏd"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/TinyMCE/utils/setupToolbarButtons.ts:24
|
||||
msgid "Insert"
|
||||
msgstr "Umetnuto"
|
||||
msgstr "Umetni"
|
||||
|
||||
#: packages/app-desktop/gui/NoteEditor/NoteBody/CodeMirror/CodeMirror.tsx:199
|
||||
msgid "Insert Hyperlink"
|
||||
@@ -2394,7 +2394,7 @@ msgstr ""
|
||||
|
||||
#: packages/lib/models/Setting.ts:1530
|
||||
msgid "Keep note history for"
|
||||
msgstr "Čuvaj povijest bilješke"
|
||||
msgstr "Čuvaj povijest bilježaka"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1403
|
||||
msgid "Keyboard Mode"
|
||||
@@ -2873,7 +2873,7 @@ msgstr "Bilješka ne postoji: „%s”. Želiš li je stvoriti?"
|
||||
#: packages/app-mobile/components/NoteEditor/NoteEditor.tsx:82
|
||||
#, fuzzy
|
||||
msgid "Note editor"
|
||||
msgstr "Povijest bilježaka"
|
||||
msgstr "Uređivač za bilješke"
|
||||
|
||||
#: packages/app-cli/app/command-edit.js:97
|
||||
msgid "Note has been saved."
|
||||
@@ -3058,7 +3058,7 @@ msgstr "Ili stvori novi račun."
|
||||
#: packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.tsx:82
|
||||
#, fuzzy
|
||||
msgid "Ordered list"
|
||||
msgstr "Stvori korisnika"
|
||||
msgstr "Razvrstan popis"
|
||||
|
||||
#: packages/app-desktop/gui/MenuBar.tsx:409
|
||||
msgid "Other applications..."
|
||||
@@ -3150,8 +3150,8 @@ msgid ""
|
||||
"Please note that if it is a large notebook, it may take a few minutes for "
|
||||
"all the notes to show up on the recipient's device."
|
||||
msgstr ""
|
||||
"Imaj na umu, da ako je velika bilježnica, može potrajati nekoliko minuta dok "
|
||||
"se sve bilješke pojave na uređaju primatelja."
|
||||
"Imaj na umu, da ako je bilježnica velika, pojavljivanje svih bilježaka na "
|
||||
"uređaju primatelja može potrajati nekoliko minuta."
|
||||
|
||||
#: packages/lib/onedrive-api-node-utils.js:118
|
||||
msgid ""
|
||||
@@ -3162,9 +3162,9 @@ msgid ""
|
||||
"will be shared with any third party."
|
||||
msgstr ""
|
||||
"Otvori sljedeći URL u pregledniku za autentificiranje programa. Program će "
|
||||
"stvoriti mapu u „Aplikacije/Joplin” i koristit će je samo za čitanje i "
|
||||
"pisanje. Program neće moći pristupiti datotekama izvan ove mape niti bilo "
|
||||
"kojim drugim osobnim podacima. Nikoji podaci se neće dijeliti s drugima."
|
||||
"stvoriti mapu u „Aplikacije/Joplin” dozvolama za čitanje i pisanje. Program "
|
||||
"neće moći pristupiti datotekama izvan ove mape niti bilo kojim drugim "
|
||||
"osobnim podacima. Nikoji podaci se neće dijeliti s drugima."
|
||||
|
||||
#: packages/app-cli/app/command-ls.js:63
|
||||
msgid "Please select a notebook first."
|
||||
@@ -3262,7 +3262,7 @@ msgstr "Pritisni za postavljanje lozinke za dešifriranje."
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/SearchPanel.tsx:278
|
||||
msgid "Previous match"
|
||||
msgstr ""
|
||||
msgstr "Prethodno poklapanje"
|
||||
|
||||
#: packages/app-desktop/gui/NotePropertiesDialog.min.js:307
|
||||
#: packages/app-desktop/gui/NotePropertiesDialog.tsx:329
|
||||
@@ -3464,7 +3464,7 @@ msgstr "Označi sve"
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/SearchPanel.tsx:236
|
||||
msgid "Replace with..."
|
||||
msgstr ""
|
||||
msgstr "Zamijeni sa …"
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/SearchPanel.tsx:257
|
||||
msgid "Replace: "
|
||||
@@ -3473,7 +3473,7 @@ msgstr ""
|
||||
#: packages/app-desktop/gui/MainScreen/commands/resetLayout.ts:7
|
||||
#, fuzzy
|
||||
msgid "Reset application layout"
|
||||
msgstr "Promijeni raspored programa"
|
||||
msgstr "Obnovi raspored programa"
|
||||
|
||||
#: packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx:219
|
||||
#: packages/app-desktop/gui/MasterPasswordDialog/Dialog.tsx:220
|
||||
@@ -3729,7 +3729,7 @@ msgstr "Dijeli bilježnicu …"
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:177
|
||||
msgid "Sharing access control"
|
||||
msgstr ""
|
||||
msgstr "Dijeljenje kontrole pristupa"
|
||||
|
||||
#: packages/app-desktop/gui/ShareFolderDialog/ShareFolderDialog.tsx:305
|
||||
msgid "Sharing notebook..."
|
||||
@@ -3737,7 +3737,7 @@ msgstr "Dijeljenje bilježnice …"
|
||||
|
||||
#: packages/app-cli/app/command-help.js:44
|
||||
msgid "Shortcuts are not available in CLI mode."
|
||||
msgstr "Prečaci nisu podržani u naredbenom retku."
|
||||
msgstr "Prečaci nisu dostupni u naredbenom retku."
|
||||
|
||||
#: packages/app-mobile/components/NoteEditor/SearchPanel.tsx:208
|
||||
#, fuzzy
|
||||
@@ -3776,7 +3776,7 @@ msgstr "Prikaži broj bilježaka"
|
||||
|
||||
#: packages/lib/models/Setting.ts:942
|
||||
msgid "Show sort order buttons"
|
||||
msgstr "Prikaži gumbe redoslijeda"
|
||||
msgstr "Prikaži gumbe za određivanje redoslijeda"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1162
|
||||
msgid "Show tray icon"
|
||||
@@ -4021,12 +4021,12 @@ msgid ""
|
||||
"Switches to [notebook] - all further operations will happen within this "
|
||||
"notebook."
|
||||
msgstr ""
|
||||
"Mijenja se u bilježnicu [notebook] – sve daljnje operacije izvode se u toj "
|
||||
"bilježnici."
|
||||
"Prebacuje na bilježnicu [notebook] – sve daljnje operacije će se izvoditi u "
|
||||
"ovoj bilježnici."
|
||||
|
||||
#: packages/lib/utils/joplinCloud.ts:147
|
||||
msgid "Sync as many devices as you want"
|
||||
msgstr ""
|
||||
msgstr "Sinkroniziraj koliko god uređaja želiš"
|
||||
|
||||
#: packages/app-mobile/components/screens/ConfigScreen.tsx:612
|
||||
msgid "Sync Status"
|
||||
@@ -4212,7 +4212,7 @@ msgid ""
|
||||
"The default encryption method has been changed to a more secure one and it "
|
||||
"is recommended that you apply it to your data."
|
||||
msgstr ""
|
||||
"Standardna metoda šifriranja promijenjena je u sigurniju metodu. "
|
||||
"Standardna metoda šifriranja je promijenjena sa sigurnijom metodom. "
|
||||
"Preporučujemo da je primijeniš na tvoje podatke."
|
||||
|
||||
#: packages/app-desktop/gui/MainScreen/MainScreen.tsx:619
|
||||
@@ -4404,7 +4404,7 @@ msgid ""
|
||||
"\n"
|
||||
"%s"
|
||||
msgstr ""
|
||||
"Došlo je do [konflikta](%s) s dolje navedenim privitkom.\n"
|
||||
"Došlo je do konflikta [conflict](%s) s dolje navedenim privitkom.\n"
|
||||
"\n"
|
||||
"%s"
|
||||
|
||||
@@ -4467,7 +4467,7 @@ msgid ""
|
||||
"access Joplin."
|
||||
msgstr ""
|
||||
"Ovaj ključ za autorizaciju potreban je samo za dozvoljavanje drugim "
|
||||
"programima pristupiti Joplinu."
|
||||
"programima da pristupe Joplinu."
|
||||
|
||||
#: packages/app-desktop/gui/ResourceScreen.tsx:231
|
||||
msgid ""
|
||||
@@ -4747,7 +4747,7 @@ msgstr ""
|
||||
#: packages/app-mobile/components/NoteEditor/MarkdownToolbar/MarkdownToolbar.tsx:69
|
||||
#, fuzzy
|
||||
msgid "Unordered list"
|
||||
msgstr "Stvori korisnika"
|
||||
msgstr "Nerazvstan popis"
|
||||
|
||||
#: packages/app-desktop/gui/ShareNoteDialog.tsx:162
|
||||
msgid "Unpublish note"
|
||||
@@ -4853,7 +4853,7 @@ msgstr "Korištenje: %s"
|
||||
|
||||
#: packages/lib/models/Setting.ts:1640
|
||||
msgid "Use biometrics to secure access to the app"
|
||||
msgstr ""
|
||||
msgstr "Koristi biometrijske podatke za osiguravanje pristupa programu"
|
||||
|
||||
#: packages/app-cli/app/command-ls.js:32 packages/app-cli/app/command-tag.js:18
|
||||
msgid ""
|
||||
@@ -5437,6 +5437,9 @@ msgstr "Umanji prikaz"
|
||||
#~ msgid "Give focus to next pane"
|
||||
#~ msgstr "Fokusiraj sljedeće okno"
|
||||
|
||||
#~ msgid "Give focus to previous pane"
|
||||
#~ msgstr "Fokusiraj prethodno okno"
|
||||
|
||||
#~ msgid "Exit command line mode"
|
||||
#~ msgstr "Napusti naredbeni redak"
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/tools",
|
||||
"version": "2.10.2",
|
||||
"version": "2.11.0",
|
||||
"description": "Various tools for Joplin",
|
||||
"main": "index.js",
|
||||
"author": "Laurent Cozic",
|
||||
@@ -20,13 +20,14 @@
|
||||
},
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "^2.10.2",
|
||||
"@joplin/renderer": "^2.10.2",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@joplin/utils": "~2.11",
|
||||
"@types/node-fetch": "2.6.2",
|
||||
"@types/yargs": "17.0.20",
|
||||
"dayjs": "1.11.7",
|
||||
"execa": "4.1.0",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"gettext-parser": "6.0.0",
|
||||
"glob": "8.1.0",
|
||||
"markdown-it": "13.0.1",
|
||||
@@ -54,7 +55,7 @@
|
||||
"jest": "29.4.3",
|
||||
"rss": "1.2.2",
|
||||
"sass": "1.58.3",
|
||||
"sqlite3": "5.1.4",
|
||||
"sqlite3": "5.1.6",
|
||||
"typescript": "4.9.4"
|
||||
},
|
||||
"gitHead": "eb4b0e64eab40a51b0895d3a40a9d8c3cb7b1b14"
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import * as fs from 'fs-extra';
|
||||
import { execCommandVerbose, execCommandWithPipes, githubRelease, githubOauthToken, fileExists, gitPullTry, completeReleaseWithChangelog, execCommand2 } from './tool-utils';
|
||||
import { execCommandVerbose, execCommandWithPipes, githubRelease, githubOauthToken, fileExists, gitPullTry, completeReleaseWithChangelog } from './tool-utils';
|
||||
const path = require('path');
|
||||
const fetch = require('node-fetch');
|
||||
const uriTemplate = require('uri-template');
|
||||
@@ -150,7 +151,7 @@ async function main() {
|
||||
const isPreRelease = !('type' in argv) || argv.type === 'prerelease';
|
||||
|
||||
process.chdir(rnDir);
|
||||
await execCommand2('yarn run build', { showStdout: false });
|
||||
await execCommand('yarn run build', { showStdout: false });
|
||||
|
||||
if (isPreRelease) console.info('Creating pre-release');
|
||||
console.info('Updating version numbers in build.gradle...');
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { execCommand2, rootDir, completeReleaseWithChangelog } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { rootDir, completeReleaseWithChangelog } from './tool-utils';
|
||||
|
||||
const appDir = `${rootDir}/packages/app-cli`;
|
||||
const changelogPath = `${rootDir}/readme/changelog_cli.md`;
|
||||
@@ -8,19 +9,19 @@ const changelogPath = `${rootDir}/readme/changelog_cli.md`;
|
||||
async function main() {
|
||||
process.chdir(appDir);
|
||||
|
||||
await execCommand2('git pull');
|
||||
await execCommand('git pull');
|
||||
|
||||
const newVersion = (await execCommand2('npm version patch')).trim();
|
||||
const newVersion = (await execCommand('npm version patch')).trim();
|
||||
console.info(`Building ${newVersion}...`);
|
||||
const newTag = `cli-${newVersion}`;
|
||||
|
||||
await execCommand2('touch app/main.js');
|
||||
await execCommand2('yarn run build');
|
||||
await execCommand2('cp ../../README.md build/');
|
||||
await execCommand('touch app/main.js');
|
||||
await execCommand('yarn run build');
|
||||
await execCommand('cp ../../README.md build/');
|
||||
|
||||
process.chdir(`${appDir}/build`);
|
||||
|
||||
await execCommand2('npm publish');
|
||||
await execCommand('npm publish');
|
||||
|
||||
await completeReleaseWithChangelog(changelogPath, newVersion, newTag, 'CLI', false);
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { execCommand2, gitCurrentBranch, githubRelease, gitPullTry, rootDir } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { gitCurrentBranch, githubRelease, gitPullTry, rootDir } from './tool-utils';
|
||||
|
||||
const appDir = `${rootDir}/packages/app-desktop`;
|
||||
|
||||
@@ -11,16 +12,16 @@ async function main() {
|
||||
|
||||
console.info(`Running from: ${process.cwd()}`);
|
||||
|
||||
const version = (await execCommand2('npm version patch')).trim();
|
||||
const version = (await execCommand('npm version patch')).trim();
|
||||
const tagName = version;
|
||||
|
||||
console.info(`New version number: ${version}`);
|
||||
|
||||
await execCommand2('git add -A');
|
||||
await execCommand2(`git commit -m "Desktop release ${version}"`);
|
||||
await execCommand2(`git tag ${tagName}`);
|
||||
await execCommand2('git push');
|
||||
await execCommand2('git push --tags');
|
||||
await execCommand('git add -A');
|
||||
await execCommand(`git commit -m "Desktop release ${version}"`);
|
||||
await execCommand(`git tag ${tagName}`);
|
||||
await execCommand('git push');
|
||||
await execCommand('git push --tags');
|
||||
|
||||
const releaseOptions = { isDraft: true, isPreRelease: !!argv.beta };
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { chdir } from 'process';
|
||||
import { rootDir, gitPullTry, execCommand2, releaseFinalGitCommands } from './tool-utils';
|
||||
import { rootDir, gitPullTry, releaseFinalGitCommands } from './tool-utils';
|
||||
|
||||
const workDir = `${rootDir}/packages/plugin-repo-cli`;
|
||||
|
||||
@@ -7,18 +8,18 @@ async function main() {
|
||||
await gitPullTry();
|
||||
|
||||
chdir(rootDir);
|
||||
await execCommand2('yarn run tsc');
|
||||
await execCommand('yarn run tsc');
|
||||
|
||||
chdir(workDir);
|
||||
await execCommand2('yarn run dist');
|
||||
await execCommand('yarn run dist');
|
||||
|
||||
const newVersion = (await execCommand2('npm version patch')).trim();
|
||||
const newVersion = (await execCommand('npm version patch')).trim();
|
||||
|
||||
console.info(`New version: ${newVersion}`);
|
||||
const tagName = `plugin-repo-cli-${newVersion}`;
|
||||
console.info(`Tag name: ${tagName}`);
|
||||
|
||||
await execCommand2('npm publish');
|
||||
await execCommand('npm publish');
|
||||
|
||||
console.info(releaseFinalGitCommands('Plugin Repo CLI', newVersion, tagName));
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { execCommand2, rootDir, gitPullTry, completeReleaseWithChangelog } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { rootDir, gitPullTry, completeReleaseWithChangelog } from './tool-utils';
|
||||
|
||||
const serverDir = `${rootDir}/packages/server`;
|
||||
|
||||
@@ -12,7 +13,7 @@ async function main() {
|
||||
await gitPullTry();
|
||||
|
||||
process.chdir(serverDir);
|
||||
const version = (await execCommand2('npm version patch')).trim();
|
||||
const version = (await execCommand('npm version patch')).trim();
|
||||
const versionSuffix = ''; // isPreRelease ? '-beta' : '';
|
||||
const tagName = `server-${version}${versionSuffix}`;
|
||||
|
||||
|
@@ -139,6 +139,7 @@ async function main() {
|
||||
await updatePackageVersion(`${rootDir}/packages/renderer/package.json`, majorMinorVersion, options);
|
||||
await updatePackageVersion(`${rootDir}/packages/server/package.json`, majorMinorVersion, options);
|
||||
await updatePackageVersion(`${rootDir}/packages/tools/package.json`, majorMinorVersion, options);
|
||||
await updatePackageVersion(`${rootDir}/packages/utils/package.json`, majorMinorVersion, options);
|
||||
|
||||
if (options.updateVersion) {
|
||||
await updateGradleVersion(`${rootDir}/packages/app-mobile/android/app/build.gradle`, majorMinorVersion);
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import yargs = require('yargs');
|
||||
import { chdir } from 'process';
|
||||
import { execCommand2, rootDir } from './tool-utils';
|
||||
import { rootDir } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
|
||||
const main = async () => {
|
||||
const argv = await yargs.argv;
|
||||
@@ -10,7 +11,7 @@ const main = async () => {
|
||||
chdir(rootDir);
|
||||
|
||||
try {
|
||||
await execCommand2(['yarn', 'run', 'cspell'].concat(filePaths), { showStderr: false, showStdout: false });
|
||||
await execCommand(['yarn', 'run', 'cspell'].concat(filePaths), { showStderr: false, showStdout: false });
|
||||
} catch (error) {
|
||||
if (!error.stdout.trim()) return;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { execCommand2 } from './tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
|
||||
async function main() {
|
||||
const argv = require('yargs').argv;
|
||||
@@ -6,9 +6,9 @@ async function main() {
|
||||
|
||||
const version = argv._[0];
|
||||
|
||||
await execCommand2(`docker pull "joplin/server:${version}"`);
|
||||
await execCommand2(`docker tag "joplin/server:${version}" "joplin/server:latest"`);
|
||||
await execCommand2('docker push joplin/server:latest');
|
||||
await execCommand(`docker pull "joplin/server:${version}"`);
|
||||
await execCommand(`docker tag "joplin/server:${version}" "joplin/server:latest"`);
|
||||
await execCommand('docker push joplin/server:latest');
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import * as fs from 'fs-extra';
|
||||
import { readCredentialFile } from '@joplin/lib/utils/credentialFiles';
|
||||
import { execCommand as execCommand2, commandToString } from '@joplin/utils';
|
||||
|
||||
const fetch = require('node-fetch');
|
||||
const execa = require('execa');
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils');
|
||||
const moment = require('moment');
|
||||
|
||||
export interface GitHubReleaseAsset {
|
||||
@@ -20,23 +20,6 @@ export interface GitHubRelease {
|
||||
draft: boolean;
|
||||
}
|
||||
|
||||
function quotePath(path: string) {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
||||
path = path.replace(/"/, '\\"');
|
||||
return `"${path}"`;
|
||||
}
|
||||
|
||||
function commandToString(commandName: string, args: string[] = []) {
|
||||
const output = [quotePath(commandName)];
|
||||
|
||||
for (const arg of args) {
|
||||
output.push(quotePath(arg));
|
||||
}
|
||||
|
||||
return output.join(' ');
|
||||
}
|
||||
|
||||
async function insertChangelog(tag: string, changelogPath: string, changelog: string, isPrerelease: boolean, repoTagUrl: string = '') {
|
||||
repoTagUrl = repoTagUrl || 'https://github.com/laurent22/joplin/releases/tag';
|
||||
|
||||
@@ -163,52 +146,6 @@ export function execCommandVerbose(commandName: string, args: string[] = []) {
|
||||
return promise;
|
||||
}
|
||||
|
||||
interface ExecCommandOptions {
|
||||
showInput?: boolean;
|
||||
showStdout?: boolean;
|
||||
showStderr?: boolean;
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
// There's lot of execCommandXXX functions, but eventually all scripts should
|
||||
// use the one below, which supports:
|
||||
//
|
||||
// - Printing the command being executed
|
||||
// - Printing the output in real time (piping to stdout)
|
||||
// - Returning the command result as string
|
||||
export async function execCommand2(command: string | string[], options: ExecCommandOptions = null): Promise<string> {
|
||||
options = {
|
||||
showInput: true,
|
||||
showStdout: true,
|
||||
showStderr: true,
|
||||
quiet: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.quiet) {
|
||||
options.showInput = false;
|
||||
options.showStdout = false;
|
||||
options.showStderr = false;
|
||||
}
|
||||
|
||||
if (options.showInput) {
|
||||
if (typeof command === 'string') {
|
||||
console.info(`> ${command}`);
|
||||
} else {
|
||||
console.info(`> ${commandToString(command[0], command.slice(1))}`);
|
||||
}
|
||||
}
|
||||
|
||||
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
|
||||
const executableName = args[0];
|
||||
args.splice(0, 1);
|
||||
const promise = execa(executableName, args);
|
||||
if (options.showStdout) promise.stdout.pipe(process.stdout);
|
||||
if (options.showStderr) promise.stdout.pipe(process.stderr);
|
||||
const result = await promise;
|
||||
return result.stdout.trim();
|
||||
}
|
||||
|
||||
export function execCommandWithPipes(executable: string, args: string[]) {
|
||||
const spawn = require('child_process').spawn;
|
||||
|
||||
|
@@ -1,43 +1,9 @@
|
||||
import { readFile } from 'fs-extra';
|
||||
import { insertContentIntoFile, rootDir } from './tool-utils';
|
||||
import markdownUtils, { MarkdownTableHeader, MarkdownTableJustify, MarkdownTableRow } from '@joplin/lib/markdownUtils';
|
||||
import { GithubSponsor, GithubUser, OrgSponsor, Sponsors } from './website/utils/types';
|
||||
import fetch from 'node-fetch';
|
||||
import { GithubSponsor, loadSponsors, OrgSponsor } from './utils/loadSponsors';
|
||||
const { escapeHtml } = require('@joplin/lib/string-utils');
|
||||
|
||||
const readmePath = `${rootDir}/README.md`;
|
||||
const sponsorsPath = `${rootDir}/packages/tools/sponsors.json`;
|
||||
|
||||
const sleep = (ms: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
};
|
||||
|
||||
const fetchWithRetry = async (url: string, opts: any = null) => {
|
||||
if (!opts) opts = {};
|
||||
let retry = opts && opts.retry || 3;
|
||||
|
||||
while (retry > 0) {
|
||||
try {
|
||||
return fetch(url, opts);
|
||||
} catch (e) {
|
||||
if (opts && opts.callback) {
|
||||
opts.callback(retry);
|
||||
}
|
||||
retry = retry - 1;
|
||||
if (retry === 0) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (opts && opts.pause) {
|
||||
if (opts && !opts.silent) console.log('pausing..');
|
||||
await sleep(opts.pause);
|
||||
if (opts && !opts.silent) console.log('done pausing...');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
async function createGitHubSponsorTable(sponsors: GithubSponsor[]): Promise<string> {
|
||||
sponsors = sponsors.slice();
|
||||
@@ -70,9 +36,7 @@ async function createGitHubSponsorTable(sponsors: GithubSponsor[]): Promise<stri
|
||||
sponsorIndex++;
|
||||
if (!sponsor) break;
|
||||
|
||||
const userResponse = await fetchWithRetry(`https://api.github.com/users/${sponsor.name}`);
|
||||
const user = await userResponse.json() as GithubUser;
|
||||
row[`col${colIndex}`] = `<img width="50" src="https://avatars2.githubusercontent.com/u/${user.id}?s=96&v=4"/></br>[${sponsor.name}](https://github.com/${sponsor.name})`;
|
||||
row[`col${colIndex}`] = `<img width="50" src="https://avatars2.githubusercontent.com/u/${sponsor.id}?s=96&v=4"/></br>[${sponsor.name}](https://github.com/${sponsor.name})`;
|
||||
}
|
||||
|
||||
if (Object.keys(row)) rows.push(row);
|
||||
@@ -94,7 +58,7 @@ async function createOrgSponsorTable(sponsors: OrgSponsor[]): Promise<string> {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const sponsors: Sponsors = JSON.parse(await readFile(sponsorsPath, 'utf8'));
|
||||
const sponsors = await loadSponsors();
|
||||
|
||||
await insertContentIntoFile(
|
||||
readmePath,
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { chdir } from 'process';
|
||||
import { execCommand2, rootDir, gitRepoCleanTry } from './tool-utils';
|
||||
import { rootDir, gitRepoCleanTry } from './tool-utils';
|
||||
import updateDownloadPage from './website/updateDownloadPage';
|
||||
|
||||
async function main() {
|
||||
@@ -7,23 +8,23 @@ async function main() {
|
||||
|
||||
if (doGitOperations) {
|
||||
await gitRepoCleanTry();
|
||||
await execCommand2(['git', 'pull', '--rebase']);
|
||||
await execCommand(['git', 'pull', '--rebase']);
|
||||
}
|
||||
|
||||
await execCommand2(['node', `${rootDir}/packages/tools/update-readme-download.js`]);
|
||||
await execCommand2(['node', `${rootDir}/packages/tools/build-release-stats.js`, '--types=changelog']);
|
||||
await execCommand2(['node', `${rootDir}/packages/tools/build-release-stats.js`, '--types=stats', '--update-interval=30']);
|
||||
await execCommand2(['node', `${rootDir}/packages/tools/update-readme-sponsors.js`]);
|
||||
await execCommand2(['node', `${rootDir}/packages/tools/build-welcome.js`]);
|
||||
await execCommand(['node', `${rootDir}/packages/tools/update-readme-download.js`]);
|
||||
await execCommand(['node', `${rootDir}/packages/tools/build-release-stats.js`, '--types=changelog']);
|
||||
await execCommand(['node', `${rootDir}/packages/tools/build-release-stats.js`, '--types=stats', '--update-interval=30']);
|
||||
await execCommand(['node', `${rootDir}/packages/tools/update-readme-sponsors.js`]);
|
||||
await execCommand(['node', `${rootDir}/packages/tools/build-welcome.js`]);
|
||||
chdir(rootDir);
|
||||
await execCommand2(['yarn', 'run', 'buildApiDoc']);
|
||||
await execCommand(['yarn', 'run', 'buildApiDoc']);
|
||||
await updateDownloadPage();
|
||||
|
||||
if (doGitOperations) {
|
||||
await execCommand2(['git', 'add', '-A']);
|
||||
await execCommand2(['git', 'commit', '-m', 'Update Markdown doc']);
|
||||
await execCommand2(['git', 'pull', '--rebase']);
|
||||
await execCommand2(['git', 'push']);
|
||||
await execCommand(['git', 'add', '-A']);
|
||||
await execCommand(['git', 'commit', '-m', 'Update Markdown doc']);
|
||||
await execCommand(['git', 'pull', '--rebase']);
|
||||
await execCommand(['git', 'push']);
|
||||
}
|
||||
}
|
||||
|
||||
|
39
packages/tools/utils/loadSponsors.ts
Normal file
39
packages/tools/utils/loadSponsors.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { readFile } from 'fs-extra';
|
||||
import { rootDir } from '@joplin/utils';
|
||||
import { fetchWithRetry } from '@joplin/utils/net';
|
||||
|
||||
const sponsorsPath = `${rootDir}/packages/tools/sponsors.json`;
|
||||
|
||||
export interface GithubSponsor {
|
||||
name: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface Sponsors {
|
||||
github: GithubSponsor[];
|
||||
orgs: OrgSponsor[];
|
||||
}
|
||||
|
||||
export interface OrgSponsor {
|
||||
url: string;
|
||||
urlWebsite?: string;
|
||||
title: string;
|
||||
imageName: string;
|
||||
}
|
||||
|
||||
export const loadSponsors = async (): Promise<Sponsors> => {
|
||||
const output: Sponsors = JSON.parse(await readFile(sponsorsPath, 'utf8'));
|
||||
|
||||
output.orgs = output.orgs.map(o => {
|
||||
if (o.urlWebsite) o.url = o.urlWebsite;
|
||||
return o;
|
||||
});
|
||||
|
||||
for (const ghSponsor of output.github) {
|
||||
const userResponse = await fetchWithRetry(`https://api.github.com/users/${ghSponsor.name}`);
|
||||
const user = await userResponse.json();
|
||||
ghSponsor.id = user.id;
|
||||
}
|
||||
|
||||
return output;
|
||||
};
|
@@ -2,7 +2,7 @@ import { readFileSync, readFile, mkdirpSync, writeFileSync, remove, copy, pathEx
|
||||
import { rootDir } from '../tool-utils';
|
||||
import { pressCarouselItems } from './utils/pressCarousel';
|
||||
import { getMarkdownIt, loadMustachePartials, markdownToPageHtml, renderMustache } from './utils/render';
|
||||
import { AssetUrls, Env, Partials, PlanPageParams, Sponsors, TemplateParams } from './utils/types';
|
||||
import { AssetUrls, Env, Partials, PlanPageParams, TemplateParams } from './utils/types';
|
||||
import { createFeatureTableMd, getPlans, loadStripeConfig } from '@joplin/lib/utils/joplinCloud';
|
||||
import { stripOffFrontMatter } from './utils/frontMatter';
|
||||
import { dirname, basename } from 'path';
|
||||
@@ -13,6 +13,7 @@ import { getNewsDateString } from './utils/news';
|
||||
import { parsePoFile, parseTranslations, Translations } from '../utils/translation';
|
||||
import { countryCodeOnly, setLocale } from '@joplin/lib/locale';
|
||||
import applyTranslations from './utils/applyTranslations';
|
||||
import { loadSponsors } from '../utils/loadSponsors';
|
||||
|
||||
interface BuildConfig {
|
||||
env: Env;
|
||||
@@ -175,16 +176,6 @@ function makeHomePageMd() {
|
||||
return md;
|
||||
}
|
||||
|
||||
async function loadSponsors(): Promise<Sponsors> {
|
||||
const sponsorsPath = `${rootDir}/packages/tools/sponsors.json`;
|
||||
const output: Sponsors = JSON.parse(await readFile(sponsorsPath, 'utf8'));
|
||||
output.orgs = output.orgs.map(o => {
|
||||
if (o.urlWebsite) o.url = o.urlWebsite;
|
||||
return o;
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
const processNewsMarkdown = (md: string, mdFilePath: string): string => {
|
||||
const info = stripOffFrontMatter(md);
|
||||
md = info.doc.trim();
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { Plan, StripePublicConfig } from '@joplin/lib/utils/joplinCloud';
|
||||
import { Sponsors } from '../../utils/loadSponsors';
|
||||
import { OpenGraphTags } from './openGraph';
|
||||
|
||||
export enum Env {
|
||||
@@ -6,26 +7,10 @@ export enum Env {
|
||||
Prod = 'prod',
|
||||
}
|
||||
|
||||
export interface GithubSponsor {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface GithubUser {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface OrgSponsor {
|
||||
url: string;
|
||||
urlWebsite?: string;
|
||||
title: string;
|
||||
imageName: string;
|
||||
}
|
||||
|
||||
export interface Sponsors {
|
||||
github: GithubSponsor[];
|
||||
orgs: OrgSponsor[];
|
||||
}
|
||||
|
||||
interface PressCarouselItem {
|
||||
active: string;
|
||||
body: string;
|
||||
|
2
packages/utils/.gitignore
vendored
Normal file
2
packages/utils/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
dist/
|
||||
node_modules/
|
3
packages/utils/README.md
Normal file
3
packages/utils/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Utility package
|
||||
|
||||
Those are generic utility functions that can be imported from any other packages. This package however shouldn't have any dependency to any other Joplin package, so that it can be imported without having to import "lib", "renderer" and so on. This is particulary important for Docker images, so that they can be trimmed down to only what's needed.
|
19
packages/utils/commandToString.test.ts
Normal file
19
packages/utils/commandToString.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import commandToString from './commandToString';
|
||||
|
||||
describe('commandToString', () => {
|
||||
|
||||
it('should convert a command array to a string', () => {
|
||||
const testCases: [string, string[], string][] = [
|
||||
['ls', ['-la'], 'ls -la'],
|
||||
['docker', ['--profile', 'with spaces'], 'docker --profile "with spaces"'],
|
||||
['', [], ''],
|
||||
['', [''], ''],
|
||||
];
|
||||
|
||||
for (const [commandName, args, expected] of testCases) {
|
||||
const actual = commandToString(commandName, args);
|
||||
expect(actual).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
16
packages/utils/commandToString.ts
Normal file
16
packages/utils/commandToString.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
const quotePath = (path: string) => {
|
||||
if (!path) return '';
|
||||
if (path.indexOf('"') < 0 && path.indexOf(' ') < 0) return path;
|
||||
path = path.replace(/"/, '\\"');
|
||||
return `"${path}"`;
|
||||
};
|
||||
|
||||
export default (commandName: string, args: string[] = []) => {
|
||||
const output = [quotePath(commandName)];
|
||||
|
||||
for (const arg of args) {
|
||||
output.push(quotePath(arg));
|
||||
}
|
||||
|
||||
return output.join(' ').trim();
|
||||
};
|
44
packages/utils/execCommand.ts
Normal file
44
packages/utils/execCommand.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import * as execa from 'execa';
|
||||
import commandToString from './commandToString';
|
||||
import splitCommandString from './splitCommandString';
|
||||
import { stdout } from 'process';
|
||||
|
||||
interface ExecCommandOptions {
|
||||
showInput?: boolean;
|
||||
showStdout?: boolean;
|
||||
showStderr?: boolean;
|
||||
quiet?: boolean;
|
||||
}
|
||||
|
||||
export default async (command: string | string[], options: ExecCommandOptions | null = null): Promise<string> => {
|
||||
options = {
|
||||
showInput: true,
|
||||
showStdout: true,
|
||||
showStderr: true,
|
||||
quiet: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.quiet) {
|
||||
options.showInput = false;
|
||||
options.showStdout = false;
|
||||
options.showStderr = false;
|
||||
}
|
||||
|
||||
if (options.showInput) {
|
||||
if (typeof command === 'string') {
|
||||
stdout.write(`> ${command}\n`);
|
||||
} else {
|
||||
stdout.write(`> ${commandToString(command[0], command.slice(1))}\n`);
|
||||
}
|
||||
}
|
||||
|
||||
const args: string[] = typeof command === 'string' ? splitCommandString(command) : command as string[];
|
||||
const executableName = args[0];
|
||||
args.splice(0, 1);
|
||||
const promise = execa(executableName, args);
|
||||
if (options.showStdout && promise.stdout) promise.stdout.pipe(process.stdout);
|
||||
if (options.showStderr && promise.stdout) promise.stdout.pipe(process.stderr);
|
||||
const result = await promise;
|
||||
return result.stdout.trim();
|
||||
};
|
13
packages/utils/index.ts
Normal file
13
packages/utils/index.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import execCommand from './execCommand';
|
||||
import commandToString from './commandToString';
|
||||
import splitCommandString from './splitCommandString';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const rootDir = dirname(dirname(dirname(__dirname)));
|
||||
|
||||
export {
|
||||
execCommand,
|
||||
commandToString,
|
||||
splitCommandString,
|
||||
rootDir,
|
||||
};
|
19
packages/utils/jest.config.js
Normal file
19
packages/utils/jest.config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
module.exports = {
|
||||
'moduleFileExtensions': [
|
||||
'ts',
|
||||
'tsx',
|
||||
'js',
|
||||
],
|
||||
|
||||
testEnvironment: 'node',
|
||||
|
||||
'transform': {
|
||||
'\\.(ts|tsx)$': 'ts-jest',
|
||||
},
|
||||
|
||||
testMatch: ['**/*.test.(ts|tsx)'],
|
||||
|
||||
testPathIgnorePatterns: ['<rootDir>/node_modules/'],
|
||||
|
||||
slowTestThreshold: 40,
|
||||
};
|
28
packages/utils/net.ts
Normal file
28
packages/utils/net.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
import { sleep } from './time';
|
||||
|
||||
export const fetchWithRetry = async (url: string, opts: any = null) => {
|
||||
if (!opts) opts = {};
|
||||
let retry = opts && opts.retry || 3;
|
||||
|
||||
while (retry > 0) {
|
||||
try {
|
||||
return fetch(url, opts);
|
||||
} catch (e) {
|
||||
if (opts && opts.callback) {
|
||||
opts.callback(retry);
|
||||
}
|
||||
retry = retry - 1;
|
||||
if (retry === 0) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (opts && opts.pause) {
|
||||
await sleep(opts.pause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
29
packages/utils/package.json
Normal file
29
packages/utils/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "@joplin/utils",
|
||||
"version": "2.11.0",
|
||||
"description": "Utilities for Joplin",
|
||||
"repository": "https://github.com/laurent22/joplin/tree/dev/packages/utils",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./net": "./dist/net.js"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"tsc": "tsc --project tsconfig.json",
|
||||
"watch": "tsc --watch --preserveWatchOutput --project tsconfig.json",
|
||||
"test": "jest --verbose=false",
|
||||
"test-ci": "yarn test"
|
||||
},
|
||||
"author": "",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"execa": "5.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "29.5.0",
|
||||
"jest": "29.5.0",
|
||||
"ts-jest": "29.0.5"
|
||||
}
|
||||
}
|
73
packages/utils/splitCommandString.ts
Normal file
73
packages/utils/splitCommandString.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
export default (command: string, options: any = null) => {
|
||||
options = options || {};
|
||||
if (!('handleEscape' in options)) {
|
||||
options.handleEscape = true;
|
||||
}
|
||||
|
||||
const args = [];
|
||||
let state = 'start';
|
||||
let current = '';
|
||||
let quote = '"';
|
||||
let escapeNext = false;
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
const c = command[i];
|
||||
|
||||
if (state === 'quotes') {
|
||||
if (c !== quote) {
|
||||
current += c;
|
||||
} else {
|
||||
args.push(current);
|
||||
current = '';
|
||||
state = 'start';
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (escapeNext) {
|
||||
current += c;
|
||||
escapeNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === '\\' && options.handleEscape) {
|
||||
escapeNext = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c === '"' || c === '\'') {
|
||||
state = 'quotes';
|
||||
quote = c;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state === 'arg') {
|
||||
if (c === ' ' || c === '\t') {
|
||||
args.push(current);
|
||||
current = '';
|
||||
state = 'start';
|
||||
} else {
|
||||
current += c;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c !== ' ' && c !== '\t') {
|
||||
state = 'arg';
|
||||
current += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (state === 'quotes') {
|
||||
throw new Error(`Unclosed quote in command line: ${command}`);
|
||||
}
|
||||
|
||||
if (current !== '') {
|
||||
args.push(current);
|
||||
}
|
||||
|
||||
if (args.length <= 0) {
|
||||
throw new Error('Empty command line');
|
||||
}
|
||||
|
||||
return args;
|
||||
};
|
5
packages/utils/time.ts
Normal file
5
packages/utils/time.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
export const sleep = (ms: number) => {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
};
|
16
packages/utils/tsconfig.json
Normal file
16
packages/utils/tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"strict": true,
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"rootDir": ".",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules",
|
||||
],
|
||||
}
|
@@ -1,5 +1,10 @@
|
||||
# Joplin changelog
|
||||
|
||||
## [v2.10.11](https://github.com/laurent22/joplin/releases/tag/v2.10.11) (Pre-release) - 2023-03-17T10:54:02Z
|
||||
|
||||
- Fixed: Fixes text wrap on new buttons ([#7938](https://github.com/laurent22/joplin/issues/7938) by [@julien](https://github.com/julien))
|
||||
- Fixed: List enabled plugins only in About Joplin and in alphabetical order ([#7923](https://github.com/laurent22/joplin/issues/7923)) ([#7920](https://github.com/laurent22/joplin/issues/7920) by [@julien](https://github.com/julien))
|
||||
|
||||
## [v2.10.10](https://github.com/laurent22/joplin/releases/tag/v2.10.10) (Pre-release) - 2023-03-13T23:16:37Z
|
||||
|
||||
- Fixed: Fix issue where search bar can randomly lose focus while searching ([bd42914](https://github.com/laurent22/joplin/commit/bd42914))
|
||||
|
@@ -1,5 +1,9 @@
|
||||
# Joplin Android app changelog
|
||||
|
||||
## [android-v2.10.9](https://github.com/laurent22/joplin/releases/tag/android-v2.10.9) (Pre-release) - 2023-03-22T18:40:57Z
|
||||
|
||||
- Improved: Mark biometrics feature as beta and ensure no call is made if it is not enabled (e44a934)
|
||||
|
||||
## [android-v2.10.8](https://github.com/laurent22/joplin/releases/tag/android-v2.10.8) (Pre-release) - 2023-02-28T18:09:21Z
|
||||
|
||||
- Improved: Stop synchronization with unsupported WebDAV providers (#7819) (#7661 by [@julien](https://github.com/julien))
|
||||
|
@@ -1,5 +1,9 @@
|
||||
# Joplin Server Changelog
|
||||
|
||||
## [server-v2.10.11](https://github.com/laurent22/joplin/releases/tag/server-v2.10.11) - 2023-03-17T17:50:13Z
|
||||
|
||||
- Improved: Updated packages nodemon (v2.0.21)
|
||||
|
||||
## [server-v2.10.10](https://github.com/laurent22/joplin/releases/tag/server-v2.10.10) - 2023-03-09T14:37:07Z
|
||||
|
||||
- Improved: Clean up share logic (107f2e1)
|
||||
|
@@ -359,6 +359,62 @@
|
||||
"created_at": "2023-03-01T10:00:26Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7856
|
||||
},
|
||||
{
|
||||
"name": "milotype",
|
||||
"id": 43657314,
|
||||
"comment_id": 1472575775,
|
||||
"created_at": "2023-03-16T18:51:40Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7937
|
||||
},
|
||||
{
|
||||
"name": "mrkaato0",
|
||||
"id": 99097412,
|
||||
"comment_id": 1474938593,
|
||||
"created_at": "2023-03-18T18:25:27Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7945
|
||||
},
|
||||
{
|
||||
"name": "jcgurango",
|
||||
"id": 1340627,
|
||||
"comment_id": 1478298810,
|
||||
"created_at": "2023-03-21T17:20:28Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7953
|
||||
},
|
||||
{
|
||||
"name": "chenqy9",
|
||||
"id": 10264688,
|
||||
"comment_id": 1480694759,
|
||||
"created_at": "2023-03-23T07:04:15Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7955
|
||||
},
|
||||
{
|
||||
"name": "HelpdeskThisIsJohn",
|
||||
"id": 17282079,
|
||||
"comment_id": 1483985669,
|
||||
"created_at": "2023-03-26T03:40:42Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7969
|
||||
},
|
||||
{
|
||||
"name": "TahaNw",
|
||||
"id": 105979405,
|
||||
"comment_id": 1490675625,
|
||||
"created_at": "2023-03-30T17:33:37Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7998
|
||||
},
|
||||
{
|
||||
"name": "Fejby",
|
||||
"id": 73366988,
|
||||
"comment_id": 1490734071,
|
||||
"created_at": "2023-03-30T18:21:27Z",
|
||||
"repoId": 79162682,
|
||||
"pullRequestNo": 7999
|
||||
}
|
||||
]
|
||||
}
|
@@ -190,6 +190,123 @@ const foo = () => {
|
||||
|
||||
As much as possible, avoid default parameters in **function definitions** and optional fields in **interface definitions**. When all parameters are required, it is much easier to refactor the code because the compiler will automatically catch any missing parameters.
|
||||
|
||||
## Escape variables
|
||||
|
||||
[XSS](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html) is one of the most common vulnerabilities in today's code. These vulnerabilities are often difficult to spot because they are not errors, they often won't fail any test units and the program will work just fine with 99% of input. Yet that remaining 1% can be exploited and used to steal user information, crash the app, etc.
|
||||
|
||||
If you search for ["XSS" in the Joplin git log](https://github.com/laurent22/joplin/search?q=xss&type=commits) you'll find several security vulnerabilities that have been fixed over the year, and that happened in various places that are hard to predict. So we need to be careful with this and make sure we correctly escape user content.
|
||||
|
||||
How you escape the data depends on where you are going to insert it so there's no single function that's going to cover all cases.
|
||||
|
||||
### To insert into a JS script
|
||||
|
||||
Use `JSON.stringify()`. For example:
|
||||
|
||||
```ts
|
||||
const jsCode = `const data = ${JSON.stringify(dynamicallyGeneratedData)};`
|
||||
```
|
||||
|
||||
### To insert into an HTML string
|
||||
|
||||
You need to convert special characters to HTML entities, which we usually do using the `html-entities` package. For example:
|
||||
|
||||
```ts
|
||||
// We generally use this rather verbose pattern, but there
|
||||
// are also helper functions that you may be able to use
|
||||
// depending on the package.
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = new Entities().encode;
|
||||
const html = `<a href="${htmlentities(attributes)}">${htmlentities(content)}</a>`;
|
||||
```
|
||||
|
||||
### To insert into a URL
|
||||
|
||||
It depends on what you're trying to do. To insert a query parameter, use `encodeURIComponent`
|
||||
|
||||
```ts
|
||||
const url = `https://example.com/?page=${encodeURIComponent(page)}`;
|
||||
```
|
||||
|
||||
If you want to encode a full URL, use `encodeURI`:
|
||||
|
||||
```ts
|
||||
encodeURI('https://domain.com/path to a document.pdf');
|
||||
// 'https://domain.com/path%20to%20a%20document.pdf'
|
||||
```
|
||||
|
||||
### Escape as late as possible
|
||||
|
||||
Ideally the application should only deal with raw, unencoded data, so it means data should be decoded and encoded at the application boundaries. Doing so means we avoid accidentally double-escaping data, or having to encode/decode within the app, which is error prone.
|
||||
|
||||
In practice it means as soon as we get user input, we should decode it to the application-specific format (for example by calling `JSON.parse` on the input). And likewise we should only escape the data when it needs to be printed or exported.
|
||||
|
||||
**BAD**
|
||||
|
||||
```ts
|
||||
let parameters = `id=${encodeURIComponent(id)}&time=${encodeURIComponent(Date.now())}`;
|
||||
|
||||
// Clumsy string concatenation because we're dealing with already escaped data.
|
||||
// and we have to remember to encode every time:
|
||||
parameters += `&other=${encodeURIComponent(otherParam)}`;
|
||||
|
||||
const url = `https://example.com?${parameters}`
|
||||
```
|
||||
|
||||
**GOOD**
|
||||
|
||||
```ts
|
||||
// Keep the data as an object
|
||||
const parameters = {
|
||||
id: id,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
// Then we can easily add to it without string concatenation:
|
||||
parameters.other = otherParam;
|
||||
|
||||
// We escape only when it is needed:
|
||||
const url = `https://example.com?${new URLSearchParams(parameters).toString()}`
|
||||
```
|
||||
|
||||
### Make wrong code look wrong
|
||||
|
||||
To name variables that are already escaped we used the technique described in "[Make wrong code look wrong](https://www.joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong/)". We add a suffix to indicate the content of the variable and to make it clear it has already been escaped. It means that the code will look wrong if a variable is inserted in a string and it does not have a suffix. For example:
|
||||
|
||||
**BAD:**
|
||||
|
||||
```ts
|
||||
const userContent = queryParameters.page;
|
||||
|
||||
// ...
|
||||
// later:
|
||||
// ...
|
||||
|
||||
const html = `<div>${userContent}</div>`
|
||||
|
||||
// The above code looks wrong because it appears we're
|
||||
// inserting user input as is in the document, and
|
||||
// indeed we are. Wrong code looks wrong.
|
||||
```
|
||||
|
||||
**GOOD:**
|
||||
|
||||
```ts
|
||||
// Here we escape the data immediately - and we add an
|
||||
// "html" prefix to specify that we have escaped the data
|
||||
// and that the variable content is actual HTML.
|
||||
const userContentHtml = htmlentities(queryParameters.page);
|
||||
|
||||
// ...
|
||||
// later:
|
||||
// ...
|
||||
|
||||
const html = `<div>${userContentHtml}</div>`
|
||||
|
||||
// This is correct and because we've added the "html" suffix
|
||||
// we know that this variable can be safely added to an HTML
|
||||
// string.
|
||||
```
|
||||
|
||||
# React
|
||||
|
||||
## Use function components for new code
|
||||
|
@@ -15,6 +15,16 @@ Otherwise, follow these instructions:
|
||||
|
||||
Make sure you disable debugging once you've finished. Leaving it enabled can cause your log.txt to grow very quickly. To disable debugging, simply delete the "flags.txt" file created.
|
||||
|
||||
### Safe mode
|
||||
|
||||
Safe mode is a special mode that disables all plugins and renders the notes as plain text. You can use this if, for example, the app is crashing or freezing on startup, or is very slow to run. By starting in safe mode you can verify if it's an issue with the app itself or with one of the plugins. In some rare cases, certain notes can also freeze the app, and safe mode would allow you to either change the note or delete it if it causing problems.
|
||||
|
||||
There's two ways to start in safe mode:
|
||||
|
||||
1. From the app, click on **Help > Toggle safe mode**. The app will restart in safe mode.
|
||||
|
||||
2. If that doesn't work, if for example the app freezes before you can access this menu, you can set a debug flag in "flags.txt" file, [as described above](#desktop-application). Simply set the content to `--safe-mode --open-dev-tools --debug --log-level debug`.
|
||||
|
||||
## CLI application
|
||||
|
||||
- Start the app with `joplin --debug --log-level debug`
|
||||
|
@@ -126,6 +126,8 @@
|
||||
// updated so having them here reduces noise.
|
||||
"eslint",
|
||||
"eslint-*",
|
||||
"jest",
|
||||
"jest-*",
|
||||
"@typescript-eslint/*",
|
||||
"yarn",
|
||||
"typescript",
|
||||
|
Reference in New Issue
Block a user