Compare commits

..
14 Commits
Author SHA1 Message Date
fyears f50ba953f1 fix ci
Release A New Version / build (20.x) (push) Failing after 50s
2024-09-29 10:19:48 +08:00
fyears 55cb26bd22 bump to 0.5.22 2024-09-29 10:11:01 +08:00
fyears 36d3018efb fix ignore logic 2024-09-29 10:09:49 +08:00
fyears 421b43d937 update complex logic for config 2024-09-29 09:32:04 +08:00
xvqinghong 17708359a1 fix bug of unwished synchronizing Config Directory and Bookmarks File (#816)
In the original code, if we set syncConfigDir to false, which means we don't want to sync this directory, but it will also cause `if( syncConfigDir && isInsideObsFolder(key, configDir) )` statement NEVER be entered.  Thus, in the original code, our setting is actually be ignored.
2024-09-29 09:20:27 +08:00
fyears 9acae1b077 fix google drive expire 2024-09-29 08:59:14 +08:00
fyears 4ade975585 warn in console about expire 2024-09-29 08:58:57 +08:00
fyears e988fc7ae9 update ci 2024-09-09 00:39:19 +08:00
fyears 5c56f2146f 0.5.21
Release A New Version / build (20.x) (push) Failing after 41s
2024-09-09 00:13:09 +08:00
fyears 74125f8dff fix config dir special empty condiction 2024-09-09 00:12:38 +08:00
fyears ace16de20c add complex logic to allow list and ignore list 2024-09-09 00:00:12 +08:00
fyears 716e5aa6c5 not detect chars in name for now 2024-09-07 15:26:19 +08:00
fyears fcdbb71493 add star and download history 2024-09-01 18:53:19 +08:00
fyears 0040e18b49 fix link 2024-09-01 18:30:31 +08:00
11 changed files with 457 additions and 113 deletions
+3 -3
View File
@@ -39,13 +39,13 @@ jobs:
steps:
- name: Checkout codes
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive
- name: Checkout LFS file list
run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
@@ -60,7 +60,7 @@ jobs:
- run: npm install
- run: npm test
- run: npm run build
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v4
with:
name: my-dist
path: |
+11 -41
View File
@@ -42,13 +42,13 @@ jobs:
steps:
- name: Checkout codes
uses: actions/checkout@v2
uses: actions/checkout@v4
with:
submodules: recursive
- name: Checkout LFS file list
run: git lfs ls-files --long | cut -d ' ' -f1 | sort > .lfs-assets-id
- name: LFS Cache
uses: actions/cache@v3
uses: actions/cache@v4
with:
path: .git/lfs/objects
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
@@ -63,44 +63,14 @@ jobs:
- run: npm install
- run: npm test
- run: npm run build
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ github.ref }}
- name: Create Release And Upload
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
files: |
main.js
manifest.json
styles.css
token: ${{ secrets.GITHUB_TOKEN }}
draft: false
prerelease: true
- name: Upload main.js
id: upload-main
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./main.js
asset_name: main.js
asset_content_type: text/javascript
- name: Upload manifest.json
id: upload-manifest
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./manifest.json
asset_name: manifest.json
asset_content_type: application/json
- name: Upload styles.css
id: upload-styles
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./styles.css
asset_name: styles.css
asset_content_type: text/css
prerelease: false
make_latest: true
+11 -1
View File
@@ -203,8 +203,18 @@ See [PRO](./docs/pro/README.md) for more details.
If you see any errors, please check the doc [here](./docs/how_to_debug/README.md) for more details.
Moreover, sometimes the program runs but slowly, you want to check the performance by [enabling the profiler](./docs/check_performance/README).
Moreover, sometimes the program runs but slowly, you want to check the performance by [enabling the profiler](./docs/check_performance/README.md).
## Bonus: Import And Export Not-Oauth2 Plugin Settings By QR Code
See [here](./docs/import_export_some_settings.md) for more details.
## Download History
Download history can be viewed on the unofficial [Obsidian Stats](https://www.moritzjung.dev/obsidian-stats/plugins/remotely-save/#downloads) (NOT affiliated with official Obsidian and GitHub and Remotely Save).
## Star History
(NOT affiliated with official Obsidian and GitHub and Remotely Save.)
[![Star History Chart](https://api.star-history.com/svg?repos=remotely-save/remotely-save&type=Date)](https://star-history.com/#remotely-save/remotely-save&Date)
+9
View File
@@ -209,3 +209,12 @@ PRO(付费)功能“智能冲突”为用户提供了另一个选项:合
详见[这里](./docs/import_export_some_settings.md)了解更多详情。
## 下载历史
下载历史可以从非官方的 [Obsidian Stats](https://www.moritzjung.dev/obsidian-stats/plugins/remotely-save/#downloads) 查阅。(和官方 ObsidianGitHubRemotely Save 均无利益关系。)
## 星星历史
(和官方 ObsidianGitHubRemotely Save 均无利益关系。)
[![星星历史 曲线图](https://api.star-history.com/svg?repos=remotely-save/remotely-save&type=Date)](https://star-history.com/#remotely-save/remotely-save&Date)
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "remotely-save",
"name": "Remotely Save",
"version": "0.5.20",
"version": "0.5.22",
"minAppVersion": "0.13.21",
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"id": "remotely-save",
"name": "Remotely Save",
"version": "0.5.20",
"version": "0.5.22",
"minAppVersion": "0.13.21",
"description": "Yet another unofficial plugin allowing users to synchronize notes between local device and the cloud service.",
"author": "fyears",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "remotely-save",
"version": "0.5.20",
"version": "0.5.22",
"description": "This is yet another sync plugin for Obsidian app.",
"scripts": {
"dev2": "node esbuild.config.mjs --watch",
+4
View File
@@ -95,6 +95,10 @@ class GoogleDriveAuthModal extends Modal {
k.expires_in * 1000;
this.plugin.settings.googledrive.accessTokenExpiresAtTimeMs =
ts + k.expires_in * 1000 - 60 * 2 * 1000;
// manually set it expired after 60 days;
this.plugin.settings.googledrive.credentialsShouldBeDeletedAtTimeMs =
Date.now() + 1000 * 60 * 60 * 24 * 59;
await this.plugin.saveSettings();
// try to remove data in clipboard
+254 -65
View File
@@ -29,6 +29,7 @@ import {
import {
atWhichLevel,
checkValidName,
getFolderLevels,
getParentFolder,
isHiddenPath,
isSpecialFolderNameToSkip,
@@ -139,7 +140,14 @@ const isBookmarksFile = (x: string, configDir: string) => {
);
};
const isSkipItemByName = (
interface IsSkipResult {
enableAllowMode: boolean;
isExplictlyAllowed: boolean;
isExplictlyIgnored: boolean;
finalIsIgnored: boolean;
}
export const checkIsSkipItemOrNotByName = (
key: string,
syncConfigDir: boolean,
syncBookmarks: boolean,
@@ -147,13 +155,15 @@ const isSkipItemByName = (
configDir: string,
ignorePaths: string[],
onlyAllowPaths: string[]
) => {
): IsSkipResult => {
if (key === undefined) {
throw Error(`isSkipItemByName meets undefinded key!`);
throw Error(`checkIsSkipItemOrNotByName meets undefinded key!`);
}
let finalIsIgnored: boolean | undefined = undefined;
let enableAllowMode = false;
let isInAllowList = false;
let isExplictlyAllowed = false;
if (onlyAllowPaths !== undefined && onlyAllowPaths.length > 0) {
for (const r of onlyAllowPaths) {
if (r.trim() === "") {
@@ -163,7 +173,7 @@ const isSkipItemByName = (
enableAllowMode = true; // we really want to check the allow list
if (XRegExp(r, "A").test(key)) {
isInAllowList = true;
isExplictlyAllowed = true;
}
}
}
@@ -173,10 +183,11 @@ const isSkipItemByName = (
// and is deferred to next checking steps
// if the key doesn't meet the allow list,
// it must be skippable.
if (enableAllowMode && !isInAllowList) {
return true; // must be skippable
if (enableAllowMode && !isExplictlyAllowed) {
finalIsIgnored = true; // must be skippable
}
let isExplictlyIgnored = false;
if (ignorePaths !== undefined && ignorePaths.length > 0) {
for (const r of ignorePaths) {
if (r.trim() === "") {
@@ -184,29 +195,141 @@ const isSkipItemByName = (
continue;
}
if (XRegExp(r, "A").test(key)) {
return true;
if (finalIsIgnored === undefined) {
isExplictlyIgnored = true;
finalIsIgnored = true;
}
}
}
}
if (syncConfigDir && isInsideObsFolder(key, configDir)) {
return false;
// we have to check bookmarks.json firstly then check the whole .obsidian folder later
if (isBookmarksFile(key, configDir) && finalIsIgnored === undefined) {
if (syncBookmarks) {
finalIsIgnored = false;
} else {
finalIsIgnored = true;
}
}
if (syncBookmarks && isBookmarksFile(key, configDir)) {
return false;
// we have to check bookmarks.json firstly then check the whole .obsidian folder later
if (isInsideObsFolder(key, configDir) && finalIsIgnored === undefined) {
if (syncConfigDir) {
finalIsIgnored = false;
} else {
finalIsIgnored = true;
}
}
if (isSpecialFolderNameToSkip(key, [])) {
// some special dirs and files are always skipped
return true;
if (finalIsIgnored === undefined) {
isExplictlyIgnored = true;
finalIsIgnored = true;
}
}
return (
const checkIsHidden =
isHiddenPath(key, true, false) ||
(!syncUnderscoreItems && isHiddenPath(key, false, true)) ||
key === "/" ||
key === DEFAULT_FILE_NAME_FOR_METADATAONREMOTE ||
key === DEFAULT_FILE_NAME_FOR_METADATAONREMOTE2
);
key === DEFAULT_FILE_NAME_FOR_METADATAONREMOTE2;
if (finalIsIgnored === undefined) {
isExplictlyIgnored = checkIsHidden;
finalIsIgnored = checkIsHidden;
}
if (finalIsIgnored === undefined) {
throw Error(`no finalIsIgnored in checkIsSkipItemOrNotByName for ${key}`);
}
return {
enableAllowMode: enableAllowMode,
isExplictlyAllowed: isExplictlyAllowed,
isExplictlyIgnored: isExplictlyIgnored,
finalIsIgnored: finalIsIgnored,
};
};
/**
* | finalIgnored | reason | explictlyIgnored | allowMode | explictlyAllowed |
* | ------------------------------- | -------------------------------------------- | ---------------- | --------- | ---------------- |
* | no | nothing blocking | no | no | no |
* | yes, MAY be changed by children | allow mode not allowed, inexplicitly ignored | no | yes | no |
* | no, MAY apply to parents | allow mode allowed | no | yes | yes |
* | yes, also apply to children | explictly ignored | yes | no | no |
* | yes, also apply to children | explictly ignored | yes | yes | no |
* | yes, also apply to children | explictly ignored | yes | yes | yes |
*/
export const getSkipItemsByList = (
skipOrNotResults: Record<string, IsSkipResult>
): string[] => {
const allPotentialKeys = Object.keys(skipOrNotResults);
// from short(shadow) to long(deep) , ascending
const sortedKeys = allPotentialKeys.sort((k1, k2) => k1.length - k2.length);
// we deal with explicty ignored list firstly, apply them to children
const explictlyIgnoredSet = new Set<string>();
for (const key of sortedKeys) {
if (skipOrNotResults[key].isExplictlyIgnored) {
skipOrNotResults[key].finalIsIgnored = true;
explictlyIgnoredSet.add(key);
} else {
const parents = getFolderLevels(key, true).reverse();
for (const key2 of parents) {
if (explictlyIgnoredSet.has(key2)) {
skipOrNotResults[key].isExplictlyIgnored = true;
skipOrNotResults[key].finalIsIgnored = true;
explictlyIgnoredSet.add(key);
break;
}
}
}
}
// we deal with explictly allow list secondly, apply them to PARENTS if possible
const enableAllowMode = skipOrNotResults[allPotentialKeys[0]].enableAllowMode;
if (enableAllowMode) {
for (let index = 0; index < sortedKeys.length; index++) {
// reverse order, long(deep) to short(shadow), ascending
const key = sortedKeys[sortedKeys.length - index - 1];
if (
!skipOrNotResults[key].isExplictlyIgnored &&
skipOrNotResults[key].isExplictlyAllowed
) {
// the file is explictly allowed, and not explictly ignored by anywhere
// we allow all its parents!
const parents = getFolderLevels(key, true).reverse();
for (const key2 of parents) {
if (
key2 in skipOrNotResults &&
!skipOrNotResults[key2].isExplictlyIgnored &&
!explictlyIgnoredSet.has(key2)
) {
skipOrNotResults[key2].isExplictlyAllowed = true;
skipOrNotResults[key2].finalIsIgnored = false; // from ignored to allowed
} else {
throw Error(
`${key}'s parent ${key2} in abnormal state: ${JSON.stringify(skipOrNotResults[key2])}`
);
}
}
}
}
}
// get all finalIsIgnored
const result: string[] = [];
for (const key of sortedKeys) {
if (skipOrNotResults[key].finalIsIgnored) {
result.push(key);
}
}
console.debug(`finalIsIgnored list= ${JSON.stringify(result)}`);
return result;
};
export type SyncPlanType = Record<string, MixedEntity>;
@@ -235,33 +358,39 @@ const ensembleMixedEnties = async (
const finalMappings: SyncPlanType = {};
const skipOrNotResults: Record<string, IsSkipResult> = {};
// remote has to be first
let remoteMaySkipCountAndNotConfig = 0;
for (const remote of remoteEntityList) {
const remoteCopied = ensureMTimeOfRemoteEntityValid(
copyEntityAndFixTimeFormat(remote, serviceType)
);
const key = remoteCopied.key!;
if (
isSkipItemByName(
key,
syncConfigDir,
syncBookmarks,
syncUnderscoreItems,
configDir,
ignorePaths,
onlyAllowPaths
)
) {
continue;
const skipOrNot = checkIsSkipItemOrNotByName(
key,
syncConfigDir,
syncBookmarks,
syncUnderscoreItems,
configDir,
ignorePaths,
onlyAllowPaths
);
skipOrNotResults[key] = skipOrNot;
if (skipOrNot.finalIsIgnored && !key.startsWith(configDir)) {
remoteMaySkipCountAndNotConfig += 1;
}
const checkValidNameResult = checkValidName(key);
if (!checkValidNameResult.result) {
throw Error(
`your remote folder/file name is invalid: ${checkValidNameResult.reason}`
);
}
// 20240907: users (not on windows) doesn't like it. revert back now.
// TODO: platform specific but not introducing obsidian dependency into sync.ts
// const checkValidNameResult = checkValidName(key);
// if (!checkValidNameResult.result) {
// throw Error(
// `your remote folder/file name is invalid: ${checkValidNameResult.reason}`
// );
// }
finalMappings[key] = {
key: key,
@@ -272,19 +401,25 @@ const ensembleMixedEnties = async (
profiler?.insert("ensembleMixedEnties: finish remote");
profiler?.insertSize("sizeof finalMappings", finalMappings);
if (Object.keys(finalMappings).length === 0 || localEntityList.length === 0) {
if (
Object.keys(finalMappings).filter((k) => !k.startsWith(configDir)).length -
remoteMaySkipCountAndNotConfig ===
0 ||
localEntityList.filter((e) => !e.key?.startsWith(configDir)).length === 0
) {
// Special checking:
// if one side is totally empty,
// usually that's a hard rest.
// So we need to ignore everything of prevSyncEntityList to avoid deletions!
// TODO: acutally erase everything of prevSyncEntityList?
// TODO: local should also go through a isSkipItemByName checking beforehand
// TODO: local should also go through a checkIsSkipItemOrNotByName checking beforehand
} else {
// normally go through the prevSyncEntityList
for (const prevSync of prevSyncEntityList) {
const key = prevSync.key!;
if (
isSkipItemByName(
if (!(key in skipOrNotResults)) {
const skipOrNot = checkIsSkipItemOrNotByName(
key,
syncConfigDir,
syncBookmarks,
@@ -292,9 +427,8 @@ const ensembleMixedEnties = async (
configDir,
ignorePaths,
onlyAllowPaths
)
) {
continue;
);
skipOrNotResults[key] = skipOrNot;
}
// TODO: abstraction leaking?
@@ -320,8 +454,9 @@ const ensembleMixedEnties = async (
// (we don't consume prevSync here because it gains no benefit)
for (const local of localEntityList) {
const key = local.key!;
if (
isSkipItemByName(
if (!(key in skipOrNotResults)) {
const skipOrNot = checkIsSkipItemOrNotByName(
key,
syncConfigDir,
syncBookmarks,
@@ -329,17 +464,18 @@ const ensembleMixedEnties = async (
configDir,
ignorePaths,
onlyAllowPaths
)
) {
continue;
);
skipOrNotResults[key] = skipOrNot;
}
const checkValidNameResult = checkValidName(key);
if (!checkValidNameResult.result) {
throw Error(
`your local folder/file name is invalid: ${checkValidNameResult.reason}`
);
}
// 20240907: users (not on windows) doesn't like it. revert back now.
// TODO: platform specific but not introducing obsidian dependency into sync.ts
// const checkValidNameResult = checkValidName(key);
// if (!checkValidNameResult.result) {
// throw Error(
// `your local folder/file name is invalid: ${checkValidNameResult.reason}`
// );
// }
// TODO: abstraction leaking?
const localCopied = await fsEncrypt.encryptEntity(
@@ -358,6 +494,15 @@ const ensembleMixedEnties = async (
profiler?.insert("ensembleMixedEnties: finish local");
profiler?.insertSize("sizeof finalMappings", finalMappings);
// we check the skipOrNotResults again! in case we adjust some paths!
const allReallySkipKeys = getSkipItemsByList(skipOrNotResults);
for (const key of allReallySkipKeys) {
delete finalMappings[key];
}
profiler?.insert("ensembleMixedEnties: finish parsing all skip items");
profiler?.insertSize("sizeof finalMappings", finalMappings);
// console.debug("in the end of ensembleMixedEnties, finalMappings is:");
// console.debug(finalMappings);
@@ -383,7 +528,7 @@ const getSyncPlanInplace = async (
) => {
profiler?.addIndent();
profiler?.insert("getSyncPlanInplace: enter");
// from long(deep) to short(shadow)
// from long(deep) to short(shadow), descending
const sortedKeys = Object.keys(mixedEntityMappings).sort(
(k1, k2) => k2.length - k1.length
);
@@ -476,14 +621,36 @@ const getSyncPlanInplace = async (
mixedEntry.change = false;
keptFolder.add(getParentFolder(key));
} else if (syncDirection === "incremental_pull_and_delete_only") {
mixedEntry.decisionBranch = 135;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
if (
key === `${configDir}/` ||
key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian folder!
mixedEntry.decisionBranch = 137;
mixedEntry.decision = "folder_existed_both_then_do_nothing";
mixedEntry.change = false;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 135;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
}
} else {
// bidirectional
mixedEntry.decisionBranch = 124;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
if (
key === `${configDir}/` ||
key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian folder!
mixedEntry.decisionBranch = 138;
mixedEntry.decision = "folder_existed_both_then_do_nothing";
mixedEntry.change = false;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 124;
mixedEntry.decision = "folder_to_be_deleted_on_local";
mixedEntry.change = true;
}
}
} else {
// then the folder is created on local
@@ -925,13 +1092,35 @@ const getSyncPlanInplace = async (
mixedEntry.decision = "conflict_created_then_do_nothing";
mixedEntry.change = false;
} else if (syncDirection === "incremental_pull_and_delete_only") {
mixedEntry.decisionBranch = 39;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
if (
key === `${configDir}/` ||
key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian/bookmarks.json file!
mixedEntry.decisionBranch = 139;
mixedEntry.decision = "conflict_created_then_keep_local";
mixedEntry.change = true;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 39;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
}
} else {
mixedEntry.decisionBranch = 7;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
if (
key === `${configDir}/` ||
key === `${configDir}/bookmarks.json`
) {
// special: never delete .obsidian/bookmarks.json file!
mixedEntry.decisionBranch = 140;
mixedEntry.decision = "conflict_created_then_keep_local";
mixedEntry.change = true;
keptFolder.add(getParentFolder(key));
} else {
mixedEntry.decisionBranch = 7;
mixedEntry.decision = "remote_is_deleted_thus_also_delete_local";
mixedEntry.change = true;
}
}
} else {
// if A is in the previous list and MODIFIED, A has been deleted by B but modified by A
+154
View File
@@ -0,0 +1,154 @@
import { strict as assert } from "assert";
import { checkIsSkipItemOrNotByName } from "../src/sync";
describe("Sync: checkIsSkipItemOrNotByName", () => {
it("should be ok everywhere for empty config", async () => {
let isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ [],
/* onlyAllowPaths */ []
).finalIsIgnored;
assert.ok(!isSkip);
isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ [""],
/* onlyAllowPaths */ ["", "\n"]
).finalIsIgnored;
assert.ok(!isSkip);
});
it("should be ok for deny list", async () => {
let isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ ["xxx"],
/* onlyAllowPaths */ []
).finalIsIgnored;
assert.ok(isSkip);
isSkip = checkIsSkipItemOrNotByName(
"yyy.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ ["xxx"],
/* onlyAllowPaths */ []
).finalIsIgnored;
assert.ok(!isSkip);
isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ ["xxx$"],
/* onlyAllowPaths */ []
).finalIsIgnored;
assert.ok(!isSkip);
// if we deny a folder, we have to deny all the sub files
// TODO: it's soooo hard to do the path resolution in this func with regex,
// so we defer the detection to later steps now.
// the test here doesn't work.
// isSkip = checkIsSkipItemOrNotByName(
// 'xxx/yyy.md',
// false,
// false,
// false,
// '.obsidian',
// /* ignorePaths */ ['xxx/$'],
// /* onlyAllowPaths */ []
// ).finalIsIgnored;
// assert.ok(isSkip);
});
it("should be ok for allow list", async () => {
let isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ [],
/* onlyAllowPaths */ ["xxx"]
).finalIsIgnored;
assert.ok(!isSkip);
isSkip = checkIsSkipItemOrNotByName(
"yyy.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ [""],
/* onlyAllowPaths */ ["xxx"]
).finalIsIgnored;
assert.ok(isSkip);
isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ [],
/* onlyAllowPaths */ ["xxx$"]
).finalIsIgnored;
assert.ok(isSkip);
// should NOT skip because we allow the sub file AND not deny the folder
// TODO: it's soooo hard to do the path resolution in this func with regex,
// so we defer the detection to later steps now.
// the test here doesn't work.
// isSkip = checkIsSkipItemOrNotByName(
// 'xxx/',
// false,
// false,
// false,
// '.obsidian',
// /* ignorePaths */ [],
// /* onlyAllowPaths */ ['xxx/yyy.md']
// ).finalIsIgnored;
// assert.ok(!isSkip);
});
it("should detect the name by two lists together", async () => {
// should skip because we ignore the path
let isSkip = checkIsSkipItemOrNotByName(
"xxx.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ ["xxx"],
/* onlyAllowPaths */ ["yyy"]
).finalIsIgnored;
assert.ok(isSkip);
// should skip because we disallow the whole folder
isSkip = checkIsSkipItemOrNotByName(
"xxx/yyy.md",
false,
false,
false,
".obsidian",
/* ignorePaths */ ["xxx"],
/* onlyAllowPaths */ ["xxx/yyy.md"]
).finalIsIgnored;
assert.ok(isSkip);
});
});
+8
View File
@@ -1560,6 +1560,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.dropbox.refreshToken !== "" &&
current >= this.settings!.dropbox!.credentialsShouldBeDeletedAtTime!
) {
console.warn(`dropbox expired`);
dropboxExpired = true;
this.settings.dropbox = cloneDeep(DEFAULT_DROPBOX_CONFIG);
needSave = true;
@@ -1570,6 +1571,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.onedrive.refreshToken !== "" &&
current >= this.settings!.onedrive!.credentialsShouldBeDeletedAtTime!
) {
console.warn(`onedrive expired`);
onedriveExpired = true;
this.settings.onedrive = cloneDeep(DEFAULT_ONEDRIVE_CONFIG);
needSave = true;
@@ -1580,6 +1582,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.onedrivefull.refreshToken !== "" &&
current >= this.settings!.onedrivefull!.credentialsShouldBeDeletedAtTime!
) {
console.warn(`onedrive full expired`);
onedriveFullExpired = true;
this.settings.onedrivefull = cloneDeep(DEFAULT_ONEDRIVEFULL_CONFIG);
needSave = true;
@@ -1590,6 +1593,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.googledrive.refreshToken !== "" &&
current >= this.settings!.googledrive!.credentialsShouldBeDeletedAtTimeMs!
) {
console.warn(`google drive expired`);
googleDriveExpired = true;
this.settings.googledrive = cloneDeep(DEFAULT_GOOGLEDRIVE_CONFIG);
needSave = true;
@@ -1600,6 +1604,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.box.refreshToken !== "" &&
current >= this.settings!.box!.credentialsShouldBeDeletedAtTimeMs!
) {
console.warn(`box expired`);
boxExpired = true;
this.settings.box = cloneDeep(DEFAULT_BOX_CONFIG);
needSave = true;
@@ -1610,6 +1615,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.pcloud.accessToken !== "" &&
current >= this.settings!.pcloud!.credentialsShouldBeDeletedAtTimeMs!
) {
console.warn(`pcloud expired`);
pCloudExpired = true;
this.settings.pcloud = cloneDeep(DEFAULT_PCLOUD_CONFIG);
needSave = true;
@@ -1620,6 +1626,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.yandexdisk.refreshToken !== "" &&
current >= this.settings!.yandexdisk!.credentialsShouldBeDeletedAtTimeMs!
) {
console.warn(`yandex disk expired`);
yandexDiskExpired = true;
this.settings.yandexdisk = cloneDeep(DEFAULT_YANDEXDISK_CONFIG);
needSave = true;
@@ -1630,6 +1637,7 @@ export default class RemotelySavePlugin extends Plugin {
this.settings.koofr.refreshToken !== "" &&
current >= this.settings!.koofr!.credentialsShouldBeDeletedAtTimeMs!
) {
console.warn(`koofr expired`);
koofrExpired = true;
this.settings.koofr = cloneDeep(DEFAULT_KOOFR_CONFIG);
needSave = true;