wide/file/files.go

858 lines
20 KiB
Go
Raw Normal View History

// Copyright (c) 2014-2019, b3log.org & hacpai.com
2014-11-13 06:54:52 +03:00
//
2014-11-12 18:13:14 +03:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-11-13 06:54:52 +03:00
//
2018-03-12 07:28:33 +03:00
// https://www.apache.org/licenses/LICENSE-2.0
2014-11-13 06:54:52 +03:00
//
2014-11-12 18:13:14 +03:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2014-12-07 06:29:45 +03:00
// Package file includes file related manipulations.
2014-08-18 17:45:43 +04:00
package file
import (
"encoding/json"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"sort"
"strings"
2014-09-07 13:31:57 +04:00
"github.com/b3log/wide/conf"
2014-10-28 19:04:46 +03:00
"github.com/b3log/wide/event"
2014-12-13 13:47:41 +03:00
"github.com/b3log/wide/log"
2014-09-17 10:35:48 +04:00
"github.com/b3log/wide/session"
2014-09-07 13:31:57 +04:00
"github.com/b3log/wide/util"
2014-08-18 17:45:43 +04:00
)
2014-12-13 13:47:41 +03:00
// Logger.
var logger = log.NewLogger(os.Stdout)
2014-12-07 06:29:45 +03:00
// Node represents a file node in file tree.
type Node struct {
2015-04-27 09:36:35 +03:00
Id string `json:"id"`
2014-12-07 06:29:45 +03:00
Name string `json:"name"`
Path string `json:"path"`
2015-03-08 07:43:04 +03:00
IconSkin string `json:"iconSkin"` // Value should be end with a space
IsParent bool `json:"isParent"`
2014-12-07 06:29:45 +03:00
Type string `json:"type"` // "f": file, "d": directory
Creatable bool `json:"creatable"` // whether can create file in this file node
Removable bool `json:"removable"` // whether can remove this file node
2014-12-25 11:08:57 +03:00
IsGoAPI bool `json:"isGOAPI"`
2014-12-07 06:29:45 +03:00
Mode string `json:"mode"`
Children []*Node `json:"children"`
2014-10-11 10:08:29 +04:00
}
2014-12-07 06:29:45 +03:00
// Snippet represents a source code snippet, used to as the result of "Find Usages", "Search".
2014-10-11 10:08:29 +04:00
type Snippet struct {
2014-10-28 19:37:47 +03:00
Path string `json:"path"` // file path
Line int `json:"line"` // line number
Ch int `json:"ch"` // column number
Contents []string `json:"contents"` // lines nearby
2014-10-11 10:08:29 +04:00
}
2014-12-07 06:29:45 +03:00
var apiNode *Node
2014-11-19 10:20:06 +03:00
// initAPINode builds the Go API file node.
func initAPINode() {
2014-11-21 12:41:51 +03:00
apiPath := util.Go.GetAPIPath()
2014-11-19 10:20:06 +03:00
2014-12-07 06:29:45 +03:00
apiNode = &Node{Name: "Go API", Path: apiPath, IconSkin: "ico-ztree-dir-api ", Type: "d",
2014-12-25 11:08:57 +03:00
Creatable: false, Removable: false, IsGoAPI: true, Children: []*Node{}}
2014-11-19 10:20:06 +03:00
2014-12-25 11:08:57 +03:00
walk(apiPath, apiNode, false, false, true)
2014-11-19 10:20:06 +03:00
}
2015-03-09 09:16:46 +03:00
// GetFilesHandler handles request of constructing user workspace file tree.
2014-09-25 13:34:05 +04:00
//
2014-11-16 11:25:27 +03:00
// The Go API source code package also as a child node,
2014-11-25 09:20:45 +03:00
// so that users can easily view the Go API source code in file tree.
2015-03-09 09:16:46 +03:00
func GetFilesHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
2014-11-26 08:49:27 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
2014-08-31 10:15:13 +04:00
2014-11-26 08:49:27 +03:00
return
}
2015-04-29 13:04:30 +03:00
username := httpSession.Values["username"].(string)
2015-10-20 13:31:20 +03:00
result := util.NewResult()
defer util.RetGzResult(w, r, result)
2014-11-26 08:49:27 +03:00
userWorkspace := conf.GetUserWorkspace(username)
2014-10-28 08:22:39 +03:00
workspaces := filepath.SplitList(userWorkspace)
2014-08-31 10:15:13 +04:00
2015-03-08 07:43:04 +03:00
root := Node{Name: "root", Path: "", IconSkin: "ico-ztree-dir ", Type: "d", IsParent: true, Children: []*Node{}}
2014-10-26 09:46:15 +03:00
2014-11-19 10:20:06 +03:00
if nil == apiNode { // lazy init
initAPINode()
}
2014-10-28 19:37:47 +03:00
// workspace node process
2014-10-26 09:46:15 +03:00
for _, workspace := range workspaces {
workspacePath := workspace + conf.PathSeparator + "src"
2015-04-27 09:36:35 +03:00
workspaceNode := Node{
2015-04-27 09:37:27 +03:00
Id: filepath.ToSlash(workspacePath), // jQuery API can't accept "\", so we convert it to "/"
2015-04-27 09:36:35 +03:00
Name: workspace[strings.LastIndex(workspace, conf.PathSeparator)+1:],
2015-07-23 11:31:37 +03:00
Path: filepath.ToSlash(workspacePath),
2015-04-27 09:36:35 +03:00
IconSkin: "ico-ztree-dir-workspace ",
Type: "d",
Creatable: true,
Removable: false,
IsGoAPI: false,
Children: []*Node{}}
2014-10-26 09:46:15 +03:00
2014-12-25 11:08:57 +03:00
walk(workspacePath, &workspaceNode, true, true, false)
2014-10-26 09:46:15 +03:00
2014-10-28 19:37:47 +03:00
// add workspace node
2014-12-07 06:29:45 +03:00
root.Children = append(root.Children, &workspaceNode)
2014-10-26 09:46:15 +03:00
}
2014-08-18 17:45:43 +04:00
2014-10-28 19:37:47 +03:00
// add Go API node
2014-12-07 06:29:45 +03:00
root.Children = append(root.Children, apiNode)
2014-08-18 17:45:43 +04:00
2015-10-20 13:31:20 +03:00
result.Data = root
2014-08-18 17:45:43 +04:00
}
2015-03-09 09:16:46 +03:00
// RefreshDirectoryHandler handles request of refresh a directory of file tree.
func RefreshDirectoryHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2014-11-25 09:20:45 +03:00
r.ParseForm()
path := r.FormValue("path")
2015-07-23 11:31:37 +03:00
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-12-07 06:29:45 +03:00
node := Node{Name: "root", Path: path, IconSkin: "ico-ztree-dir ", Type: "d", Children: []*Node{}}
2014-11-25 09:20:45 +03:00
2014-12-25 11:08:57 +03:00
walk(path, &node, true, true, false)
2014-11-25 09:20:45 +03:00
w.Header().Set("Content-Type", "application/json")
2014-12-07 06:29:45 +03:00
data, err := json.Marshal(node.Children)
2014-11-25 09:20:45 +03:00
if err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-11-25 09:20:45 +03:00
return
}
w.Write(data)
}
2015-03-09 09:16:46 +03:00
// GetFileHandler handles request of opening file by editor.
func GetFileHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-09-01 14:55:11 +04:00
2014-08-18 17:45:43 +04:00
var args map[string]interface{}
2014-10-28 16:32:19 +03:00
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
return
}
path := args["path"].(string)
2015-07-23 11:31:37 +03:00
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
size := util.File.GetFileSize(path)
if size > 5242880 { // 5M
2015-11-24 11:30:37 +03:00
result.Succ = false
result.Msg = "This file is too large to open :("
return
}
2015-11-24 11:30:37 +03:00
data := map[string]interface{}{}
2015-11-27 11:55:28 +03:00
result.Data = &data
2015-11-24 11:30:37 +03:00
2014-08-18 17:45:43 +04:00
buf, _ := ioutil.ReadFile(path)
2014-09-05 17:10:28 +04:00
extension := filepath.Ext(path)
2014-09-01 17:40:10 +04:00
2014-11-16 11:25:27 +03:00
if util.File.IsImg(extension) {
2014-10-28 19:37:47 +03:00
// image file will be open in a browser tab
2014-09-01 17:40:10 +04:00
data["mode"] = "img"
username := conf.GetOwner(path)
if "" == username {
2018-10-28 05:07:01 +03:00
logger.Warnf("The path [%s] has no owner", path)
data["path"] = ""
return
}
user := conf.GetUser(username)
2017-05-05 08:35:09 +03:00
data["path"] = "/workspace/" + user.Name + "/" + strings.Replace(path, user.WorkspacePath(), "", 1)
2014-09-01 17:40:10 +04:00
return
}
2014-10-31 08:15:40 +03:00
content := string(buf)
2014-09-01 14:55:11 +04:00
2014-11-16 11:25:27 +03:00
if util.File.IsBinary(content) {
2015-11-24 11:30:37 +03:00
result.Succ = false
result.Msg = "Can't open a binary file :("
2014-09-01 14:55:11 +04:00
} else {
2014-10-31 08:15:40 +03:00
data["content"] = content
2014-09-13 09:05:50 +04:00
data["path"] = path
2014-08-18 17:45:43 +04:00
}
}
2015-03-09 09:16:46 +03:00
// SaveFileHandler handles request of saving file.
func SaveFileHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-09-01 16:50:51 +04:00
2014-08-18 17:45:43 +04:00
var args map[string]interface{}
2014-10-28 16:32:19 +03:00
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
return
}
filePath := args["file"].(string)
2014-10-29 04:29:12 +03:00
sid := args["sid"].(string)
2014-08-18 17:45:43 +04:00
2015-07-23 11:31:37 +03:00
if util.Go.IsAPI(filePath) || !session.CanAccess(username, filePath) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-08-18 17:45:43 +04:00
fout, err := os.Create(filePath)
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
return
}
code := args["code"].(string)
fout.WriteString(code)
if err := fout.Close(); nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
2014-10-29 04:29:12 +03:00
wSession := session.WideSessions.Get(sid)
2014-10-28 19:37:47 +03:00
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't save file " + filePath}
2014-08-18 17:45:43 +04:00
return
}
}
2015-03-09 09:16:46 +03:00
// NewFileHandler handles request of creating file or directory.
func NewFileHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-09-01 16:50:51 +04:00
2014-08-18 17:45:43 +04:00
var args map[string]interface{}
2014-10-28 16:32:19 +03:00
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
return
}
path := args["path"].(string)
2015-04-29 13:04:30 +03:00
2015-07-23 11:31:37 +03:00
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-08-18 17:45:43 +04:00
fileType := args["fileType"].(string)
2014-10-28 19:04:46 +03:00
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
2014-08-18 17:45:43 +04:00
if !createFile(path, fileType) {
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-09-22 10:20:17 +04:00
2014-10-28 19:04:46 +03:00
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't create file " + path}
2014-09-22 10:20:17 +04:00
return
}
if "f" == fileType {
2015-03-13 16:04:31 +03:00
logger.Debugf("Created a file [%s] by user [%s]", path, wSession.Username)
} else {
logger.Debugf("Created a dir [%s] by user [%s]", path, wSession.Username)
2014-08-18 17:45:43 +04:00
}
2015-03-13 16:04:31 +03:00
2014-08-18 17:45:43 +04:00
}
2015-03-09 09:16:46 +03:00
// RemoveFileHandler handles request of removing file or directory.
func RemoveFileHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-09-01 16:50:51 +04:00
2014-08-18 17:45:43 +04:00
var args map[string]interface{}
2014-10-28 16:32:19 +03:00
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-08-18 17:45:43 +04:00
return
}
path := args["path"].(string)
2015-07-23 11:31:37 +03:00
if util.Go.IsAPI(path) || !session.CanAccess(username, path) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-10-29 04:29:12 +03:00
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
2014-08-18 17:45:43 +04:00
if !removeFile(path) {
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-10-28 19:37:47 +03:00
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
Data: "can't remove file " + path}
2015-03-13 16:04:31 +03:00
return
2014-08-18 17:45:43 +04:00
}
2015-03-13 16:04:31 +03:00
logger.Debugf("Removed a file [%s] by user [%s]", path, wSession.Username)
2014-08-18 17:45:43 +04:00
}
2015-03-09 09:16:46 +03:00
// RenameFileHandler handles request of renaming file or directory.
func RenameFileHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-11-07 12:22:20 +03:00
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-11-07 12:22:20 +03:00
return
}
oldPath := args["oldPath"].(string)
2015-07-23 11:31:37 +03:00
if util.Go.IsAPI(oldPath) ||
!session.CanAccess(username, oldPath) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-11-07 12:22:20 +03:00
newPath := args["newPath"].(string)
2015-07-23 11:31:37 +03:00
if util.Go.IsAPI(newPath) || !session.CanAccess(username, newPath) {
2015-04-29 13:04:30 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2014-11-07 12:22:20 +03:00
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
if !renameFile(oldPath, newPath) {
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-11-07 12:22:20 +03:00
wSession.EventQueue.Queue <- &event.Event{Code: event.EvtCodeServerInternalError, Sid: sid,
2014-11-07 18:11:37 +03:00
Data: "can't rename file " + oldPath}
2015-03-13 16:04:31 +03:00
return
2014-11-07 12:22:20 +03:00
}
2015-03-13 16:04:31 +03:00
logger.Debugf("Renamed a file [%s] to [%s] by user [%s]", oldPath, newPath, wSession.Username)
2014-11-07 12:22:20 +03:00
}
2014-11-13 12:00:29 +03:00
// Use to find results sorting.
type foundPath struct {
Path string `json:"path"`
score int
}
type foundPaths []*foundPath
func (f foundPaths) Len() int { return len(f) }
func (f foundPaths) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
func (f foundPaths) Less(i, j int) bool { return f[i].score > f[j].score }
2015-03-09 09:16:46 +03:00
// FindHandler handles request of find files under the specified directory with the specified filename pattern.
func FindHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
username := httpSession.Values["username"].(string)
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-11-13 06:54:52 +03:00
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-11-13 06:54:52 +03:00
return
}
2014-11-13 12:00:29 +03:00
path := args["path"].(string) // path of selected file in file tree
2015-07-23 11:31:37 +03:00
if !util.Go.IsAPI(path) && !session.CanAccess(username, path) {
2014-11-26 08:49:27 +03:00
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2015-04-29 13:04:30 +03:00
name := args["name"].(string)
2014-11-26 08:49:27 +03:00
userWorkspace := conf.GetUserWorkspace(username)
2014-11-13 12:00:29 +03:00
workspaces := filepath.SplitList(userWorkspace)
2014-11-16 11:25:27 +03:00
if "" != path && !util.File.IsDir(path) {
2014-11-13 12:00:29 +03:00
path = filepath.Dir(path)
}
founds := foundPaths{}
for _, workspace := range workspaces {
2014-11-13 16:52:25 +03:00
rs := find(workspace+conf.PathSeparator+"src", name, []*string{})
2014-11-13 12:00:29 +03:00
for _, r := range rs {
substr := util.Str.LCS(path, *r)
2015-09-26 10:39:09 +03:00
founds = append(founds, &foundPath{Path: filepath.ToSlash(*r), score: len(substr)})
2014-11-13 12:00:29 +03:00
}
}
sort.Sort(founds)
2014-11-13 06:54:52 +03:00
2015-11-24 11:30:37 +03:00
result.Data = founds
2014-11-13 06:54:52 +03:00
}
2015-03-09 09:16:46 +03:00
// SearchTextHandler handles request of searching files under the specified directory with the specified keyword.
func SearchTextHandler(w http.ResponseWriter, r *http.Request) {
2015-04-29 13:04:30 +03:00
httpSession, _ := session.HTTPSession.Get(r, "wide-session")
if httpSession.IsNew {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}
2015-11-24 11:30:37 +03:00
result := util.NewResult()
defer util.RetResult(w, r, result)
2014-10-11 10:08:29 +04:00
var args map[string]interface{}
if err := json.NewDecoder(r.Body).Decode(&args); err != nil {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-10-11 10:08:29 +04:00
return
}
2014-12-05 10:08:44 +03:00
sid := args["sid"].(string)
wSession := session.WideSessions.Get(sid)
if nil == wSession {
2015-11-24 11:30:37 +03:00
result.Succ = false
2014-12-05 10:08:44 +03:00
return
}
// XXX: just one directory
2014-10-11 10:08:29 +04:00
dir := args["dir"].(string)
2014-12-05 10:08:44 +03:00
if "" == dir {
userWorkspace := conf.GetUserWorkspace(wSession.Username)
2014-12-05 10:08:44 +03:00
workspaces := filepath.SplitList(userWorkspace)
dir = workspaces[0]
}
2014-10-11 10:59:38 +04:00
extension := args["extension"].(string)
text := args["text"].(string)
2014-10-11 10:08:29 +04:00
2014-12-23 06:50:01 +03:00
founds := []*Snippet{}
if util.File.IsDir(dir) {
founds = search(dir, extension, text, []*Snippet{})
} else {
founds = searchInFile(dir, text)
}
2014-10-11 10:08:29 +04:00
2015-11-24 11:30:37 +03:00
result.Data = founds
2014-08-18 17:45:43 +04:00
}
2014-10-28 19:37:47 +03:00
// walk traverses the specified path to build a file tree.
2014-12-25 11:08:57 +03:00
func walk(path string, node *Node, creatable, removable, isGOAPI bool) {
2014-08-18 17:45:43 +04:00
files := listFiles(path)
for _, filename := range files {
fpath := filepath.Join(path, filename)
fio, _ := os.Lstat(fpath)
2015-04-27 09:36:35 +03:00
child := Node{
2015-04-27 09:37:27 +03:00
Id: filepath.ToSlash(fpath), // jQuery API can't accept "\", so we convert it to "/"
2015-04-27 09:36:35 +03:00
Name: filename,
2015-07-23 11:31:37 +03:00
Path: filepath.ToSlash(fpath),
2015-04-27 09:36:35 +03:00
Removable: removable,
IsGoAPI: isGOAPI,
Children: []*Node{}}
2014-12-07 06:29:45 +03:00
node.Children = append(node.Children, &child)
2014-08-18 17:45:43 +04:00
if nil == fio {
2014-12-13 13:47:41 +03:00
logger.Warnf("Path [%s] is nil", fpath)
2014-08-18 17:45:43 +04:00
continue
}
if fio.IsDir() {
child.Type = "d"
2014-11-06 12:06:53 +03:00
child.Creatable = creatable
2014-09-05 13:39:21 +04:00
child.IconSkin = "ico-ztree-dir "
2015-03-08 07:43:04 +03:00
child.IsParent = true
2014-08-23 19:06:23 +04:00
2014-12-25 11:08:57 +03:00
walk(fpath, &child, creatable, removable, isGOAPI)
2014-08-18 17:45:43 +04:00
} else {
child.Type = "f"
2014-11-06 12:06:53 +03:00
child.Creatable = creatable
2014-09-05 17:10:28 +04:00
ext := filepath.Ext(fpath)
2014-09-05 13:01:20 +04:00
2014-09-05 17:10:28 +04:00
child.IconSkin = getIconSkin(ext)
2014-08-18 17:45:43 +04:00
}
}
return
}
2014-10-28 19:37:47 +03:00
// listFiles lists names of files under the specified dirname.
2014-08-18 17:45:43 +04:00
func listFiles(dirname string) []string {
f, _ := os.Open(dirname)
names, _ := f.Readdirnames(-1)
f.Close()
sort.Strings(names)
2014-08-23 19:06:23 +04:00
dirs := []string{}
files := []string{}
2014-10-28 19:37:47 +03:00
// sort: directories in front of files
2014-08-23 19:06:23 +04:00
for _, name := range names {
2014-11-12 09:49:14 +03:00
path := filepath.Join(dirname, name)
fio, err := os.Lstat(path)
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Warnf("Can't read file info [%s]", path)
2014-11-12 09:49:14 +03:00
continue
}
2014-08-23 19:06:23 +04:00
if fio.IsDir() {
// exclude the .git, .svn, .hg direcitory
if ".git" == fio.Name() || ".svn" == fio.Name() || ".hg" == fio.Name() {
2014-08-31 10:07:35 +04:00
continue
}
2014-08-23 19:06:23 +04:00
dirs = append(dirs, name)
} else {
2015-09-12 09:31:30 +03:00
// exclude the .DS_Store directory on Mac OS X
if ".DS_Store" == fio.Name() {
continue
}
2014-08-23 19:06:23 +04:00
files = append(files, name)
}
}
return append(dirs, files...)
2014-08-18 17:45:43 +04:00
}
2014-10-28 19:37:47 +03:00
// getIconSkin gets CSS class name of icon with the specified filename extension.
2014-09-25 13:34:05 +04:00
//
2014-10-28 19:37:47 +03:00
// Refers to the zTree document for CSS class names.
2014-09-05 13:01:20 +04:00
func getIconSkin(filenameExtension string) string {
2014-11-16 11:25:27 +03:00
if util.File.IsImg(filenameExtension) {
2014-09-16 13:54:50 +04:00
return "ico-ztree-img "
}
2014-09-05 13:01:20 +04:00
switch filenameExtension {
2014-09-16 13:54:50 +04:00
case ".html", ".htm":
return "ico-ztree-html "
case ".go":
return "ico-ztree-go "
case ".css":
return "ico-ztree-css "
2014-09-05 13:01:20 +04:00
case ".txt":
2014-09-05 13:39:21 +04:00
return "ico-ztree-text "
2014-09-16 13:54:50 +04:00
case ".sql":
return "ico-ztree-sql "
2014-09-05 13:01:20 +04:00
case ".properties":
2014-09-05 13:39:21 +04:00
return "ico-ztree-pro "
2014-09-16 13:54:50 +04:00
case ".md":
return "ico-ztree-md "
2014-09-20 09:21:38 +04:00
case ".js", ".json":
2014-09-16 13:54:50 +04:00
return "ico-ztree-js "
case ".xml":
return "ico-ztree-xml "
2014-09-05 13:01:20 +04:00
default:
2014-09-16 13:54:50 +04:00
return "ico-ztree-other "
2014-09-05 13:01:20 +04:00
}
}
2014-10-28 19:37:47 +03:00
// createFile creates file on the specified path.
2014-09-25 13:34:05 +04:00
//
// fileType:
//
2014-10-28 19:37:47 +03:00
// "f": file
// "d": directory
2014-08-18 17:45:43 +04:00
func createFile(path, fileType string) bool {
switch fileType {
case "f":
2014-11-12 06:32:04 +03:00
file, err := os.OpenFile(path, os.O_CREATE, 0775)
2014-08-18 17:45:43 +04:00
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-08-18 17:45:43 +04:00
return false
}
defer file.Close()
2015-03-13 16:04:31 +03:00
logger.Tracef("Created file [%s]", path)
2014-08-18 17:45:43 +04:00
return true
case "d":
err := os.Mkdir(path, 0775)
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Error(err)
2014-10-26 10:59:12 +03:00
return false
2014-08-18 17:45:43 +04:00
}
2015-03-13 16:04:31 +03:00
logger.Tracef("Created directory [%s]", path)
2014-08-18 17:45:43 +04:00
return true
default:
2014-12-13 13:47:41 +03:00
logger.Errorf("Unsupported file type [%s]", fileType)
2014-08-18 17:45:43 +04:00
return false
}
}
2014-10-28 19:37:47 +03:00
// removeFile removes file on the specified path.
2014-08-18 17:45:43 +04:00
func removeFile(path string) bool {
if err := os.RemoveAll(path); nil != err {
2014-12-13 13:47:41 +03:00
logger.Errorf("Removes [%s] failed: [%s]", path, err.Error())
2014-08-18 17:45:43 +04:00
return false
}
2015-03-13 16:04:31 +03:00
logger.Tracef("Removed [%s]", path)
2014-08-18 17:45:43 +04:00
return true
}
2014-09-01 17:40:10 +04:00
2014-11-07 12:22:20 +03:00
// renameFile renames (moves) a file from the specified old path to the specified new path.
func renameFile(oldPath, newPath string) bool {
if err := os.Rename(oldPath, newPath); nil != err {
2014-12-13 13:47:41 +03:00
logger.Errorf("Renames [%s] failed: [%s]", oldPath, err.Error())
2014-11-07 12:22:20 +03:00
return false
}
2015-03-13 16:04:31 +03:00
logger.Tracef("Renamed [%s] to [%s]", oldPath, newPath)
2014-11-07 12:22:20 +03:00
return true
}
2014-11-14 12:31:16 +03:00
// Default exclude file name patterns when find.
var defaultExcludesFind = []string{".git", ".svn", ".repository", "CVS", "RCS", "SCCS", ".bzr", ".metadata", ".hg"}
2014-11-13 09:12:21 +03:00
// find finds files under the specified dir and its sub-directoryies with the specified name,
2014-11-13 06:54:52 +03:00
// likes the command 'find dir -name name'.
func find(dir, name string, results []*string) []*string {
if !strings.HasSuffix(dir, conf.PathSeparator) {
dir += conf.PathSeparator
}
f, _ := os.Open(dir)
fileInfos, err := f.Readdir(-1)
f.Close()
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Errorf("Read dir [%s] failed: [%s]", dir, err.Error())
2014-11-13 06:54:52 +03:00
return results
}
for _, fileInfo := range fileInfos {
2014-11-14 12:31:16 +03:00
fname := fileInfo.Name()
path := dir + fname
2014-11-13 06:54:52 +03:00
if fileInfo.IsDir() {
2014-11-14 12:31:16 +03:00
if util.Str.Contains(fname, defaultExcludesFind) {
continue
}
2014-11-13 06:54:52 +03:00
// enter the directory recursively
results = find(path, name, results)
} else {
// match filename
pattern := filepath.Dir(path) + conf.PathSeparator + name
2014-11-28 05:55:46 +03:00
match, err := filepath.Match(strings.ToLower(pattern), strings.ToLower(path))
2014-11-13 06:54:52 +03:00
if nil != err {
2014-12-31 13:02:04 +03:00
logger.Errorf("Find match filename failed: [%s]", err.Error())
2014-11-13 06:54:52 +03:00
continue
}
if match {
results = append(results, &path)
}
}
}
return results
}
// search finds file under the specified dir and its sub-directories with the specified text, likes the command 'grep'
// or 'findstr'.
2014-10-11 10:08:29 +04:00
func search(dir, extension, text string, snippets []*Snippet) []*Snippet {
2014-10-13 10:34:42 +04:00
if !strings.HasSuffix(dir, conf.PathSeparator) {
dir += conf.PathSeparator
2014-10-11 11:29:21 +04:00
}
2014-10-11 10:08:29 +04:00
f, _ := os.Open(dir)
2014-10-11 10:45:44 +04:00
fileInfos, err := f.Readdir(-1)
2014-10-11 10:08:29 +04:00
f.Close()
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Errorf("Read dir [%s] failed: [%s]", dir, err.Error())
2014-10-11 10:08:29 +04:00
return snippets
}
2014-10-11 10:45:44 +04:00
for _, fileInfo := range fileInfos {
2014-10-11 11:29:21 +04:00
path := dir + fileInfo.Name()
2014-10-11 10:45:44 +04:00
2014-10-11 11:29:21 +04:00
if fileInfo.IsDir() {
2014-10-28 19:37:47 +03:00
// enter the directory recursively
2014-10-11 11:29:21 +04:00
snippets = search(path, extension, text, snippets)
} else if strings.HasSuffix(path, extension) {
2014-10-28 19:37:47 +03:00
// grep in file
2014-10-11 10:45:44 +04:00
ss := searchInFile(path, text)
snippets = append(snippets, ss...)
}
}
2014-10-11 10:08:29 +04:00
return snippets
}
2014-10-28 19:37:47 +03:00
// searchInFile finds file with the specified path and text.
2014-10-11 10:45:44 +04:00
func searchInFile(path string, text string) []*Snippet {
ret := []*Snippet{}
bytes, err := ioutil.ReadFile(path)
if nil != err {
2014-12-13 13:47:41 +03:00
logger.Errorf("Read file [%s] failed: [%s]", path, err.Error())
2014-10-11 10:45:44 +04:00
return ret
}
content := string(bytes)
2014-11-16 11:25:27 +03:00
if util.File.IsBinary(content) {
2014-10-31 08:15:40 +03:00
return ret
}
2014-10-11 10:45:44 +04:00
lines := strings.Split(content, "\n")
for idx, line := range lines {
2014-12-23 06:50:01 +03:00
ch := strings.Index(strings.ToLower(line), strings.ToLower(text))
2014-10-11 10:45:44 +04:00
if -1 != ch {
2015-09-26 11:01:09 +03:00
snippet := &Snippet{Path: filepath.ToSlash(path),
Line: idx + 1, Ch: ch + 1, Contents: []string{line}}
2014-10-11 10:45:44 +04:00
ret = append(ret, snippet)
}
}
return ret
}