You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-12-26 23:38:08 +02:00
Compare commits
1 Commits
sync_wizar
...
fixing_ci_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95157514da |
@@ -7,6 +7,4 @@ packages/app-cli
|
||||
packages/app-mobile
|
||||
packages/app-clipper
|
||||
packages/generator-joplin
|
||||
packages/plugin-repo-cli
|
||||
packages/server/db-*.sqlite
|
||||
packages/server/temp
|
||||
packages/plugin-repo-cli
|
||||
@@ -573,9 +573,6 @@ packages/app-desktop/gui/Sidebar/styles/index.js.map
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.d.ts
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.js
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.js.map
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.d.ts
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js.map
|
||||
packages/app-desktop/gui/TagList.d.ts
|
||||
packages/app-desktop/gui/TagList.js
|
||||
packages/app-desktop/gui/TagList.js.map
|
||||
@@ -873,9 +870,6 @@ packages/lib/SyncTargetJoplinServer.js.map
|
||||
packages/lib/SyncTargetOneDrive.d.ts
|
||||
packages/lib/SyncTargetOneDrive.js
|
||||
packages/lib/SyncTargetOneDrive.js.map
|
||||
packages/lib/SyncTargetRegistry.d.ts
|
||||
packages/lib/SyncTargetRegistry.js
|
||||
packages/lib/SyncTargetRegistry.js.map
|
||||
packages/lib/Synchronizer.d.ts
|
||||
packages/lib/Synchronizer.js
|
||||
packages/lib/Synchronizer.js.map
|
||||
@@ -933,12 +927,6 @@ packages/lib/fs-driver-node.js.map
|
||||
packages/lib/fsDriver.test.d.ts
|
||||
packages/lib/fsDriver.test.js
|
||||
packages/lib/fsDriver.test.js.map
|
||||
packages/lib/hooks/useElementSize.d.ts
|
||||
packages/lib/hooks/useElementSize.js
|
||||
packages/lib/hooks/useElementSize.js.map
|
||||
packages/lib/hooks/useEventListener.d.ts
|
||||
packages/lib/hooks/useEventListener.js
|
||||
packages/lib/hooks/useEventListener.js.map
|
||||
packages/lib/htmlUtils.d.ts
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.js.map
|
||||
|
||||
2
.github/scripts/run_ci.sh
vendored
2
.github/scripts/run_ci.sh
vendored
@@ -134,7 +134,7 @@ if [[ $GIT_TAG_NAME = v* ]]; then
|
||||
elif [[ $GIT_TAG_NAME = server-v* ]] && [[ $IS_LINUX = 1 ]]; then
|
||||
echo "Step: Building Docker Image..."
|
||||
cd "$ROOT_DIR"
|
||||
npm run buildServerDocker -- --tag-name $GIT_TAG_NAME --push-images
|
||||
npm run buildServerDocker -- --tag-name $GIT_TAG_NAME
|
||||
else
|
||||
echo "Step: Building but *not* publishing desktop application..."
|
||||
USE_HARD_LINKS=false npm run dist -- --publish=never
|
||||
|
||||
12
.gitignore
vendored
12
.gitignore
vendored
@@ -558,9 +558,6 @@ packages/app-desktop/gui/Sidebar/styles/index.js.map
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.d.ts
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.js
|
||||
packages/app-desktop/gui/StatusScreen/StatusScreen.js.map
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.d.ts
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js
|
||||
packages/app-desktop/gui/SyncWizard/Dialog.js.map
|
||||
packages/app-desktop/gui/TagList.d.ts
|
||||
packages/app-desktop/gui/TagList.js
|
||||
packages/app-desktop/gui/TagList.js.map
|
||||
@@ -858,9 +855,6 @@ packages/lib/SyncTargetJoplinServer.js.map
|
||||
packages/lib/SyncTargetOneDrive.d.ts
|
||||
packages/lib/SyncTargetOneDrive.js
|
||||
packages/lib/SyncTargetOneDrive.js.map
|
||||
packages/lib/SyncTargetRegistry.d.ts
|
||||
packages/lib/SyncTargetRegistry.js
|
||||
packages/lib/SyncTargetRegistry.js.map
|
||||
packages/lib/Synchronizer.d.ts
|
||||
packages/lib/Synchronizer.js
|
||||
packages/lib/Synchronizer.js.map
|
||||
@@ -918,12 +912,6 @@ packages/lib/fs-driver-node.js.map
|
||||
packages/lib/fsDriver.test.d.ts
|
||||
packages/lib/fsDriver.test.js
|
||||
packages/lib/fsDriver.test.js.map
|
||||
packages/lib/hooks/useElementSize.d.ts
|
||||
packages/lib/hooks/useElementSize.js
|
||||
packages/lib/hooks/useElementSize.js.map
|
||||
packages/lib/hooks/useEventListener.d.ts
|
||||
packages/lib/hooks/useEventListener.js
|
||||
packages/lib/hooks/useEventListener.js.map
|
||||
packages/lib/htmlUtils.d.ts
|
||||
packages/lib/htmlUtils.js
|
||||
packages/lib/htmlUtils.js.map
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
# https://versatile.nl/blog/deploying-lerna-web-apps-with-docker
|
||||
|
||||
FROM node:16
|
||||
FROM node:12
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get --yes install vim
|
||||
|
||||
RUN echo "Node: $(node --version)"
|
||||
RUN echo "Npm: $(npm --version)"
|
||||
|
||||
ARG user=joplin
|
||||
|
||||
RUN useradd --create-home --shell /bin/bash $user
|
||||
|
||||
@@ -10,7 +10,7 @@ const { cliUtils } = require('./cli-utils.js');
|
||||
const md5 = require('md5');
|
||||
const locker = require('proper-lockfile');
|
||||
const fs = require('fs-extra');
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry').default;
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry');
|
||||
const MigrationHandler = require('@joplin/lib/services/synchronizer/MigrationHandler').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
28810
packages/app-cli/package-lock.json
generated
28810
packages/app-cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -32,7 +32,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "2.3.2",
|
||||
"version": "2.3.0",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import MdToHtml from '@joplin/renderer/MdToHtml';
|
||||
const os = require('os');
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import shim from '@joplin/lib/shim';
|
||||
@@ -56,8 +57,10 @@ describe('MdToHtml', function() {
|
||||
const result = await mdToHtml.render(markdown, null, mdToHtmlOptions);
|
||||
let actualHtml = result.html;
|
||||
|
||||
expectedHtml = expectedHtml.replace(/\r?\n/g, '\n');
|
||||
actualHtml = actualHtml.replace(/\r?\n/g, '\n');
|
||||
if (os.EOL === '\r\n') {
|
||||
expectedHtml = expectedHtml.replace(/\r\n/g, '\n');
|
||||
actualHtml = actualHtml.replace(/\r\n/g, '\n');
|
||||
}
|
||||
|
||||
if (actualHtml !== expectedHtml) {
|
||||
console.info('');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="joplin-editable"><pre class="joplin-source" data-joplin-language="javascript" data-joplin-source-open="```javascript " data-joplin-source-close=" ```">function() {
|
||||
console.info('bonjour');
|
||||
}</pre><pre class="hljs"><code><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) {
|
||||
<span class="hljs-variable language_">console</span>.<span class="hljs-title function_">info</span>(<span class="hljs-string">'bonjour'</span>);
|
||||
}</pre><pre class="hljs"><code><span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
|
||||
<span class="hljs-built_in">console</span>.info(<span class="hljs-string">'bonjour'</span>);
|
||||
}</code></pre></div>
|
||||
|
||||
1
packages/app-desktop/.gitignore
vendored
1
packages/app-desktop/.gitignore
vendored
@@ -9,4 +9,3 @@ gui/note-viewer/fonts/
|
||||
gui/note-viewer/lib.js
|
||||
gui/NoteEditor/NoteBody/TinyMCE/supportedLocales.js
|
||||
runForSharingCommands-*
|
||||
runForTestingCommands-*
|
||||
|
||||
@@ -114,10 +114,6 @@ interface AppStateRoute {
|
||||
props: any;
|
||||
}
|
||||
|
||||
export interface AppStateDialog {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AppState extends State {
|
||||
route: AppStateRoute;
|
||||
navHistory: any[];
|
||||
@@ -134,7 +130,6 @@ export interface AppState extends State {
|
||||
// Extra reducer keys go here
|
||||
watchedResources: any;
|
||||
mainLayout: LayoutItem;
|
||||
dialogs: AppStateDialog[];
|
||||
}
|
||||
|
||||
const appDefaultState: AppState = {
|
||||
@@ -155,7 +150,6 @@ const appDefaultState: AppState = {
|
||||
layoutMoveMode: false,
|
||||
mainLayout: null,
|
||||
startupPluginsLoaded: false,
|
||||
dialogs: [],
|
||||
...resourceEditWatcherDefaultState,
|
||||
};
|
||||
|
||||
@@ -376,30 +370,6 @@ class Application extends BaseApplication {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DIALOG_OPEN':
|
||||
|
||||
{
|
||||
newState = Object.assign({}, state);
|
||||
const newDialogs = newState.dialogs.slice();
|
||||
|
||||
if (newDialogs.find(d => d.name === action.name)) throw new Error(`This dialog is already opened: ${action.name}`);
|
||||
|
||||
newDialogs.push({
|
||||
name: action.name,
|
||||
});
|
||||
newState.dialogs = newDialogs;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'DIALOG_CLOSE':
|
||||
|
||||
{
|
||||
newState = Object.assign({}, state);
|
||||
const newDialogs = newState.dialogs.slice().filter(d => d.name !== action.name);
|
||||
newState.dialogs = newDialogs;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'LAYOUT_MOVE_MODE_SET':
|
||||
|
||||
newState = {
|
||||
@@ -864,14 +834,6 @@ class Application extends BaseApplication {
|
||||
// });
|
||||
// }, 5000);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
this.dispatch({
|
||||
type: 'DIALOG_OPEN',
|
||||
name: 'syncWizard',
|
||||
});
|
||||
}, 2000);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="43px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 43 40" version="1.1" height="40px">
|
||||
<path d="m12.5 0l-12.5 8.1 8.7 7 12.5-7.8-8.7-7.3zm-12.5 21.9l12.5 8.2 8.7-7.3-12.5-7.7-8.7 6.8zm21.2 0.9l8.8 7.3 12.4-8.1-8.6-6.9-12.6 7.7zm21.2-14.7l-12.4-8.1-8.8 7.3 12.6 7.8 8.6-7zm-21.1 16.3l-8.8 7.3-3.7-2.5v2.8l12.5 7.5 12.5-7.5v-2.8l-3.8 2.5-8.7-7.3z" fill="#007EE5"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 441 B |
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 682.66669 682.66669" height="682.66669" width="682.66669" xml:space="preserve" id="svg2" version="1.1">
|
||||
<defs id="defs6">
|
||||
<linearGradient id="linearGradient26" spreadMethod="pad" gradientTransform="matrix(-4387.91,4387.91,4387.91,4387.91,4753.95,366.05)" gradientUnits="userSpaceOnUse" y2="0" x2="1" y1="0" x1="0">
|
||||
<stop id="stop22" offset="0" style="stop-opacity:1;stop-color:#004caf"/>
|
||||
<stop id="stop24" offset="1" style="stop-opacity:1;stop-color:#1f95f8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g transform="matrix(1.3333333,0,0,-1.3333333,0,682.66667)" id="g10">
|
||||
<g transform="scale(0.1)" id="g12">
|
||||
<g id="g14">
|
||||
<g clip-path="url(#clipPath20)" id="g16">
|
||||
<path id="path28" style="fill:url(#linearGradient26);fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 3873.89,0 H 1246.11 C 560.754,0 0,560.75 0,1246.11 V 3873.88 C 0,4559.25 560.754,5120 1246.11,5120 H 3873.89 C 4559.25,5120 5120,4559.25 5120,3873.88 V 1246.11 C 5120,560.75 4559.25,0 3873.89,0"/>
|
||||
</g>
|
||||
</g>
|
||||
<path id="path30" style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" d="M 3961.59,4435.23 H 2570.18 c -13.15,0 -23.78,-10.64 -23.78,-23.77 v -441.84 c 0,-14.87 12.04,-26.92 26.92,-26.92 h 190.77 c 77.16,0 139.73,-59.35 146.43,-134.77 V 3505 3336.23 1728.75 1717.36 h -0.05 c 0.48,-16.84 -0.19,-33.4 -1.83,-49.71 -0.18,-2.38 -0.5,-4.73 -0.79,-7.09 -1.1,-9.53 -2.32,-19.01 -4.17,-28.29 -1.01,-5.29 -2.44,-10.44 -3.71,-15.65 -1.71,-6.93 -3.09,-13.97 -5.22,-20.75 -12.58,-40.27 -32.47,-77.62 -59.98,-110.5 -1.01,-1.17 -2.26,-2.25 -3.26,-3.41 -8.39,-9.72 -17.2,-19.19 -26.95,-28.06 -9.84,-8.95 -20.26,-17.27 -31.21,-25 -77.84,-55.14 -182.61,-79.4 -299.67,-68.2 -149.26,14.03 -297.34,81.72 -417.03,190.62 -119.67,108.89 -194.08,243.62 -209.48,379.41 -13.85,121.48 22.55,228.38 102.42,301.05 0.21,0.16 0.4,0.31 0.56,0.48 3.09,2.77 6.49,5.2 9.67,7.87 57.16,47.89 131.67,76.91 216.7,84.91 0.96,0.09 1.88,0.24 2.79,0.32 8.95,0.79 18.07,1.15 27.27,1.49 4.81,0.16 9.56,0.5 14.44,0.54 1.62,0.02 3.16,0.19 4.78,0.19 2.9,0 5.91,-0.38 8.81,-0.42 13.4,-0.21 26.9,-0.76 40.67,-1.94 1.74,-0.14 3.4,-0.08 5.19,-0.24 1.27,-0.13 2.53,-0.41 3.8,-0.54 78,-7.82 155.23,-31.11 228.52,-66.4 1.53,-0.07 3.3,-0.54 5.51,-1.76 22.34,-12.34 26.62,0.9 27.28,9.65 v 382.24 282.82 c 0,19.05 -13.25,35.9 -31.83,39.99 -394.76,86.88 -782.08,-3.55 -1055.38,-252.34 -238.75,-217.18 -354.24,-530.58 -316.82,-859.79 33.39,-293.23 183.91,-574.94 423.88,-793.33 233.89,-212.79 531.69,-345.86 838.88,-374.801 42.33,-3.918 84.86,-5.938 126.36,-5.938 293.38,0 565.61,100.598 766.54,283.379 190.34,173.3 304.35,411.27 321.08,670.16 l 1.55,1697.91 h 0.17 v 453.97 h 0.06 v 7.92 c 1.72,80.12 67.05,144.58 147.61,144.58 h 190.77 c 14.86,0 26.92,12.05 26.92,26.92 v 441.84 c 0,13.13 -10.63,23.77 -23.78,23.77"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 23.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_extend "http://ns.adobe.com/Extensibility/1.0/">
|
||||
<!ENTITY ns_ai "http://ns.adobe.com/AdobeIllustrator/10.0/">
|
||||
<!ENTITY ns_graphs "http://ns.adobe.com/Graphs/1.0/">
|
||||
<!ENTITY ns_vars "http://ns.adobe.com/Variables/1.0/">
|
||||
<!ENTITY ns_imrep "http://ns.adobe.com/ImageReplacement/1.0/">
|
||||
<!ENTITY ns_sfw "http://ns.adobe.com/SaveForWeb/1.0/">
|
||||
<!ENTITY ns_custom "http://ns.adobe.com/GenericCustomNamespace/1.0/">
|
||||
<!ENTITY ns_adobe_xpath "http://ns.adobe.com/XPath/1.0/">
|
||||
]>
|
||||
<svg version="1.1" id="Livello_1" xmlns:x="&ns_extend;" xmlns:i="&ns_ai;" xmlns:graph="&ns_graphs;"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 1030.04 659.922"
|
||||
enable-background="new 0 0 1030.04 659.922" xml:space="preserve">
|
||||
<metadata>
|
||||
<sfw xmlns="&ns_sfw;">
|
||||
<slices></slices>
|
||||
<sliceSourceBounds bottomLeftOrigin="true" height="659.922" width="1030.04" x="-490" y="-344.922"></sliceSourceBounds>
|
||||
</sfw>
|
||||
</metadata>
|
||||
<g id="STYLE_COLOR_1_">
|
||||
<path fill="#0364B8" d="M622.292,445.338l212.613-203.327C790.741,69.804,615.338-33.996,443.13,10.168
|
||||
C365.58,30.056,298.224,78.13,254.209,145.005C257.5,144.922,622.292,445.338,622.292,445.338z"/>
|
||||
<path fill="#0078D4" d="M392.776,183.283l-0.01,0.035c-40.626-25.162-87.479-38.462-135.267-38.397
|
||||
c-1.104,0-2.189,0.07-3.291,0.083C112.064,146.765-1.74,263.423,0.02,405.567c0.638,51.562,16.749,101.743,46.244,144.04
|
||||
l318.528-39.894l244.209-196.915L392.776,183.283z"/>
|
||||
<path fill="#1490DF" d="M834.905,242.012c-4.674-0.312-9.371-0.528-14.123-0.528c-28.523-0.028-56.749,5.798-82.93,17.117
|
||||
l-0.006-0.022l-128.844,54.22l142.041,175.456l253.934,61.728c54.799-101.732,16.752-228.625-84.98-283.424
|
||||
c-26.287-14.16-55.301-22.529-85.091-24.546V242.012z"/>
|
||||
<path fill="#28A8EA" d="M46.264,549.607C94.359,618.756,173.27,659.966,257.5,659.922h563.281
|
||||
c76.946,0.022,147.691-42.202,184.195-109.937L609.001,312.798L46.264,549.607z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@@ -11,7 +11,7 @@ import EncryptionConfigScreen from '../EncryptionConfigScreen';
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const pathUtils = require('@joplin/lib/path-utils');
|
||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry');
|
||||
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||
import ClipperConfigScreen from '../ClipperConfigScreen';
|
||||
const { KeymapConfigScreen } = require('../KeymapConfig/KeymapConfigScreen');
|
||||
@@ -94,11 +94,6 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
Setting.setValue('sync.startupOperation', SyncStartupOperation.ClearLocalData);
|
||||
await Setting.saveAll();
|
||||
bridge().restart();
|
||||
} else if (key === 'sync.openSyncWizard') {
|
||||
this.props.dispatch({
|
||||
type: 'DIALOG_OPEN',
|
||||
name: 'syncWizard',
|
||||
});
|
||||
} else {
|
||||
throw new Error(`Unhandled key: ${key}`);
|
||||
}
|
||||
@@ -611,15 +606,11 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_BUTTON) {
|
||||
const labelComp = md.hideLabel ? null : (
|
||||
<div style={labelStyle}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
{labelComp}
|
||||
<div style={labelStyle}>
|
||||
<label>{md.label()}</label>
|
||||
</div>
|
||||
<Button level={ButtonLevel.Secondary} title={md.label()} onClick={md.onClick ? md.onClick : () => this.handleSettingButton(key)}/>
|
||||
{descriptionComp}
|
||||
</div>
|
||||
|
||||
@@ -18,11 +18,10 @@ const DialogRoot = styled.div`
|
||||
background-color: ${props => props.theme.backgroundColor};
|
||||
padding: 16px;
|
||||
box-shadow: 6px 6px 20px rgba(0,0,0,0.5);
|
||||
margin: 20px;
|
||||
margin-top: 20px;
|
||||
min-height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 10px;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Root = styled.div`
|
||||
display: flex;
|
||||
justify-content: ${props => props.justifyContent ? props.justifyContent : 'flex-start'};
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
font-size: ${props => props.theme.fontSize * 1.5}px;
|
||||
line-height: 1.6em;
|
||||
@@ -14,11 +12,10 @@ const Root = styled.div`
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
justifyContent?: string;
|
||||
}
|
||||
|
||||
export default function DialogTitle(props: Props) {
|
||||
return (
|
||||
<Root justifyContent={props.justifyContent}>{props.title}</Root>
|
||||
<Root>{props.title}</Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ import bridge from '../services/bridge';
|
||||
import shared from '@joplin/lib/components/shared/encryption-config-shared';
|
||||
import { MasterKeyEntity } from '@joplin/lib/services/database/types';
|
||||
import { getEncryptionEnabled, SyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
import { toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
|
||||
import MasterKey from '@joplin/lib/models/MasterKey';
|
||||
import { toggleAndSetupEncryption } from '../../lib/services/e2ee/utils';
|
||||
import MasterKey from '../../lib/models/MasterKey';
|
||||
|
||||
interface Props {}
|
||||
|
||||
|
||||
@@ -33,11 +33,10 @@ const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import ErrorBoundary from '../../../ErrorBoundary';
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
|
||||
const menuUtils = new MenuUtils(CommandService.instance());
|
||||
|
||||
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
|
||||
function markupRenderOptions(override: any = null) {
|
||||
return { ...override };
|
||||
}
|
||||
|
||||
@@ -385,12 +384,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
if (Setting.value('style.editor.monospaceFontFamily')) monospaceFonts.push(`"${Setting.value('style.editor.monospaceFontFamily')}"`);
|
||||
monospaceFonts.push('monospace');
|
||||
|
||||
const maxWidthCss = props.contentMaxWidth ? `
|
||||
margin-right: auto !important;
|
||||
margin-left: auto !important;
|
||||
max-width: ${props.contentMaxWidth}px !important;
|
||||
` : '';
|
||||
|
||||
const element = document.createElement('style');
|
||||
element.setAttribute('id', 'codemirrorStyle');
|
||||
document.head.appendChild(element);
|
||||
@@ -425,7 +418,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
/* Add a fixed right padding to account for the appearance (and disappearance) */
|
||||
/* of the sidebar */
|
||||
padding-right: 10px !important;
|
||||
${maxWidthCss}
|
||||
}
|
||||
|
||||
/* This enforces monospace for certain elements (code, tables, etc.) */
|
||||
@@ -475,20 +467,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
color: ${theme.codeColor};
|
||||
}
|
||||
|
||||
div.CodeMirror span.cm-comment.cm-jn-inline-code {
|
||||
border: 1px solid ${theme.codeBorderColor};
|
||||
background-color: ${theme.codeBackgroundColor};
|
||||
padding-right: .2em;
|
||||
padding-left: .2em;
|
||||
border-radius: .25em;
|
||||
}
|
||||
|
||||
div.CodeMirror pre.cm-jn-code-block {
|
||||
background-color: ${theme.codeBackgroundColor};
|
||||
padding-right: .2em;
|
||||
padding-left: .2em;
|
||||
}
|
||||
|
||||
div.CodeMirror span.cm-strong {
|
||||
color: ${theme.colorBright};
|
||||
}
|
||||
@@ -555,7 +533,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
return () => {
|
||||
document.head.removeChild(element);
|
||||
};
|
||||
}, [props.themeId, props.contentMaxWidth]);
|
||||
}, [props.themeId]);
|
||||
|
||||
const webview_domReady = useCallback(() => {
|
||||
setWebviewReady(true);
|
||||
@@ -594,10 +572,7 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
bodyToRender = `<i>${_('This note has no content. Click on "%s" to toggle the editor and edit the note.', _('Layout'))}</i>`;
|
||||
}
|
||||
|
||||
const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({
|
||||
resourceInfos: props.resourceInfos,
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
}));
|
||||
const result = await props.markupToHtml(props.contentMarkupLanguage, bodyToRender, markupRenderOptions({ resourceInfos: props.resourceInfos }));
|
||||
|
||||
if (cancelled) return;
|
||||
|
||||
@@ -820,7 +795,6 @@ function CodeMirror(props: NoteBodyEditorProps, ref: any) {
|
||||
viewerStyle={styles.viewer}
|
||||
onIpcMessage={webview_ipcMessage}
|
||||
onDomReady={webview_domReady}
|
||||
contentMaxWidth={props.contentMaxWidth}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -7,7 +7,6 @@ interface JoplinModeState {
|
||||
outer: any;
|
||||
openCharacter: string;
|
||||
inTable: boolean;
|
||||
inCodeBlock: boolean;
|
||||
inner: any;
|
||||
}
|
||||
|
||||
@@ -49,7 +48,6 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
outer: CodeMirror.startState(markdownMode),
|
||||
openCharacter: '',
|
||||
inTable: false,
|
||||
inCodeBlock: false,
|
||||
inner: CodeMirror.startState(stex),
|
||||
};
|
||||
},
|
||||
@@ -59,7 +57,6 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
outer: CodeMirror.copyState(markdownMode, state.outer),
|
||||
openCharacter: state.openCharacter,
|
||||
inTable: state.inTable,
|
||||
inCodeBlock: state.inCodeBlock,
|
||||
inner: CodeMirror.copyState(stex, state.inner),
|
||||
};
|
||||
},
|
||||
@@ -118,26 +115,9 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
let isMonospace = false;
|
||||
// After being passed to the markdown mode we can check if the
|
||||
// code state variables are set
|
||||
// Code
|
||||
if (state.outer.code > 0) {
|
||||
// state.outer.code holds the number of preceding backticks
|
||||
// anything > 0 backticks is an inline-code-block
|
||||
// -1 is used for actual code blocks
|
||||
// Code Block
|
||||
if (state.outer.code || (state.outer.thisLine && state.outer.thisLine.fencedCodeEnd)) {
|
||||
isMonospace = true;
|
||||
token = `${token} jn-inline-code`;
|
||||
} else if (state.outer.thisLine && state.outer.thisLine.fencedCodeEnd) {
|
||||
state.inCodeBlock = false;
|
||||
isMonospace = true;
|
||||
token = `${token} line-cm-jn-code-block`;
|
||||
} else if (state.outer.code === -1 || state.inCodeBlock) {
|
||||
state.inCodeBlock = true;
|
||||
isMonospace = true;
|
||||
token = `${token} line-cm-jn-code-block`;
|
||||
} else if (stream.pos > 0 && stream.string[stream.pos - 1] === '`' &&
|
||||
!!token && token.includes('comment')) {
|
||||
// This grabs the closing backtick for inline Code
|
||||
isMonospace = true;
|
||||
token = `${token} jn-inline-code`;
|
||||
}
|
||||
// Indented Code
|
||||
if (state.outer.indentedCode) {
|
||||
@@ -183,10 +163,6 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
}
|
||||
|
||||
state.inTable = false;
|
||||
|
||||
if (state.inCodeBlock) return 'line-cm-jn-code-block';
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
electricChars: markdownMode.electricChars,
|
||||
|
||||
@@ -14,18 +14,18 @@ import { _, closestSupportedLocale } from '@joplin/lib/locale';
|
||||
import useContextMenu from './utils/useContextMenu';
|
||||
import { copyHtmlToClipboard } from '../../utils/clipboardUtils';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { MarkupToHtml } from '@joplin/renderer';
|
||||
|
||||
const { MarkupToHtml } = require('@joplin/renderer');
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
import BaseItem from '@joplin/lib/models/BaseItem';
|
||||
import setupToolbarButtons from './utils/setupToolbarButtons';
|
||||
import { plainTextToHtml } from '@joplin/lib/htmlUtils';
|
||||
import openEditDialog from './utils/openEditDialog';
|
||||
import { MarkupToHtmlOptions } from '../../utils/useMarkupToHtml';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
const { clipboard } = require('electron');
|
||||
const supportedLocales = require('./supportedLocales');
|
||||
|
||||
function markupRenderOptions(override: MarkupToHtmlOptions = null): MarkupToHtmlOptions {
|
||||
function markupRenderOptions(override: any = null) {
|
||||
return {
|
||||
plugins: {
|
||||
checkbox: {
|
||||
@@ -148,6 +148,8 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
||||
if (!resourceMd) return;
|
||||
const result = await props.markupToHtml(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, resourceMd, markupRenderOptions({ bodyOnly: true }));
|
||||
editor.insertContent(result.html);
|
||||
// editor.fire('joplinChange');
|
||||
// dispatchDidUpdate(editor);
|
||||
}, [props.markupToHtml, editor]);
|
||||
|
||||
const insertResourcesIntoContentRef = useRef(null);
|
||||
|
||||
@@ -159,8 +159,8 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
customCss: props.customCss,
|
||||
});
|
||||
|
||||
return markupToHtml.allAssets(markupLanguage, theme, { contentMaxWidth: props.contentMaxWidth });
|
||||
}, [props.themeId, props.customCss, props.contentMaxWidth]);
|
||||
return markupToHtml.allAssets(markupLanguage, theme);
|
||||
}, [props.themeId, props.customCss]);
|
||||
|
||||
const handleProvisionalFlag = useCallback(() => {
|
||||
if (props.isProvisional) {
|
||||
@@ -400,7 +400,6 @@ function NoteEditor(props: NoteEditorProps) {
|
||||
noteToolbarButtonInfos: props.toolbarButtonInfos,
|
||||
plugins: props.plugins,
|
||||
fontSize: Setting.value('style.editor.fontSize'),
|
||||
contentMaxWidth: props.contentMaxWidth,
|
||||
};
|
||||
|
||||
let editor = null;
|
||||
@@ -602,7 +601,6 @@ const mapStateToProps = (state: AppState) => {
|
||||
setTagsToolbarButtonInfo: toolbarButtonUtils.commandsToToolbarButtons([
|
||||
'setTags',
|
||||
], whenClauseContext)[0],
|
||||
contentMaxWidth: state.settings['style.editor.contentMaxWidth'],
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ToolbarButtonInfo } from '@joplin/lib/services/commands/ToolbarButtonUt
|
||||
import { PluginStates } from '@joplin/lib/services/plugins/reducer';
|
||||
import { MarkupLanguage } from '@joplin/renderer';
|
||||
import { RenderResult, RenderResultPluginAsset } from '@joplin/renderer/MarkupToHtml';
|
||||
import { MarkupToHtmlOptions } from './useMarkupToHtml';
|
||||
|
||||
export interface ToolbarButtonInfos {
|
||||
[key: string]: ToolbarButtonInfo;
|
||||
@@ -38,7 +37,6 @@ export interface NoteEditorProps {
|
||||
toolbarButtonInfos: ToolbarButtonInfo[];
|
||||
setTagsToolbarButtonInfo: ToolbarButtonInfo;
|
||||
richTextBannerDismissed: boolean;
|
||||
contentMaxWidth: number;
|
||||
}
|
||||
|
||||
export interface NoteBodyEditorProps {
|
||||
@@ -53,7 +51,7 @@ export interface NoteBodyEditorProps {
|
||||
onWillChange(event: any): void;
|
||||
onMessage(event: any): void;
|
||||
onScroll(event: any): void;
|
||||
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: MarkupToHtmlOptions)=> Promise<RenderResult>;
|
||||
markupToHtml: (markupLanguage: MarkupLanguage, markup: string, options: any)=> Promise<RenderResult>;
|
||||
htmlToMarkdown: Function;
|
||||
allAssets: (markupLanguage: MarkupLanguage)=> Promise<RenderResultPluginAsset[]>;
|
||||
disabled: boolean;
|
||||
@@ -69,7 +67,6 @@ export interface NoteBodyEditorProps {
|
||||
noteToolbarButtonInfos: ToolbarButtonInfo[];
|
||||
plugins: PluginStates;
|
||||
fontSize: number;
|
||||
contentMaxWidth: number;
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
|
||||
@@ -13,12 +13,9 @@ interface HookDependencies {
|
||||
plugins: PluginStates;
|
||||
}
|
||||
|
||||
export interface MarkupToHtmlOptions {
|
||||
interface MarkupToHtmlOptions {
|
||||
replaceResourceInternalToExternalLinks?: boolean;
|
||||
resourceInfos?: ResourceInfos;
|
||||
contentMaxWidth?: number;
|
||||
plugins?: Record<string, any>;
|
||||
bodyOnly?: boolean;
|
||||
}
|
||||
|
||||
export default function useMarkupToHtml(deps: HookDependencies) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import app, { AppState, AppStateDialog } from '../app';
|
||||
import app from '../app';
|
||||
import MainScreen from './MainScreen/MainScreen';
|
||||
import ConfigScreen from './ConfigScreen/ConfigScreen';
|
||||
import StatusScreen from './StatusScreen/StatusScreen';
|
||||
@@ -10,6 +10,7 @@ import { Size } from './ResizableLayout/utils/types';
|
||||
import MenuBar from './MenuBar';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
const React = require('react');
|
||||
|
||||
const { render } = require('react-dom');
|
||||
const { connect, Provider } = require('react-redux');
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
@@ -18,7 +19,6 @@ import ClipperServer from '@joplin/lib/ClipperServer';
|
||||
import DialogTitle from './DialogTitle';
|
||||
import DialogButtonRow, { ButtonSpec, ClickEvent, ClickEventHandler } from './DialogButtonRow';
|
||||
import Dialog from './Dialog';
|
||||
import SyncWizardDialog from './SyncWizard/Dialog';
|
||||
const { ImportScreen } = require('./ImportScreen.min.js');
|
||||
const { ResourceScreen } = require('./ResourceScreen.js');
|
||||
const { Navigator } = require('./Navigator.min.js');
|
||||
@@ -33,7 +33,6 @@ interface Props {
|
||||
size: Size;
|
||||
zoomFactor: number;
|
||||
needApiAuth: boolean;
|
||||
dialogs: AppStateDialog;
|
||||
}
|
||||
|
||||
interface ModalDialogProps {
|
||||
@@ -43,24 +42,6 @@ interface ModalDialogProps {
|
||||
onClick: ClickEventHandler;
|
||||
}
|
||||
|
||||
interface RegisteredDialogProps {
|
||||
themeId: number;
|
||||
key: string;
|
||||
dispatch: Function;
|
||||
}
|
||||
|
||||
interface RegisteredDialog {
|
||||
render: (props: RegisteredDialogProps)=> any;
|
||||
}
|
||||
|
||||
const registeredDialogs: Record<string, RegisteredDialog> = {
|
||||
syncWizard: {
|
||||
render: (props: RegisteredDialogProps) => {
|
||||
return <SyncWizardDialog key={props.key} dispatch={props.dispatch} themeId={props.themeId}/>;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
@@ -170,22 +151,6 @@ class RootComponent extends React.Component<Props, any> {
|
||||
};
|
||||
}
|
||||
|
||||
private renderDialogs() {
|
||||
if (!this.props.dialogs.length) return null;
|
||||
|
||||
const output: any[] = [];
|
||||
for (const dialog of this.props.dialogs) {
|
||||
const md = registeredDialogs[dialog.name];
|
||||
if (!md) throw new Error(`Unknown dialog: ${dialog.name}`);
|
||||
output.push(md.render({
|
||||
key: dialog.name,
|
||||
themeId: this.props.themeId,
|
||||
dispatch: this.props.dispatch,
|
||||
}));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public render() {
|
||||
const navigatorStyle = {
|
||||
width: this.props.size.width / this.props.zoomFactor,
|
||||
@@ -211,21 +176,19 @@ class RootComponent extends React.Component<Props, any> {
|
||||
<GlobalStyle/>
|
||||
<Navigator style={navigatorStyle} screens={screens} />
|
||||
{this.renderModalMessage(this.modalDialogProps())}
|
||||
{this.renderDialogs()}
|
||||
</ThemeProvider>
|
||||
</StyleSheetManager>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
const mapStateToProps = (state: any) => {
|
||||
return {
|
||||
size: state.windowContentSize,
|
||||
zoomFactor: state.settings.windowContentZoomFactor / 100,
|
||||
appState: state.appState,
|
||||
themeId: state.settings.theme,
|
||||
needApiAuth: state.needApiAuth,
|
||||
dialogs: state.dialogs,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -1,343 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useRef, useCallback } from 'react';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import DialogButtonRow from '../DialogButtonRow';
|
||||
import Dialog from '../Dialog';
|
||||
import styled from 'styled-components';
|
||||
import DialogTitle from '../DialogTitle';
|
||||
import SyncTargetRegistry, { SyncTargetInfo } from '@joplin/lib/SyncTargetRegistry';
|
||||
import useElementSize from '@joplin/lib/hooks/useElementSize';
|
||||
import Button, { ButtonLevel } from '../Button/Button';
|
||||
import bridge from '../../services/bridge';
|
||||
import StyledInput from '../style/StyledInput';
|
||||
import Setting from '../../../lib/models/Setting';
|
||||
import SyncTargetJoplinCloud from '../../../lib/SyncTargetJoplinCloud';
|
||||
import StyledLink from '../style/StyledLink';
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
dispatch: Function;
|
||||
}
|
||||
|
||||
const StyledRoot = styled.div`
|
||||
min-width: 500px;
|
||||
max-width: 1200px;
|
||||
`;
|
||||
|
||||
const SyncTargetDescription = styled.div`
|
||||
${props => props.height ? `height: ${props.height}px` : ''};
|
||||
margin-bottom: 1.3em;
|
||||
line-height: ${props => props.theme.lineHeight};
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const CreateAccountLink = styled(StyledLink)`
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const ContentRoot = styled.div`
|
||||
background-color: ${props => props.theme.backgroundColor3};
|
||||
padding: 1em;
|
||||
padding-right: 0;
|
||||
`;
|
||||
|
||||
const SelfHostingMessage = styled.div`
|
||||
color: ${props => props.theme.color};
|
||||
padding-right: 1em;
|
||||
font-style: italic;
|
||||
margin-top: 1em;
|
||||
opacity: 0.6;
|
||||
`;
|
||||
|
||||
const SyncTargetBoxes = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
const SyncTargetTitle = styled.p`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-weight: bold;
|
||||
font-size: 1.7em;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
const SyncTargetLogo = styled.img`
|
||||
height: 1.3em;
|
||||
margin-right: 0.4em;
|
||||
`;
|
||||
|
||||
const SyncTargetBox = styled.div`
|
||||
display: flex;
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
color: ${props => props.theme.color};
|
||||
background-color: ${props => props.theme.backgroundColor};
|
||||
border: 1px solid ${props => props.theme.dividerColor};
|
||||
border-radius: 8px;
|
||||
padding: 0.8em 2.2em 2em 2.2em;
|
||||
margin-right: 1em;
|
||||
max-width: 400px;
|
||||
opacity: ${props => props.faded ? 0.5 : 1};
|
||||
`;
|
||||
|
||||
const FeatureList = styled.div`
|
||||
margin-bottom: 1em;
|
||||
`;
|
||||
|
||||
const FeatureIcon = styled.i`
|
||||
display: inline-flex;
|
||||
width: 16px;
|
||||
justify-content: center;
|
||||
color: ${props => props.theme.color4};
|
||||
position: absolute;
|
||||
`;
|
||||
|
||||
const FeatureLine = styled.div`
|
||||
margin-bottom: .5em;
|
||||
opacity: ${props => props.enabled ? 1 : 0.5};
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
const FeatureLabel = styled.div`
|
||||
margin-left: 24px;
|
||||
line-height: ${props => props.theme.lineHeight};
|
||||
`;
|
||||
|
||||
const SelectButton = styled(Button)`
|
||||
padding: 10px 10px;
|
||||
height: auto;
|
||||
min-height: auto;
|
||||
max-height: fit-content;
|
||||
font-size: 1em;
|
||||
`;
|
||||
|
||||
const JoplinCloudLoginForm = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
`;
|
||||
|
||||
const FormLabel = styled.label`
|
||||
font-weight: bold;
|
||||
margin: 1em 0 0.6em 0;
|
||||
`;
|
||||
|
||||
const syncTargetNames: string[] = [
|
||||
'joplinCloud',
|
||||
'dropbox',
|
||||
'onedrive',
|
||||
'nextcloud',
|
||||
'webdav',
|
||||
'amazon_s3',
|
||||
'joplinServer',
|
||||
];
|
||||
|
||||
|
||||
const logosImageNames: Record<string, string> = {
|
||||
'dropbox': 'Dropbox.svg',
|
||||
'joplinCloud': 'JoplinCloud.svg',
|
||||
'onedrive': 'OneDrive.svg',
|
||||
};
|
||||
|
||||
export default function(props: Props) {
|
||||
const [showJoplinCloudForm, setShowJoplinCloudForm] = useState(false);
|
||||
const joplinCloudDescriptionRef = useRef(null);
|
||||
const [joplinCloudEmail, setJoplinCloudEmail] = useState('');
|
||||
const [joplinCloudPassword, setJoplinCloudPassword] = useState('');
|
||||
const [joplinCloudLoginInProgress, setJoplinCloudLoginInProgress] = useState(false);
|
||||
|
||||
function closeDialog(dispatch: Function) {
|
||||
dispatch({
|
||||
type: 'DIALOG_CLOSE',
|
||||
name: 'syncWizard',
|
||||
});
|
||||
}
|
||||
|
||||
const onButtonRowClick = useCallback(() => {
|
||||
closeDialog(props.dispatch);
|
||||
}, [props.dispatch]);
|
||||
|
||||
const { height: descriptionHeight } = useElementSize(joplinCloudDescriptionRef);
|
||||
|
||||
function renderFeature(enabled: boolean, label: string) {
|
||||
const className = enabled ? 'fas fa-check' : 'fas fa-times';
|
||||
return (
|
||||
<FeatureLine enabled={enabled} key={label}><FeatureIcon className={className}></FeatureIcon> <FeatureLabel>{label}</FeatureLabel></FeatureLine>
|
||||
);
|
||||
}
|
||||
|
||||
function renderFeatures(name: string) {
|
||||
return (
|
||||
<FeatureList>
|
||||
{[
|
||||
renderFeature(true, _('Sync your notes')),
|
||||
renderFeature(name === 'joplinCloud', _('Publish notes to the internet')),
|
||||
renderFeature(name === 'joplinCloud', _('Collaborate on notebooks with others')),
|
||||
]}
|
||||
</FeatureList>
|
||||
);
|
||||
}
|
||||
|
||||
const onJoplinCloudEmailChange = useCallback((event: any) => {
|
||||
setJoplinCloudEmail(event.target.value);
|
||||
}, []);
|
||||
|
||||
const onJoplinCloudPasswordChange = useCallback((event: any) => {
|
||||
setJoplinCloudPassword(event.target.value);
|
||||
}, []);
|
||||
|
||||
const onJoplinCloudLoginClick = useCallback(async () => {
|
||||
setJoplinCloudLoginInProgress(true);
|
||||
|
||||
try {
|
||||
const result = await SyncTargetJoplinCloud.checkConfig({
|
||||
password: () => joplinCloudPassword,
|
||||
path: () => Setting.value('sync.10.path'),
|
||||
userContentPath: () => Setting.value('sync.10.userContentPath'),
|
||||
username: () => joplinCloudEmail,
|
||||
});
|
||||
|
||||
if (result.ok) {
|
||||
Setting.setValue('sync.target', 10);
|
||||
Setting.setValue('sync.10.username', joplinCloudEmail);
|
||||
Setting.setValue('sync.10.password', joplinCloudPassword);
|
||||
await Setting.saveAll();
|
||||
|
||||
alert(_('Thank you! Your Joplin Cloud account is now setup and ready to use.'));
|
||||
|
||||
closeDialog(props.dispatch);
|
||||
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Main',
|
||||
});
|
||||
} else {
|
||||
alert(_('There was an error setting up your Joplin Cloud account. Please verify your email and password and try again. Error was:\n\n%s', result.errorMessage));
|
||||
}
|
||||
} finally {
|
||||
setJoplinCloudLoginInProgress(false);
|
||||
}
|
||||
}, [joplinCloudEmail, joplinCloudPassword, props.dispatch]);
|
||||
|
||||
const onJoplinCloudCreateAccountClick = useCallback(() => {
|
||||
bridge().openExternal('https://joplinapp.org/plans/');
|
||||
}, []);
|
||||
|
||||
function renderJoplinCloudLoginForm() {
|
||||
return (
|
||||
<JoplinCloudLoginForm>
|
||||
<div>{_('Login below.')} <CreateAccountLink href="#" onClick={onJoplinCloudCreateAccountClick}>{_('Or create an account.')}</CreateAccountLink></div>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<StyledInput type="email" onChange={onJoplinCloudEmailChange}/>
|
||||
<FormLabel>Password</FormLabel>
|
||||
<StyledInput type="password" onChange={onJoplinCloudPasswordChange}/>
|
||||
<SelectButton mt="1.3em" disabled={joplinCloudLoginInProgress} level={ButtonLevel.Primary} title={_('Login')} onClick={onJoplinCloudLoginClick}/>
|
||||
</JoplinCloudLoginForm>
|
||||
);
|
||||
}
|
||||
|
||||
const onSelectButtonClick = useCallback(async (name: string) => {
|
||||
if (name === 'joplinCloud') {
|
||||
setShowJoplinCloudForm(true);
|
||||
} else {
|
||||
Setting.setValue('sync.target', name === 'dropbox' ? 7 : 3);
|
||||
await Setting.saveAll();
|
||||
closeDialog(props.dispatch);
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: name === 'dropbox' ? 'DropboxLogin' : 'OneDriveLogin',
|
||||
});
|
||||
}
|
||||
}, [props.dispatch]);
|
||||
|
||||
function renderSelectArea(info: SyncTargetInfo) {
|
||||
if (info.name === 'joplinCloud' && showJoplinCloudForm) {
|
||||
return renderJoplinCloudLoginForm();
|
||||
} else {
|
||||
return (
|
||||
<SelectButton
|
||||
level={ButtonLevel.Primary}
|
||||
title={_('Select')}
|
||||
onClick={() => onSelectButtonClick(info.name)}
|
||||
disabled={joplinCloudLoginInProgress}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renderSyncTarget(info: SyncTargetInfo) {
|
||||
const key = `syncTarget_${info.name}`;
|
||||
const height = info.name !== 'joplinCloud' ? descriptionHeight : null;
|
||||
|
||||
const logoImageName = logosImageNames[info.name];
|
||||
const logoImageSrc = logoImageName ? `${bridge().buildDir()}/images/syncTargetLogos/${logoImageName}` : '';
|
||||
const logo = logoImageSrc ? <SyncTargetLogo src={logoImageSrc}/> : null;
|
||||
const descriptionComp = <SyncTargetDescription height={height} ref={info.name === 'joplinCloud' ? joplinCloudDescriptionRef : null}>{info.description}</SyncTargetDescription>;
|
||||
const featuresComp = showJoplinCloudForm && info.name === 'joplinCloud' ? null : renderFeatures(info.name);
|
||||
|
||||
return (
|
||||
<SyncTargetBox id={key} key={key} faded={showJoplinCloudForm && info.name !== 'joplinCloud'}>
|
||||
<SyncTargetTitle>{logo}{info.label}</SyncTargetTitle>
|
||||
{descriptionComp}
|
||||
{featuresComp}
|
||||
{renderSelectArea(info)}
|
||||
</SyncTargetBox>
|
||||
);
|
||||
}
|
||||
|
||||
const onSelfHostingClick = useCallback(() => {
|
||||
closeDialog(props.dispatch);
|
||||
|
||||
props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Config',
|
||||
props: {
|
||||
defaultSection: 'sync',
|
||||
},
|
||||
});
|
||||
}, [props.dispatch]);
|
||||
|
||||
function renderContent() {
|
||||
const boxes: any[] = [];
|
||||
|
||||
for (const name of syncTargetNames) {
|
||||
const info = SyncTargetRegistry.infoByName(name);
|
||||
if (info.supportsSelfHosted) continue;
|
||||
boxes.push(renderSyncTarget(info));
|
||||
}
|
||||
|
||||
const selfHostingMessage = showJoplinCloudForm ? null : <SelfHostingMessage>Self-hosting? Joplin also supports various self-hosting options such as Nextcloud, WebDAV, AWS S3 and Joplin Server. <a href="#" onClick={onSelfHostingClick}>Click here to select one</a>.</SelfHostingMessage>;
|
||||
|
||||
return (
|
||||
<ContentRoot>
|
||||
<SyncTargetBoxes>
|
||||
{boxes}
|
||||
</SyncTargetBoxes>
|
||||
{selfHostingMessage}
|
||||
</ContentRoot>
|
||||
);
|
||||
}
|
||||
|
||||
function renderDialogWrapper() {
|
||||
return (
|
||||
<StyledRoot>
|
||||
<DialogTitle title={_('Joplin can synchronise your notes using various providers. Select one from the list below.')} justifyContent="center"/>
|
||||
{renderContent()}
|
||||
<DialogButtonRow
|
||||
themeId={props.themeId}
|
||||
onClick={onButtonRowClick}
|
||||
okButtonShow={false}
|
||||
cancelButtonLabel={_('Close')}
|
||||
/>
|
||||
</StyledRoot>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog renderContent={renderDialogWrapper}/>
|
||||
);
|
||||
}
|
||||
726
packages/app-desktop/package-lock.json
generated
726
packages/app-desktop/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/app-desktop",
|
||||
"version": "2.3.3",
|
||||
"version": "2.3.0",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"private": true,
|
||||
|
||||
@@ -17,7 +17,7 @@ USER_NUM=$1
|
||||
COMMANDS=($(echo $2 | tr "," "\n"))
|
||||
PROFILE_DIR=~/.config/joplindev-desktop-$USER_NUM
|
||||
|
||||
CMD_FILE="$SCRIPT_DIR/runForTestingCommands-$USER_NUM.txt"
|
||||
CMD_FILE="$SCRIPT_DIR/runForSharingCommands-$USER_NUM.txt"
|
||||
rm -f "$CMD_FILE"
|
||||
touch "$CMD_FILE"
|
||||
|
||||
@@ -40,17 +40,17 @@ do
|
||||
USER_EMAIL="user$USER_NUM@example.com"
|
||||
rm -rf "$PROFILE_DIR"
|
||||
|
||||
# rm -rf "$HOME/Temp/SyncTestE2EE copy"
|
||||
# rsync -a "$HOME/Temp/SyncTestE2EE/" "$HOME/Temp/SyncTestE2EE copy/"
|
||||
rm -rf "$HOME/Temp/SyncTestE2EE copy"
|
||||
rsync -a "$HOME/Temp/SyncTestE2EE/" "$HOME/Temp/SyncTestE2EE copy/"
|
||||
|
||||
# echo "config sync.target 2" >> "$CMD_FILE"
|
||||
# echo "config sync.2.path \"$HOME/Temp/SyncTestE2EE copy/\"" >> "$CMD_FILE"
|
||||
echo "config sync.target 2" >> "$CMD_FILE"
|
||||
echo "config sync.2.path \"$HOME/Temp/SyncTestE2EE copy/\"" >> "$CMD_FILE"
|
||||
|
||||
echo "config keychain.supported 0" >> "$CMD_FILE"
|
||||
echo "config sync.target 10" >> "$CMD_FILE"
|
||||
# echo "config sync.10.path http://api.joplincloud.local:22300" >> "$CMD_FILE"
|
||||
echo "config sync.10.username $USER_EMAIL" >> "$CMD_FILE"
|
||||
echo "config sync.10.password hunter1hunter2hunter3" >> "$CMD_FILE"
|
||||
# echo "config keychain.supported 0" >> "$CMD_FILE"
|
||||
# echo "config sync.target 10" >> "$CMD_FILE"
|
||||
# # echo "config sync.10.path http://api.joplincloud.local:22300" >> "$CMD_FILE"
|
||||
# echo "config sync.10.username $USER_EMAIL" >> "$CMD_FILE"
|
||||
# echo "config sync.10.password hunter1hunter2hunter3" >> "$CMD_FILE"
|
||||
|
||||
elif [[ $CMD == "e2ee" ]]; then
|
||||
|
||||
|
||||
@@ -141,10 +141,10 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097648
|
||||
versionName "2.3.4"
|
||||
versionCode 2097644
|
||||
versionName "2.3.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
|
||||
// https://github.com/react-native-community/react-native-camera/issues/2138
|
||||
@@ -158,7 +158,7 @@ android {
|
||||
reset()
|
||||
enable enableSeparateBuildPerCPUArchitecture
|
||||
universalApk false // If true, also generate a universal APK
|
||||
include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||
include "armeabi-v7a", "x86"
|
||||
}
|
||||
}
|
||||
signingConfigs {
|
||||
|
||||
@@ -19,7 +19,7 @@ const { BaseScreenComponent } = require('../base-screen.js');
|
||||
const { Dropdown } = require('../Dropdown.js');
|
||||
const { themeStyle } = require('../global-style.js');
|
||||
const shared = require('@joplin/lib/components/shared/config-shared.js');
|
||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry');
|
||||
const RNFS = require('react-native-fs');
|
||||
|
||||
class ConfigScreenComponent extends BaseScreenComponent {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { MasterKeyEntity } from '@joplin/lib/services/database/types';
|
||||
import { State } from '@joplin/lib/reducer';
|
||||
import { SyncInfo } from '@joplin/lib/services/synchronizer/syncInfoUtils';
|
||||
import { setupAndDisableEncryption, toggleAndSetupEncryption } from '@joplin/lib/services/e2ee/utils';
|
||||
import MasterKey from '@joplin/lib/models/MasterKey';
|
||||
import MasterKey from '../../../lib/models/MasterKey';
|
||||
|
||||
interface Props {}
|
||||
|
||||
|
||||
@@ -486,13 +486,13 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 72;
|
||||
CURRENT_PROJECT_VERSION = 71;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.3.1;
|
||||
MARKETING_VERSION = 12.3.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -514,12 +514,12 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 72;
|
||||
CURRENT_PROJECT_VERSION = 71;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 12.3.1;
|
||||
MARKETING_VERSION = 12.3.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
@@ -659,14 +659,14 @@
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 72;
|
||||
CURRENT_PROJECT_VERSION = 71;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.3.1;
|
||||
MARKETING_VERSION = 12.3.0;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
@@ -690,14 +690,14 @@
|
||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 72;
|
||||
CURRENT_PROJECT_VERSION = 71;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||
MARKETING_VERSION = 12.3.1;
|
||||
MARKETING_VERSION = 12.3.0;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
30897
packages/app-mobile/package-lock.json
generated
30897
packages/app-mobile/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -78,7 +78,7 @@ import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
|
||||
const WelcomeUtils = require('@joplin/lib/WelcomeUtils');
|
||||
const { themeStyle } = require('./components/global-style.js');
|
||||
|
||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('@joplin/lib/SyncTargetRegistry.js');
|
||||
const SyncTargetFilesystem = require('@joplin/lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetNextcloud = require('@joplin/lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('@joplin/lib/SyncTargetWebDAV.js');
|
||||
|
||||
22719
packages/fork-htmlparser2/package-lock.json
generated
22719
packages/fork-htmlparser2/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@joplin/fork-htmlparser2",
|
||||
"description": "Fast & forgiving HTML/XML/RSS parser",
|
||||
"version": "4.1.33",
|
||||
"version": "4.1.32",
|
||||
"author": "Felix Boehm <me@feedic.com>",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
16765
packages/fork-sax/package-lock.json
generated
16765
packages/fork-sax/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
||||
"name": "@joplin/fork-sax",
|
||||
"description": "An evented streaming XML parser in JavaScript",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||
"version": "1.2.37",
|
||||
"version": "1.2.36",
|
||||
"main": "lib/sax.js",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
26377
packages/generator-joplin/package-lock.json
generated
26377
packages/generator-joplin/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -30,7 +30,7 @@ const fs = require('fs-extra');
|
||||
import JoplinError from './JoplinError';
|
||||
const EventEmitter = require('events');
|
||||
const syswidecas = require('./vendor/syswide-cas');
|
||||
import SyncTargetRegistry from './SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('./SyncTargetRegistry.js');
|
||||
const SyncTargetFilesystem = require('./SyncTargetFilesystem.js');
|
||||
const SyncTargetNextcloud = require('./SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('./SyncTargetWebDAV.js');
|
||||
|
||||
@@ -25,14 +25,6 @@ export default class BaseSyncTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static description(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public static supportsSelfHosted(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public option(name: string, defaultValue: any = null) {
|
||||
return this.options_ && name in this.options_ ? this.options_[name] : defaultValue;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,6 @@ class SyncTargetAmazonS3 extends BaseSyncTarget {
|
||||
return `${_('AWS S3')} (Beta)`;
|
||||
}
|
||||
|
||||
static description() {
|
||||
return 'A service offered by Amazon Web Services (AWS) that provides object storage through a web service interface.';
|
||||
}
|
||||
|
||||
async isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -25,14 +25,6 @@ class SyncTargetDropbox extends BaseSyncTarget {
|
||||
return _('Dropbox');
|
||||
}
|
||||
|
||||
static description() {
|
||||
return 'A file hosting service that offers cloud storage and file synchronization';
|
||||
}
|
||||
|
||||
static supportsSelfHosted() {
|
||||
return false;
|
||||
}
|
||||
|
||||
authRouteName() {
|
||||
return 'DropboxLogin';
|
||||
}
|
||||
|
||||
@@ -30,14 +30,6 @@ export default class SyncTargetJoplinCloud extends BaseSyncTarget {
|
||||
return _('Joplin Cloud');
|
||||
}
|
||||
|
||||
public static description() {
|
||||
return _('Joplin\'s own sync service. Also gives access to Joplin-specific features such as publishing notes or collaborating on notebooks with others.');
|
||||
}
|
||||
|
||||
public static supportsSelfHosted(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public async isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,10 +51,6 @@ export default class SyncTargetJoplinServer extends BaseSyncTarget {
|
||||
return 'joplinServer';
|
||||
}
|
||||
|
||||
public static description() {
|
||||
return 'Besides synchronisation and improved performances, Joplin Server also gives access to Joplin-specific sharing features.';
|
||||
}
|
||||
|
||||
public static label() {
|
||||
return `${_('Joplin Server')} (Beta)`;
|
||||
}
|
||||
|
||||
@@ -25,10 +25,6 @@ class SyncTargetNextcloud extends BaseSyncTarget {
|
||||
return _('Nextcloud');
|
||||
}
|
||||
|
||||
static description() {
|
||||
return 'A suite of client-server software for creating and using file hosting services.';
|
||||
}
|
||||
|
||||
async isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -29,14 +29,6 @@ export default class SyncTargetOneDrive extends BaseSyncTarget {
|
||||
return _('OneDrive');
|
||||
}
|
||||
|
||||
public static description() {
|
||||
return 'A file hosting service operated by Microsoft as part of its web version of Office.';
|
||||
}
|
||||
|
||||
public static supportsSelfHosted(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
async isAuthenticated() {
|
||||
return !!this.api().auth();
|
||||
}
|
||||
|
||||
@@ -1,47 +1,25 @@
|
||||
export interface SyncTargetInfo {
|
||||
id: number;
|
||||
name: string;
|
||||
label: string;
|
||||
supportsSelfHosted: boolean;
|
||||
supportsConfigCheck: boolean;
|
||||
description: string;
|
||||
classRef: any;
|
||||
}
|
||||
|
||||
export default class SyncTargetRegistry {
|
||||
|
||||
private static reg_: Record<number, SyncTargetInfo> = {};
|
||||
|
||||
public static classById(syncTargetId: number) {
|
||||
class SyncTargetRegistry {
|
||||
static classById(syncTargetId) {
|
||||
const info = SyncTargetRegistry.reg_[syncTargetId];
|
||||
if (!info) throw new Error(`Invalid id: ${syncTargetId}`);
|
||||
return info.classRef;
|
||||
}
|
||||
|
||||
public static infoByName(name: string): SyncTargetInfo {
|
||||
for (const [, info] of Object.entries(this.reg_)) {
|
||||
if (info.name === name) return info;
|
||||
}
|
||||
throw new Error(`Unknown name: ${name}`);
|
||||
}
|
||||
|
||||
public static addClass(SyncTargetClass: any) {
|
||||
static addClass(SyncTargetClass) {
|
||||
this.reg_[SyncTargetClass.id()] = {
|
||||
id: SyncTargetClass.id(),
|
||||
name: SyncTargetClass.targetName(),
|
||||
label: SyncTargetClass.label(),
|
||||
classRef: SyncTargetClass,
|
||||
description: SyncTargetClass.description(),
|
||||
supportsSelfHosted: SyncTargetClass.supportsSelfHosted(),
|
||||
supportsConfigCheck: SyncTargetClass.supportsConfigCheck(),
|
||||
};
|
||||
}
|
||||
|
||||
public static allIds() {
|
||||
static allIds() {
|
||||
return Object.keys(this.reg_);
|
||||
}
|
||||
|
||||
public static nameToId(name: string) {
|
||||
static nameToId(name) {
|
||||
for (const n in this.reg_) {
|
||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
||||
if (this.reg_[n].name === name) return this.reg_[n].id;
|
||||
@@ -49,7 +27,7 @@ export default class SyncTargetRegistry {
|
||||
throw new Error(`Name not found: ${name}. Was the sync target registered?`);
|
||||
}
|
||||
|
||||
public static idToMetadata(id: number) {
|
||||
static idToMetadata(id) {
|
||||
for (const n in this.reg_) {
|
||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
||||
if (this.reg_[n].id === id) return this.reg_[n];
|
||||
@@ -57,12 +35,12 @@ export default class SyncTargetRegistry {
|
||||
throw new Error(`ID not found: ${id}`);
|
||||
}
|
||||
|
||||
public static idToName(id: number) {
|
||||
static idToName(id) {
|
||||
return this.idToMetadata(id).name;
|
||||
}
|
||||
|
||||
public static idAndLabelPlainObject(os: string) {
|
||||
const output: Record<string, string> = {};
|
||||
static idAndLabelPlainObject(os) {
|
||||
const output = {};
|
||||
for (const n in this.reg_) {
|
||||
if (!this.reg_.hasOwnProperty(n)) continue;
|
||||
const info = this.reg_[n];
|
||||
@@ -74,3 +52,7 @@ export default class SyncTargetRegistry {
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
SyncTargetRegistry.reg_ = {};
|
||||
|
||||
module.exports = SyncTargetRegistry;
|
||||
@@ -23,10 +23,6 @@ class SyncTargetWebDAV extends BaseSyncTarget {
|
||||
return _('WebDAV');
|
||||
}
|
||||
|
||||
static description() {
|
||||
return 'The WebDAV protocol allows users to create, change and move documents on a server. There are many WebDAV compatible servers, including SeaFile, Nginx or Apache.';
|
||||
}
|
||||
|
||||
async isAuthenticated() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Setting = require('../../models/Setting').default;
|
||||
const SyncTargetRegistry = require('../../SyncTargetRegistry').default;
|
||||
const SyncTargetRegistry = require('../../SyncTargetRegistry');
|
||||
const ObjectUtils = require('../../ObjectUtils');
|
||||
const { _ } = require('../../locale');
|
||||
const { createSelector } = require('reselect');
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const shim = require('../../shim').default;
|
||||
const SyncTargetRegistry = require('../../SyncTargetRegistry').default;
|
||||
const SyncTargetRegistry = require('../../SyncTargetRegistry');
|
||||
const { reg } = require('../../registry.js');
|
||||
const { _ } = require('../../locale');
|
||||
const Setting = require('../../models/Setting').default;
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
import shim from '../shim';
|
||||
const { useCallback, useEffect, useState } = shim.react();
|
||||
import useEventListener from './useEventListener';
|
||||
|
||||
interface Size {
|
||||
width: number;
|
||||
height: number;
|
||||
}
|
||||
|
||||
function useElementSize(elementRef: any): Size {
|
||||
const [size, setSize] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
// Prevent too many rendering using useCallback
|
||||
const updateSize = useCallback(() => {
|
||||
const node = elementRef?.current;
|
||||
if (node) {
|
||||
setSize({
|
||||
width: node.offsetWidth || 0,
|
||||
height: node.offsetHeight || 0,
|
||||
});
|
||||
}
|
||||
}, [elementRef]);
|
||||
|
||||
// Initial size on mount
|
||||
useEffect(() => {
|
||||
updateSize();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEventListener('resize', updateSize);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
export default useElementSize;
|
||||
@@ -1,41 +0,0 @@
|
||||
import shim from '../shim';
|
||||
const { useEffect, useRef } = shim.react();
|
||||
|
||||
function useEventListener(
|
||||
eventName: any,
|
||||
handler: any,
|
||||
element?: any
|
||||
) {
|
||||
// Create a ref that stores handler
|
||||
const savedHandler = useRef();
|
||||
|
||||
useEffect(() => {
|
||||
// Define the listening target
|
||||
const targetElement = element?.current || window;
|
||||
if (!(targetElement && targetElement.addEventListener)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Update saved handler if necessary
|
||||
if (savedHandler.current !== handler) {
|
||||
savedHandler.current = handler;
|
||||
}
|
||||
|
||||
// Create event listener that calls handler function stored in ref
|
||||
const eventListener = (event: Event) => {
|
||||
// eslint-disable-next-line no-extra-boolean-cast
|
||||
if (!!savedHandler?.current) {
|
||||
savedHandler.current(event);
|
||||
}
|
||||
};
|
||||
|
||||
targetElement.addEventListener(eventName, eventListener);
|
||||
|
||||
// Remove event listener on cleanup
|
||||
return () => {
|
||||
targetElement.removeEventListener(eventName, eventListener);
|
||||
};
|
||||
}, [eventName, element, handler]);
|
||||
}
|
||||
|
||||
export default useEventListener;
|
||||
@@ -3,7 +3,7 @@ import { _, supportedLocalesToLanguages, defaultLocale } from '../locale';
|
||||
import eventManager from '../eventManager';
|
||||
import BaseModel from '../BaseModel';
|
||||
import Database from '../database';
|
||||
import SyncTargetRegistry from '../SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('../SyncTargetRegistry.js');
|
||||
import time from '../time';
|
||||
import FileHandler, { SettingValues } from './settings/FileHandler';
|
||||
const { sprintf } = require('sprintf-js');
|
||||
@@ -55,7 +55,6 @@ export interface SettingItem {
|
||||
needRestart?: boolean;
|
||||
autoSave?: boolean;
|
||||
storage?: SettingStorage;
|
||||
hideLabel?: boolean;
|
||||
}
|
||||
|
||||
interface SettingItems {
|
||||
@@ -307,19 +306,6 @@ class Setting extends BaseModel {
|
||||
appTypes: [AppType.Desktop],
|
||||
storage: SettingStorage.File,
|
||||
},
|
||||
|
||||
'sync.openSyncWizard': {
|
||||
value: null,
|
||||
type: SettingItemType.Button,
|
||||
public: true,
|
||||
appTypes: [AppType.Desktop],
|
||||
label: () => _('Open Sync Wizard...'),
|
||||
hideLabel: true,
|
||||
section: 'sync',
|
||||
// advanced: true,
|
||||
// description: () => 'If the data on the sync target is incorrect or empty, you can use this button to force a re-upload of your data to the sync target. Application will have to be restarted',
|
||||
},
|
||||
|
||||
'sync.target': {
|
||||
value: SyncTargetRegistry.nameToId('dropbox'),
|
||||
type: SettingItemType.Int,
|
||||
@@ -986,8 +972,6 @@ class Setting extends BaseModel {
|
||||
storage: SettingStorage.File,
|
||||
},
|
||||
|
||||
'style.editor.contentMaxWidth': { value: 600, type: SettingItemType.Int, public: true, storage: SettingStorage.File, appTypes: [AppType.Desktop], section: 'appearance', label: () => _('Editor maximum width'), description: () => _('Set it to 0 to make it take the complete available space.') },
|
||||
|
||||
'ui.layout': { value: {}, type: SettingItemType.Object, storage: SettingStorage.File, public: false, appTypes: [AppType.Desktop] },
|
||||
|
||||
// TODO: Is there a better way to do this? The goal here is to simply have
|
||||
|
||||
2
packages/lib/package-lock.json
generated
2
packages/lib/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/lib",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/lib",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.0",
|
||||
"description": "Joplin Core library",
|
||||
"author": "Laurent Cozic",
|
||||
"homepage": "",
|
||||
@@ -25,11 +25,11 @@
|
||||
"typescript": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.33",
|
||||
"@joplin/fork-sax": "^1.2.37",
|
||||
"@joplin/renderer": "^2.3.1",
|
||||
"@joplin/turndown": "^4.0.55",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.37",
|
||||
"@joplin/fork-htmlparser2": "^4.1.32",
|
||||
"@joplin/fork-sax": "^1.2.36",
|
||||
"@joplin/renderer": "~2.3",
|
||||
"@joplin/turndown": "^4.0.54",
|
||||
"@joplin/turndown-plugin-gfm": "^1.0.36",
|
||||
"async-mutex": "^0.1.3",
|
||||
"aws-sdk": "^2.588.0",
|
||||
"base-64": "^0.1.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Logger from './Logger';
|
||||
import Setting from './models/Setting';
|
||||
import shim from './shim';
|
||||
import SyncTargetRegistry from './SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('./SyncTargetRegistry.js');
|
||||
|
||||
class Registry {
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { SqlQuery } from '../../database';
|
||||
import JoplinDatabase from '../../JoplinDatabase';
|
||||
import BaseItem from '../../models/BaseItem';
|
||||
import Setting from '../../models/Setting';
|
||||
import SyncTargetRegistry from '../../SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('../../SyncTargetRegistry');
|
||||
|
||||
async function clearSyncContext() {
|
||||
const syncTargetIds = SyncTargetRegistry.allIds();
|
||||
|
||||
@@ -35,7 +35,7 @@ const { FileApiDriverWebDav } = require('../file-api-driver-webdav.js');
|
||||
const { FileApiDriverDropbox } = require('../file-api-driver-dropbox.js');
|
||||
const { FileApiDriverOneDrive } = require('../file-api-driver-onedrive.js');
|
||||
const { FileApiDriverAmazonS3 } = require('../file-api-driver-amazon-s3.js');
|
||||
import SyncTargetRegistry from '../SyncTargetRegistry';
|
||||
const SyncTargetRegistry = require('../SyncTargetRegistry.js');
|
||||
const SyncTargetMemory = require('../SyncTargetMemory.js');
|
||||
const SyncTargetFilesystem = require('../SyncTargetFilesystem.js');
|
||||
const SyncTargetNextcloud = require('../SyncTargetNextcloud.js');
|
||||
|
||||
6355
packages/plugin-repo-cli/package-lock.json
generated
6355
packages/plugin-repo-cli/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/plugin-repo-cli",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"bin": {
|
||||
@@ -18,8 +18,8 @@
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@joplin/lib": "^2.3.1",
|
||||
"@joplin/tools": "^2.3.1",
|
||||
"@joplin/lib": "~2.3",
|
||||
"@joplin/tools": "~2.3",
|
||||
"fs-extra": "^9.0.1",
|
||||
"gh-release-assets": "^2.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
|
||||
@@ -154,7 +154,6 @@ export default class HtmlToHtml {
|
||||
return {
|
||||
html: html,
|
||||
pluginAssets: [],
|
||||
cssStrings: [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -169,7 +168,6 @@ export default class HtmlToHtml {
|
||||
const output: RenderResult = {
|
||||
html: splitted.html,
|
||||
pluginAssets: [],
|
||||
cssStrings: [],
|
||||
};
|
||||
|
||||
if (options.externalAssetsOnly) {
|
||||
@@ -184,7 +182,6 @@ export default class HtmlToHtml {
|
||||
return {
|
||||
html: styleHtml + html,
|
||||
pluginAssets: [],
|
||||
cssStrings: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import MdToHtml from './MdToHtml';
|
||||
import HtmlToHtml from './HtmlToHtml';
|
||||
import htmlUtils from './htmlUtils';
|
||||
import { Options as NoteStyleOptions } from './noteStyle';
|
||||
const MarkdownIt = require('markdown-it');
|
||||
|
||||
export enum MarkupLanguage {
|
||||
@@ -25,7 +24,7 @@ export interface RenderResultPluginAsset {
|
||||
export interface RenderResult {
|
||||
html: string;
|
||||
pluginAssets: RenderResultPluginAsset[];
|
||||
cssStrings: string[];
|
||||
cssStrings?: string[];
|
||||
}
|
||||
|
||||
export interface OptionsResourceModel {
|
||||
@@ -49,7 +48,7 @@ export default class MarkupToHtml {
|
||||
private options_: Options;
|
||||
private rawMarkdownIt_: any;
|
||||
|
||||
public constructor(options: Options = null) {
|
||||
public constructor(options: Options) {
|
||||
this.options_ = {
|
||||
ResourceModel: {
|
||||
isResourceUrl: () => false,
|
||||
@@ -120,7 +119,7 @@ export default class MarkupToHtml {
|
||||
return this.renderer(markupLanguage).render(markup, theme, options);
|
||||
}
|
||||
|
||||
public async allAssets(markupLanguage: MarkupLanguage, theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||
return this.renderer(markupLanguage).allAssets(theme, noteStyleOptions);
|
||||
public async allAssets(markupLanguage: MarkupLanguage, theme: any) {
|
||||
return this.renderer(markupLanguage).allAssets(theme);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,28 +5,10 @@ import setupLinkify from './MdToHtml/setupLinkify';
|
||||
import validateLinks from './MdToHtml/validateLinks';
|
||||
import { ItemIdToUrlHandler } from './utils';
|
||||
import { RenderResult, RenderResultPluginAsset } from './MarkupToHtml';
|
||||
import { Options as NoteStyleOptions } from './noteStyle';
|
||||
|
||||
const MarkdownIt = require('markdown-it');
|
||||
const md5 = require('md5');
|
||||
|
||||
export interface RenderOptions {
|
||||
contentMaxWidth?: number;
|
||||
bodyOnly?: boolean;
|
||||
splitted?: boolean;
|
||||
externalAssetsOnly?: boolean;
|
||||
postMessageSyntax?: string;
|
||||
highlightedKeywords?: string[];
|
||||
codeTheme?: string;
|
||||
theme?: any;
|
||||
plugins?: Record<string, any>;
|
||||
audioPlayerEnabled?: boolean;
|
||||
videoPlayerEnabled?: boolean;
|
||||
pdfViewerEnabled?: boolean;
|
||||
codeHighlightCacheKey?: string;
|
||||
plainResourceRendering?: boolean;
|
||||
}
|
||||
|
||||
interface RendererRule {
|
||||
install(context: any, ruleOptions: any): any;
|
||||
assets?(theme: any): any;
|
||||
@@ -349,7 +331,7 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
// This is similar to allProcessedAssets() but used only by the Rich Text editor
|
||||
public async allAssets(theme: any, noteStyleOptions: NoteStyleOptions = null) {
|
||||
public async allAssets(theme: any) {
|
||||
const assets: any = {};
|
||||
for (const key in rules) {
|
||||
if (!this.pluginEnabled(key)) continue;
|
||||
@@ -361,7 +343,7 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
const processedAssets = this.processPluginAssets(assets);
|
||||
processedAssets.cssStrings.splice(0, 0, noteStyle(theme, noteStyleOptions).join('\n'));
|
||||
processedAssets.cssStrings.splice(0, 0, noteStyle(theme).join('\n'));
|
||||
if (this.customCss_) processedAssets.cssStrings.push(this.customCss_);
|
||||
const output = await this.outputAssetsToExternalAssets_(processedAssets);
|
||||
return output.pluginAssets;
|
||||
@@ -394,9 +376,8 @@ export default class MdToHtml {
|
||||
}
|
||||
|
||||
// "theme" is the theme as returned by themeStyle()
|
||||
public async render(body: string, theme: any = null, options: RenderOptions = null): Promise<RenderResult> {
|
||||
|
||||
options = {
|
||||
public async render(body: string, theme: any = null, options: any = null): Promise<RenderResult> {
|
||||
options = Object.assign({}, {
|
||||
// In bodyOnly mode, the rendered Markdown is returned without the wrapper DIV
|
||||
bodyOnly: false,
|
||||
// In splitted mode, the CSS and HTML will be returned in separate properties.
|
||||
@@ -414,10 +395,7 @@ export default class MdToHtml {
|
||||
audioPlayerEnabled: this.pluginEnabled('audioPlayer'),
|
||||
videoPlayerEnabled: this.pluginEnabled('videoPlayer'),
|
||||
pdfViewerEnabled: this.pluginEnabled('pdfViewer'),
|
||||
|
||||
contentMaxWidth: 0,
|
||||
...options,
|
||||
};
|
||||
}, options);
|
||||
|
||||
// The "codeHighlightCacheKey" option indicates what set of cached object should be
|
||||
// associated with this particular Markdown body. It is only used to allow us to
|
||||
@@ -547,9 +525,7 @@ export default class MdToHtml {
|
||||
|
||||
const renderedBody = markdownIt.render(body, context);
|
||||
|
||||
let cssStrings = noteStyle(options.theme, {
|
||||
contentMaxWidth: options.contentMaxWidth,
|
||||
});
|
||||
let cssStrings = noteStyle(options.theme);
|
||||
|
||||
let output = { ...this.allProcessedAssets(allRules, options.theme, options.codeTheme) };
|
||||
cssStrings = cssStrings.concat(output.cssStrings);
|
||||
|
||||
@@ -7,28 +7,11 @@ function formatCssSize(v: any): string {
|
||||
return `${v}px`;
|
||||
}
|
||||
|
||||
export interface Options {
|
||||
contentMaxWidth?: number;
|
||||
}
|
||||
|
||||
export default function(theme: any, options: Options = null) {
|
||||
options = {
|
||||
contentMaxWidth: 0,
|
||||
...options,
|
||||
};
|
||||
|
||||
export default function(theme: any) {
|
||||
theme = theme ? theme : {};
|
||||
|
||||
const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
|
||||
|
||||
const maxWidthCss = options.contentMaxWidth ? `
|
||||
#rendered-md {
|
||||
max-width: ${options.contentMaxWidth}px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
` : '';
|
||||
|
||||
const css =
|
||||
`
|
||||
/* https://necolas.github.io/normalize.css/ */
|
||||
@@ -78,8 +61,6 @@ export default function(theme: any, options: Options = null) {
|
||||
background: rgba(100, 100, 100, 0.7);
|
||||
}
|
||||
|
||||
${maxWidthCss}
|
||||
|
||||
/* Remove top padding and margin from first child so that top of rendered text is aligned to top of text editor text */
|
||||
|
||||
#rendered-md > h1:first-child,
|
||||
|
||||
19
packages/renderer/package-lock.json
generated
19
packages/renderer/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/renderer",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"highlight.js": "^11.2.0",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"katex": "^0.13.3",
|
||||
@@ -3551,11 +3551,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/highlight.js": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.2.0.tgz",
|
||||
"integrity": "sha512-JOySjtOEcyG8s4MLR2MNbLUyaXqUunmSnL2kdV/KuGJOmHZuAR5xC54Ko7goAXBWNhf09Vy3B+U7vR62UZ/0iw==",
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g==",
|
||||
"deprecated": "Potential vulnerability. Please upgrade to @latest",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/hosted-git-info": {
|
||||
@@ -11518,9 +11519,9 @@
|
||||
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.2.0.tgz",
|
||||
"integrity": "sha512-JOySjtOEcyG8s4MLR2MNbLUyaXqUunmSnL2kdV/KuGJOmHZuAR5xC54Ko7goAXBWNhf09Vy3B+U7vR62UZ/0iw=="
|
||||
"version": "10.2.1",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.2.1.tgz",
|
||||
"integrity": "sha512-A+sckVPIb9zQTUydC9lpRX1qRFO/N0OKEh0NwIr65ckvWA/oMY8v9P3+kGRK3w2ULSh9E8v5MszXafodQ6039g=="
|
||||
},
|
||||
"hosted-git-info": {
|
||||
"version": "2.8.9",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@joplin/renderer",
|
||||
"version": "2.3.1",
|
||||
"version": "2.3.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",
|
||||
@@ -24,10 +24,10 @@
|
||||
"typescript": "^4.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/fork-htmlparser2": "^4.1.33",
|
||||
"@joplin/fork-htmlparser2": "^4.1.32",
|
||||
"font-awesome-filetypes": "^2.1.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"highlight.js": "^11.2.0",
|
||||
"highlight.js": "^10.2.1",
|
||||
"html-entities": "^1.2.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"katex": "^0.13.3",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// We don't want the tests to fail due to timeout, especially on CI, and certain
|
||||
// tests can take more time since we do integration testing too.
|
||||
jest.setTimeout(30 * 1000);
|
||||
|
||||
process.env.JOPLIN_IS_TESTING = '1';
|
||||
|
||||
27966
packages/server/package-lock.json
generated
27966
packages/server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,14 @@
|
||||
{
|
||||
"name": "@joplin/server",
|
||||
"version": "2.3.7",
|
||||
"version": "2.3.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start-dev": "nodemon --config nodemon.json --ext ts,js,mustache,css,tsx dist/app.js --env dev",
|
||||
"start-dev-no-watch": "node dist/app.js --env dev",
|
||||
"devCreateDb": "node dist/app.js --env dev --create-db",
|
||||
"devDropTables": "node dist/app.js --env dev --drop-tables",
|
||||
"devDropDb": "node dist/app.js --env dev --drop-db",
|
||||
"start": "node dist/app.js",
|
||||
"generateTypes": "rm -f db-buildTypes.sqlite && npm run start -- --migrate-latest --env buildTypes && node dist/tools/generateTypes.js && mv db-buildTypes.sqlite schema.sqlite",
|
||||
"generateTypes": "rm -f db-buildTypes.sqlite && npm run start -- --migrate-db --env buildTypes && node dist/tools/generateTypes.js && mv db-buildTypes.sqlite schema.sqlite",
|
||||
"tsc": "tsc --project tsconfig.json",
|
||||
"test": "jest --verbose=false",
|
||||
"test-ci": "npm run test",
|
||||
@@ -43,7 +42,6 @@
|
||||
"pg": "^8.5.1",
|
||||
"pretty-bytes": "^5.6.0",
|
||||
"query-string": "^6.8.3",
|
||||
"rate-limiter-flexible": "^2.2.4",
|
||||
"raw-body": "^2.4.1",
|
||||
"sqlite3": "^4.1.0",
|
||||
"stripe": "^8.150.0",
|
||||
|
||||
@@ -57,10 +57,6 @@ ul li {
|
||||
list-style-type: disc;
|
||||
}
|
||||
|
||||
ul.pagination-list li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.readable-block {
|
||||
max-width: 740px;
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import { argv } from 'yargs';
|
||||
import Logger, { LoggerWrapper, TargetType } from '@joplin/lib/Logger';
|
||||
import config, { initConfig, runningInDocker, EnvVariables } from './config';
|
||||
import { createDb, dropDb } from './tools/dbTools';
|
||||
import { dropTables, connectDb, disconnectDb, migrateLatest, waitForConnection, sqliteDefaultDir, migrateList, migrateUp, migrateDown } from './db';
|
||||
import { dropTables, connectDb, disconnectDb, migrateDb, waitForConnection, sqliteDefaultDir } from './db';
|
||||
import { AppContext, Env, KoaNext } from './utils/types';
|
||||
import FsDriverNode from '@joplin/lib/fs-driver-node';
|
||||
import routeHandler from './middleware/routeHandler';
|
||||
@@ -205,23 +205,10 @@ async function main() {
|
||||
fs.writeFileSync(pidFile, `${process.pid}`);
|
||||
}
|
||||
|
||||
let runCommandAndExitApp = true;
|
||||
|
||||
if (argv.migrateLatest) {
|
||||
if (argv.migrateDb) {
|
||||
const db = await connectDb(config().database);
|
||||
await migrateLatest(db);
|
||||
await migrateDb(db);
|
||||
await disconnectDb(db);
|
||||
} else if (argv.migrateUp) {
|
||||
const db = await connectDb(config().database);
|
||||
await migrateUp(db);
|
||||
await disconnectDb(db);
|
||||
} else if (argv.migrateDown) {
|
||||
const db = await connectDb(config().database);
|
||||
await migrateDown(db);
|
||||
await disconnectDb(db);
|
||||
} else if (argv.migrateList) {
|
||||
const db = await connectDb(config().database);
|
||||
console.info(await migrateList(db));
|
||||
} else if (argv.dropDb) {
|
||||
await dropDb(config().database, { ignoreIfNotExists: true });
|
||||
} else if (argv.dropTables) {
|
||||
@@ -231,8 +218,6 @@ async function main() {
|
||||
} else if (argv.createDb) {
|
||||
await createDb(config().database);
|
||||
} else {
|
||||
runCommandAndExitApp = false;
|
||||
|
||||
appLogger().info(`Starting server v${config().appVersion} (${env}) on port ${config().port} and PID ${process.pid}...`);
|
||||
appLogger().info('Running in Docker:', runningInDocker());
|
||||
appLogger().info('Public base URL:', config().baseUrl);
|
||||
@@ -254,7 +239,7 @@ async function main() {
|
||||
await initializeJoplinUtils(config(), ctx.joplinBase.models, ctx.joplinBase.services.mustache);
|
||||
|
||||
appLogger().info('Migrating database...');
|
||||
await migrateLatest(ctx.joplinBase.db);
|
||||
await migrateDb(ctx.joplinBase.db);
|
||||
|
||||
appLogger().info('Starting services...');
|
||||
await startServices(ctx.joplinBase.services);
|
||||
@@ -263,8 +248,6 @@ async function main() {
|
||||
|
||||
app.listen(config().port);
|
||||
}
|
||||
|
||||
if (runCommandAndExitApp) process.exit(0);
|
||||
}
|
||||
|
||||
main().catch((error: any) => {
|
||||
|
||||
@@ -46,8 +46,6 @@ export interface EnvVariables {
|
||||
SUPPORT_NAME?: string;
|
||||
|
||||
BUSINESS_EMAIL?: string;
|
||||
|
||||
COOKIES_SECURE?: string;
|
||||
}
|
||||
|
||||
let runningInDocker_: boolean = false;
|
||||
@@ -170,7 +168,6 @@ export async function initConfig(envType: Env, env: EnvVariables, overrides: any
|
||||
supportEmail,
|
||||
supportName: env.SUPPORT_NAME || appName,
|
||||
businessEmail: env.BUSINESS_EMAIL || supportEmail,
|
||||
cookieSecure: env.COOKIES_SECURE === '1',
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -121,85 +121,14 @@ export async function disconnectDb(db: DbConnection) {
|
||||
await db.destroy();
|
||||
}
|
||||
|
||||
export async function migrateLatest(db: DbConnection) {
|
||||
export async function migrateDb(db: DbConnection) {
|
||||
await db.migrate.latest({
|
||||
directory: migrationDir,
|
||||
// Disable transactions because the models might open one too
|
||||
disableTransactions: true,
|
||||
});
|
||||
}
|
||||
|
||||
export async function migrateUp(db: DbConnection) {
|
||||
await db.migrate.up({
|
||||
directory: migrationDir,
|
||||
});
|
||||
}
|
||||
|
||||
export async function migrateDown(db: DbConnection) {
|
||||
await db.migrate.down({
|
||||
directory: migrationDir,
|
||||
});
|
||||
}
|
||||
|
||||
export async function migrateList(db: DbConnection, asString: boolean = true) {
|
||||
const migrations: any = await db.migrate.list({
|
||||
directory: migrationDir,
|
||||
});
|
||||
|
||||
// The migration array has a rather inconsistent format:
|
||||
//
|
||||
// [
|
||||
// // Done migrations
|
||||
// [
|
||||
// '20210809222118_email_key_fix.js',
|
||||
// '20210814123815_testing.js',
|
||||
// '20210814123816_testing.js'
|
||||
// ],
|
||||
// // Not done migrations
|
||||
// [
|
||||
// {
|
||||
// file: '20210814123817_testing.js',
|
||||
// directory: '/path/to/packages/server/dist/migrations'
|
||||
// }
|
||||
// ]
|
||||
// ]
|
||||
|
||||
if (!asString) return migrations;
|
||||
|
||||
const formatName = (migrationInfo: any) => {
|
||||
const name = migrationInfo.file ? migrationInfo.file : migrationInfo;
|
||||
|
||||
const s = name.split('.');
|
||||
s.pop();
|
||||
return s.join('.');
|
||||
};
|
||||
|
||||
interface Line {
|
||||
text: string;
|
||||
done: boolean;
|
||||
}
|
||||
|
||||
const output: Line[] = [];
|
||||
|
||||
for (const s of migrations[0]) {
|
||||
output.push({
|
||||
text: formatName(s),
|
||||
done: true,
|
||||
});
|
||||
}
|
||||
|
||||
for (const s of migrations[1]) {
|
||||
output.push({
|
||||
text: formatName(s),
|
||||
done: false,
|
||||
});
|
||||
}
|
||||
|
||||
output.sort((a, b) => {
|
||||
return a.text < b.text ? -1 : +1;
|
||||
});
|
||||
|
||||
return output.map(l => `${l.done ? '✓' : '✗'} ${l.text}`).join('\n');
|
||||
}
|
||||
|
||||
function allTableNames(): string[] {
|
||||
const tableNames = Object.keys(databaseSchema);
|
||||
tableNames.push('knex_migrations');
|
||||
|
||||
@@ -38,8 +38,6 @@ export default async function(ctx: AppContext) {
|
||||
|
||||
const responseFormat = routeResponseFormat(ctx);
|
||||
|
||||
if (error.retryAfterMs) ctx.response.set('Retry-After', Math.ceil(error.retryAfterMs / 1000).toString());
|
||||
|
||||
if (error.code === 'invalidOrigin') {
|
||||
ctx.response.body = error.message;
|
||||
} else if (responseFormat === RouteResponseFormat.Html) {
|
||||
|
||||
@@ -2,17 +2,13 @@ import { Knex } from 'knex';
|
||||
import { DbConnection } from '../db';
|
||||
|
||||
export async function up(db: DbConnection): Promise<any> {
|
||||
try {
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.text('key', 'mediumtext').defaultTo('').notNullable();
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Could not add "emails.key" column', error);
|
||||
}
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.text('key', 'mediumtext').defaultTo('').notNullable();
|
||||
});
|
||||
|
||||
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
// table.unique(['recipient_email', 'key']);
|
||||
// });
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.unique(['recipient_email', 'key']);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(_db: DbConnection): Promise<any> {
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
// import { Knex } from 'knex';
|
||||
import { Knex } from 'knex';
|
||||
import { DbConnection } from '../db';
|
||||
|
||||
export async function up(_db: DbConnection): Promise<any> {
|
||||
// try {
|
||||
// await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
// table.dropUnique(['recipient_email', 'key']);
|
||||
// });
|
||||
// } catch (error) {
|
||||
// // console.warn('Could not drop unique constraint - this is not an error.', error);
|
||||
// }
|
||||
export async function up(db: DbConnection): Promise<any> {
|
||||
await db.schema.alterTable('emails', function(table: Knex.CreateTableBuilder) {
|
||||
table.dropUnique(['recipient_email', 'key']);
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(_db: DbConnection): Promise<any> {
|
||||
|
||||
@@ -5,15 +5,12 @@ import { ErrorForbidden } from '../../utils/errors';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import { bodyFields } from '../../utils/requestUtils';
|
||||
import { User } from '../../db';
|
||||
import limiterLoginBruteForce from '../../utils/request/limiterLoginBruteForce';
|
||||
|
||||
const router = new Router(RouteType.Api);
|
||||
|
||||
router.public = true;
|
||||
|
||||
router.post('api/sessions', async (_path: SubPath, ctx: AppContext) => {
|
||||
await limiterLoginBruteForce(ctx.ip);
|
||||
|
||||
const fields: User = await bodyFields(ctx.req);
|
||||
const user = await ctx.joplin.models.user().login(fields.email, fields.password);
|
||||
if (!user) throw new ErrorForbidden('Invalid username or password');
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Session } from '../../db';
|
||||
import routeHandler from '../../middleware/routeHandler';
|
||||
import { cookieGet } from '../../utils/cookies';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, models, parseHtml, createUser } from '../../utils/testing/testUtils';
|
||||
import { AppContext } from '../../utils/types';
|
||||
|
||||
@@ -53,7 +52,7 @@ describe('index_login', function() {
|
||||
const user = await createUser(1);
|
||||
|
||||
const context = await doLogin(user.email, '123456');
|
||||
const sessionId = cookieGet(context, 'sessionId');
|
||||
const sessionId = context.cookies.get('sessionId');
|
||||
const session: Session = await models().session().load(sessionId);
|
||||
expect(session.user_id).toBe(user.id);
|
||||
});
|
||||
@@ -63,12 +62,12 @@ describe('index_login', function() {
|
||||
|
||||
{
|
||||
const context = await doLogin('bad', '123456');
|
||||
expect(!cookieGet(context, 'sessionId')).toBe(true);
|
||||
expect(!context.cookies.get('sessionId')).toBe(true);
|
||||
}
|
||||
|
||||
{
|
||||
const context = await doLogin(user.email, 'bad');
|
||||
expect(!cookieGet(context, 'sessionId')).toBe(true);
|
||||
expect(!context.cookies.get('sessionId')).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import { formParse } from '../../utils/requestUtils';
|
||||
import config from '../../config';
|
||||
import defaultView from '../../utils/defaultView';
|
||||
import { View } from '../../services/MustacheService';
|
||||
import limiterLoginBruteForce from '../../utils/request/limiterLoginBruteForce';
|
||||
import { cookieSet } from '../../utils/cookies';
|
||||
|
||||
function makeView(error: any = null): View {
|
||||
const view = defaultView('login', 'Login');
|
||||
@@ -27,13 +25,11 @@ router.get('login', async (_path: SubPath, _ctx: AppContext) => {
|
||||
});
|
||||
|
||||
router.post('login', async (_path: SubPath, ctx: AppContext) => {
|
||||
await limiterLoginBruteForce(ctx.ip);
|
||||
|
||||
try {
|
||||
const body = await formParse(ctx.req);
|
||||
|
||||
const session = await ctx.joplin.models.session().authenticate(body.fields.email, body.fields.password);
|
||||
cookieSet(ctx, 'sessionId', session.id);
|
||||
ctx.cookies.set('sessionId', session.id);
|
||||
return redirect(ctx, `${config().baseUrl}/home`);
|
||||
} catch (error) {
|
||||
return makeView(error);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import routeHandler from '../../middleware/routeHandler';
|
||||
import { cookieGet } from '../../utils/cookies';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, models, createUserAndSession } from '../../utils/testing/testUtils';
|
||||
|
||||
describe('index_logout', function() {
|
||||
@@ -27,11 +26,11 @@ describe('index_logout', function() {
|
||||
},
|
||||
});
|
||||
|
||||
expect(cookieGet(context, 'sessionId')).toBe(session.id);
|
||||
expect(context.cookies.get('sessionId')).toBe(session.id);
|
||||
expect(!!(await models().session().load(session.id))).toBe(true);
|
||||
await routeHandler(context);
|
||||
|
||||
expect(!cookieGet(context, 'sessionId')).toBe(true);
|
||||
expect(!context.cookies.get('sessionId')).toBe(true);
|
||||
expect(!!(await models().session().load(session.id))).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ import { RouteType } from '../../utils/types';
|
||||
import { AppContext } from '../../utils/types';
|
||||
import config from '../../config';
|
||||
import { contextSessionId } from '../../utils/requestUtils';
|
||||
import { cookieSet } from '../../utils/cookies';
|
||||
|
||||
const router = new Router(RouteType.Web);
|
||||
|
||||
router.post('logout', async (_path: SubPath, ctx: AppContext) => {
|
||||
const sessionId = contextSessionId(ctx, false);
|
||||
cookieSet(ctx, 'sessionId', '');
|
||||
ctx.cookies.set('sessionId', '');
|
||||
await ctx.joplin.models.session().logout(sessionId);
|
||||
return redirect(ctx, `${config().baseUrl}/login`);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NotificationKey } from '../../models/NotificationModel';
|
||||
import { AccountType } from '../../models/UserModel';
|
||||
import { getCanShareFolder, getMaxItemSize } from '../../models/utils/user';
|
||||
import { MB } from '../../utils/bytes';
|
||||
import { cookieGet } from '../../utils/cookies';
|
||||
import { execRequestC } from '../../utils/testing/apiUtils';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, models } from '../../utils/testing/testUtils';
|
||||
import uuidgen from '../../utils/uuidgen';
|
||||
@@ -51,7 +50,7 @@ describe('index_signup', function() {
|
||||
expect(getMaxItemSize(user)).toBe(10 * MB);
|
||||
|
||||
// Check that the user is logged in
|
||||
const session = await models().session().load(cookieGet(context, 'sessionId'));
|
||||
const session = await models().session().load(context.cookies.get('sessionId'));
|
||||
expect(session.user_id).toBe(user.id);
|
||||
|
||||
// Check that the notification has been created
|
||||
|
||||
@@ -10,7 +10,6 @@ import { checkRepeatPassword } from './users';
|
||||
import { NotificationKey } from '../../models/NotificationModel';
|
||||
import { AccountType } from '../../models/UserModel';
|
||||
import { ErrorForbidden } from '../../utils/errors';
|
||||
import { cookieSet } from '../../utils/cookies';
|
||||
|
||||
function makeView(error: Error = null): View {
|
||||
const view = defaultView('signup', 'Sign Up');
|
||||
@@ -52,7 +51,7 @@ router.post('signup', async (_path: SubPath, ctx: AppContext) => {
|
||||
});
|
||||
|
||||
const session = await ctx.joplin.models.session().createUserSession(user.id);
|
||||
cookieSet(ctx, 'sessionId', session.id);
|
||||
ctx.cookies.set('sessionId', session.id);
|
||||
|
||||
await ctx.joplin.models.notification().add(user.id, NotificationKey.ConfirmEmail);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { User } from '../../db';
|
||||
import routeHandler from '../../middleware/routeHandler';
|
||||
import { NotificationKey } from '../../models/NotificationModel';
|
||||
import { cookieGet } from '../../utils/cookies';
|
||||
import { ErrorForbidden } from '../../utils/errors';
|
||||
import { execRequest, execRequestC } from '../../utils/testing/apiUtils';
|
||||
import { beforeAllDb, afterAllTests, beforeEachDb, koaAppContext, createUserAndSession, models, parseHtml, checkContextError, expectHttpError } from '../../utils/testing/testUtils';
|
||||
@@ -241,7 +240,7 @@ describe('index/users', function() {
|
||||
password: newPassword,
|
||||
password2: newPassword,
|
||||
});
|
||||
const sessionId = cookieGet(context, 'sessionId');
|
||||
const sessionId = context.cookies.get('sessionId');
|
||||
expect(sessionId).toBeFalsy();
|
||||
}
|
||||
|
||||
@@ -254,7 +253,7 @@ describe('index/users', function() {
|
||||
password2: newPassword,
|
||||
token: token2,
|
||||
});
|
||||
const sessionId = cookieGet(context, 'sessionId');
|
||||
const sessionId = context.cookies.get('sessionId');
|
||||
expect(sessionId).toBeFalsy();
|
||||
}
|
||||
|
||||
@@ -267,7 +266,7 @@ describe('index/users', function() {
|
||||
});
|
||||
|
||||
// Check that the user has been logged in
|
||||
const sessionId = cookieGet(context, 'sessionId');
|
||||
const sessionId = context.cookies.get('sessionId');
|
||||
const session = await models().session().load(sessionId);
|
||||
expect(session.user_id).toBe(user1.id);
|
||||
|
||||
@@ -304,7 +303,7 @@ describe('index/users', function() {
|
||||
user1 = await models().user().load(user1.id);
|
||||
|
||||
// Check that the user has been logged in
|
||||
const sessionId = cookieGet(context, 'sessionId');
|
||||
const sessionId = context.cookies.get('sessionId');
|
||||
expect(sessionId).toBeFalsy();
|
||||
|
||||
// Check that the email has been verified
|
||||
|
||||
@@ -19,7 +19,6 @@ import { confirmUrl } from '../../utils/urlUtils';
|
||||
import { cancelSubscriptionByUserId, updateSubscriptionType } from '../../utils/stripe';
|
||||
import { createCsrfTag } from '../../utils/csrf';
|
||||
import { formatDateTime } from '../../utils/time';
|
||||
import { cookieSet } from '../../utils/cookies';
|
||||
|
||||
export interface CheckRepeatPasswordInput {
|
||||
password: string;
|
||||
@@ -237,7 +236,7 @@ router.post('users/:id/confirm', async (path: SubPath, ctx: AppContext) => {
|
||||
await ctx.joplin.models.token().deleteByValue(userId, fields.token);
|
||||
|
||||
const session = await ctx.joplin.models.session().createUserSession(userId);
|
||||
cookieSet(ctx, 'sessionId', session.id);
|
||||
ctx.cookies.set('sessionId', session.id);
|
||||
|
||||
await ctx.joplin.models.notification().add(userId, NotificationKey.PasswordSet);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { connectDb, disconnectDb, migrateLatest } from '../db';
|
||||
import { connectDb, disconnectDb, migrateDb } from '../db';
|
||||
import * as fs from 'fs-extra';
|
||||
import { DatabaseConfig } from '../utils/types';
|
||||
|
||||
@@ -46,7 +46,7 @@ export async function createDb(config: DatabaseConfig, options: CreateDbOptions
|
||||
|
||||
try {
|
||||
const db = await connectDb(config);
|
||||
await migrateLatest(db);
|
||||
await migrateDb(db);
|
||||
await disconnectDb(db);
|
||||
} catch (error) {
|
||||
error.message += `: ${config.name}`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DbConnection, dropTables, migrateLatest } from '../db';
|
||||
import { DbConnection, dropTables, migrateDb } from '../db';
|
||||
import newModelFactory from '../models/factory';
|
||||
import { AccountType } from '../models/UserModel';
|
||||
import { Config } from '../utils/types';
|
||||
@@ -15,7 +15,7 @@ export async function handleDebugCommands(argv: any, db: DbConnection, config: C
|
||||
|
||||
export async function createTestUsers(db: DbConnection, config: Config) {
|
||||
await dropTables(db);
|
||||
await migrateLatest(db);
|
||||
await migrateDb(db);
|
||||
|
||||
const password = 'hunter1hunter2hunter3';
|
||||
const models = newModelFactory(db, config);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import config from '../config';
|
||||
import { AppContext } from './types';
|
||||
|
||||
export function cookieSet(ctx: AppContext, name: string, value: string) {
|
||||
ctx.cookies.set(name, value, {
|
||||
// Means that the cookies cannot be accessed from JavaScript
|
||||
httpOnly: true,
|
||||
// Can only be transferred over https
|
||||
secure: config().cookieSecure,
|
||||
// Prevent cookies from being sent in cross-site requests
|
||||
sameSite: true,
|
||||
});
|
||||
}
|
||||
|
||||
export function cookieGet(ctx: AppContext, name: string) {
|
||||
return ctx.cookies.get(name);
|
||||
}
|
||||
@@ -97,17 +97,6 @@ export class ErrorPayloadTooLarge extends ApiError {
|
||||
}
|
||||
}
|
||||
|
||||
export class ErrorTooManyRequests extends ApiError {
|
||||
public static httpCode: number = 429;
|
||||
public retryAfterMs: number = 0;
|
||||
|
||||
public constructor(message: string = null, retryAfterMs: number = 0) {
|
||||
super(message === null ? 'Too Many Requests' : message, ErrorTooManyRequests.httpCode);
|
||||
this.retryAfterMs = retryAfterMs;
|
||||
Object.setPrototypeOf(this, ErrorTooManyRequests.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
export function errorToString(error: Error): string {
|
||||
const msg: string[] = [];
|
||||
msg.push(error.message ? error.message : 'Unknown error');
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import { RateLimiterMemory, RateLimiterRes } from 'rate-limiter-flexible';
|
||||
import { ErrorTooManyRequests } from '../errors';
|
||||
|
||||
const limiterSlowBruteByIP = new RateLimiterMemory({
|
||||
points: 3, // Up to 3 requests per IP
|
||||
duration: 30, // Per 30 seconds
|
||||
});
|
||||
|
||||
export default async function(ip: string) {
|
||||
// Tests need to make many requests quickly so we disable it in this case.
|
||||
if (process.env.JOPLIN_IS_TESTING === '1') return;
|
||||
|
||||
try {
|
||||
await limiterSlowBruteByIP.consume(ip);
|
||||
} catch (error) {
|
||||
const result = error as RateLimiterRes;
|
||||
throw new ErrorTooManyRequests(`Too many login attempts. Please try again in ${Math.ceil(result.msBeforeNext / 1000)} seconds.`, result.msBeforeNext);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { cookieGet } from './cookies';
|
||||
import { ErrorForbidden } from './errors';
|
||||
import { AppContext } from './types';
|
||||
|
||||
@@ -62,7 +61,7 @@ export function headerSessionId(headers: any): string {
|
||||
export function contextSessionId(ctx: AppContext, throwIfNotFound = true): string {
|
||||
if (ctx.headers['x-api-auth']) return ctx.headers['x-api-auth'];
|
||||
|
||||
const id = cookieGet(ctx, 'sessionId');
|
||||
const id = ctx.cookies.get('sessionId');
|
||||
if (!id && throwIfNotFound) throw new ErrorForbidden('Invalid or missing session');
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -106,9 +106,9 @@ async function main() {
|
||||
|
||||
fs.removeSync(`${serverRoot}/db-testing.sqlite`);
|
||||
|
||||
// const migrateCommand = 'NODE_ENV=testing node dist/app.js --migrate-latest --env dev';
|
||||
// const migrateCommand = 'NODE_ENV=testing node dist/app.js --migrate-db --env dev';
|
||||
const clearCommand = 'node dist/app.js --env dev --drop-tables';
|
||||
const migrateCommand = 'node dist/app.js --env dev --migrate-latest';
|
||||
const migrateCommand = 'node dist/app.js --env dev --migrate-db';
|
||||
|
||||
await execCommand(clearCommand);
|
||||
await execCommand(migrateCommand);
|
||||
|
||||
@@ -21,7 +21,6 @@ import { initializeJoplinUtils } from '../joplinUtils';
|
||||
import MustacheService from '../../services/MustacheService';
|
||||
import uuidgen from '../uuidgen';
|
||||
import { createCsrfToken } from '../csrf';
|
||||
import { cookieSet } from '../cookies';
|
||||
|
||||
// Takes into account the fact that this file will be inside the /dist directory
|
||||
// when it runs.
|
||||
@@ -212,7 +211,7 @@ export async function koaAppContext(options: AppContextTestOptions = null): Prom
|
||||
};
|
||||
|
||||
if (options.sessionId) {
|
||||
cookieSet(appContext, 'sessionId', options.sessionId);
|
||||
appContext.cookies.set('sessionId', options.sessionId);
|
||||
}
|
||||
|
||||
return appContext as AppContext;
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Day, Month, Second } from './time';
|
||||
|
||||
describe('time', function() {
|
||||
|
||||
it('should have correct interval durations', async function() {
|
||||
expect(Second).toBe(1000);
|
||||
expect(Day).toBe(86400000);
|
||||
expect(Month).toBe(2592000000);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -14,7 +14,7 @@ function initDayJs() {
|
||||
|
||||
initDayJs();
|
||||
|
||||
export const Second = 1000;
|
||||
export const Second = 60 * 1000;
|
||||
export const Minute = 60 * Second;
|
||||
export const Hour = 60 * Minute;
|
||||
export const Day = 24 * Hour;
|
||||
|
||||
@@ -108,7 +108,6 @@ export interface Config {
|
||||
supportName: string;
|
||||
businessEmail: string;
|
||||
isJoplinCloud: boolean;
|
||||
cookieSecure: boolean;
|
||||
}
|
||||
|
||||
export enum HttpMethod {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user