Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/appearance/langs/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@
"fileTree13": "For example, <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}</code>, starting with <code class='fn__code'>/</code> uses the selected notebook's root path, not starting with <code class='fn__code'>/</code> uses the current document's relative path",
"fileTree14": "Save path (support date format template variables, such as <code class='fn__code'>/Daily Notes/{{now | date \"2006/01\"}}/{{now | date \"2006-01-02\"}}</code>)",
"fileTree15": "Daily note template path (for example, <code class='fn__code'>dailynote.md</code>; leave blank to apply no template; the file needs to be placed under workspace/data/templates/)",
"dailyNoteDatabaseId": "Target database",
"dailyNoteDatabaseIdHint": "Storage database",
"dailyNoteDatabasePick": "Select database…",
"fileTree16": "Maximum number to list",
"fileTree17": "If there are too many sub-docs/tags, this restriction can be used to improve performance",
"fileTree18": "Allows creation of sub-documents deeper than 7 levels",
Expand Down
3 changes: 3 additions & 0 deletions app/appearance/langs/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@
"fileTree13": "例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}</code>,以 <code class='fn__code'>/</code> 开头则使用选中笔记本的根路径,不以 <code class='fn__code'>/</code> 开头则使用当前文档相对路径",
"fileTree14": "存放路径(可使用日期格式模板变量,例如 <code class='fn__code'>/日记/{{now | date \"2006/01\"}}/{{now | date \"2006-01-02\"}}</code>)",
"fileTree15": "日记模板路径(例如 <code class='fn__code'>dailynote.md</code>,留空时不应用模板,需要将该文件放置于工作空间/data/templates/ 下)",
"dailyNoteDatabaseId": "目标数据库",
"dailyNoteDatabaseIdHint": "存放数据库",
"dailyNoteDatabasePick": "选择数据库…",
"fileTree16": "最大列出数量",
"fileTree17": "如果子文档/标签非常多,可通过该项限制以提升性能",
"fileTree18": "允许创建深度大于 7 层的子文档",
Expand Down
3 changes: 3 additions & 0 deletions app/appearance/langs/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,9 @@
"fileTree13": "例如 <code class='fn__code'>/folder1/{{now | date \"20060102150405\"}}</code>,以 <code class='fn__code'>/</code> 開頭則使用選中筆記本的根路徑,不以 <code class='fn__code'>/</code> 開頭則使用當前文檔相對路徑",
"fileTree14": "存放路徑(可使用日期格式範本變數,例如 <code class='fn__code'>/日記/{{now | date \"2006/01\"}}/{{now | date \"2006-01-02\"}}</code>)",
"fileTree15": "日記範本路徑(例如 <code class='fn__code'>dailynote.md</code>,留空時不套用範本,需要將該檔放置於工作空間/data/templates/ 下)",
"dailyNoteDatabaseId": "目標資料庫",
"dailyNoteDatabaseIdHint": "存放資料庫",
"dailyNoteDatabasePick": "選擇資料庫…",
"fileTree16": "最大列出數量",
"fileTree17": "如果子文檔/標籤非常多,可通過該項限制以提升性能",
"fileTree18": "允許建立深度大於 7 層的子文檔",
Expand Down
82 changes: 70 additions & 12 deletions app/src/menus/onGetnotebookconf.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Dialog} from "../dialog";
import {fetchPost} from "../util/fetch";
import {fetchPost, fetchSyncPost} from "../util/fetch";
import {isMobile} from "../util/functions";
import {escapeHtml} from "../util/escape";
import {writeText} from "../protyle/util/compatibility";
Expand All @@ -18,6 +18,7 @@ declare interface INotebookConf {
docCreateSaveBox: string;
docCreateTemplatePath: string;
dailyNoteTemplatePath: string
dailyNoteDatabaseID: string
}
}

