Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b036852b2d | |||
| f693dc31e8 | |||
| af8f5b11b8 | |||
| 8ca943f87b | |||
| 35cc9da626 | |||
| 44cc86a1a5 | |||
| 7c3bbfd45e | |||
| a1f939e3a0 | |||
| 4ca7886090 | |||
| b942163748 | |||
| 5b6d15acb3 | |||
| 841420360e | |||
| a680bb4612 | |||
| f763e13996 | |||
| e73e1262ae | |||
| b774d56cf7 | |||
| bae55f2e8b | |||
| 6c16cdb011 | |||
| 86fcbb0354 | |||
| 17d030e800 | |||
| 6fbf28b30d | |||
| 8171b68b18 | |||
| d30e3a4052 | |||
| 87aab7ac5b | |||
| c46f5805b1 | |||
| dd6a29dfc0 | |||
| a0933c7f10 | |||
| 492adc2909 | |||
| 90a52f57b5 | |||
| a1469854e8 | |||
| e2c2993255 | |||
| 270d63b265 | |||
| 1d004be91d | |||
| b8a74c727e | |||
| c87b592277 |
@@ -10,7 +10,7 @@ Trilium Notes is a hierarchical note taking application with focus on building l
|
||||
* Notes can be arranged into arbitrarily deep tree. Single note can be placed into multiple places in the tree (see [cloning](https://github.com/zadam/trilium/wiki/Cloning-notes))
|
||||
* Rich WYSIWYG note editing including e.g. tables and images with markdown [autoformat](https://github.com/zadam/trilium/wiki/Text-editor#autoformat)
|
||||
* Support for editing [notes with source code](https://github.com/zadam/trilium/wiki/Code-notes), including syntax highlighting
|
||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation)
|
||||
* Fast and easy [navigation between notes](https://github.com/zadam/trilium/wiki/Note-navigation), full text search and [note hoisting](https://github.com/zadam/trilium/wiki/Note-hoisting)
|
||||
* Seamless [note versioning](https://github.com/zadam/trilium/wiki/Note-revisions)
|
||||
* Note [attributes](https://github.com/zadam/trilium/wiki/Attributes) can be used for note organization, querying and advanced [scripting](https://github.com/zadam/trilium/wiki/Scripts)
|
||||
* [Synchronization](https://github.com/zadam/trilium/wiki/Synchronization) with self-hosted sync server
|
||||
@@ -23,9 +23,9 @@ Trilium Notes is a hierarchical note taking application with focus on building l
|
||||
|
||||
## Builds
|
||||
|
||||
Trilium is provided as either desktop application (Linux, Windows) or web application hosted on your server (Linux). Mac is planned but not available at the moment.
|
||||
Trilium is provided as either desktop application (Linux, Windows, Mac) or web application hosted on your server (Linux).
|
||||
|
||||
* If you want to use Trilium on the desktop, download binary release for your platform (currently Linux and Windows are supported) from [latest release](https://github.com/zadam/trilium/releases/latest), unzip the package and run ```trilium``` executable.
|
||||
* If you want to use Trilium on the desktop, download binary release for your platform from [latest release](https://github.com/zadam/trilium/releases/latest), unzip the package and run ```trilium``` executable.
|
||||
* If you want to install Trilium on server, follow [this page](https://github.com/zadam/trilium/wiki/Server-installation).
|
||||
* Currently only recent Chrome and Firefox are supported (tested) browsers.
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
BUILD_DIR=./dist/trilium-linux-ia32
|
||||
rm -rf $BUILD_DIR
|
||||
|
||||
echo "Rebuilding binaries for linux-ia32"
|
||||
./node_modules/.bin/electron-rebuild --arch=ia32
|
||||
|
||||
./node_modules/.bin/electron-packager . --out=dist --executable-name=trilium --platform=linux --arch=ia32 --overwrite
|
||||
|
||||
mv "./dist/Trilium Notes-linux-ia32" $BUILD_DIR
|
||||
|
||||
rm -r $BUILD_DIR/resources/app/bin/deps
|
||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||
rm -r $BUILD_DIR/swiftshader
|
||||
|
||||
echo "Packaging linux ia32 electron distribution..."
|
||||
VERSION=`jq -r ".version" package.json`
|
||||
7z a $BUILD_DIR-${VERSION}.7z $BUILD_DIR
|
||||
@@ -11,6 +11,10 @@ echo "Rebuilding binaries for linux-x64"
|
||||
|
||||
mv "./dist/Trilium Notes-linux-x64" $BUILD_DIR
|
||||
|
||||
rm -r "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/*"
|
||||
|
||||
cp -r bin/deps/linux-x64/sqlite/electron* "$BUILD_DIR/resources/app/node_modules/sqlite3/lib/binding/"
|
||||
|
||||
rm -r $BUILD_DIR/resources/app/bin/deps
|
||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||
rm -r $BUILD_DIR/swiftshader
|
||||
|
||||
@@ -14,10 +14,10 @@ MAC_RES_DIR=$BUILD_DIR/Trilium\ Notes.app/Contents/Resources/app
|
||||
|
||||
rm -r "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/*"
|
||||
|
||||
cp -r bin/deps/mac/sqlite/* "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/"
|
||||
cp bin/deps/mac/image/cjpeg "$MAC_RES_DIR/node_modules/mozjpeg/vendor/"
|
||||
cp bin/deps/mac/image/pngquant "$MAC_RES_DIR/node_modules/pngquant-bin/vendor/"
|
||||
cp bin/deps/mac/image/gifsicle "$MAC_RES_DIR/node_modules/giflossy/vendor/"
|
||||
cp -r bin/deps/mac-x64/sqlite/* "$MAC_RES_DIR/node_modules/sqlite3/lib/binding/"
|
||||
cp bin/deps/mac-x64/image/cjpeg "$MAC_RES_DIR/node_modules/mozjpeg/vendor/"
|
||||
cp bin/deps/mac-x64/image/pngquant "$MAC_RES_DIR/node_modules/pngquant-bin/vendor/"
|
||||
cp bin/deps/mac-x64/image/gifsicle "$MAC_RES_DIR/node_modules/giflossy/vendor/"
|
||||
|
||||
rm -r "$MAC_RES_DIR/bin/deps"
|
||||
|
||||
|
||||
+5
-1
@@ -24,10 +24,14 @@ cp -r ../../config-sample.ini ./
|
||||
|
||||
rm -r ./node_modules/electron*
|
||||
|
||||
rm -r ./node_modules/sqlite3/lib/binding/*
|
||||
|
||||
cp -r ../../bin/deps/linux-x64/sqlite/node* ./node_modules/sqlite3/lib/binding/
|
||||
|
||||
printf "#/bin/sh\n./node/bin/node src/www" > trilium.sh
|
||||
chmod 755 trilium.sh
|
||||
|
||||
cd ..
|
||||
|
||||
VERSION=`jq -r ".version" package.json`
|
||||
VERSION=`jq -r ".version" ../package.json`
|
||||
7z a trilium-linux-x64-server-${VERSION}.7z trilium-linux-x64-server
|
||||
@@ -11,10 +11,10 @@ echo "Copying required windows binaries"
|
||||
|
||||
WIN_RES_DIR=$BUILD_DIR/resources/app
|
||||
|
||||
cp -r bin/deps/win/sqlite/* $WIN_RES_DIR/node_modules/sqlite3/lib/binding/
|
||||
cp bin/deps/win/image/cjpeg.exe $WIN_RES_DIR/node_modules/mozjpeg/vendor/
|
||||
cp bin/deps/win/image/pngquant.exe $WIN_RES_DIR/node_modules/pngquant-bin/vendor/
|
||||
cp bin/deps/win/image/gifsicle.exe $WIN_RES_DIR/node_modules/giflossy/vendor/
|
||||
cp -r bin/deps/win-x64/sqlite/* $WIN_RES_DIR/node_modules/sqlite3/lib/binding/
|
||||
cp bin/deps/win-x64/image/cjpeg.exe $WIN_RES_DIR/node_modules/mozjpeg/vendor/
|
||||
cp bin/deps/win-x64/image/pngquant.exe $WIN_RES_DIR/node_modules/pngquant-bin/vendor/
|
||||
cp bin/deps/win-x64/image/gifsicle.exe $WIN_RES_DIR/node_modules/giflossy/vendor/
|
||||
|
||||
rm -r $WIN_RES_DIR/bin/deps
|
||||
# removing software WebGL binaries because they are pretty huge and not necessary
|
||||
|
||||
+2
-2
@@ -8,11 +8,11 @@ echo "Deleting existing builds"
|
||||
|
||||
rm -r dist/*
|
||||
|
||||
bin/build-linux-ia32.sh
|
||||
|
||||
bin/build-win-x64.sh
|
||||
|
||||
bin/build-mac-x64.sh
|
||||
|
||||
# building X64 linux as the last so electron-rebuild will prepare X64 binaries for local development
|
||||
bin/build-linux-x64.sh
|
||||
|
||||
bin/build-server.sh
|
||||
Binary file not shown.
Binary file not shown.
+1
-11
@@ -43,7 +43,6 @@ git push origin $TAG
|
||||
bin/build.sh
|
||||
|
||||
LINUX_X64_BUILD=trilium-linux-x64-$VERSION.7z
|
||||
LINUX_IA32_BUILD=trilium-linux-ia32-$VERSION.7z
|
||||
WINDOWS_X64_BUILD=trilium-windows-x64-$VERSION.7z
|
||||
MAC_X64_BUILD=trilium-mac-x64-$VERSION.7z
|
||||
SERVER_BUILD=trilium-linux-x64-server-$VERSION.7z
|
||||
@@ -67,13 +66,6 @@ github-release upload \
|
||||
--name "$LINUX_X64_BUILD" \
|
||||
--file "dist/$LINUX_X64_BUILD"
|
||||
|
||||
echo "Uploading linux ia32 build"
|
||||
|
||||
github-release upload \
|
||||
--tag $TAG \
|
||||
--name "$LINUX_IA32_BUILD" \
|
||||
--file "dist/$LINUX_IA32_BUILD"
|
||||
|
||||
echo "Uploading windows x64 build"
|
||||
|
||||
github-release upload \
|
||||
@@ -88,9 +80,7 @@ github-release upload \
|
||||
--name "$MAC_X64_BUILD" \
|
||||
--file "dist/$MAC_X64_BUILD"
|
||||
|
||||
echo "Packaging server version"
|
||||
|
||||
bin/build-server.sh $VERSION
|
||||
echo "Uploading linux x64 server build"
|
||||
|
||||
github-release upload \
|
||||
--tag $TAG \
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
|
||||
VALUES ('hoistedNoteId', 'root', '2018-12-11T18:31:00.874Z', '2018-12-11T18:31:00.874Z', 0);
|
||||
Generated
+64
-114
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "trilium",
|
||||
"version": "0.24.5",
|
||||
"version": "0.25.2",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -671,9 +671,9 @@
|
||||
"integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0="
|
||||
},
|
||||
"asar": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/asar/-/asar-0.14.5.tgz",
|
||||
"integrity": "sha512-2Di/TnY1sridHFKMFgxBh0Wk0gVxSZN4qQhRhjJn3UywZAvP5MHI0RNVSkpzmJ+n6t0BC8w/+1257wtSgQ3Kdg==",
|
||||
"version": "0.14.6",
|
||||
"resolved": "https://registry.npmjs.org/asar/-/asar-0.14.6.tgz",
|
||||
"integrity": "sha512-ZqybKcdO5At6y3ge2RHxVImc6Eltb2t3sxT7lk4T4zjZBSFUuIGCIZY6f41dCjlvJSizN5QPRr8YTgMhpgBjLg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chromium-pickle-js": "^0.2.0",
|
||||
@@ -682,7 +682,7 @@
|
||||
"glob": "^6.0.4",
|
||||
"minimatch": "^3.0.3",
|
||||
"mkdirp": "^0.5.0",
|
||||
"mksnapshot": "^0.3.0",
|
||||
"mksnapshot": "^0.3.4",
|
||||
"tmp": "0.0.28"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -969,7 +969,8 @@
|
||||
"bluebird": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.2.tgz",
|
||||
"integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg=="
|
||||
"integrity": "sha512-dhHTWMI7kMx5whMQntl7Vr9C6BvV10lFXDAasnqnrMYhXVCzzk6IO9Fo2L75jXHT07WrOngL1WDXOp+yYS91Yg==",
|
||||
"dev": true
|
||||
},
|
||||
"bmp-js": {
|
||||
"version": "0.1.0",
|
||||
@@ -2034,7 +2035,7 @@
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
|
||||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||
"dev": true
|
||||
}
|
||||
@@ -2374,9 +2375,9 @@
|
||||
"integrity": "sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ=="
|
||||
},
|
||||
"electron": {
|
||||
"version": "4.0.0-beta.8",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-4.0.0-beta.8.tgz",
|
||||
"integrity": "sha512-zo0Tf3t1uary5O9Skdlo37axs+vvh9CPC+MkvZnnX3MA/3kWIc+I6jN+AL4bHUdxsMVtjbY8KnusjnCjKSb0sg==",
|
||||
"version": "4.0.0-beta.9",
|
||||
"resolved": "https://registry.npmjs.org/electron/-/electron-4.0.0-beta.9.tgz",
|
||||
"integrity": "sha512-BPFkN4BFQy88x2ZHVmzI03i1mUgaQF/uROPb/TlGB/WNAD3v2OvA9Ak9yZ5ADNnwhlR28DtUGs/MuZfDZHZBoQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "^8.0.24",
|
||||
@@ -2526,10 +2527,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"electron-notarize": {
|
||||
"version": "0.0.5",
|
||||
"resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.0.5.tgz",
|
||||
"integrity": "sha512-YzrqZ6RDQ7Wt2RWlxzRoQUuxnTeXrfp7laH7XKcmQqrZ6GaAr50DMPvFMpqDKdrZSHSbcgZgB7ktIQbjvITmCQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"fs-extra": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"electron-osx-sign": {
|
||||
"version": "0.4.10",
|
||||
"resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz",
|
||||
"integrity": "sha1-vk87ibKnWh3F8eckkIGrKSnKOiY=",
|
||||
"version": "0.4.11",
|
||||
"resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.11.tgz",
|
||||
"integrity": "sha512-VVd40nrnVqymvFrY9ZkOYgHJOvexHHYTR3di/SN+mjJ0OWhR1I8BRVj3U+Yamw6hnkZZNKZp52rqL5EFAAPFkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"bluebird": "^3.5.0",
|
||||
@@ -2537,7 +2548,7 @@
|
||||
"debug": "^2.6.8",
|
||||
"isbinaryfile": "^3.0.2",
|
||||
"minimist": "^1.2.0",
|
||||
"plist": "^2.1.0"
|
||||
"plist": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
@@ -2552,69 +2563,50 @@
|
||||
}
|
||||
},
|
||||
"electron-packager": {
|
||||
"version": "12.2.0",
|
||||
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-12.2.0.tgz",
|
||||
"integrity": "sha512-T5W/FIK4VXhYIOWxkehmz6zXt2S/sA9JZ3AL+/jeKCicQY6QVQ0K8B7W801L+GPTwbgTPycHjO+iqEf1BhZ+Iw==",
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-13.0.1.tgz",
|
||||
"integrity": "sha512-fXfldaZ1wihpPaMTSGMxvCeETJwVArlnMmKafVXLJbbZwS+WTjY4iL7ju9WMQ0LNGuiiIwSMCQFxt5iA087mqg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asar": "^0.14.0",
|
||||
"debug": "^3.0.0",
|
||||
"debug": "^4.0.1",
|
||||
"electron-download": "^4.1.1",
|
||||
"electron-osx-sign": "^0.4.1",
|
||||
"electron-notarize": "^0.0.5",
|
||||
"electron-osx-sign": "^0.4.11",
|
||||
"extract-zip": "^1.0.3",
|
||||
"fs-extra": "^5.0.0",
|
||||
"fs-extra": "^7.0.0",
|
||||
"galactus": "^0.2.1",
|
||||
"get-package-info": "^1.0.0",
|
||||
"nodeify": "^1.0.1",
|
||||
"parse-author": "^2.0.0",
|
||||
"pify": "^3.0.0",
|
||||
"plist": "^2.0.0",
|
||||
"pify": "^4.0.0",
|
||||
"plist": "^3.0.0",
|
||||
"rcedit": "^1.0.0",
|
||||
"resolve": "^1.1.6",
|
||||
"sanitize-filename": "^1.6.0",
|
||||
"semver": "^5.3.0",
|
||||
"yargs-parser": "^10.0.0"
|
||||
"yargs-parser": "^11.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"camelcase": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
||||
"integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
|
||||
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz",
|
||||
"integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"graceful-fs": "^4.1.2",
|
||||
"jsonfile": "^4.0.0",
|
||||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
|
||||
"integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
|
||||
"pify": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
|
||||
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
|
||||
"dev": true
|
||||
},
|
||||
"yargs-parser": {
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz",
|
||||
"integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==",
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
|
||||
"integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"camelcase": "^4.1.0"
|
||||
"camelcase": "^5.0.0",
|
||||
"decamelize": "^1.2.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4305,7 +4297,7 @@
|
||||
},
|
||||
"pify": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||
"dev": true
|
||||
},
|
||||
@@ -5608,12 +5600,6 @@
|
||||
"resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz",
|
||||
"integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU="
|
||||
},
|
||||
"is-promise": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz",
|
||||
"integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=",
|
||||
"dev": true
|
||||
},
|
||||
"is-proto-prop": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-proto-prop/-/is-proto-prop-1.0.1.tgz",
|
||||
@@ -6542,19 +6528,19 @@
|
||||
"dev": true
|
||||
},
|
||||
"mksnapshot": {
|
||||
"version": "0.3.1",
|
||||
"resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.3.1.tgz",
|
||||
"integrity": "sha1-JQHAVldDbXQs6Vik/5LHfkDdN+Y=",
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/mksnapshot/-/mksnapshot-0.3.4.tgz",
|
||||
"integrity": "sha512-FgUTiWiY+35LgL95P/MDYrBuQO5o0s3MmaWKX6ZJWoX4vMOY9vPsAv763l1OSSelL9jPsBQ/wf4bzfqTLNPSFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"decompress-zip": "0.3.0",
|
||||
"fs-extra": "0.26.7",
|
||||
"request": "^2.79.0"
|
||||
"request": "2.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": {
|
||||
"version": "0.26.7",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
|
||||
"resolved": "http://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
|
||||
"integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -6582,9 +6568,9 @@
|
||||
"integrity": "sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE="
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.22.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz",
|
||||
"integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y="
|
||||
"version": "2.23.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.23.0.tgz",
|
||||
"integrity": "sha512-3IE39bHVqFbWWaPOMHZF98Q9c3LDKGTmypMiTM2QygGXXElkFWIH7GxfmlwmY2vwa+wmNsoYZmG2iusf1ZjJoA=="
|
||||
},
|
||||
"mozjpeg": {
|
||||
"version": "6.0.1",
|
||||
@@ -7286,16 +7272,6 @@
|
||||
"resolved": "https://registry.npmjs.org/node-status-codes/-/node-status-codes-1.0.0.tgz",
|
||||
"integrity": "sha1-WuVUHQJGRdMqWPzdyc7s6nrjrC8="
|
||||
},
|
||||
"nodeify": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nodeify/-/nodeify-1.0.1.tgz",
|
||||
"integrity": "sha1-ZKtpp7268DzhB7TwM1yHwLnpGx0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-promise": "~1.0.0",
|
||||
"promise": "~1.3.0"
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
|
||||
@@ -8041,26 +8017,20 @@
|
||||
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
|
||||
},
|
||||
"plist": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-2.1.0.tgz",
|
||||
"integrity": "sha1-V8zbeggh3yGDEhejytVOPhRqECU=",
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/plist/-/plist-3.0.1.tgz",
|
||||
"integrity": "sha512-GpgvHHocGRyQm74b6FWEZZVRroHKE1I0/BTjAmySaohK+cUn+hZpbqXkc3KWgW3gQYkqcQej35FohcT0FRlkRQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"base64-js": "1.2.0",
|
||||
"xmlbuilder": "8.2.2",
|
||||
"base64-js": "^1.2.3",
|
||||
"xmlbuilder": "^9.0.7",
|
||||
"xmldom": "0.1.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"base64-js": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.0.tgz",
|
||||
"integrity": "sha1-o5mS1yNYSBGYK+XikLtqU9hnAPE=",
|
||||
"dev": true
|
||||
},
|
||||
"xmlbuilder": {
|
||||
"version": "8.2.2",
|
||||
"resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
|
||||
"integrity": "sha1-aSSGc0ELS6QuGmE2VR0pIjNap3M=",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
|
||||
"integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
@@ -8490,15 +8460,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"promise": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "http://registry.npmjs.org/promise/-/promise-1.3.0.tgz",
|
||||
"integrity": "sha1-5cyaTIJ45GZP/twBx9qEhCsEAXU=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-promise": "~1"
|
||||
}
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
@@ -8826,17 +8787,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"request-promise": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
|
||||
"integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.0",
|
||||
"request-promise-core": "1.1.1",
|
||||
"stealthy-require": "^1.1.0",
|
||||
"tough-cookie": ">=2.3.3"
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
|
||||
|
||||
+4
-6
@@ -2,7 +2,7 @@
|
||||
"name": "trilium",
|
||||
"productName": "Trilium Notes",
|
||||
"description": "Trilium Notes",
|
||||
"version": "0.25.0-beta",
|
||||
"version": "0.26.0-beta",
|
||||
"license": "AGPL-3.0-only",
|
||||
"main": "electron.js",
|
||||
"bin": {
|
||||
@@ -45,13 +45,11 @@
|
||||
"ini": "1.3.5",
|
||||
"jimp": "0.6.0",
|
||||
"mime-types": "^2.1.21",
|
||||
"moment": "2.22.2",
|
||||
"moment": "2.23.0",
|
||||
"multer": "1.4.1",
|
||||
"open": "0.0.5",
|
||||
"rand-token": "0.4.0",
|
||||
"rcedit": "1.1.1",
|
||||
"request": "2.88.0",
|
||||
"request-promise": "4.2.2",
|
||||
"rimraf": "2.6.2",
|
||||
"sanitize-filename": "1.6.1",
|
||||
"sax": "^1.2.4",
|
||||
@@ -67,9 +65,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"devtron": "1.4.0",
|
||||
"electron": "4.0.0-beta.8",
|
||||
"electron": "4.0.0-beta.9",
|
||||
"electron-compile": "6.4.3",
|
||||
"electron-packager": "12.2.0",
|
||||
"electron-packager": "13.0.1",
|
||||
"electron-rebuild": "1.8.2",
|
||||
"lorem-ipsum": "1.0.6",
|
||||
"tape": "4.9.1",
|
||||
|
||||
+26
-1
@@ -32,6 +32,7 @@ import tooltip from './tooltip.js';
|
||||
import bundle from "./bundle.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import libraryLoader from "./library_loader.js";
|
||||
import hoistedNoteService from './hoisted_note.js';
|
||||
|
||||
// required for CKEditor image upload plugin
|
||||
window.glob.getCurrentNode = treeService.getCurrentNode;
|
||||
@@ -84,6 +85,8 @@ $(document).on("click", "button[data-help-page]", e => {
|
||||
|
||||
$("#logout-button").toggle(!utils.isElectron());
|
||||
|
||||
$("#tree").on("click", ".unhoist-button", hoistedNoteService.unhoist);
|
||||
|
||||
if (utils.isElectron()) {
|
||||
require('electron').ipcRenderer.on('create-day-sub-note', async function(event, parentNoteId) {
|
||||
// this might occur when day note had to be created
|
||||
@@ -104,6 +107,28 @@ if (utils.isElectron()) {
|
||||
});
|
||||
}
|
||||
|
||||
function exec(cmd) {
|
||||
document.execCommand(cmd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (utils.isElectron() && utils.isMac()) {
|
||||
utils.bindShortcut('ctrl+c', () => exec("copy"));
|
||||
utils.bindShortcut('ctrl+v', () => exec('paste'));
|
||||
utils.bindShortcut('ctrl+x', () => exec('cut'));
|
||||
utils.bindShortcut('ctrl+a', () => exec('selectAll'));
|
||||
utils.bindShortcut('ctrl+z', () => exec('undo'));
|
||||
utils.bindShortcut('ctrl+y', () => exec('redo'));
|
||||
|
||||
utils.bindShortcut('meta+c', () => exec("copy"));
|
||||
utils.bindShortcut('meta+v', () => exec('paste'));
|
||||
utils.bindShortcut('meta+x', () => exec('cut'));
|
||||
utils.bindShortcut('meta+a', () => exec('selectAll'));
|
||||
utils.bindShortcut('meta+z', () => exec('undo'));
|
||||
utils.bindShortcut('meta+y', () => exec('redo'));
|
||||
}
|
||||
|
||||
$("#export-note-button").click(function () {
|
||||
if ($(this).hasClass("disabled")) {
|
||||
return;
|
||||
@@ -118,4 +143,4 @@ entrypoints.registerEntrypoints();
|
||||
|
||||
tooltip.setupTooltip();
|
||||
|
||||
bundle.executeStartupBundles();
|
||||
bundle.executeStartupBundles();
|
||||
|
||||
@@ -4,6 +4,10 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) {
|
||||
$contextMenuContainer.empty();
|
||||
|
||||
for (const item of contextMenuItems) {
|
||||
if (item.hidden) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.title === '----') {
|
||||
$contextMenuContainer.append($("<div>").addClass("dropdown-divider"));
|
||||
} else {
|
||||
|
||||
@@ -4,6 +4,7 @@ import treeChangesService from './branches.js';
|
||||
const dragAndDropSetup = {
|
||||
autoExpandMS: 600,
|
||||
dragStart: (node, data) => {
|
||||
// don't allow dragging root node
|
||||
if (node.data.noteId === 'root') {
|
||||
return false;
|
||||
}
|
||||
@@ -22,11 +23,7 @@ const dragAndDropSetup = {
|
||||
// Return false to cancel dragging of node.
|
||||
return true;
|
||||
},
|
||||
dragEnter: (node, data) => {
|
||||
// we don't allow moving root to any other location in the tree
|
||||
// we allow it to be placed on the relation map though, that's handled in a different drop handler
|
||||
return node.data.noteId === 'root';
|
||||
}, // allow drop on any node
|
||||
dragEnter: (node, data) => true, // allow drop on any node
|
||||
dragDrop: (node, data) => {
|
||||
// This function MUST be defined to enable dropping of items on the tree.
|
||||
// data.hitMode is 'before', 'after', or 'over'.
|
||||
|
||||
@@ -95,7 +95,7 @@ function registerEntrypoints() {
|
||||
}
|
||||
});
|
||||
|
||||
$(document).bind('keydown', 'ctrl+f', () => {
|
||||
function openInPageSearch() {
|
||||
if (utils.isElectron()) {
|
||||
const $searchWindowWebview = $(".electron-in-page-search-window");
|
||||
$searchWindowWebview.show();
|
||||
@@ -113,7 +113,13 @@ function registerEntrypoints() {
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
utils.bindShortcut('ctrl+f', openInPageSearch);
|
||||
|
||||
if (utils.isMac()) {
|
||||
utils.bindShortcut('meta+f', openInPageSearch);
|
||||
}
|
||||
|
||||
// FIXME: do we really need these at this point?
|
||||
utils.bindShortcut("ctrl+shift+up", () => {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import optionsInit from './options_init.js';
|
||||
import server from "./server.js";
|
||||
import tree from "./tree.js";
|
||||
|
||||
let hoistedNoteId;
|
||||
|
||||
optionsInit.optionsReady.then(options => {
|
||||
hoistedNoteId = options['hoistedNoteId'];
|
||||
});
|
||||
|
||||
async function getHoistedNoteId() {
|
||||
await optionsInit.optionsReady;
|
||||
|
||||
return hoistedNoteId;
|
||||
}
|
||||
|
||||
async function setHoistedNoteId(noteId) {
|
||||
hoistedNoteId = noteId;
|
||||
|
||||
await server.put('options/hoistedNoteId/' + noteId);
|
||||
|
||||
await tree.reload();
|
||||
}
|
||||
|
||||
async function unhoist() {
|
||||
await setHoistedNoteId('root');
|
||||
}
|
||||
|
||||
export default {
|
||||
getHoistedNoteId,
|
||||
setHoistedNoteId,
|
||||
unhoist
|
||||
}
|
||||
@@ -14,6 +14,8 @@ import treeBuilder from "./tree_builder.js";
|
||||
import treeKeyBindings from "./tree_keybindings.js";
|
||||
import Branch from '../entities/branch.js';
|
||||
import NoteShort from '../entities/note_short.js';
|
||||
import hoistedNoteService from '../services/hoisted_note.js';
|
||||
import confirmDialog from "../dialogs/confirm.js";
|
||||
|
||||
const $tree = $("#tree");
|
||||
const $createTopLevelNoteButton = $("#create-top-level-note-button");
|
||||
@@ -88,10 +90,11 @@ async function expandToNote(notePath, expandOpts) {
|
||||
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
let parentNoteId = 'none';
|
||||
let parentNoteId = null;
|
||||
|
||||
for (const childNoteId of runPath) {
|
||||
const node = getNodesByNoteId(childNoteId).find(node => node.data.parentNoteId === parentNoteId);
|
||||
// for first node (!parentNoteId) it doesn't matter which node is found
|
||||
const node = getNodesByNoteId(childNoteId).find(node => !parentNoteId || node.data.parentNoteId === parentNoteId);
|
||||
|
||||
if (!node) {
|
||||
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
|
||||
@@ -111,6 +114,17 @@ async function expandToNote(notePath, expandOpts) {
|
||||
async function activateNote(notePath, newNote) {
|
||||
utils.assertArguments(notePath);
|
||||
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
if (hoistedNoteId !== 'root' && !notePath.includes(hoistedNoteId)) {
|
||||
if (!await confirmDialog.confirm("Requested note is outside of hoisted note subtree. Do you want to unhoist?")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// unhoist so we can activate the note
|
||||
await hoistedNoteService.unhoist();
|
||||
}
|
||||
|
||||
if (glob.activeDialog) {
|
||||
glob.activeDialog.modal('hide');
|
||||
}
|
||||
@@ -143,6 +157,8 @@ async function getRunPath(notePath) {
|
||||
path.push('root');
|
||||
}
|
||||
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
const effectivePath = [];
|
||||
let childNoteId = null;
|
||||
let i = 0;
|
||||
@@ -195,13 +211,12 @@ async function getRunPath(notePath) {
|
||||
}
|
||||
}
|
||||
|
||||
if (parentNoteId === 'none') {
|
||||
effectivePath.push(parentNoteId);
|
||||
childNoteId = parentNoteId;
|
||||
|
||||
if (parentNoteId === hoistedNoteId) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
effectivePath.push(parentNoteId);
|
||||
childNoteId = parentNoteId;
|
||||
}
|
||||
}
|
||||
|
||||
return effectivePath.reverse();
|
||||
@@ -402,6 +417,15 @@ function initFancyTree(tree) {
|
||||
},
|
||||
clones: {
|
||||
highlightActiveClones: true
|
||||
},
|
||||
renderNode: async function (event, data) {
|
||||
const node = data.node;
|
||||
|
||||
if (node.data.noteId !== 'root' && node.data.noteId === await hoistedNoteService.getHoistedNoteId()) {
|
||||
const unhoistButton = $('<span> (<a class="unhoist-button">unhoist</a>)</span>');
|
||||
|
||||
$(node.span).append(unhoistButton);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -545,7 +569,7 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection
|
||||
branchId: branchEntity.branchId,
|
||||
isProtected: isProtected,
|
||||
extraClasses: await treeBuilder.getExtraClasses(noteEntity),
|
||||
icon: treeBuilder.getIcon(noteEntity)
|
||||
icon: await treeBuilder.getIcon(noteEntity)
|
||||
};
|
||||
|
||||
if (target === 'after') {
|
||||
|
||||
@@ -4,13 +4,26 @@ import Branch from "../entities/branch.js";
|
||||
import server from "./server.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import messagingService from "./messaging.js";
|
||||
import hoistedNoteService from "./hoisted_note.js";
|
||||
|
||||
async function prepareTree(noteRows, branchRows, relations) {
|
||||
utils.assertArguments(noteRows, branchRows, relations);
|
||||
|
||||
treeCache.load(noteRows, branchRows, relations);
|
||||
|
||||
return [ await prepareNode(await treeCache.getBranch('root')) ];
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
let hoistedBranch;
|
||||
|
||||
if (hoistedNoteId === 'root') {
|
||||
hoistedBranch = await treeCache.getBranch('root');
|
||||
}
|
||||
else {
|
||||
const hoistedNote = await treeCache.getNote(hoistedNoteId);
|
||||
hoistedBranch = (await hoistedNote.getBranches())[0];
|
||||
}
|
||||
|
||||
return [ await prepareNode(hoistedBranch) ];
|
||||
}
|
||||
|
||||
async function prepareBranch(note) {
|
||||
@@ -22,10 +35,15 @@ async function prepareBranch(note) {
|
||||
}
|
||||
}
|
||||
|
||||
function getIcon(note) {
|
||||
async function getIcon(note) {
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
if (note.noteId === 'root') {
|
||||
return "jam jam-chevrons-right";
|
||||
}
|
||||
else if (note.noteId === hoistedNoteId) {
|
||||
return "jam jam-arrow-up";
|
||||
}
|
||||
else if (note.type === 'text') {
|
||||
if (note.hasChildren()) {
|
||||
return "jam jam-folder";
|
||||
@@ -57,6 +75,7 @@ function getIcon(note) {
|
||||
async function prepareNode(branch) {
|
||||
const note = await branch.getNote();
|
||||
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
const node = {
|
||||
noteId: note.noteId,
|
||||
@@ -65,9 +84,9 @@ async function prepareNode(branch) {
|
||||
isProtected: note.isProtected,
|
||||
title: utils.escapeHtml(title),
|
||||
extraClasses: await getExtraClasses(note),
|
||||
icon: getIcon(note),
|
||||
icon: await getIcon(note),
|
||||
refKey: note.noteId,
|
||||
expanded: note.type !== 'search' && branch.isExpanded
|
||||
expanded: (note.type !== 'search' && branch.isExpanded) || hoistedNoteId === note.noteId
|
||||
};
|
||||
|
||||
if (note.hasChildren() || note.type === 'search') {
|
||||
@@ -135,10 +154,6 @@ async function getExtraClasses(note) {
|
||||
|
||||
const extraClasses = [];
|
||||
|
||||
if (note.noteId === 'root') {
|
||||
extraClasses.push("tree-root");
|
||||
}
|
||||
|
||||
if (note.isProtected) {
|
||||
extraClasses.push("protected");
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import exportDialog from '../dialogs/export.js';
|
||||
import infoService from "./info.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import syncService from "./sync.js";
|
||||
import contextMenuService from "./context_menu.js";
|
||||
import hoistedNoteService from './hoisted_note.js';
|
||||
|
||||
const $tree = $("#tree");
|
||||
|
||||
@@ -83,6 +83,8 @@ const contextMenuItems = [
|
||||
{title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus"},
|
||||
{title: "Delete", cmd: "delete", uiIcon: "trash"},
|
||||
{title: "----"},
|
||||
{title: "Hoist note <kbd>CTRL-H</kbd>", cmd: "hoist", uiIcon: "arrow-up"},
|
||||
{title: "Unhoist note <kbd>CTRL-H</kbd>", cmd: "unhoist", uiIcon: "arrow-up"},
|
||||
{title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "pencil"},
|
||||
{title: "----"},
|
||||
{title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check"},
|
||||
@@ -101,6 +103,16 @@ const contextMenuItems = [
|
||||
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "arrows-v"}
|
||||
];
|
||||
|
||||
function hideItem(cmd, hidden) {
|
||||
const item = contextMenuItems.find(item => item.cmd === cmd);
|
||||
|
||||
if (!item) {
|
||||
throw new Error(`Command ${cmd} has not been found!`);
|
||||
}
|
||||
|
||||
item.hidden = hidden;
|
||||
}
|
||||
|
||||
function enableItem(cmd, enabled) {
|
||||
const item = contextMenuItems.find(item => item.cmd === cmd);
|
||||
|
||||
@@ -130,6 +142,11 @@ async function getContextMenuItems(event) {
|
||||
enableItem("export", note.type !== 'search');
|
||||
enableItem("editBranchPrefix", isNotRoot && parentNote.type !== 'search');
|
||||
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
hideItem("hoist", note.noteId === hoistedNoteId);
|
||||
hideItem("unhoist", note.noteId !== hoistedNoteId || !isNotRoot);
|
||||
|
||||
// Activate node on right-click
|
||||
node.setActive();
|
||||
|
||||
@@ -194,6 +211,12 @@ function selectContextMenuItem(event, cmd) {
|
||||
else if (cmd === "sortAlphabetically") {
|
||||
treeService.sortAlphabetically(node.data.noteId);
|
||||
}
|
||||
else if (cmd === "hoist") {
|
||||
hoistedNoteService.setHoistedNoteId(node.data.noteId);
|
||||
}
|
||||
else if (cmd === "unhoist") {
|
||||
hoistedNoteService.unhoist();
|
||||
}
|
||||
else {
|
||||
messagingService.logError("Unknown command: " + cmd);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import treeChangesService from "./branches.js";
|
||||
import contextMenuService from "./tree_context_menu.js";
|
||||
import treeService from "./tree.js";
|
||||
import editBranchPrefixDialog from "../dialogs/branch_prefix.js";
|
||||
import hoistedNoteService from "./hoisted_note.js";
|
||||
|
||||
const keyBindings = {
|
||||
"del": node => {
|
||||
@@ -113,6 +114,18 @@ const keyBindings = {
|
||||
node.getParent().setActive().then(treeService.clearSelectedNodes);
|
||||
}
|
||||
},
|
||||
"ctrl+h": node => {
|
||||
hoistedNoteService.getHoistedNoteId().then(hoistedNoteId => {
|
||||
if (node.data.noteId === hoistedNoteId) {
|
||||
hoistedNoteService.unhoist();
|
||||
}
|
||||
else {
|
||||
hoistedNoteService.setHoistedNoteId(node.data.noteId);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
},
|
||||
// code below shouldn't be necessary normally, however there's some problem with interaction with context menu plugin
|
||||
// after opening context menu, standard shortcuts don't work, but they are detected here
|
||||
// so we essentially takeover the standard handling with our implementation.
|
||||
|
||||
@@ -46,6 +46,10 @@ function isElectron() {
|
||||
return window && window.process && window.process.type;
|
||||
}
|
||||
|
||||
function isMac() {
|
||||
return navigator.platform.indexOf('Mac') > -1;
|
||||
}
|
||||
|
||||
function assertArguments() {
|
||||
for (const i in arguments) {
|
||||
if (!arguments[i]) {
|
||||
@@ -150,6 +154,7 @@ export default {
|
||||
formatDateTime,
|
||||
now,
|
||||
isElectron,
|
||||
isMac,
|
||||
assertArguments,
|
||||
isTopLevelNode,
|
||||
isRootNode,
|
||||
|
||||
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -107,7 +107,7 @@ body {
|
||||
(it kept growing with content) */
|
||||
#container:not(.distraction-free-mode) #note-detail-wrapper.protected {
|
||||
/* DON'T COLLAPSE THE RULES INTO SINGLE ONE, BACKGROUND WON'T DISPLAY */
|
||||
background: url('/images/shield.svg') no-repeat;
|
||||
background: url('../images/shield.svg') no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
}
|
||||
@@ -531,8 +531,8 @@ table.promoted-attributes-in-tooltip td, table.promoted-attributes-in-tooltip th
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.context-menu-container {
|
||||
font-size: small;
|
||||
#context-menu-container {
|
||||
padding: 3px 0 0;
|
||||
}
|
||||
|
||||
#context-menu-container .dropdown-item {
|
||||
@@ -698,4 +698,10 @@ div[data-notify="container"] {
|
||||
|
||||
#export-form .form-check-label {
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.unhoist-button {
|
||||
text-decoration: underline !important;
|
||||
color: blue !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
@@ -3,6 +3,8 @@
|
||||
const noteCacheService = require('../../services/note_cache');
|
||||
const repository = require('../../services/repository');
|
||||
const log = require('../../services/log');
|
||||
const utils = require('../../services/utils');
|
||||
const optionService = require('../../services/options');
|
||||
|
||||
async function getAutocomplete(req) {
|
||||
const query = req.query.query;
|
||||
@@ -16,7 +18,7 @@ async function getAutocomplete(req) {
|
||||
results = await getRecentNotes(currentNoteId);
|
||||
}
|
||||
else {
|
||||
results = noteCacheService.findNotes(query);
|
||||
results = await noteCacheService.findNotes(query);
|
||||
}
|
||||
|
||||
const msTaken = Date.now() - timestampStarted;
|
||||
@@ -29,6 +31,13 @@ async function getAutocomplete(req) {
|
||||
}
|
||||
|
||||
async function getRecentNotes(currentNoteId) {
|
||||
let extraCondition = '';
|
||||
|
||||
const hoistedNoteId = await optionService.getOption('hoistedNoteId');
|
||||
if (hoistedNoteId !== 'root') {
|
||||
extraCondition = `AND recent_notes.notePath LIKE '%${utils.sanitizeSql(hoistedNoteId)}%'`;
|
||||
}
|
||||
|
||||
const recentNotes = await repository.getEntities(`
|
||||
SELECT
|
||||
recent_notes.*
|
||||
@@ -39,6 +48,7 @@ async function getRecentNotes(currentNoteId) {
|
||||
recent_notes.isDeleted = 0
|
||||
AND branches.isDeleted = 0
|
||||
AND branches.noteId != ?
|
||||
${extraCondition}
|
||||
ORDER BY
|
||||
dateCreated DESC
|
||||
LIMIT 200`, [currentNoteId]);
|
||||
|
||||
@@ -29,7 +29,7 @@ async function loginSync(req) {
|
||||
const syncVersion = req.body.syncVersion;
|
||||
|
||||
if (syncVersion !== appInfo.syncVersion) {
|
||||
return [400, { message: 'Non-matching sync versions, local is version ' + appInfo.syncVersion }];
|
||||
return [400, { message: `Non-matching sync versions, local is version ${appInfo.syncVersion}, remote is ${syncVersion}` }];
|
||||
}
|
||||
|
||||
const documentSecret = await options.getOption('documentSecret');
|
||||
|
||||
@@ -6,7 +6,7 @@ const log = require('../../services/log');
|
||||
|
||||
// options allowed to be updated directly in options dialog
|
||||
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
|
||||
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent'];
|
||||
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent', 'hoistedNoteId'];
|
||||
|
||||
async function getOptions() {
|
||||
return await optionService.getOptionsMap(ALLOWED_OPTIONS);
|
||||
|
||||
@@ -20,7 +20,7 @@ async function searchNotes(req) {
|
||||
let searchTextResults = null;
|
||||
|
||||
if (searchText.trim().length > 0) {
|
||||
searchTextResults = noteCacheService.findNotes(searchText);
|
||||
searchTextResults = await noteCacheService.findNotes(searchText);
|
||||
|
||||
let fullTextNoteIds = await getFullTextResults(searchText);
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ async function getNotes(noteIds) {
|
||||
async function getRelations(noteIds) {
|
||||
// we need to fetch both parentNoteId and noteId matches because we can have loaded child
|
||||
// of which only some of the parents has been loaded.
|
||||
// also now with note hoisting, it is possible to have the note displayed without its parent chain being loaded
|
||||
|
||||
const relations = await sql.getManyRows(`SELECT branchId, noteId AS 'childNoteId', parentNoteId, notePosition FROM branches WHERE isDeleted = 0
|
||||
AND (parentNoteId IN (???) OR noteId IN (???))`, noteIds);
|
||||
@@ -50,18 +51,23 @@ async function getRelations(noteIds) {
|
||||
}
|
||||
|
||||
async function getTree() {
|
||||
const hoistedNoteId = await optionService.getOption('hoistedNoteId');
|
||||
|
||||
// we fetch all branches of notes, even if that particular branch isn't visible
|
||||
// this allows us to e.g. detect and properly display clones
|
||||
const branches = await sql.getRows(`
|
||||
WITH RECURSIVE
|
||||
tree(branchId, noteId, isExpanded) AS (
|
||||
SELECT branchId, noteId, isExpanded FROM branches WHERE branchId = 'root'
|
||||
SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?
|
||||
UNION ALL
|
||||
SELECT branches.branchId, branches.noteId, branches.isExpanded FROM branches
|
||||
JOIN tree ON branches.parentNoteId = tree.noteId
|
||||
WHERE tree.isExpanded = 1 AND branches.isDeleted = 0
|
||||
)
|
||||
SELECT branches.* FROM tree JOIN branches USING(noteId) WHERE branches.isDeleted = 0 ORDER BY branches.notePosition`);
|
||||
SELECT branches.* FROM tree JOIN branches USING(noteId) WHERE branches.isDeleted = 0 ORDER BY branches.notePosition`, [hoistedNoteId]);
|
||||
|
||||
// we also want root branch in there because all the paths start with root
|
||||
branches.push(await sql.getRow(`SELECT * FROM branches WHERE branchId = 'root'`));
|
||||
|
||||
const noteIds = Array.from(new Set(branches.map(b => b.noteId)));
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ function register(app) {
|
||||
apiRoute(GET, '/api/recent-changes', recentChangesApiRoute.getRecentChanges);
|
||||
|
||||
apiRoute(GET, '/api/options', optionsApiRoute.getOptions);
|
||||
apiRoute(PUT, '/api/options/:name/:value', optionsApiRoute.updateOption);
|
||||
apiRoute(PUT, '/api/options/:name/:value*', optionsApiRoute.updateOption);
|
||||
apiRoute(PUT, '/api/options', optionsApiRoute.updateOptions);
|
||||
|
||||
apiRoute(POST, '/api/password/change', passwordApiRoute.changePassword);
|
||||
@@ -169,7 +169,9 @@ function register(app) {
|
||||
|
||||
apiRoute(GET, '/api/event-log', eventLogRoute.getEventLog);
|
||||
|
||||
apiRoute(PUT, '/api/recent-notes/:branchId/:notePath', recentNotesRoute.addRecentNote);
|
||||
// * at the end means this will match params containing slash as well
|
||||
// this is a problem with nginx (and possibly other proxies) which translate escaped slash back to the literal slash character
|
||||
apiRoute(PUT, '/api/recent-notes/:branchId/:notePath*', recentNotesRoute.addRecentNote);
|
||||
apiRoute(GET, '/api/app-info', appInfoRoute.getAppInfo);
|
||||
|
||||
route(GET, '/api/setup/status', [], setupApiRoute.getStatus, apiResultHandler);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
|
||||
const APP_DB_VERSION = 120;
|
||||
const APP_DB_VERSION = 121;
|
||||
const SYNC_VERSION = 2;
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -1 +1 @@
|
||||
module.exports = { buildDate:"2018-12-02T00:30:07+01:00", buildRevision: "350cb52c07668f67c4d9c7700ceb9f91445a1acb" };
|
||||
module.exports = { buildDate:"2018-12-18T22:49:48+01:00", buildRevision: "f693dc31e82f1822b7393d12b53afdc0d06f5297" };
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
const optionService = require('./options');
|
||||
const sqlInit = require('./sql_init');
|
||||
const eventService = require('./events');
|
||||
|
||||
let hoistedNoteId = 'root';
|
||||
|
||||
eventService.subscribe(eventService.ENTITY_CHANGED, async ({entityName, entity}) => {
|
||||
if (entityName === 'options' && entity.name === 'hoistedNoteId') {
|
||||
hoistedNoteId = entity.value;
|
||||
}
|
||||
});
|
||||
|
||||
sqlInit.dbReady.then(async () => {
|
||||
hoistedNoteId = await optionService.getOption('hoistedNoteId');
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
getHoistedNoteId: () => hoistedNoteId
|
||||
};
|
||||
@@ -54,7 +54,11 @@ async function importEnex(file, parentNote) {
|
||||
function extractContent(enNote) {
|
||||
// [] thing is workaround for https://github.com/Leonidas-from-XIV/node-xml2js/issues/484
|
||||
let content = xmlBuilder.buildObject([enNote]);
|
||||
content = content.substr(3, content.length - 7).trim();
|
||||
|
||||
const endOfFirstTagIndex = content.indexOf('>');
|
||||
|
||||
// strip the <0> and </0> tags
|
||||
content = content.substr(endOfFirstTagIndex + 1, content.length - endOfFirstTagIndex - 5).trim();
|
||||
|
||||
// workaround for https://github.com/ckeditor/ckeditor5-list/issues/116
|
||||
content = content.replace(/<li>\s+<div>/g, "<li>");
|
||||
|
||||
+24
-11
@@ -4,6 +4,7 @@ const eventService = require('./events');
|
||||
const repository = require('./repository');
|
||||
const protectedSessionService = require('./protected_session');
|
||||
const utils = require('./utils');
|
||||
const hoistedNoteService = require('./hoisted_note');
|
||||
|
||||
let loaded = false;
|
||||
let noteTitles = {};
|
||||
@@ -63,7 +64,7 @@ function highlightResults(results, allTokens) {
|
||||
}
|
||||
}
|
||||
|
||||
function findNotes(query) {
|
||||
async function findNotes(query) {
|
||||
if (!noteTitles || !query.length) {
|
||||
return [];
|
||||
}
|
||||
@@ -72,7 +73,7 @@ function findNotes(query) {
|
||||
// filtering '/' because it's used as separator
|
||||
const allTokens = query.trim().toLowerCase().split(" ").filter(token => token !== '/');
|
||||
const tokens = allTokens.slice();
|
||||
const results = [];
|
||||
let results = [];
|
||||
|
||||
let noteIds = Object.keys(noteTitles);
|
||||
|
||||
@@ -120,6 +121,10 @@ function findNotes(query) {
|
||||
}
|
||||
}
|
||||
|
||||
if (hoistedNoteService.getHoistedNoteId() !== 'root') {
|
||||
results = results.filter(res => res.pathArray.includes(hoistedNoteService.getHoistedNoteId()));
|
||||
}
|
||||
|
||||
// sort results by depth of the note. This is based on the assumption that more important results
|
||||
// are closer to the note root.
|
||||
results.sort((a, b) => {
|
||||
@@ -214,21 +219,25 @@ function getNoteTitle(noteId, parentNoteId) {
|
||||
function getNoteTitleArrayForPath(path) {
|
||||
const titles = [];
|
||||
|
||||
if (path[0] === 'root') {
|
||||
if (path.length === 1) {
|
||||
return [ getNoteTitle('root') ];
|
||||
}
|
||||
else {
|
||||
path = path.slice(1);
|
||||
}
|
||||
if (path[0] === hoistedNoteService.getHoistedNoteId() && path.length === 1) {
|
||||
return [ getNoteTitle(hoistedNoteService.getHoistedNoteId()) ];
|
||||
}
|
||||
|
||||
let parentNoteId = 'root';
|
||||
let hoistedNotePassed = false;
|
||||
|
||||
for (const noteId of path) {
|
||||
const title = getNoteTitle(noteId, parentNoteId);
|
||||
// start collecting path segment titles only after hoisted note
|
||||
if (hoistedNotePassed) {
|
||||
const title = getNoteTitle(noteId, parentNoteId);
|
||||
|
||||
titles.push(title);
|
||||
}
|
||||
|
||||
if (noteId === hoistedNoteService.getHoistedNoteId()) {
|
||||
hoistedNotePassed = true;
|
||||
}
|
||||
|
||||
titles.push(title);
|
||||
parentNoteId = noteId;
|
||||
}
|
||||
|
||||
@@ -246,6 +255,10 @@ function getSomePath(noteId, path) {
|
||||
path.push(noteId);
|
||||
path.reverse();
|
||||
|
||||
if (!path.includes(hoistedNoteService.getHoistedNoteId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ async function initSyncedOptions(username, password) {
|
||||
|
||||
async function initNotSyncedOptions(initialized, startNotePath = 'root', syncServerHost = '', syncProxy = '') {
|
||||
await optionService.createOption('startNotePath', startNotePath, false);
|
||||
await optionService.createOption('hoistedNoteId', 'root', false);
|
||||
await optionService.createOption('lastDailyBackupDate', dateUtils.nowDate(), false);
|
||||
await optionService.createOption('lastWeeklyBackupDate', dateUtils.nowDate(), false);
|
||||
await optionService.createOption('lastMonthlyBackupDate', dateUtils.nowDate(), false);
|
||||
|
||||
@@ -94,9 +94,10 @@ async function updateEntity(entity) {
|
||||
|
||||
const primaryKey = entity[primaryKeyName];
|
||||
|
||||
if (entity.isChanged && (entityName !== 'options' || entity.isSynced)) {
|
||||
|
||||
await syncTableService.addEntitySync(entityName, primaryKey);
|
||||
if (entity.isChanged) {
|
||||
if (entityName !== 'options' || entity.isSynced) {
|
||||
await syncTableService.addEntitySync(entityName, primaryKey);
|
||||
}
|
||||
|
||||
if (!cls.isEntityEventsDisabled()) {
|
||||
const eventPayload = {
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
|
||||
const utils = require('./utils');
|
||||
const log = require('./log');
|
||||
const url = require('url');
|
||||
|
||||
// this service provides abstraction over node's HTTP/HTTPS and electron net.client APIs
|
||||
// this allows to support system proxy
|
||||
|
||||
function exec(opts) {
|
||||
const client = getClient(opts);
|
||||
const parsedTargetUrl = url.parse(opts.url);
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const headers = {
|
||||
Cookie: (opts.cookieJar && opts.cookieJar.header) || "",
|
||||
'Content-Type': 'application/json'
|
||||
};
|
||||
|
||||
if (opts.auth) {
|
||||
const token = new Buffer(opts.auth.user + ":" + opts.auth.pass).toString('base64');
|
||||
|
||||
headers['Authorization'] = `Basic ${token}`;
|
||||
}
|
||||
|
||||
let host = parsedTargetUrl.hostname;
|
||||
let protocol = parsedTargetUrl.protocol;
|
||||
let port = parsedTargetUrl.port;
|
||||
let path = parsedTargetUrl.path;
|
||||
|
||||
if (opts.proxy) {
|
||||
// see https://stackoverflow.com/questions/3862813/how-can-i-use-an-http-proxy-with-node-js-http-client
|
||||
const parsedProxyUrl = url.parse(opts.proxy);
|
||||
|
||||
protocol = parsedProxyUrl.protocol;
|
||||
host = parsedProxyUrl.hostname;
|
||||
port = parsedProxyUrl.port;
|
||||
path = opts.url;
|
||||
|
||||
headers['Host'] = parsedTargetUrl.host; // host also includes port
|
||||
}
|
||||
|
||||
const request = client.request({
|
||||
method: opts.method,
|
||||
// url is used by electron net module
|
||||
url: opts.url,
|
||||
// 4 fields below are used by http and https node modules
|
||||
protocol,
|
||||
host,
|
||||
port,
|
||||
path,
|
||||
timeout: opts.timeout,
|
||||
headers
|
||||
});
|
||||
|
||||
request.on('response', response => {
|
||||
if (![200, 201, 204].includes(response.statusCode)) {
|
||||
reject(generateError(opts, response.statusCode + ' ' + response.statusMessage));
|
||||
}
|
||||
|
||||
if (opts.cookieJar && response.headers['set-cookie']) {
|
||||
opts.cookieJar.header = response.headers['set-cookie'];
|
||||
}
|
||||
|
||||
let responseStr = '';
|
||||
|
||||
response.on('data', chunk => responseStr += chunk);
|
||||
|
||||
response.on('end', () => {
|
||||
try {
|
||||
const jsonObj = responseStr.trim() ? JSON.parse(responseStr) : null;
|
||||
|
||||
resolve(jsonObj);
|
||||
}
|
||||
catch (e) {
|
||||
log.error("Failed to deserialize sync response: " + responseStr);
|
||||
|
||||
reject(generateError(opts, e.message));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
request.end(opts.body ? JSON.stringify(opts.body) : undefined);
|
||||
}
|
||||
catch (e) {
|
||||
reject(generateError(opts, e.message));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getClient(opts) {
|
||||
// it's not clear how to explicitly configure proxy (as opposed to system proxy)
|
||||
// so in that case we always use node's modules
|
||||
if (utils.isElectron() && !opts.proxy) {
|
||||
return require('electron').net;
|
||||
}
|
||||
else {
|
||||
// in case there's explicit proxy then we need to use protocol of the proxy since we're actually
|
||||
// connecting to the proxy server and not to the end-target server
|
||||
const {protocol} = url.parse(opts.proxy || opts.url);
|
||||
|
||||
if (protocol === 'http:' || protocol === 'https:') {
|
||||
return require(protocol.substr(0, protocol.length - 1));
|
||||
}
|
||||
else {
|
||||
throw new Error(`Unrecognized protocol "${protocol}"`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generateError(opts, message) {
|
||||
return new Error(`Request to ${opts.method} ${opts.url} failed, error: ${message}`);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exec
|
||||
};
|
||||
+12
-25
@@ -1,10 +1,10 @@
|
||||
const rp = require('request-promise');
|
||||
const syncService = require('./sync');
|
||||
const log = require('./log');
|
||||
const sqlInit = require('./sql_init');
|
||||
const repository = require('./repository');
|
||||
const optionService = require('./options');
|
||||
const syncOptions = require('./sync_options');
|
||||
const request = require('./request');
|
||||
|
||||
async function hasSyncServerSchemaAndSeed() {
|
||||
const response = await requestToSyncServer('GET', '/api/setup/status');
|
||||
@@ -37,23 +37,13 @@ async function sendSeedToSyncServer() {
|
||||
}
|
||||
|
||||
async function requestToSyncServer(method, path, body = null) {
|
||||
const rpOpts = {
|
||||
uri: await syncOptions.getSyncServerHost() + path,
|
||||
method: method,
|
||||
json: true
|
||||
};
|
||||
|
||||
if (body) {
|
||||
rpOpts.body = body;
|
||||
}
|
||||
|
||||
const syncProxy = await syncOptions.getSyncProxy();
|
||||
|
||||
if (syncProxy) {
|
||||
rpOpts.proxy = syncProxy;
|
||||
}
|
||||
|
||||
return await rp(rpOpts);
|
||||
return await request.exec({
|
||||
method,
|
||||
url: await syncOptions.getSyncServerHost() + path,
|
||||
body,
|
||||
proxy: await syncOptions.getSyncProxy(),
|
||||
timeout: await syncOptions.getSyncTimeout()
|
||||
});
|
||||
}
|
||||
|
||||
async function setupSyncFromSyncServer(syncServerHost, syncProxy, username, password) {
|
||||
@@ -68,19 +58,16 @@ async function setupSyncFromSyncServer(syncServerHost, syncProxy, username, pass
|
||||
log.info("Getting document options from sync server.");
|
||||
|
||||
// response is expected to contain documentId and documentSecret options
|
||||
const options = await rp.get({
|
||||
uri: syncServerHost + '/api/setup/sync-seed',
|
||||
const options = await request.exec({
|
||||
method: 'get',
|
||||
url: syncServerHost + '/api/setup/sync-seed',
|
||||
auth: {
|
||||
'user': username,
|
||||
'pass': password
|
||||
},
|
||||
json: true
|
||||
proxy: syncProxy
|
||||
});
|
||||
|
||||
if (syncProxy) {
|
||||
options.proxy = syncProxy;
|
||||
}
|
||||
|
||||
await sqlInit.createDatabaseForSync(options, syncServerHost, syncProxy);
|
||||
|
||||
triggerSync();
|
||||
|
||||
+17
-27
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const url = require('url');
|
||||
const log = require('./log');
|
||||
const rp = require('request-promise');
|
||||
const sql = require('./sql');
|
||||
const sqlInit = require('./sql_init');
|
||||
const optionService = require('./options');
|
||||
@@ -14,6 +14,7 @@ const appInfo = require('./app_info');
|
||||
const syncOptions = require('./sync_options');
|
||||
const syncMutexService = require('./sync_mutex');
|
||||
const cls = require('./cls');
|
||||
const request = require('./request');
|
||||
|
||||
let proxyToggle = true;
|
||||
|
||||
@@ -49,7 +50,7 @@ async function sync() {
|
||||
catch (e) {
|
||||
proxyToggle = !proxyToggle;
|
||||
|
||||
if (e.message.indexOf('ECONNREFUSED') !== -1) {
|
||||
if (e.message && e.message.indexOf('ECONNREFUSED') !== -1) {
|
||||
log.info("No connection to sync server.");
|
||||
|
||||
return {
|
||||
@@ -84,7 +85,7 @@ async function doLogin() {
|
||||
const documentSecret = await optionService.getOption('documentSecret');
|
||||
const hash = utils.hmac(documentSecret, timestamp);
|
||||
|
||||
const syncContext = { cookieJar: rp.jar() };
|
||||
const syncContext = { cookieJar: {} };
|
||||
|
||||
const resp = await syncRequest(syncContext, 'POST', '/api/login/sync', {
|
||||
timestamp: timestamp,
|
||||
@@ -111,6 +112,10 @@ async function pullSync(syncContext) {
|
||||
const resp = await syncRequest(syncContext, 'GET', changesUri);
|
||||
stats.outstandingPulls = resp.maxSyncId - lastSyncedPull;
|
||||
|
||||
if (stats.outstandingPulls < 0) {
|
||||
stats.outstandingPulls = 0;
|
||||
}
|
||||
|
||||
const rows = resp.syncs;
|
||||
|
||||
if (rows.length === 0) {
|
||||
@@ -212,30 +217,15 @@ async function checkContentHash(syncContext) {
|
||||
await contentHashService.checkContentHashes(resp.hashes);
|
||||
}
|
||||
|
||||
async function syncRequest(syncContext, method, uri, body) {
|
||||
const fullUri = await syncOptions.getSyncServerHost() + uri;
|
||||
|
||||
try {
|
||||
const options = {
|
||||
method: method,
|
||||
uri: fullUri,
|
||||
jar: syncContext.cookieJar,
|
||||
json: true,
|
||||
body: body,
|
||||
timeout: await syncOptions.getSyncTimeout()
|
||||
};
|
||||
|
||||
const syncProxy = await syncOptions.getSyncProxy();
|
||||
|
||||
if (syncProxy && proxyToggle) {
|
||||
options.proxy = syncProxy;
|
||||
}
|
||||
|
||||
return await rp(options);
|
||||
}
|
||||
catch (e) {
|
||||
throw new Error(`Request to ${method} ${fullUri} failed, error: ${e.message}`);
|
||||
}
|
||||
async function syncRequest(syncContext, method, requestPath, body) {
|
||||
return await request.exec({
|
||||
method,
|
||||
url: await syncOptions.getSyncServerHost() + requestPath,
|
||||
cookieJar: syncContext.cookieJar,
|
||||
timeout: await syncOptions.getSyncTimeout(),
|
||||
body,
|
||||
proxy: proxyToggle ? await syncOptions.getSyncProxy() : null
|
||||
});
|
||||
}
|
||||
|
||||
const primaryKeys = {
|
||||
|
||||
@@ -17,6 +17,6 @@ async function get(name) {
|
||||
module.exports = {
|
||||
getSyncServerHost: async () => await get('syncServerHost'),
|
||||
isSyncSetup: async () => !!await get('syncServerHost'),
|
||||
getSyncTimeout: async () => await get('syncServerTimeout'),
|
||||
getSyncTimeout: async () => parseInt(await get('syncServerTimeout')),
|
||||
getSyncProxy: async () => await get('syncProxy')
|
||||
};
|
||||
@@ -147,6 +147,8 @@
|
||||
<div class="form-group">
|
||||
<label for="sync-proxy">Sync proxy server (optional)</label>
|
||||
<input class="form-control" id="sync-proxy" placeholder="https://<host>:<port>">
|
||||
|
||||
<p><strong>Note:</strong> If you leave proxy setting blank, system proxy will be used (applies to desktop/electron build only)</p>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: space-between;">
|
||||
|
||||
+14
-14
@@ -201,7 +201,7 @@
|
||||
<% include dialogs/confirm.ejs %>
|
||||
</div>
|
||||
|
||||
<webview class="electron-in-page-search-window" nodeintegration disablewebsecurity src="/libraries/electron-in-page-search/search-window.html"></webview>
|
||||
<webview class="electron-in-page-search-window" nodeintegration disablewebsecurity src="libraries/electron-in-page-search/search-window.html"></webview>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.baseApiUrl = 'api/';
|
||||
@@ -216,29 +216,29 @@
|
||||
<!-- Required for correct loading of scripts in Electron -->
|
||||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
|
||||
|
||||
<script src="/libraries/jquery.min.js"></script>
|
||||
<script src="libraries/jquery.min.js"></script>
|
||||
|
||||
<link href="/libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="/libraries/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<link href="libraries/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="libraries/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<script src="/libraries/bootstrap-notify.min.js"></script>
|
||||
<script src="libraries/bootstrap-notify.min.js"></script>
|
||||
|
||||
<!-- Include Fancytree skin and library -->
|
||||
<link href="/libraries/fancytree/skin-win8/ui.fancytree.css" rel="stylesheet">
|
||||
<script src="/libraries/fancytree/jquery.fancytree-all-deps.min.js"></script>
|
||||
<link href="libraries/fancytree/skin-win8/ui.fancytree.css" rel="stylesheet">
|
||||
<script src="libraries/fancytree/jquery.fancytree-all-deps.min.js"></script>
|
||||
|
||||
<script src="/libraries/jquery.hotkeys.js"></script>
|
||||
<script src="/libraries/jquery.fancytree.hotkeys.js"></script>
|
||||
<script src="libraries/jquery.hotkeys.js"></script>
|
||||
<script src="libraries/jquery.fancytree.hotkeys.js"></script>
|
||||
|
||||
<script src="/libraries/knockout.min.js"></script>
|
||||
<script src="libraries/knockout.min.js"></script>
|
||||
|
||||
<script src="/libraries/autocomplete.jquery.min.js"></script>
|
||||
<script src="libraries/autocomplete.jquery.min.js"></script>
|
||||
|
||||
<link href="/stylesheets/style.css" rel="stylesheet">
|
||||
<link href="stylesheets/style.css" rel="stylesheet">
|
||||
|
||||
<script src="/javascripts/services/bootstrap.js" crossorigin type="module"></script>
|
||||
<script src="javascripts/services/bootstrap.js" crossorigin type="module"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/libraries/jam/css/jam.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="libraries/jam/css/jam.min.css">
|
||||
|
||||
<script type="text/javascript">
|
||||
// we hide container initally because otherwise it is rendered first without CSS and then flickers into
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
<div class="form-group">
|
||||
<label for="sync-proxy">Proxy server (optional)</label>
|
||||
<input type="text" id="sync-proxy" class="form-control" data-bind="value: syncProxy" placeholder="https://<hostname>:<port>">
|
||||
|
||||
<p><strong>Note:</strong> If you leave proxy setting blank, system proxy will be used (applies to desktop/electron build only)</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="username">Username</label>
|
||||
|
||||
Reference in New Issue
Block a user