Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.

Commit c0f4e16

Browse files
committed
Merge branch 'master' into imed-docu-branch
2 parents d7782b6 + be3cb4e commit c0f4e16

File tree

11 files changed

+309
-47
lines changed

11 files changed

+309
-47
lines changed

docs/docs/changelog.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# 2.3.5
2+
*Published on 2022/09/05*
3+
### Shiny new things
4+
- New command & ribbon icon to generate a new database with a helpful wizard to guide you through the process [ISSUE#126](https://github.com/RafaelGB/obsidian-db-folder/issues/126)
5+
### No longer broken
6+
- Problem with saving query on yaml solved [ISSUE#325](https://github.com/RafaelGB/obsidian-db-folder/issues/325)
17
# 2.3.4
28
*Published on 2022/09/04*
39
### Improved

manifest-beta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "dbfolder",
33
"name": "DB Folder",
4-
"version": "2.3.4",
4+
"version": "2.3.5",
55
"minAppVersion": "0.15.9",
66
"description": "Folder with the capability to store and retrieve data from a folder like database",
77
"author": "RafaelGB",

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "dbfolder",
33
"name": "DB Folder",
4-
"version": "2.3.4",
4+
"version": "2.3.5",
55
"minAppVersion": "0.15.9",
66
"description": "Folder with the capability to store and retrieve data from a folder like database",
77
"author": "RafaelGB",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-dbfolder",
3-
"version": "2.3.4",
3+
"version": "2.3.5",
44
"description": "This is a sample plugin for Obsidian (https://obsidian.md)",
55
"main": "main.js",
66
"scripts": {

src/DatabaseView.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import DatabaseInfo from "services/DatabaseInfo";
3232
import { LOGGER } from "services/Logger";
3333
import { SettingsModal } from "Settings";
3434
import StateManager from "StateManager";
35-
export const databaseIcon = "blocks";
3635

3736
export class DatabaseView extends TextFileView implements HoverParent {
3837
plugin: DBFolderPlugin;

src/commands/addDatabaseHelper/databaseHelperCreationModal.ts

Lines changed: 228 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,20 @@
1-
import { Modal } from "obsidian";
1+
import { LocalSettings } from "cdm/SettingsModel";
2+
import { generateDbConfiguration, generateNewDatabase } from "helpers/CommandsHelper";
3+
import { InputType, SourceDataTypes, StyleClasses } from "helpers/Constants";
4+
import { resolve_tfolder } from "helpers/FileManagement";
5+
import { generateDataviewTableQuery } from "helpers/QueryHelper";
6+
import { Modal, Notice, Setting, TFolder } from "obsidian";
7+
import { DataviewService } from "services/DataviewService";
8+
import { add_dropdown, add_setting_header } from "settings/SettingsComponents";
9+
import { FileSuggest } from "settings/suggesters/FileSuggester";
10+
import { FolderSuggest } from "settings/suggesters/FolderSuggester";
211

312
export class DatabaseHelperCreationModal extends Modal {
413
databaseHelperCreationModalManager: DatabaseHelperCreationModalManager;
5-
constructor() {
14+
local_settings: LocalSettings;
15+
constructor(local_settings: LocalSettings) {
616
super(app);
17+
this.local_settings = local_settings;
718
this.databaseHelperCreationModalManager = new DatabaseHelperCreationModalManager(this);
819
}
920
onOpen() {
@@ -20,11 +31,225 @@ export class DatabaseHelperCreationModal extends Modal {
2031

2132
export class DatabaseHelperCreationModalManager {
2233
databaseHelperCreationModal: DatabaseHelperCreationModal;
34+
destinationFolder = '/';
35+
databaseName = '';
2336
constructor(
2437
databaseHelperCreationModal: DatabaseHelperCreationModal,
2538
) {
2639
this.databaseHelperCreationModal = databaseHelperCreationModal;
40+
41+
2742
}
2843
constructUI(containerEl: HTMLElement) {
44+
add_setting_header(containerEl, "Database creator helper", 'h2');
45+
const helperBody = containerEl.createDiv();
46+
helperBody.addClass(StyleClasses.SETTINGS_MODAL_BODY);
47+
helperBody.setAttribute("id", StyleClasses.SETTINGS_MODAL_BODY);
48+
this.constructSettingBody(helperBody);
2949
}
30-
}
50+
51+
constructSettingBody(bodyElement: HTMLElement) {
52+
new Setting(bodyElement)
53+
.setName("Database name")
54+
.setDesc("Name of the database to create")
55+
.addText(text => {
56+
text.setPlaceholder("Database name")
57+
.setValue(this.databaseName)
58+
.onChange(async (value: string): Promise<void> => {
59+
this.databaseName = this.parseValueToThuthyYaml(value);
60+
});
61+
});
62+
63+
add_dropdown(
64+
bodyElement,
65+
'Select source',
66+
'Select from which source you want to create your custom database.',
67+
this.databaseHelperCreationModal.local_settings.source_data,
68+
{
69+
current_folder: SourceDataTypes.CURRENT_FOLDER,
70+
tag: SourceDataTypes.TAG,
71+
outgoing_link: SourceDataTypes.OUTGOING_LINK,
72+
incoming_link: SourceDataTypes.INCOMING_LINK,
73+
query: SourceDataTypes.QUERY,
74+
},
75+
async (value: string) => {
76+
this.databaseHelperCreationModal.local_settings.source_data = value;
77+
this.reset();
78+
}
79+
);
80+
switch (this.databaseHelperCreationModal.local_settings.source_data) {
81+
case SourceDataTypes.TAG:
82+
this.tagHandler(bodyElement);
83+
break;
84+
case SourceDataTypes.INCOMING_LINK:
85+
case SourceDataTypes.OUTGOING_LINK:
86+
this.outgoingAndIncomingHandler(bodyElement);
87+
break;
88+
case SourceDataTypes.QUERY:
89+
this.queryHandler(bodyElement);
90+
break;
91+
default:
92+
// do nothing
93+
}
94+
95+
new Setting(bodyElement)
96+
.setName('Select where to save your database')
97+
.setDesc('Select the destination of where you want to save your database.')
98+
.addSearch((cb) => {
99+
new FolderSuggest(
100+
cb.inputEl
101+
);
102+
cb.setPlaceholder("Example: path/to/folder")
103+
.setValue(this.destinationFolder)
104+
.onChange((value: string) => {
105+
this.destinationFolder = value;
106+
});
107+
});
108+
109+
new Setting(bodyElement)
110+
.setName('Submit')
111+
.setDesc('Close to cancel or submit to create your database.')
112+
.addButton((cb) => {
113+
cb.setButtonText('Close')
114+
.onClick(() => {
115+
this.databaseHelperCreationModal.close();
116+
});
117+
}).addButton((cb) => {
118+
cb.setButtonText('Create')
119+
.onClick(async () => {
120+
await this.createButtonHandler()
121+
});
122+
});
123+
}
124+
125+
reset() {
126+
const bodyElement = activeDocument.getElementById(StyleClasses.SETTINGS_MODAL_BODY);
127+
// remove all sections
128+
bodyElement.empty();
129+
this.constructSettingBody(bodyElement);
130+
}
131+
132+
async createButtonHandler() {
133+
try {
134+
const targetFolder = resolve_tfolder(this.destinationFolder);
135+
this.databaseHelperCreationModal.local_settings.source_form_result = this.parseValueToThuthyYaml(
136+
this.databaseHelperCreationModal.local_settings.source_form_result
137+
);
138+
await generateNewDatabase(
139+
generateDbConfiguration(this.databaseHelperCreationModal.local_settings),
140+
targetFolder,
141+
this.databaseName
142+
);
143+
new Notice(`Database "${this.databaseName}" created in "${targetFolder.path}"`, 1500);
144+
} catch (e) {
145+
new Notice(`Error creating database "${this.databaseName}": ${e}`, 1500);
146+
}
147+
this.databaseHelperCreationModal.close();
148+
}
149+
150+
tagHandler(containerEl: HTMLElement) {
151+
const tagArray: Record<string, number> = (app.metadataCache as unknown as any).getTags();
152+
if (tagArray) {
153+
const tagRecords: Record<string, string> = {};
154+
// Order tagRecord by key (tag name)
155+
Object.entries(tagArray)
156+
.sort((a, b) => a[0].localeCompare(b[0]))
157+
.forEach(([key, value]) => {
158+
tagRecords[key] = `${key}(${value})`;
159+
});
160+
const source_form_promise = async (value: string): Promise<void> => {
161+
// update form result
162+
this.databaseHelperCreationModal.local_settings.source_form_result = value.slice(1);
163+
};
164+
165+
add_dropdown(
166+
containerEl,
167+
'Select a tag',
168+
'Select tag to get data from',
169+
`#${this.databaseHelperCreationModal.local_settings.source_form_result}`,
170+
tagRecords,
171+
source_form_promise
172+
);
173+
this.destinationFolderHandler(containerEl);
174+
}
175+
}
176+
177+
queryHandler(containerEl: HTMLElement) {
178+
const query_promise = async (value: string): Promise<void> => {
179+
// update settings
180+
this.databaseHelperCreationModal.local_settings.source_form_result = value;
181+
};
182+
new Setting(containerEl)
183+
.setName('Dataview query')
184+
.setDesc('Enter a dataview query starting with FROM (the plugin autocomplete the query with TABLE & the column fields)')
185+
.addTextArea((textArea) => {
186+
textArea.setValue(this.databaseHelperCreationModal.local_settings.source_form_result);
187+
textArea.setPlaceholder('Write here your dataview query...');
188+
textArea.onChange(query_promise);
189+
}).addExtraButton((cb) => {
190+
cb.setIcon("check")
191+
.setTooltip("Validate query")
192+
.onClick(async (): Promise<void> => {
193+
const query = generateDataviewTableQuery(
194+
[],
195+
this.databaseHelperCreationModal.local_settings.source_form_result);
196+
if (query) {
197+
DataviewService.getDataviewAPI().tryQuery(query.replace('TABLE ,', 'TABLE '))
198+
.then(() => {
199+
new Notice(`Dataview query "${query}" is valid!`, 2000);
200+
})
201+
.catch((e) => {
202+
new Notice(`Dataview query "${query}" is invalid: ${e.message}`, 10000);
203+
});
204+
}
205+
});
206+
});
207+
this.destinationFolderHandler(containerEl);
208+
}
209+
210+
outgoingAndIncomingHandler(containerEl: HTMLElement) {
211+
const source_form_promise = async (value: string): Promise<void> => {
212+
// update form result
213+
this.databaseHelperCreationModal.local_settings.source_form_result = value;
214+
};
215+
new Setting(containerEl)
216+
.setName('Select a file')
217+
.setDesc('Select file from vault to be used as source of data.')
218+
.addSearch((cb) => {
219+
new FileSuggest(
220+
cb.inputEl,
221+
"/"
222+
);
223+
cb.setPlaceholder("Example: folder1/template_file")
224+
.setValue(this.databaseHelperCreationModal.local_settings.source_form_result)
225+
.onChange(source_form_promise);
226+
});
227+
this.destinationFolderHandler(containerEl);
228+
}
229+
230+
destinationFolderHandler(containerEl: HTMLElement) {
231+
const source_form_promise = async (value: string): Promise<void> => {
232+
// update settings
233+
this.databaseHelperCreationModal.local_settings.source_destination_path = value;
234+
};
235+
new Setting(containerEl)
236+
.setName('Select destination folder')
237+
.setDesc('Select the destination of new entries for this source')
238+
.addSearch((cb) => {
239+
new FolderSuggest(
240+
cb.inputEl
241+
);
242+
cb.setPlaceholder("Example: path/to/folder")
243+
.setValue(this.databaseHelperCreationModal.local_settings.source_destination_path)
244+
.onChange(source_form_promise);
245+
});
246+
}
247+
248+
parseValueToThuthyYaml(value: string): string {
249+
return DataviewService.parseLiteral(
250+
value,
251+
InputType.MARKDOWN,
252+
this.databaseHelperCreationModal.local_settings
253+
).toString();
254+
}
255+
}

src/helpers/CommandsHelper.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { LocalSettings } from "cdm/SettingsModel";
2+
import { Notice, TFile, TFolder } from "obsidian";
3+
import { LOGGER } from "services/Logger";
4+
import { DatabaseCore, DatabaseFrontmatterOptions, DEFAULT_SETTINGS, YAML_INDENT } from "helpers/Constants";
5+
6+
export async function generateNewDatabase(ddbbConfig: string, folder?: TFolder, ddbbName?: string) {
7+
const targetFolder = folder
8+
?? app.fileManager.getNewFileParent(
9+
app.workspace.getActiveFile()?.path || ''
10+
);
11+
12+
try {
13+
const database: TFile = await (
14+
app.fileManager as any
15+
).createNewMarkdownFile(targetFolder, ddbbName ?? 'Untitled database');
16+
17+
await app.vault.modify(
18+
database,
19+
DatabaseFrontmatterOptions.BASIC
20+
.concat('\n')
21+
.concat(ddbbConfig)
22+
.concat('\n')
23+
.concat('%%>')
24+
);
25+
await app.workspace.getMostRecentLeaf().setViewState({
26+
type: DatabaseCore.FRONTMATTER_KEY,
27+
state: { file: database.path },
28+
});
29+
} catch (e) {
30+
LOGGER.error('Error creating database folder:', e);
31+
new Notice(`Error creating database folder: ${e}`, 5000);
32+
}
33+
}
34+
35+
/**
36+
* Returns the default configuration for a database file.
37+
*/
38+
export function generateDbConfiguration(customLocalSettings: LocalSettings): string {
39+
const defaultConfig = [];
40+
defaultConfig.push("config:");
41+
Object.entries(DEFAULT_SETTINGS.local_settings).forEach(([key, value]) => {
42+
const defaultValue = customLocalSettings[key as keyof LocalSettings] !== undefined ? customLocalSettings[key as keyof LocalSettings] : value;
43+
defaultConfig.push(`${YAML_INDENT}${key}: ${defaultValue}`);
44+
});
45+
return defaultConfig.join('\n');
46+
}

src/helpers/Constants.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ export function getOperatorFilterValue(keyToFind: string): string {
274274

275275
export const MarkdownBreakerRules = Object.freeze({
276276
INIT_CHARS: ['`', '"', '[', '{', '*', '!'],
277-
BETWEEN_CHARS: [':'],
277+
BETWEEN_CHARS: [':', '"'],
278278
UNIQUE_CHARS: ['?'],
279279
})
280280

@@ -331,5 +331,12 @@ export const SUGGESTER_REGEX = Object.freeze({
331331

332332
LINK_BLOCK: /\B\[\[([^#\]]+)#?\^([^\]]*)$/,
333333
EMBED_BLOCK: /\B!\[\[([^#\]]+)#?\^([^\]]*)$/
334+
});
334335

336+
/******************************************************************************
337+
* ICONS CONSTANTS
338+
******************************************************************************/
339+
export const DB_ICONS = Object.freeze({
340+
NAME: 'database-folder-icon',
341+
ICON: `<g transform="matrix(0.06 0 0 0.05 52 52)"><path stroke="currentColor" fill="#fff" vector-effect="non-scaling-stroke" transform=" translate(-896, -896)" d="M 896 768 q 237 0 443 -43 t 325 -127 v 170 q 0 69 -103 128 t -280 93.5 t -385 34.5 t -385 -34.5 t -280 -93.5 t -103 -128 v -170 q 119 84 325 127 t 443 43 z m 0 768 q 237 0 443 -43 t 325 -127 v 170 q 0 69 -103 128 t -280 93.5 t -385 34.5 t -385 -34.5 t -280 -93.5 t -103 -128 v -170 q 119 84 325 127 t 443 43 z m 0 -384 q 237 0 443 -43 t 325 -127 v 170 q 0 69 -103 128 t -280 93.5 t -385 34.5 t -385 -34.5 t -280 -93.5 t -103 -128 v -170 q 119 84 325 127 t 443 43 z m 0 -1152 q 208 0 385 34.5 t 280 93.5 t 103 128 v 128 q 0 69 -103 128 t -280 93.5 t -385 34.5 t -385 -34.5 t -280 -93.5 t -103 -128 v -128 q 0 -69 103 -128 t 280 -93.5 t 385 -34.5 z" stroke-linecap="round" /></g>`
335342
});

src/helpers/Generators.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@ export function useIMEInputProps() {
1919
const isComposingRef = useRef<boolean>(false);
2020

2121
return {
22-
// Note: these are lowercased because we use preact
23-
// See: https://github.com/preactjs/preact/issues/3003
2422
onCompositionStart: () => {
2523
isComposingRef.current = true;
2624
},

0 commit comments

Comments
 (0)