mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-21 09:38:01 +02:00
This commit is contained in:
parent
5a44f62fb6
commit
02a0d0d0cc
@ -150,6 +150,8 @@ packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
packages/app-desktop/commands/editProfileConfig.js
|
||||
packages/app-desktop/commands/emptyTrash.js
|
||||
packages/app-desktop/commands/exportDeletionLog.test.js
|
||||
packages/app-desktop/commands/exportDeletionLog.js
|
||||
packages/app-desktop/commands/exportFolders.js
|
||||
packages/app-desktop/commands/exportNotes.js
|
||||
packages/app-desktop/commands/focusElement.js
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -127,6 +127,8 @@ packages/app-desktop/checkForUpdates.js
|
||||
packages/app-desktop/commands/copyDevCommand.js
|
||||
packages/app-desktop/commands/editProfileConfig.js
|
||||
packages/app-desktop/commands/emptyTrash.js
|
||||
packages/app-desktop/commands/exportDeletionLog.test.js
|
||||
packages/app-desktop/commands/exportDeletionLog.js
|
||||
packages/app-desktop/commands/exportFolders.js
|
||||
packages/app-desktop/commands/exportNotes.js
|
||||
packages/app-desktop/commands/focusElement.js
|
||||
|
102
packages/app-desktop/commands/exportDeletionLog.test.ts
Normal file
102
packages/app-desktop/commands/exportDeletionLog.test.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import shim from '@joplin/lib/shim';
|
||||
import * as exportDeletionLog from './exportDeletionLog';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import { AppState, createAppDefaultState } from '../app.reducer';
|
||||
|
||||
jest.mock('../services/bridge', () => ({
|
||||
__esModule: true,
|
||||
default: () => ({
|
||||
openItem: jest.fn,
|
||||
}),
|
||||
}));
|
||||
|
||||
const logContentWithDeleteAction = `
|
||||
2024-09-17 18:34:28: Running migration: 20
|
||||
2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 1
|
||||
2024-09-17 18:34:28: Running migration: 27
|
||||
2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 2
|
||||
2024-09-17 18:34:28: Running migration: 33
|
||||
2024-09-17 18:34:28: SearchEngine: Updating FTS table...
|
||||
2024-09-17 18:34:28: Updating items_normalized from {"updated_time":0,"id":""}
|
||||
2024-09-17 18:34:28: SearchEngine: Updated FTS table in 1ms. Inserted: 0. Deleted: 0
|
||||
2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 3
|
||||
2024-09-17 18:34:29: Running migration: 35
|
||||
2024-09-17 18:34:29: SearchEngine: Updating FTS table...
|
||||
2024-09-17 18:34:29: Updating items_normalized from {"updated_time":0,"id":""}
|
||||
2024-09-17 18:34:29: SearchEngine: Updated FTS table in 1ms. Inserted: 0. Deleted: 0
|
||||
2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 4
|
||||
2024-09-17 18:34:29: Running migration: 42
|
||||
2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 5
|
||||
2024-09-17 18:34:29: App: "syncInfoCache" was changed - setting up encryption related code
|
||||
`;
|
||||
|
||||
const createFakeLogFile = async (filename: string, content: string) => {
|
||||
await shim.fsDriver().writeFile(`${Setting.value('profileDir')}/${filename}`, content, 'utf8');
|
||||
};
|
||||
|
||||
describe('exportDeletionLog', () => {
|
||||
let state: AppState = undefined;
|
||||
|
||||
beforeAll(() => {
|
||||
state = createAppDefaultState({}, {});
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(new Date('2024-09-18T12:00:00Z').getTime());
|
||||
});
|
||||
|
||||
it('should get all deletion lines from the log file', async () => {
|
||||
await createFakeLogFile('log.txt', logContentWithDeleteAction);
|
||||
|
||||
await exportDeletionLog.runtime().execute({ state, dispatch: () => {} });
|
||||
const result = await shim.fsDriver().readFile(`${Setting.value('profileDir')}/deletion_log_20240918.txt`, 'utf-8');
|
||||
expect(result).toBe(
|
||||
`2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 1
|
||||
2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 2
|
||||
2024-09-17 18:34:28: DeleteAction: MigrationService: ; Item IDs: 3
|
||||
2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 4
|
||||
2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 5
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return a empty file if there is not deletion lines', async () => {
|
||||
await createFakeLogFile('log.txt', '');
|
||||
await exportDeletionLog.runtime().execute({ state, dispatch: () => {} });
|
||||
const result = await shim.fsDriver().readFile(`${Setting.value('profileDir')}/deletion_log_20240918.txt`, 'utf-8');
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should ignore logs from log files that are not recent', async () => {
|
||||
const before = new Date('2024-09-16').getTime();
|
||||
const after = new Date('2024-09-17').getTime();
|
||||
await createFakeLogFile(`log-${before}.txt`, logContentWithDeleteAction);
|
||||
await createFakeLogFile(`log-${after}.txt`, '');
|
||||
await createFakeLogFile('log.txt', '');
|
||||
|
||||
await exportDeletionLog.runtime().execute({ state, dispatch: () => {} });
|
||||
const result = await shim.fsDriver().readFile(`${Setting.value('profileDir')}/deletion_log_20240918.txt`, 'utf-8');
|
||||
expect(result).toBe('');
|
||||
});
|
||||
|
||||
it('should contain the content from the recent log files', async () => {
|
||||
const rotateLog = new Date('2024-09-17').getTime();
|
||||
await createFakeLogFile(`log-${rotateLog}.txt`, '2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 4');
|
||||
await createFakeLogFile('log.txt', '2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 5');
|
||||
|
||||
await exportDeletionLog.runtime().execute({ state, dispatch: () => {} });
|
||||
const result = await shim.fsDriver().readFile(`${Setting.value('profileDir')}/deletion_log_20240918.txt`, 'utf-8');
|
||||
expect(result).toBe(
|
||||
`2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 4
|
||||
2024-09-17 18:34:29: DeleteAction: MigrationService: ; Item IDs: 5
|
||||
`);
|
||||
});
|
||||
|
||||
it('should contain the date in the filename', async () => {
|
||||
await shim.fsDriver().remove(`${Setting.value('profileDir')}/deletion_log_20230111.txt`);
|
||||
jest.setSystemTime(new Date('2023-01-11T12:00:00Z').getTime());
|
||||
await createFakeLogFile('log.txt', '');
|
||||
|
||||
await exportDeletionLog.runtime().execute({ state, dispatch: () => {} });
|
||||
const exists = await shim.fsDriver().exists(`${Setting.value('profileDir')}/deletion_log_20230111.txt`);
|
||||
expect(exists).toBe(true);
|
||||
});
|
||||
|
||||
});
|
52
packages/app-desktop/commands/exportDeletionLog.ts
Normal file
52
packages/app-desktop/commands/exportDeletionLog.ts
Normal file
@ -0,0 +1,52 @@
|
||||
|
||||
import { CommandRuntime, CommandDeclaration } from '@joplin/lib/services/CommandService';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import bridge from '../services/bridge';
|
||||
import { formatMsToLocal } from '@joplin/utils/time';
|
||||
|
||||
export const declaration: CommandDeclaration = {
|
||||
name: 'exportDeletionLog',
|
||||
label: () => _('Export deletion log'),
|
||||
};
|
||||
|
||||
const getDeletionLines = async (filePath: string) => {
|
||||
const logFile: string = await shim.fsDriver().readFile(`${Setting.value('profileDir')}/${filePath}`);
|
||||
|
||||
const deletionLines = logFile
|
||||
.split('\n')
|
||||
.filter(line => line.includes('DeleteAction'));
|
||||
|
||||
if (!deletionLines.length) return '';
|
||||
|
||||
return `${deletionLines.join('\n')}\n`;
|
||||
};
|
||||
|
||||
export const runtime = (): CommandRuntime => {
|
||||
return {
|
||||
execute: async () => {
|
||||
const files = await shim.fsDriver().readDirStats(Setting.value('profileDir'));
|
||||
// Get all log.txt and log-{timestamp}.txt files but ignore deletion_log.txt
|
||||
const logFiles = files.filter(f => f.path.match(/^log(-\d+)?\.txt$/gi));
|
||||
|
||||
const lastOneAndCurrent = logFiles.sort().slice(logFiles.length - 2);
|
||||
|
||||
let allDeletionLines = '';
|
||||
for (const file of lastOneAndCurrent) {
|
||||
|
||||
const deletionLines = await getDeletionLines(file.path);
|
||||
|
||||
allDeletionLines += deletionLines;
|
||||
}
|
||||
|
||||
const fileName = `deletion_log_${formatMsToLocal(Date.now(), 'YYYYMMDD')}.txt`;
|
||||
|
||||
const deletionLogPath = `${Setting.value('profileDir')}/${fileName}`;
|
||||
|
||||
await shim.fsDriver().writeFile(deletionLogPath, allDeletionLines, 'utf8');
|
||||
|
||||
await bridge().openItem(deletionLogPath);
|
||||
},
|
||||
};
|
||||
};
|
@ -2,6 +2,7 @@
|
||||
import * as copyDevCommand from './copyDevCommand';
|
||||
import * as editProfileConfig from './editProfileConfig';
|
||||
import * as emptyTrash from './emptyTrash';
|
||||
import * as exportDeletionLog from './exportDeletionLog';
|
||||
import * as exportFolders from './exportFolders';
|
||||
import * as exportNotes from './exportNotes';
|
||||
import * as focusElement from './focusElement';
|
||||
@ -22,6 +23,7 @@ const index: any[] = [
|
||||
copyDevCommand,
|
||||
editProfileConfig,
|
||||
emptyTrash,
|
||||
exportDeletionLog,
|
||||
exportFolders,
|
||||
exportNotes,
|
||||
focusElement,
|
||||
|
@ -927,6 +927,7 @@ function useMenu(props: Props) {
|
||||
separator(),
|
||||
syncStatusItem,
|
||||
separator(),
|
||||
menuItemDic.exportDeletionLog,
|
||||
{
|
||||
id: 'help:toggleDevTools',
|
||||
label: _('Toggle development tools'),
|
||||
|
@ -56,6 +56,7 @@ export default function() {
|
||||
'editor.sortSelectedLines',
|
||||
'editor.swapLineUp',
|
||||
'editor.swapLineDown',
|
||||
'exportDeletionLog',
|
||||
'toggleSafeMode',
|
||||
'showShareNoteDialog',
|
||||
'showShareFolderDialog',
|
||||
|
@ -155,6 +155,10 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
||||
void NavService.go('Log');
|
||||
};
|
||||
|
||||
private deletionLogButtonPress_ = () => {
|
||||
void NavService.go('Log', { defaultFilter: 'DeleteAction' });
|
||||
};
|
||||
|
||||
private manageSharesPress_ = () => {
|
||||
void NavService.go('ShareManager');
|
||||
};
|
||||
@ -543,6 +547,7 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
||||
addSettingButton('profiles_buttons', _('Manage profiles'), this.manageProfilesButtonPress_);
|
||||
addSettingButton('status_button', _('Sync Status'), this.syncStatusButtonPress_);
|
||||
addSettingButton('log_button', _('Log'), this.logButtonPress_);
|
||||
addSettingButton('deletion_log_button', _('Deletion log'), this.deletionLogButtonPress_);
|
||||
addSettingButton('fix_search_engine_index', this.state.fixingSearchIndex ? _('Fixing search index...') : _('Fix search index'), this.fixSearchEngineIndexButtonPress_, { disabled: this.state.fixingSearchIndex, description: _('Use this to rebuild the search index if there is a problem with search. It may take a long time depending on the number of notes.') });
|
||||
const syncTargetInfo = SyncTargetRegistry.infoById(this.state.settings['sync.target']);
|
||||
if (syncTargetInfo.supportsShare) {
|
||||
|
Loading…
Reference in New Issue
Block a user