From 2dd9ad4777f57ca3a8958c9400bfa478291be8d1 Mon Sep 17 00:00:00 2001 From: Wim Brand Date: Mon, 8 Mar 2021 15:30:05 +0100 Subject: [PATCH] Add export to CSV function --- go.mod | 2 +- res/template/account/user_home.html | 4 ++ webcontroller/user_export.go | 106 ++++++++++++++++++++++++++++ webcontroller/web_controller.go | 39 +++++----- 4 files changed, 128 insertions(+), 23 deletions(-) create mode 100644 webcontroller/user_export.go diff --git a/go.mod b/go.mod index aa9cc8d..2279371 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/Fornaxian/config v0.0.0-20180915150834-ac41cf746a70 github.com/Fornaxian/log v0.0.0-20190617093801-1c7ce9a7c9b3 github.com/Fornaxian/pd_mime_type v0.0.0-20200204165508-2815edf3a145 - github.com/google/uuid v1.1.1 + github.com/google/uuid v1.2.0 github.com/julienschmidt/httprouter v1.3.0 github.com/microcosm-cc/bluemonday v1.0.4 github.com/russross/blackfriday/v2 v2.1.0 diff --git a/res/template/account/user_home.html b/res/template/account/user_home.html index e596913..d41f988 100644 --- a/res/template/account/user_home.html +++ b/res/template/account/user_home.html @@ -35,6 +35,10 @@ Change account settings +

+ Export uploaded files to CSV + Export created lists to CSV +

Statistics

