mirror of https://github.com/writeas/writefreely
this adds basic support for importing files as blog posts. .txt and .md are supported at this time and the collection is selectable, defaulting to draft. if a collection is specified the post is federated.import-zips
parent
8557119451
commit
ee4fe2f4ad
@ -0,0 +1,134 @@ |
||||
package writefreely |
||||
|
||||
import ( |
||||
"fmt" |
||||
"html/template" |
||||
"io" |
||||
"io/ioutil" |
||||
"net/http" |
||||
"os" |
||||
"path/filepath" |
||||
|
||||
"github.com/hashicorp/go-multierror" |
||||
"github.com/writeas/impart" |
||||
wfimport "github.com/writeas/import" |
||||
"github.com/writeas/web-core/log" |
||||
) |
||||
|
||||
func viewImport(app *App, u *User, w http.ResponseWriter, r *http.Request) error { |
||||
// Fetch extra user data
|
||||
p := NewUserPage(app, r, u, "Import", nil) |
||||
|
||||
c, err := app.db.GetCollections(u) |
||||
if err != nil { |
||||
return impart.HTTPError{http.StatusInternalServerError, fmt.Sprintf("unable to fetch collections: %v", err)} |
||||
} |
||||
|
||||
d := struct { |
||||
*UserPage |
||||
Collections *[]Collection |
||||
Flashes []template.HTML |
||||
}{ |
||||
UserPage: p, |
||||
Collections: c, |
||||
Flashes: []template.HTML{}, |
||||
} |
||||
|
||||
flashes, _ := getSessionFlashes(app, w, r, nil) |
||||
for _, flash := range flashes { |
||||
d.Flashes = append(d.Flashes, template.HTML(flash)) |
||||
} |
||||
|
||||
showUserPage(w, "import", d) |
||||
return nil |
||||
} |
||||
|
||||
func handleImport(app *App, u *User, w http.ResponseWriter, r *http.Request) error { |
||||
// limit 10MB per submission
|
||||
r.ParseMultipartForm(10 << 20) |
||||
files := r.MultipartForm.File["files"] |
||||
var fileErrs []error |
||||
for _, formFile := range files { |
||||
// TODO: count uploaded files that succeed and report back with message
|
||||
file, err := formFile.Open() |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to open form file: %s", formFile.Filename)) |
||||
log.Error("import textfile: open from form: %v", err) |
||||
continue |
||||
} |
||||
defer file.Close() |
||||
|
||||
tempFile, err := ioutil.TempFile("", "post-upload-*.txt") |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to create temporary file for: %s", formFile.Filename)) |
||||
log.Error("import textfile: create temp file: %v", err) |
||||
continue |
||||
} |
||||
defer tempFile.Close() |
||||
|
||||
_, err = io.Copy(tempFile, file) |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to copy file into temporary location: %s", formFile.Filename)) |
||||
log.Error("import textfile: copy to temp: %v", err) |
||||
continue |
||||
} |
||||
|
||||
info, err := tempFile.Stat() |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to get file info of: %s", formFile.Filename)) |
||||
log.Error("import textfile: stat temp file: %v", err) |
||||
continue |
||||
} |
||||
post, err := wfimport.FromFile(filepath.Join(os.TempDir(), info.Name())) |
||||
if err == wfimport.ErrEmptyFile { |
||||
// not a real error so don't log
|
||||
_ = addSessionFlash(app, w, r, fmt.Sprintf("%s was empty, import skipped", formFile.Filename), nil) |
||||
continue |
||||
} else if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to read copy of %s", formFile.Filename)) |
||||
log.Error("import textfile: file to post: %v", err) |
||||
continue |
||||
} |
||||
|
||||
post.Collection = r.PostFormValue("collection") |
||||
coll, _ := app.db.GetCollection(post.Collection) |
||||
if coll == nil { |
||||
coll = &Collection{ |
||||
ID: 0, |
||||
} |
||||
} |
||||
|
||||
submittedPost := SubmittedPost{ |
||||
Title: &post.Title, |
||||
Content: &post.Content, |
||||
Font: "norm", |
||||
} |
||||
if coll.ID != 0 && app.cfg.App.Federation { |
||||
token, err := app.db.GetAccessToken(u.ID) |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to authenticate uploading: %s", formFile.Filename)) |
||||
log.Error("import textfile: get accesstoken: %+v", err) |
||||
continue |
||||
} |
||||
ownedPost, err := app.db.CreateOwnedPost(&submittedPost, token, coll.Alias, app.cfg.App.Host) |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to create owned post for %s", formFile.Filename)) |
||||
log.Error("import textfile: create owned post: %v", err) |
||||
continue |
||||
} |
||||
go federatePost(app, ownedPost, coll.ID, false) |
||||
} else { |
||||
_, err = app.db.CreatePost(u.ID, coll.ID, &submittedPost) |
||||
if err != nil { |
||||
fileErrs = append(fileErrs, fmt.Errorf("failed to create post from %s", formFile.Filename)) |
||||
log.Error("import textfile: create db post: %v", err) |
||||
continue |
||||
} |
||||
} |
||||
} |
||||
if len(fileErrs) != 0 { |
||||
_ = addSessionFlash(app, w, r, multierror.ListFormatFunc(fileErrs), nil) |
||||
} |
||||
|
||||
return impart.HTTPError{http.StatusFound, "/me/import"} |
||||
} |
@ -0,0 +1,33 @@ |
||||
{{define "import"}} |
||||
{{template "header" .}} |
||||
|
||||
<div class="snug content-container"> |
||||
<h2 id="posts-header">Import</h2> |
||||
<p>Upload text or markdown files to import as posts.</p> |
||||
<div class="formContainer"> |
||||
<form id="importPosts" class="import" enctype="multipart/form-data" action="/me/import" method="POST"> |
||||
<label for="file" hidden>Browse files to upload</label> |
||||
<input class="fileInput" name="files" type="file" multiple accept=".txt,.md"/> |
||||
<br /> |
||||
<label for="collection">Select a blog to import the posts under.</label> |
||||
<select name="collection"> |
||||
{{range $i, $el := .Collections}} |
||||
<option value={{.Alias}}> |
||||
{{if .Title}}{{.Title}}{{else}}{{.Alias}}{{end}} |
||||
</option> |
||||
{{end}} |
||||
<option value="" selected>drafts</option> |
||||
</select> |
||||
<br /> |
||||
<input type="submit" value="Import" /> |
||||
</form> |
||||
</div> |
||||
{{if .Flashes}} |
||||
<ul class="errors"> |
||||
{{range .Flashes}}<li class="urgent">{{.}}</li>{{end}} |
||||
</ul> |
||||
{{end}} |
||||
</div> |
||||
|
||||
{{template "footer" .}} |
||||
{{end}} |
Loading…
Reference in new issue