123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- package main
- import (
- "crypto/md5"
- "encoding/hex"
- "encoding/json"
- "errors"
- "fmt"
- "image"
- "io"
- "log"
- "net/http"
- "os"
- "path/filepath"
- "strings"
- "github.com/disintegration/imaging"
- reflect "reflect"
- )
- /*
- Photo - The worst Photo Viewer for ArOZ Online
- By Alan Yeung, 2020
- */
- //SupportFileExt shouldn't be exported
- var SupportFileExt = []string{".jpg", ".jpeg", ".gif", ".tiff", ".png", ".tif", ".heif"}
- //Output shouldn't be exported.
- type Output struct {
- URL string
- Filename string
- Size string
- CacheURL string
- Height int
- Width int
- }
- //OutputFolder shouldn't be exported
- type OutputFolder struct {
- VPath string
- Foldername string
- }
- //SearchReturn shouldn't be exported
- type SearchReturn struct {
- Success bool `json:"success"`
- Results []struct {
- Name string `json:"name"`
- Value string `json:"value"`
- Text string `json:"text"`
- Disabled bool `json:"disabled,omitempty"`
- } `json:"results"`
- }
- func module_Photo_init() {
- http.HandleFunc("/Photo/listPhoto", ModulePhotoListPhoto)
- http.HandleFunc("/Photo/listFolder", ModulePhotoListFolder)
- http.HandleFunc("/Photo/search", ModulePhotoSearch)
- //http.HandleFunc("/Photo/getMeta", module_Photo_getMeta)
- //Register this module to system
- registerModule(moduleInfo{
- Name: "Photo",
- Desc: "The Photo Viewer for ArOZ Online",
- Group: "Media",
- IconPath: "Photo/img/module_icon.png",
- Version: "0.0.1",
- StartDir: "Photo/index.html",
- SupportFW: true,
- LaunchFWDir: "Photo/index.html",
- SupportEmb: true,
- LaunchEmb: "Photo/embedded.html",
- InitFWSize: []int{800, 600},
- InitEmbSize: []int{800, 600},
- SupportedExt: SupportFileExt,
- })
- }
- //ModulePhotoListPhoto shouldn't be exported
- func ModulePhotoListPhoto(w http.ResponseWriter, r *http.Request) {
- username, err := system_auth_getUserName(w, r)
- if err != nil {
- redirectToLoginPage(w, r)
- return
- }
- //obtain folder name from GET request
- folder, ok := r.URL.Query()["folder"]
- //check if GET request exists, if not then using default path
- if !ok || len(folder[0]) < 1 {
- folder = append(folder, "user:/Photo/Photo/uploads")
- }
- //obtain filter from GET request
- filter, ok := r.URL.Query()["q"]
- //check if GET request exists, if not then using null
- if !ok || len(folder[0]) < 1 {
- filter = append(filter, "")
- }
- Alldata, _ := QueryDir(folder[0], filter[0], username)
- jsonString, _ := json.Marshal(Alldata)
- sendJSONResponse(w, string(jsonString))
- }
- //ModulePhotoListFolder shouldn't be exported
- func ModulePhotoListFolder(w http.ResponseWriter, r *http.Request) {
- username, err := system_auth_getUserName(w, r)
- if err != nil {
- redirectToLoginPage(w, r)
- return
- }
- storageDir, _ := virtualPathToRealPath("user:/Photo/Photo/storage", username)
- os.MkdirAll(storageDir, 0755)
- Alldata := []OutputFolder{}
- filepath.Walk(storageDir, func(path string, info os.FileInfo, e error) error {
- if e != nil {
- return e
- }
- if info.Mode().IsDir() && path != storageDir {
- vPath, _ := realpathToVirtualpath(path, username)
- folderData := OutputFolder{
- VPath: vPath,
- Foldername: filepath.Base(path),
- }
- Alldata = append(Alldata, folderData)
- }
- return nil
- })
- jsonString, _ := json.Marshal(Alldata)
- sendJSONResponse(w, string(jsonString))
- }
- //QueryDir shouldn't be exported.
- func QueryDir(vPath string, filter string, username string) ([]Output, error) {
- //create dir
- uploadDir, _ := virtualPathToRealPath(vPath, username)
- cacheDir, _ := virtualPathToRealPath("user:/Photo/Photo/thumbnails", username)
- os.MkdirAll(uploadDir, 0755)
- os.MkdirAll(cacheDir, 0755)
- Alldata := []Output{}
- files, _ := filepath.Glob(uploadDir + "/*")
- for _, file := range files {
- if stringInSlice(filepath.Ext(file), SupportFileExt) {
- if chkFilter(file, filter) {
- //File path (vpath)
- vpath, _ := realpathToVirtualpath(file, username)
- //File Size
- _, hsize, unit, _ := system_fs_getFileSize(file)
- size := fmt.Sprintf("%.2f", hsize) + unit
- //File cache location
- cacheFilename, _ := resizePhoto(file, username)
- cacheFilevPath := "user:/Photo/Photo/thumbnails/" + cacheFilename
- //Get image width height
- cacheFilePhyPath, _ := virtualPathToRealPath(cacheFilevPath, username)
- width, height := getImageDimension(cacheFilePhyPath)
- fileData := Output{
- URL: vpath,
- Filename: filepath.Base(file),
- Size: size,
- CacheURL: cacheFilevPath,
- Height: height,
- Width: width,
- }
- Alldata = append(Alldata, fileData)
- }
- }
- }
- return Alldata, nil
- }
- //ModulePhotoSearch shouldn't be exported
- func ModulePhotoSearch(w http.ResponseWriter, r *http.Request) {
- username, err := system_auth_getUserName(w, r)
- if err != nil {
- redirectToLoginPage(w, r)
- return
- }
- _ = username
- //obtain folder name from GET request
- queryString, ok := r.URL.Query()["q"]
- QResult := new(SearchReturn)
- QResult.Success = true
- //check if GET request exists, if not then using default val
- if !ok || len(queryString[0]) < 1 {
- QResult.Success = false
- } else {
- n := struct {
- Name string `json:"name"`
- Value string `json:"value"`
- Text string `json:"text"`
- Disabled bool `json:"disabled,omitempty"`
- }{Name: "file:" + queryString[0], Value: "file:" + queryString[0], Text: "file:" + queryString[0], Disabled: false}
- QResult.Results = append(QResult.Results, n)
- }
- jsonString, _ := json.Marshal(QResult)
- sendJSONResponse(w, string(jsonString))
- }
- func resizePhoto(filename string, username string) (string, error) {
- cacheDir, _ := virtualPathToRealPath("user:/Photo/Photo/thumbnails", username)
- //Generate hash for that file
- md5, err := hashFilemd5(filename)
- if err != nil {
- return "", err
- }
- //check if file exist, if true then return
- if fileExists(cacheDir + "/" + md5 + ".jpg") {
- return md5 + ".jpg", nil
- }
- // Open image.
- src, _ := imaging.Open(filename)
- // Resize the cropped image to width = 200px preserving the aspect ratio.
- src = imaging.Resize(src, 200, 0, imaging.Lanczos)
- // Save the resulting image as JPEG.
- err = imaging.Save(src, cacheDir+"/"+md5+".jpg")
- if err != nil {
- log.Fatalf("failed to save image: %v", err)
- }
- return md5 + ".jpg", nil
- }
- //hashFilemd5 copy from https://mrwaggel.be/post/generate-md5-hash-of-a-file-in-golang/
- func hashFilemd5(filePath string) (string, error) {
- var returnMD5String string
- file, err := os.Open(filePath)
- if err != nil {
- return returnMD5String, err
- }
- defer file.Close()
- hash := md5.New()
- if _, err := io.Copy(hash, file); err != nil {
- return returnMD5String, err
- }
- hashInBytes := hash.Sum(nil)[:16]
- returnMD5String = hex.EncodeToString(hashInBytes)
- return returnMD5String, nil
- }
- //thanks https://gist.github.com/sergiotapia/7882944
- func getImageDimension(imagePath string) (int, int) {
- file, err := os.Open(imagePath)
- if err != nil {
- //log.Fprintf(os.Stderr, "%v\n", err)
- }
- image, _, err := image.DecodeConfig(file)
- if err != nil {
- //log.Fprintf(os.Stderr, "%s: %v\n", imagePath, err)
- }
- return image.Width, image.Height
- }
- var funcs = map[string]interface{}{"file": file}
- func chkFilter(imagePath string, filter string) bool {
- if filter == "" {
- return true
- }
- filtersli := strings.Split(filter, ",")
- Filterbool := make(map[string]bool)
- if len(filtersli) > 0 {
- log.Println(filtersli)
- for _, item := range filtersli {
- itemArr := strings.Split(item, ":") // [0] = func name , [1] = value
- log.Println(item)
- returnResult, _ := Call(funcs, itemArr[0], itemArr[1], imagePath, filepath.Base(imagePath))
- Filterbool[item] = Filterbool[item] || returnResult[0].Bool()
- }
- }
- returnBool := true
- if len(Filterbool) > 0 {
- for _, item := range Filterbool {
- returnBool = returnBool && item
- }
- }
- return returnBool
- }
- //https://mikespook.com/2012/07/function-call-by-name-in-golang/
- //Call shouldn not be exported
- func Call(m map[string]interface{}, name string, params ...interface{}) (result []reflect.Value, err error) {
- f := reflect.ValueOf(m[name])
- if len(params) != f.Type().NumIn() {
- err = errors.New("The number of params is not adapted.")
- return
- }
- in := make([]reflect.Value, len(params))
- for k, param := range params {
- in[k] = reflect.ValueOf(param)
- }
- result = f.Call(in)
- return
- }
- func file(queryString, filename string, filePath string) bool {
- if strings.Contains(filename, queryString) {
- return true
- } else {
- return false
- }
- }
|