diff --git a/webcontroller/user_export.go b/webcontroller/user_export.go new file mode 100644 index 0000000..ecb3da7 --- /dev/null +++ b/webcontroller/user_export.go @@ -0,0 +1,106 @@ +package webcontroller + +import ( + "io" + "net/http" + "sort" + "strconv" + "strings" + "time" + + "github.com/Fornaxian/log" + "github.com/julienschmidt/httprouter" +) + +func writeCSVLine(w io.Writer, fields ...interface{}) { + for i, field := range fields { + if i != 0 { + w.Write([]byte(",")) + } + + switch val := field.(type) { + case string: + // In CSV files quotes are escaped by replacing them with double quotes + w.Write([]byte(`"` + strings.ReplaceAll(val, `"`, `""`) + `"`)) + case int: + w.Write([]byte(strconv.Itoa(val))) + case int64: + w.Write([]byte(strconv.FormatInt(val, 10))) + case time.Time: + w.Write([]byte(`"` + val.Format(time.RFC3339) + `"`)) + default: + panic("unknown CSV field type") + } + } + w.Write([]byte("\n")) +} + +func (wc *WebController) serveUserExportFiles( + w http.ResponseWriter, + r *http.Request, + p httprouter.Params, +) { + td := wc.newTemplateData(w, r) + if !td.Authenticated { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + files, err := td.PixelAPI.UserFiles() + if err != nil { + log.Error("Failed to get user files: %s", err) + return + } + + sort.Slice(files.Files, func(i, j int) (less bool) { + return files.Files[i].DateUpload.Before(files.Files[j].DateUpload) + }) + + w.Header().Add("Content-Description", "File Transfer") + w.Header().Add("Content-Disposition", `attachment; filename=pixeldrain_user_files.csv`) + w.Header().Add("Content-Type", "text/csv") + w.Header().Add("Transfer-Encoding", "chunked") // Replacement for Content-Length + + writeCSVLine( + w, "id", "name", "size", "type", "date_upload", "date_last_view", + "views", "downloads", "bandwidth_used", + ) + for _, file := range files.Files { + writeCSVLine( + w, file.ID, file.Name, file.Size, file.MimeType, file.DateUpload, + file.DateLastView, file.Views, file.Downloads, file.BandwidthUsed, + ) + } +} + +func (wc *WebController) serveUserExportLists( + w http.ResponseWriter, + r *http.Request, + p httprouter.Params, +) { + td := wc.newTemplateData(w, r) + if !td.Authenticated { + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } + + lists, err := td.PixelAPI.UserLists() + if err != nil { + log.Error("Failed to get user lists: %s", err) + return + } + + sort.Slice(lists.Lists, func(i, j int) (less bool) { + return lists.Lists[i].DateCreated.Before(lists.Lists[j].DateCreated) + }) + + w.Header().Add("Content-Description", "File Transfer") + w.Header().Add("Content-Disposition", `attachment; filename=pixeldrain_user_lists.csv`) + w.Header().Add("Content-Type", "text/csv") + w.Header().Add("Transfer-Encoding", "chunked") // Replacement for Content-Length + + writeCSVLine(w, "id", "title", "date_created", "file_count") + for _, list := range lists.Lists { + writeCSVLine(w, list.ID, list.Title, list.DateCreated, list.FileCount) + } +} diff --git a/webcontroller/web_controller.go b/webcontroller/web_controller.go index a098c11..6a0ee92 100644 --- a/webcontroller/web_controller.go +++ b/webcontroller/web_controller.go @@ -151,19 +151,21 @@ func New( {GET, "apps" /* */, wc.serveTemplate("apps", false)}, // User account pages - {GET, "register" /* */, wc.serveForm(wc.registerForm, false)}, - {PST, "register" /* */, wc.serveForm(wc.registerForm, false)}, - {GET, "login" /* */, wc.serveForm(wc.loginForm, false)}, - {PST, "login" /* */, wc.serveForm(wc.loginForm, false)}, - {GET, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, false)}, - {PST, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, false)}, - {GET, "logout" /* */, wc.serveTemplate("logout", true)}, - {PST, "logout" /* */, wc.serveLogout}, - {GET, "user" /* */, wc.serveTemplate("user_home", true)}, - {GET, "user/files" /* */, wc.serveTemplate("user_files", true)}, - {GET, "user/lists" /* */, wc.serveTemplate("user_lists", true)}, - {GET, "user/buckets" /* */, wc.serveTemplate("user_buckets", true)}, - {GET, "user/filemanager" /**/, wc.serveTemplate("file_manager", true)}, + {GET, "register" /* */, wc.serveForm(wc.registerForm, false)}, + {PST, "register" /* */, wc.serveForm(wc.registerForm, false)}, + {GET, "login" /* */, wc.serveForm(wc.loginForm, false)}, + {PST, "login" /* */, wc.serveForm(wc.loginForm, false)}, + {GET, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, false)}, + {PST, "password_reset" /* */, wc.serveForm(wc.passwordResetForm, false)}, + {GET, "logout" /* */, wc.serveTemplate("logout", true)}, + {PST, "logout" /* */, wc.serveLogout}, + {GET, "user" /* */, wc.serveTemplate("user_home", true)}, + {GET, "user/files" /* */, wc.serveTemplate("user_files", true)}, + {GET, "user/lists" /* */, wc.serveTemplate("user_lists", true)}, + {GET, "user/buckets" /* */, wc.serveTemplate("user_buckets", true)}, + {GET, "user/filemanager" /* */, wc.serveTemplate("file_manager", true)}, + {GET, "user/export/files" /**/, wc.serveUserExportFiles}, + {GET, "user/export/lists" /**/, wc.serveUserExportLists}, // User account settings {GET, "user/settings" /* */, wc.serveUserSettings}, @@ -200,15 +202,8 @@ func New( return wc } -func (wc *WebController) serveTemplate( - tpl string, - requireAuth bool, -) httprouter.Handle { - return func( - w http.ResponseWriter, - r *http.Request, - p httprouter.Params, - ) { +func (wc *WebController) serveTemplate(tpl string, requireAuth bool) httprouter.Handle { + return func(w http.ResponseWriter, r *http.Request, p httprouter.Params) { var tpld = wc.newTemplateData(w, r) if requireAuth && !tpld.Authenticated { http.Redirect(w, r, "/login", http.StatusSeeOther)