module.Photo.go_disabled 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. package main
  2. import (
  3. "crypto/md5"
  4. "encoding/hex"
  5. "encoding/json"
  6. "errors"
  7. "fmt"
  8. "image"
  9. "io"
  10. "log"
  11. "net/http"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. "github.com/disintegration/imaging"
  16. reflect "reflect"
  17. )
  18. /*
  19. Photo - The worst Photo Viewer for ArOZ Online
  20. By Alan Yeung, 2020
  21. */
  22. //SupportFileExt shouldn't be exported
  23. var SupportFileExt = []string{".jpg", ".jpeg", ".gif", ".tiff", ".png", ".tif", ".heif"}
  24. //Output shouldn't be exported.
  25. type Output struct {
  26. URL string
  27. Filename string
  28. Size string
  29. CacheURL string
  30. Height int
  31. Width int
  32. }
  33. //OutputFolder shouldn't be exported
  34. type OutputFolder struct {
  35. VPath string
  36. Foldername string
  37. }
  38. //SearchReturn shouldn't be exported
  39. type SearchReturn struct {
  40. Success bool `json:"success"`
  41. Results []struct {
  42. Name string `json:"name"`
  43. Value string `json:"value"`
  44. Text string `json:"text"`
  45. Disabled bool `json:"disabled,omitempty"`
  46. } `json:"results"`
  47. }
  48. func module_Photo_init() {
  49. http.HandleFunc("/Photo/listPhoto", ModulePhotoListPhoto)
  50. http.HandleFunc("/Photo/listFolder", ModulePhotoListFolder)
  51. http.HandleFunc("/Photo/search", ModulePhotoSearch)
  52. //http.HandleFunc("/Photo/getMeta", module_Photo_getMeta)
  53. //Register this module to system
  54. registerModule(moduleInfo{
  55. Name: "Photo",
  56. Desc: "The Photo Viewer for ArOZ Online",
  57. Group: "Media",
  58. IconPath: "Photo/img/module_icon.png",
  59. Version: "0.0.1",
  60. StartDir: "Photo/index.html",
  61. SupportFW: true,
  62. LaunchFWDir: "Photo/index.html",
  63. SupportEmb: true,
  64. LaunchEmb: "Photo/embedded.html",
  65. InitFWSize: []int{800, 600},
  66. InitEmbSize: []int{800, 600},
  67. SupportedExt: SupportFileExt,
  68. })
  69. }
  70. //ModulePhotoListPhoto shouldn't be exported
  71. func ModulePhotoListPhoto(w http.ResponseWriter, r *http.Request) {
  72. username, err := system_auth_getUserName(w, r)
  73. if err != nil {
  74. redirectToLoginPage(w, r)
  75. return
  76. }
  77. //obtain folder name from GET request
  78. folder, ok := r.URL.Query()["folder"]
  79. //check if GET request exists, if not then using default path
  80. if !ok || len(folder[0]) < 1 {
  81. folder = append(folder, "user:/Photo/Photo/uploads")
  82. }
  83. //obtain filter from GET request
  84. filter, ok := r.URL.Query()["q"]
  85. //check if GET request exists, if not then using null
  86. if !ok || len(folder[0]) < 1 {
  87. filter = append(filter, "")
  88. }
  89. Alldata, _ := QueryDir(folder[0], filter[0], username)
  90. jsonString, _ := json.Marshal(Alldata)
  91. sendJSONResponse(w, string(jsonString))
  92. }
  93. //ModulePhotoListFolder shouldn't be exported
  94. func ModulePhotoListFolder(w http.ResponseWriter, r *http.Request) {
  95. username, err := system_auth_getUserName(w, r)
  96. if err != nil {
  97. redirectToLoginPage(w, r)
  98. return
  99. }
  100. storageDir, _ := virtualPathToRealPath("user:/Photo/Photo/storage", username)
  101. os.MkdirAll(storageDir, 0755)
  102. Alldata := []OutputFolder{}
  103. filepath.Walk(storageDir, func(path string, info os.FileInfo, e error) error {
  104. if e != nil {
  105. return e
  106. }
  107. if info.Mode().IsDir() && path != storageDir {
  108. vPath, _ := realpathToVirtualpath(path, username)
  109. folderData := OutputFolder{
  110. VPath: vPath,
  111. Foldername: filepath.Base(path),
  112. }
  113. Alldata = append(Alldata, folderData)
  114. }
  115. return nil
  116. })
  117. jsonString, _ := json.Marshal(Alldata)
  118. sendJSONResponse(w, string(jsonString))
  119. }
  120. //QueryDir shouldn't be exported.
  121. func QueryDir(vPath string, filter string, username string) ([]Output, error) {
  122. //create dir
  123. uploadDir, _ := virtualPathToRealPath(vPath, username)
  124. cacheDir, _ := virtualPathToRealPath("user:/Photo/Photo/thumbnails", username)
  125. os.MkdirAll(uploadDir, 0755)
  126. os.MkdirAll(cacheDir, 0755)
  127. Alldata := []Output{}
  128. files, _ := filepath.Glob(uploadDir + "/*")
  129. for _, file := range files {
  130. if stringInSlice(filepath.Ext(file), SupportFileExt) {
  131. if chkFilter(file, filter) {
  132. //File path (vpath)
  133. vpath, _ := realpathToVirtualpath(file, username)
  134. //File Size
  135. _, hsize, unit, _ := system_fs_getFileSize(file)
  136. size := fmt.Sprintf("%.2f", hsize) + unit
  137. //File cache location
  138. cacheFilename, _ := resizePhoto(file, username)
  139. cacheFilevPath := "user:/Photo/Photo/thumbnails/" + cacheFilename
  140. //Get image width height
  141. cacheFilePhyPath, _ := virtualPathToRealPath(cacheFilevPath, username)
  142. width, height := getImageDimension(cacheFilePhyPath)
  143. fileData := Output{
  144. URL: vpath,
  145. Filename: filepath.Base(file),
  146. Size: size,
  147. CacheURL: cacheFilevPath,
  148. Height: height,
  149. Width: width,
  150. }
  151. Alldata = append(Alldata, fileData)
  152. }
  153. }
  154. }
  155. return Alldata, nil
  156. }
  157. //ModulePhotoSearch shouldn't be exported
  158. func ModulePhotoSearch(w http.ResponseWriter, r *http.Request) {
  159. username, err := system_auth_getUserName(w, r)
  160. if err != nil {
  161. redirectToLoginPage(w, r)
  162. return
  163. }
  164. _ = username
  165. //obtain folder name from GET request
  166. queryString, ok := r.URL.Query()["q"]
  167. QResult := new(SearchReturn)
  168. QResult.Success = true
  169. //check if GET request exists, if not then using default val
  170. if !ok || len(queryString[0]) < 1 {
  171. QResult.Success = false
  172. } else {
  173. n := struct {
  174. Name string `json:"name"`
  175. Value string `json:"value"`
  176. Text string `json:"text"`
  177. Disabled bool `json:"disabled,omitempty"`
  178. }{Name: "file:" + queryString[0], Value: "file:" + queryString[0], Text: "file:" + queryString[0], Disabled: false}
  179. QResult.Results = append(QResult.Results, n)
  180. }
  181. jsonString, _ := json.Marshal(QResult)
  182. sendJSONResponse(w, string(jsonString))
  183. }
  184. func resizePhoto(filename string, username string) (string, error) {
  185. cacheDir, _ := virtualPathToRealPath("user:/Photo/Photo/thumbnails", username)
  186. //Generate hash for that file
  187. md5, err := hashFilemd5(filename)
  188. if err != nil {
  189. return "", err
  190. }
  191. //check if file exist, if true then return
  192. if fileExists(cacheDir + "/" + md5 + ".jpg") {
  193. return md5 + ".jpg", nil
  194. }
  195. // Open image.
  196. src, _ := imaging.Open(filename)
  197. // Resize the cropped image to width = 200px preserving the aspect ratio.
  198. src = imaging.Resize(src, 200, 0, imaging.Lanczos)
  199. // Save the resulting image as JPEG.
  200. err = imaging.Save(src, cacheDir+"/"+md5+".jpg")
  201. if err != nil {
  202. log.Fatalf("failed to save image: %v", err)
  203. }
  204. return md5 + ".jpg", nil
  205. }
  206. //hashFilemd5 copy from https://mrwaggel.be/post/generate-md5-hash-of-a-file-in-golang/
  207. func hashFilemd5(filePath string) (string, error) {
  208. var returnMD5String string
  209. file, err := os.Open(filePath)
  210. if err != nil {
  211. return returnMD5String, err
  212. }
  213. defer file.Close()
  214. hash := md5.New()
  215. if _, err := io.Copy(hash, file); err != nil {
  216. return returnMD5String, err
  217. }
  218. hashInBytes := hash.Sum(nil)[:16]
  219. returnMD5String = hex.EncodeToString(hashInBytes)
  220. return returnMD5String, nil
  221. }
  222. //thanks https://gist.github.com/sergiotapia/7882944
  223. func getImageDimension(imagePath string) (int, int) {
  224. file, err := os.Open(imagePath)
  225. if err != nil {
  226. //log.Fprintf(os.Stderr, "%v\n", err)
  227. }
  228. image, _, err := image.DecodeConfig(file)
  229. if err != nil {
  230. //log.Fprintf(os.Stderr, "%s: %v\n", imagePath, err)
  231. }
  232. return image.Width, image.Height
  233. }
  234. var funcs = map[string]interface{}{"file": file}
  235. func chkFilter(imagePath string, filter string) bool {
  236. if filter == "" {
  237. return true
  238. }
  239. filtersli := strings.Split(filter, ",")
  240. Filterbool := make(map[string]bool)
  241. if len(filtersli) > 0 {
  242. log.Println(filtersli)
  243. for _, item := range filtersli {
  244. itemArr := strings.Split(item, ":") // [0] = func name , [1] = value
  245. log.Println(item)
  246. returnResult, _ := Call(funcs, itemArr[0], itemArr[1], imagePath, filepath.Base(imagePath))
  247. Filterbool[item] = Filterbool[item] || returnResult[0].Bool()
  248. }
  249. }
  250. returnBool := true
  251. if len(Filterbool) > 0 {
  252. for _, item := range Filterbool {
  253. returnBool = returnBool && item
  254. }
  255. }
  256. return returnBool
  257. }
  258. //https://mikespook.com/2012/07/function-call-by-name-in-golang/
  259. //Call shouldn not be exported
  260. func Call(m map[string]interface{}, name string, params ...interface{}) (result []reflect.Value, err error) {
  261. f := reflect.ValueOf(m[name])
  262. if len(params) != f.Type().NumIn() {
  263. err = errors.New("The number of params is not adapted.")
  264. return
  265. }
  266. in := make([]reflect.Value, len(params))
  267. for k, param := range params {
  268. in[k] = reflect.ValueOf(param)
  269. }
  270. result = f.Call(in)
  271. return
  272. }
  273. func file(queryString, filename string, filePath string) bool {
  274. if strings.Contains(filename, queryString) {
  275. return true
  276. } else {
  277. return false
  278. }
  279. }