Expand Down Expand Up @@ -86,6 +87,13 @@ export const onGetnotebookconf = (data: INotebookConf) => {
<div class="b3-label__text">${window.siyuan.languages.fileTree15}</div>
<div class="fn__hr"></div>
<input class="b3-text-field fn__flex-center fn__block" id="dailyNoteTemplatePath" value="${data.conf.dailyNoteTemplatePath}">
<div class="fn__hr"></div>
<div class="b3-label__text">${window.siyuan.languages.dailyNoteDatabaseIdHint}</div>
<div class="fn__hr"></div>
<div class="fn__flex" style="align-items: center; gap: 8px">
<input class="b3-text-field fn__flex-1" id="dailyNoteDatabaseId" value="" readonly placeholder="${window.siyuan.languages.dailyNoteDatabaseId}">
<button class="b3-button b3-button--outline" id="dailyNoteDatabasePick">${window.siyuan.languages.dailyNoteDatabasePick}</button>
</div>
</div></div>`;
if (isMobile()) {
openModel({
Expand Down Expand Up @@ -122,19 +130,69 @@ const bindSettingEvent = (contentElement: Element, data: INotebookConf) => {
refCreateSavePathElement.value = data.conf.refCreateSavePath;
const dailyNoteTemplatePathElement = contentElement.querySelector("#dailyNoteTemplatePath") as HTMLInputElement;
dailyNoteTemplatePathElement.value = data.conf.dailyNoteTemplatePath;
const dailyNoteDatabaseIdElement = contentElement.querySelector("#dailyNoteDatabaseId") as HTMLInputElement;
dailyNoteDatabaseIdElement.dataset.id = data.conf.dailyNoteDatabaseID || "";
if (data.conf.dailyNoteDatabaseID) {
fetchSyncPost("/api/av/searchAttributeView", {keyword: "", avID: "", blockID: "", excludes: []}).then((response) => {
const results = (response?.data?.results || []) as Array<{avID: string, avName: string, blockID: string, hPath: string}>;
const result = results.find((item) => item.blockID === data.conf.dailyNoteDatabaseID);
if (result) {
dailyNoteDatabaseIdElement.value = `${result.avName}${result.hPath ? " · " + result.hPath : ""}`;
} else {
dailyNoteDatabaseIdElement.value = data.conf.dailyNoteDatabaseID;
}
});
}
const dailyNoteDatabasePickElement = contentElement.querySelector("#dailyNoteDatabasePick") as HTMLButtonElement;
const saveConf = () => {
fetchPost("/api/notebook/setNotebookConf", {
notebook: data.box,
conf: {
refCreateSavePath: refCreateSavePathElement.value,
refCreateSaveBox: (contentElement.querySelector("#refCreateSaveBox") as HTMLInputElement).value,
docCreateSaveBox: (contentElement.querySelector("#docCreateSaveBox") as HTMLInputElement).value,
docCreateSavePath: docCreateSavePathElement.value,
docCreateTemplatePath: docCreateTemplatePathElement.value,
dailyNoteSavePath: dailyNoteSavePathElement.value,
dailyNoteTemplatePath: dailyNoteTemplatePathElement.value,
dailyNoteDatabaseID: dailyNoteDatabaseIdElement.dataset.id || "",
}
});
};
contentElement.querySelectorAll("input, select").forEach((item) => {
item.addEventListener("change", () => {
fetchPost("/api/notebook/setNotebookConf", {
notebook: data.box,
conf: {
refCreateSavePath: refCreateSavePathElement.value,
refCreateSaveBox: (contentElement.querySelector("#refCreateSaveBox") as HTMLInputElement).value,
docCreateSaveBox: (contentElement.querySelector("#docCreateSaveBox") as HTMLInputElement).value,
docCreateSavePath: docCreateSavePathElement.value,
docCreateTemplatePath: docCreateTemplatePathElement.value,
dailyNoteSavePath: dailyNoteSavePathElement.value,
dailyNoteTemplatePath: dailyNoteTemplatePathElement.value,
}
saveConf();
});
});
dailyNoteDatabasePickElement.addEventListener("click", () => {
fetchSyncPost("/api/av/searchAttributeView", {keyword: "", avID: "", blockID: "", excludes: []}).then((response) => {
const results = (response?.data?.results || []) as Array<{avID: string, avName: string, blockID: string, hPath: string}>;
const nameById = new Map(results.map(item => [item.blockID, item.avName]));
const listHTML = results.map((item) => {
const location = item.hPath ? `<span class="b3-list-item__meta">${escapeHtml(item.hPath)}</span>` : "";
return `<div class="b3-list-item b3-list-item--outline fn__pointer" data-id="${item.blockID}" role="button" tabindex="0">
<svg class="b3-list-item__graphic"><use xlink:href="#iconDatabase"></use></svg>
<span class="b3-list-item__text">${escapeHtml(item.avName)}${location}</span>
</div>`;
}).join("");
const picker = new Dialog({
title: window.siyuan.languages.dailyNoteDatabaseId,
content: `<div class="b3-dialog__content"><div class="b3-list b3-list--background">
<div class="b3-list-item b3-list-item--outline fn__pointer" data-id="" role="button" tabindex="0">
<svg class="b3-list-item__graphic"><use xlink:href="#iconClose"></use></svg>
<span class="b3-list-item__text">${window.siyuan.languages.clear}</span>
</div>${listHTML}</div></div>`,
width: isMobile() ? "92vw" : "640px",
});
picker.element.setAttribute("data-key", "dialog-dailynote-database");
picker.element.querySelectorAll(".b3-list-item").forEach((item) => {
item.addEventListener("click", () => {
const blockID = item.getAttribute("data-id") || "";
dailyNoteDatabaseIdElement.dataset.id = blockID;
dailyNoteDatabaseIdElement.value = blockID ? (nameById.get(blockID) || blockID) : "";
saveConf();
picker.destroy();
});
});
});
});
Expand Down
3 changes: 3 additions & 0 deletions kernel/api/notebook.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ func setNotebookConf(c *gin.Context) {

boxConf.DailyNoteTemplatePath = util.NormalizeTemplatePath(boxConf.DailyNoteTemplatePath)

// 目标数据库(属性视图)块 ID,仅去除首尾空白,允许为空,不在此处校验块是否存在
boxConf.DailyNoteDatabaseID = strings.TrimSpace(boxConf.DailyNoteDatabaseID)

if err := box.SaveConf(boxConf); err != nil {
ret.Code = -1
ret.Msg = err.Error()
Expand Down
2 changes: 2 additions & 0 deletions kernel/conf/box.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ type BoxConf struct {
DocCreateTemplatePath string `json:"docCreateTemplatePath"` // 新建文档使用的模板路径
DailyNoteSavePath string `json:"dailyNoteSavePath"` // 新建日记存储路径
DailyNoteTemplatePath string `json:"dailyNoteTemplatePath"` // 新建日记使用的模板路径
DailyNoteDatabaseID string `json:"dailyNoteDatabaseID"` // 新建日记自动添加到的目标数据库(属性视图)块 ID
SortMode int `json:"sortMode"` // 排序方式
Encrypted bool `json:"encrypted"` // 是否为加密笔记本
BoxCrypt *BoxEncryption `json:"boxCrypt"` // 笔记本加密参数,仅 Encrypted=true 时有值
Expand All @@ -51,6 +52,7 @@ func NewBoxConf() *BoxConf {
Closed: true,
DailyNoteSavePath: "/daily note/{{now | date \"2006/01\"}}/{{now | date \"2006-01-02\"}}",
DailyNoteTemplatePath: "",
DailyNoteDatabaseID: "",
SortMode: util.SortModeFileTree,
Encrypted: false,
}
Expand Down
45 changes: 45 additions & 0 deletions kernel/model/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -1482,9 +1482,54 @@ func CreateDailyNote(boxID string) (p string, existed bool, err error) {
sql.FlushQueue()
}

addDailyNoteToDatabase(boxConf.DailyNoteDatabaseID, id)

return
}

// addDailyNoteToDatabase 将新建的日记文档作为行添加到笔记本配置的目标数据库(属性视图)中。
// 该功能为尽力而为:配置无效或插入失败时仅记录警告,不影响日记创建。
func addDailyNoteToDatabase(dbBlockID, docID string) {
if "" == dbBlockID {
return
}

bt := treenode.GetBlockTree(dbBlockID)
if nil == bt {
logging.LogWarnf("daily note database block [%s] not found, skip adding daily note [%s] to database", dbBlockID, docID)
return
}

tree, err := LoadTreeByBlockID(bt.RootID)
if err != nil {
logging.LogWarnf("load tree of daily note database block [%s] failed: %s", bt.RootID, err)
return
}
node := treenode.GetNodeInTree(tree, dbBlockID)
if nil == node || ast.NodeAttributeView != node.Type {
logging.LogWarnf("daily note database block [%s] is not an attribute view block, skip adding daily note [%s] to database", dbBlockID, docID)
return
}
avID := node.AttributeViewID
if "" == avID {
logging.LogWarnf("daily note database block [%s] has no attribute view id, skip adding daily note [%s] to database", dbBlockID, docID)
return
}

// 幂等:文档已经绑定到该数据库时不再重复添加
if itemID := GetAttributeViewItemIDs(avID, []string{docID})[docID]; "" != itemID {
return
}

srcs := []map[string]any{{"id": docID, "isDetached": false}}
if err = AddAttributeViewBlock(nil, srcs, avID, dbBlockID, "", "", "", true); err != nil {
logging.LogWarnf("add daily note [%s] to database [%s] failed: %s", docID, avID, err)
return
}
ReloadAttrView(avID)
logging.LogInfof("daily note [%s] added to database [%s]", docID, avID)
}

func GetHPathByPath(boxID, p string) (hPath string, err error) {
if "/" == p {
hPath = "/"
Expand Down
1 change: 1 addition & 0 deletions kernel/model/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ func importSY(zipPath, boxID, toPath string, createNotebook, autoDetect bool) (c
boxConf.DocCreateTemplatePath = importedBoxConf.DocCreateTemplatePath
boxConf.DailyNoteSavePath = importedBoxConf.DailyNoteSavePath
boxConf.DailyNoteTemplatePath = importedBoxConf.DailyNoteTemplatePath
boxConf.DailyNoteDatabaseID = importedBoxConf.DailyNoteDatabaseID
boxConf.SortMode = importedBoxConf.SortMode
if err = box.SaveConf(boxConf); err != nil {
return createdBoxID, err
Expand Down
Loading