go-blog/controllers/file.go

322 lines
7.1 KiB
Go

package controllers
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"syscall"
"git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models"
)
type FileHandler struct {
Context *middleware.AppContext
}
//FileGetHandler serves the file based on the url filename
func (fh FileHandler) FileGetHandler(w http.ResponseWriter, r *http.Request) {
rv := getVar(r, "uniquename")
f, err := fh.Context.FileService.GetByUniqueName(rv, nil)
if err != nil {
http.Error(w, "the file was not found", http.StatusNotFound)
return
}
loc := filepath.Join(fh.Context.ConfigService.Location, f.UniqueName)
if f.Inline {
w.Header().Set("Content-Type", f.ContentType)
w.Header().Set("Content-Disposition", "inline")
} else {
w.Header().Set("Content-Type", f.ContentType)
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", f.FullFilename))
}
rf, err := os.Open(loc)
if err != nil {
if os.IsNotExist(err) {
logger.Log.Errorf("the file %s was not found - %v", loc, err)
http.Error(w, "404 page not found", http.StatusNotFound)
}
if os.IsPermission(err) {
logger.Log.Errorf("not permitted to read file %s - %v", loc, err)
http.Error(w, "404 page not found", http.StatusForbidden)
}
logger.Log.Errorf("an internal error while reading file %s - %v", loc, err)
http.Error(w, "500 Internal Server Error", http.StatusInternalServerError)
}
defer rf.Close()
http.ServeContent(w, r, loc, f.LastModified, rf)
}
//AdminListFilesHandler returns the template which lists all uploaded files belonging to a user, admins will see all files
func AdminListFilesHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
u, _ := middleware.User(r)
page := getPageParam(r)
t, err := ctx.FileService.Count(u)
if err != nil {
return &middleware.Template{
Name: tplAdminFiles,
Active: "files",
Err: err,
}
}
p := &models.Pagination{
Total: t,
Limit: 20,
CurrentPage: page,
RelURL: "admin/files/page",
}
fs, err := ctx.FileService.List(u, p)
if err != nil {
return &middleware.Template{
Name: tplAdminFiles,
Active: "files",
Err: err,
}
}
return &middleware.Template{
Name: tplAdminFiles,
Active: "files",
Data: map[string]interface{}{
"files": fs,
"pagination": p,
}}
}
//AdminUploadFileHandler returns the form for uploading a file
func AdminUploadFileHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
return &middleware.Template{
Name: tplAdminFileUpload,
Active: "files",
}
}
func AdminToggleInlineFilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
u, _ := middleware.User(r)
rv := getVar(r, "fileID")
id, err := parseInt(rv)
if err != nil {
return &middleware.Template{
RedirectPath: "/admin/files",
Err: err,
Active: "files",
}
}
err = ctx.FileService.ToggleInline(id, u)
if err != nil {
return &middleware.Template{
RedirectPath: "/admin/files",
Err: err,
Active: "files",
}
}
return &middleware.Template{
Active: "files",
RedirectPath: "/admin/files",
SuccessMsg: "File successfully updated.",
}
}
//AdminUploadFilePostHandler handles the upload
func AdminUploadFilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
file, err := parseFileField(ctx, w, r)
if err != nil {
return &middleware.Template{
Name: tplAdminFileUpload,
Active: "files",
Err: err,
}
}
file.Inline = convertCheckbox(r, "admin")
_, err = ctx.FileService.Upload(file)
if err != nil {
return &middleware.Template{
Name: tplAdminFileUpload,
Active: "files",
Err: err,
}
}
return &middleware.Template{
RedirectPath: "/admin/files",
SuccessMsg: "Successfully uploaded file.",
Active: "files",
}
}
func AdminUploadJSONFilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) (*models.JSONData, error) {
file, err := parseFileField(ctx, w, r)
if err != nil {
return nil, err
}
file.Inline = true
_, err = ctx.FileService.Upload(file)
if err != nil {
return nil, err
}
file.Link = ctx.ConfigService.Application.Domain
json := &models.JSONData{
Data: file,
}
return json, nil
}
//AdminUploadDeleteHandler returns the action template which asks the user if the file should be removed
func AdminUploadDeleteHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
u, _ := middleware.User(r)
rv := getVar(r, "fileID")
id, err := parseInt(rv)
if err != nil {
return &middleware.Template{
Name: tplAdminFiles,
Err: err,
Active: "files",
}
}
f, err := ctx.FileService.GetByID(id, u)
if err != nil {
return &middleware.Template{
Name: tplAdminFiles,
Err: err,
Active: "files",
}
}
action := models.Action{
ID: "deleteFile",
ActionURL: fmt.Sprintf("/admin/file/delete/%d", f.ID),
Description: fmt.Sprintf("%s %s?", "Do you want to delete the file", f.UniqueName),
Title: "Confirm removal of file",
}
return &middleware.Template{
Name: tplAdminAction,
Active: "articles",
Data: map[string]interface{}{
"action": action,
},
}
}
//AdminUploadDeletePostHandler removes a file
func AdminUploadDeletePostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
u, _ := middleware.User(r)
rv := getVar(r, "fileID")
id, err := parseInt(rv)
if err != nil {
return &middleware.Template{
RedirectPath: "/admin/files",
Err: err,
Active: "files",
}
}
err = ctx.FileService.Delete(id, u)
warnMsg := ""
if err != nil {
if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOENT {
warnMsg = "File removed from database, but was not found in filesystem anymore."
} else {
return &middleware.Template{
RedirectPath: "/admin/files",
Err: err,
Active: "files",
}
}
}
return &middleware.Template{
Active: "files",
RedirectPath: "admin/files",
SuccessMsg: "File successfully deleted.",
WarnMsg: warnMsg,
}
}
func parseFileField(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) (*models.File, error) {
if r.ContentLength > int64(ctx.ConfigService.MaxUploadSize) {
return nil, httperror.New(http.StatusUnprocessableEntity, "Filesize too large", errors.New("filesize too large"))
}
r.Body = http.MaxBytesReader(w, r.Body, int64(ctx.ConfigService.MaxUploadSize))
err := r.ParseMultipartForm(1024)
if err != nil {
return nil, err
}
u, _ := middleware.User(r)
ff, h, err := r.FormFile("file")
if err != nil {
return nil, err
}
defer ff.Close()
data, err := ioutil.ReadAll(ff)
if err != nil {
return nil, err
}
ct := http.DetectContentType(data)
file := &models.File{
ContentType: ct,
Author: u,
Size: int64(len(data)),
FullFilename: h.Filename,
Data: data,
}
return file, nil
}