Forráskód Böngészése

:art: 文档树支持 `Ctrl+Click` 和 `Shift+↑/↓` 进行多选 https://github.com/siyuan-note/siyuan/issues/1359

Liang Ding 2 éve
szülő
commit
e45a0694bd
4 módosított fájl, 168 hozzáadás és 26 törlés
  1. 47 0
      kernel/api/filetree.go
  2. 2 0
      kernel/api/router.go
  3. 11 0
      kernel/model/box.go
  4. 108 26
      kernel/model/file.go

+ 47 - 0
kernel/api/filetree.go

@@ -267,6 +267,31 @@ func moveDoc(c *gin.Context) {
 	util.PushEvent(evt)
 }
 
+func moveDocs(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
+		return
+	}
+
+	var fromPaths []string
+	fromPathsArg := arg["fromPaths"].([]interface{})
+	for _, fromPath := range fromPathsArg {
+		fromPaths = append(fromPaths, fromPath.(string))
+	}
+	toPath := arg["toPath"].(string)
+
+	err := model.MoveDocs(fromPaths, toPath)
+	if nil != err {
+		ret.Code = -1
+		ret.Msg = err.Error()
+		ret.Data = map[string]interface{}{"closeTimeout": 7000}
+		return
+	}
+}
+
 func removeDoc(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)
@@ -294,6 +319,28 @@ func removeDoc(c *gin.Context) {
 	util.PushEvent(evt)
 }
 
+func removeDocs(c *gin.Context) {
+	ret := gulu.Ret.NewResult()
+	defer c.JSON(http.StatusOK, ret)
+
+	arg, ok := util.JsonArg(c, ret)
+	if !ok {
+		return
+	}
+
+	pathsArg := arg["paths"].([]interface{})
+	var paths []string
+	for _, path := range pathsArg {
+		paths = append(paths, path.(string))
+	}
+	err := model.RemoveDocs(paths)
+	if nil != err {
+		ret.Code = -1
+		ret.Msg = err.Error()
+		return
+	}
+}
+
 func renameDoc(c *gin.Context) {
 	ret := gulu.Ret.NewResult()
 	defer c.JSON(http.StatusOK, ret)

+ 2 - 0
kernel/api/router.go

@@ -87,7 +87,9 @@ func ServeAPI(ginServer *gin.Engine) {
 	ginServer.Handle("POST", "/api/filetree/createDoc", model.CheckAuth, model.CheckReadonly, createDoc)
 	ginServer.Handle("POST", "/api/filetree/renameDoc", model.CheckAuth, model.CheckReadonly, renameDoc)
 	ginServer.Handle("POST", "/api/filetree/removeDoc", model.CheckAuth, model.CheckReadonly, removeDoc)
+	ginServer.Handle("POST", "/api/filetree/removeDocs", model.CheckAuth, model.CheckReadonly, removeDocs)
 	ginServer.Handle("POST", "/api/filetree/moveDoc", model.CheckAuth, model.CheckReadonly, moveDoc)
+	ginServer.Handle("POST", "/api/filetree/moveDocs", model.CheckAuth, model.CheckReadonly, moveDocs)
 	ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckReadonly, duplicateDoc)
 	ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath)
 	ginServer.Handle("POST", "/api/filetree/getHPathByID", model.CheckAuth, getHPathByID)

+ 11 - 0
kernel/model/box.go

@@ -545,3 +545,14 @@ func TryAccessFileByBlockID(id string) (ok bool) {
 	}
 	return true
 }
+
+func getBoxesByPaths(paths []string) (ret map[string]*Box) {
+	for _, p := range paths {
+		id := strings.TrimSuffix(path.Base(p), ".sy")
+		bt := treenode.GetBlockTree(id)
+		if nil != bt {
+			ret[p] = Conf.Box(bt.BoxID)
+		}
+	}
+	return
+}

+ 108 - 26
kernel/model/file.go

@@ -988,6 +988,8 @@ func GetFullHPathByID(id string) (hPath string, err error) {
 }
 
 func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err error) {
+	WaitForWritingFiles()
+
 	if fromBoxID == toBoxID && fromPath == toPath {
 		return
 	}
@@ -1004,13 +1006,6 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 		return
 	}
 
-	WaitForWritingFiles()
-	tree, err := LoadTree(fromBoxID, fromPath)
-	if nil != err {
-		err = ErrBlockNotFound
-		return
-	}
-
 	childDepth := util.GetChildDocDepth(filepath.Join(util.DataDir, fromBoxID, fromPath))
 	if depth := strings.Count(toPath, "/") + childDepth; 6 < depth && !Conf.FileTree.AllowCreateDeeper {
 		err = errors.New(Conf.Language(118))
@@ -1022,7 +1017,19 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 		err = errors.New(Conf.Language(0))
 		return
 	}
-	isSameBox := fromBoxID == toBoxID
+
+	newPath, err = moveDoc(fromBox, fromPath, toBox, toPath)
+	if nil != err {
+		return
+	}
+
+	cache.ClearDocsIAL()
+	IncSync()
+	return
+}
+
+func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string) (newPath string, err error) {
+	isSameBox := fromBox.ID == toBox.ID
 
 	if isSameBox {
 		if !fromBox.Exist(toPath) {
@@ -1036,6 +1043,12 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 		}
 	}
 
+	tree, err := LoadTree(fromBox.ID, fromPath)
+	if nil != err {
+		err = ErrBlockNotFound
+		return
+	}
+
 	moveToRoot := "/" == toPath
 	toBlockID := tree.ID
 	fromFolder := path.Join(path.Dir(fromPath), tree.ID)
@@ -1043,9 +1056,9 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 	if !moveToRoot {
 		var toTree *parse.Tree
 		if isSameBox {
-			toTree, err = LoadTree(fromBoxID, toPath)
+			toTree, err = LoadTree(fromBox.ID, toPath)
 		} else {
-			toTree, err = LoadTree(toBoxID, toPath)
+			toTree, err = LoadTree(toBox.ID, toPath)
 		}
 		if nil != err {
 			err = ErrBlockNotFound
@@ -1075,14 +1088,14 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 				return
 			}
 		} else {
-			absFromPath := filepath.Join(util.DataDir, fromBoxID, fromFolder)
-			absToPath := filepath.Join(util.DataDir, toBoxID, newFolder)
+			absFromPath := filepath.Join(util.DataDir, fromBox.ID, fromFolder)
+			absToPath := filepath.Join(util.DataDir, toBox.ID, newFolder)
 			if gulu.File.IsExist(absToPath) {
 				filelock.Remove(absToPath)
 			}
 			if err = filelock.Move(absFromPath, absToPath); nil != err {
 				msg := fmt.Sprintf(Conf.Language(5), fromBox.Name, fromPath, err)
-				logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBoxID, err)
+				logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBox.ID, err)
 				err = errors.New(msg)
 				return
 			}
@@ -1096,32 +1109,68 @@ func MoveDoc(fromBoxID, fromPath, toBoxID, toPath string) (newPath string, err e
 			return
 		}
 
-		tree, err = LoadTree(fromBoxID, newPath)
+		tree, err = LoadTree(fromBox.ID, newPath)
 		if nil != err {
 			return
 		}
 
 		moveTree(tree)
 	} else {
-		absFromPath := filepath.Join(util.DataDir, fromBoxID, fromPath)
-		absToPath := filepath.Join(util.DataDir, toBoxID, newPath)
+		absFromPath := filepath.Join(util.DataDir, fromBox.ID, fromPath)
+		absToPath := filepath.Join(util.DataDir, toBox.ID, newPath)
 		if err = filelock.Move(absFromPath, absToPath); nil != err {
 			msg := fmt.Sprintf(Conf.Language(5), fromBox.Name, fromPath, err)
-			logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBoxID, err)
+			logging.LogErrorf("move [path=%s] in box [%s] failed: %s", fromPath, fromBox.ID, err)
 			err = errors.New(msg)
 			return
 		}
 
-		tree, err = LoadTree(toBoxID, newPath)
+		tree, err = LoadTree(toBox.ID, newPath)
 		if nil != err {
 			return
 		}
 
 		moveTree(tree)
-		moveSorts(tree.ID, fromBoxID, toBoxID)
+		moveSorts(tree.ID, fromBox.ID, toBox.ID)
+	}
+
+	return
+}
+
+func MoveDocs(fromPaths []string, toPath string) (err error) {
+	util.PushEndlessProgress(Conf.Language(116))
+
+	util.PushEndlessProgress(Conf.Language(113))
+	sql.WaitForWritingDatabase()
+	util.ReloadUI()
+	return
+}
+
+func filterFromPaths(fromPaths []string, toPath string) (retFromPaths []string) {
+	fromPaths = append(fromPaths, toPath)
+	retFromPaths = filterSelfChildDocs(fromPaths)
+	return
+}
+
+func filterSelfChildDocs(paths []string) (ret []string) {
+	sort.Slice(paths, func(i, j int) bool { return len(paths[i]) < len(paths[j]) })
+
+	dirs := map[string]string{}
+	for _, fromPath := range paths {
+		dir := strings.TrimSuffix(fromPath, ".sy")
+		existParent := false
+		for _, d := range dirs {
+			if strings.HasPrefix(d, fromPath) {
+				existParent = true
+				break
+			}
+		}
+		if existParent {
+			continue
+		}
+		dirs[dir] = fromPath
+		ret = append(ret, fromPath)
 	}
-	cache.ClearDocsIAL()
-	IncSync()
 	return
 }
 
@@ -1133,7 +1182,40 @@ func RemoveDoc(boxID, p string) (err error) {
 	}
 
 	WaitForWritingFiles()
-	tree, err := LoadTree(boxID, p)
+	err = removeDoc(box, p)
+	if nil != err {
+		return
+	}
+	IncSync()
+	return
+}
+
+func RemoveDocs(paths []string) (err error) {
+	util.PushEndlessProgress(Conf.Language(116))
+
+	paths = filterSelfChildDocs(paths)
+	var ids []string
+	for _, p := range paths {
+		ids = append(ids, strings.TrimSuffix(path.Base(p), ".sy"))
+	}
+
+	boxes := getBoxesByPaths(ids)
+	WaitForWritingFiles()
+	for p, box := range boxes {
+		err = removeDoc(box, p)
+		if nil != err {
+			return
+		}
+	}
+
+	util.PushEndlessProgress(Conf.Language(113))
+	sql.WaitForWritingDatabase()
+	util.ReloadUI()
+	return
+}
+
+func removeDoc(box *Box, p string) (err error) {
+	tree, err := LoadTree(box.ID, p)
 	if nil != err {
 		return
 	}
@@ -1144,13 +1226,13 @@ func RemoveDoc(boxID, p string) (err error) {
 		return
 	}
 
-	historyPath := filepath.Join(historyDir, boxID, p)
-	absPath := filepath.Join(util.DataDir, boxID, p)
+	historyPath := filepath.Join(historyDir, box.ID, p)
+	absPath := filepath.Join(util.DataDir, box.ID, p)
 	if err = filelock.Copy(absPath, historyPath); nil != err {
 		return errors.New(fmt.Sprintf(Conf.Language(70), box.Name, absPath, err))
 	}
 
-	copyDocAssetsToDataAssets(boxID, p)
+	copyDocAssetsToDataAssets(box.ID, p)
 
 	rootID := tree.ID
 	dir := path.Dir(p)
@@ -1178,7 +1260,7 @@ func RemoveDoc(boxID, p string) (err error) {
 	sql.RemoveTreePathQueue(box.ID, childrenDir)
 
 	if "/" != dir {
-		others, err := os.ReadDir(filepath.Join(util.DataDir, boxID, dir))
+		others, err := os.ReadDir(filepath.Join(util.DataDir, box.ID, dir))
 		if nil == err && 1 > len(others) {
 			box.Remove(dir)
 		}