parent
26894f1438
commit
cc12981a50
12 changed files with 216 additions and 13 deletions
11
createdb.sql
11
createdb.sql
|
@ -129,3 +129,14 @@ CREATE TABLE visits (
|
|||
);
|
||||
CREATE INDEX CONCURRENTLY visits_downloads_idx on visits(downloads DESC NULLS LAST);
|
||||
CREATE INDEX CONCURRENTLY visits_views_idx on visits(views DESC NULLS LAST);
|
||||
|
||||
|
||||
CREATE TABLE submissions (
|
||||
id serial unique,
|
||||
submission_id varchar(16),
|
||||
filename text,
|
||||
status text,
|
||||
book_id varchar(16) unique REFERENCES books(id) ON DELETE SET NULL
|
||||
);
|
||||
CREATE INDEX CONCURRENTLY submissions_id_idx on submissions(submission_id);
|
||||
CREATE INDEX CONCURRENTLY submissions_book_idx on submissions(book_id);
|
||||
|
|
10
lib/admin.go
10
lib/admin.go
|
@ -34,6 +34,10 @@ func deleteHandler(h handler) {
|
|||
h.sess.Notify("Book not found!", "The book with id '"+id+"' is not there", "error")
|
||||
continue
|
||||
}
|
||||
err = h.db.UpdateSubmissionByBook(id, "Rejected", nil)
|
||||
if err != nil {
|
||||
log.Error("There was a problem updating the submission: ", err)
|
||||
}
|
||||
h.store.Delete(id)
|
||||
h.db.DeleteBook(id)
|
||||
|
||||
|
@ -216,6 +220,12 @@ func storeHandler(h handler) {
|
|||
log.Error("Error storing book '", book.Title, "': ", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
err = h.db.UpdateSubmissionByBook(id, "Included in the library", &book)
|
||||
if err != nil {
|
||||
log.Error("There was a problem updating the submission: ", err)
|
||||
}
|
||||
|
||||
titles = append(titles, book.Title)
|
||||
}
|
||||
if titles != nil {
|
||||
|
|
|
@ -27,6 +27,10 @@ type DB interface {
|
|||
IncViews(ID string) error
|
||||
IncDownloads(ID string) error
|
||||
GetFrontPage() FrontPage
|
||||
AddSubmission(submission Submission) (id int, err error)
|
||||
UpdateSubmission(id int, status string, book *Book) error
|
||||
UpdateSubmissionByBook(bookID string, status string, book *Book) error
|
||||
GetSubmission(submissionID string) (submission []Submission, err error)
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
|
@ -88,3 +88,19 @@ func (db *roDB) IncDownloads(ID string) error {
|
|||
func (db *roDB) GetFrontPage() FrontPage {
|
||||
return db.db.GetFrontPage()
|
||||
}
|
||||
|
||||
func (db *roDB) AddSubmission(submission Submission) (id int, err error) {
|
||||
return 0, errors.New("RO database")
|
||||
}
|
||||
|
||||
func (db *roDB) UpdateSubmission(id int, status string, book *Book) error {
|
||||
return errors.New("RO database")
|
||||
}
|
||||
|
||||
func (db *roDB) UpdateSubmissionByBook(bookID string, status string, book *Book) error {
|
||||
return errors.New("RO database")
|
||||
}
|
||||
|
||||
func (db *roDB) GetSubmission(submissionID string) (submission []Submission, err error) {
|
||||
return db.db.GetSubmission(submissionID)
|
||||
}
|
||||
|
|
48
lib/database/submissions.go
Normal file
48
lib/database/submissions.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
package database
|
||||
|
||||
type Submission struct {
|
||||
ID int
|
||||
SubmissionID string
|
||||
Filename string
|
||||
Status string
|
||||
BookID string
|
||||
Book *Book
|
||||
}
|
||||
|
||||
func (db *pgDB) AddSubmission(submission Submission) (id int, err error) {
|
||||
err = db.sql.Insert(&submission)
|
||||
return submission.ID, err
|
||||
}
|
||||
|
||||
func (db *pgDB) UpdateSubmission(id int, status string, book *Book) error {
|
||||
_, err := db.sql.Model(&Submission{}).
|
||||
Set("status = ?", status).
|
||||
Set("book_id = ?", extractID(book)).
|
||||
Where("id = ?", id).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *pgDB) UpdateSubmissionByBook(bookID string, status string, book *Book) error {
|
||||
_, err := db.sql.Model(&Submission{}).
|
||||
Set("status = ?", status).
|
||||
Set("book_id = ?", extractID(book)).
|
||||
Where("book_id = ?", bookID).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
func (db *pgDB) GetSubmission(submissionID string) (submission []Submission, err error) {
|
||||
err = db.sql.Model(&submission).
|
||||
Column("Book").
|
||||
Where("submission_id = ?", submissionID).
|
||||
Select()
|
||||
return
|
||||
}
|
||||
|
||||
func extractID(book *Book) interface{} {
|
||||
if book == nil {
|
||||
return nil
|
||||
}
|
||||
return book.ID
|
||||
}
|
|
@ -65,6 +65,7 @@ func InitTemplate(assetsPath string) *Template {
|
|||
path.Join(templatePath, "book.html"),
|
||||
path.Join(templatePath, "search.html"),
|
||||
path.Join(templatePath, "upload.html"),
|
||||
path.Join(templatePath, "submission.html"),
|
||||
path.Join(templatePath, "login.html"),
|
||||
path.Join(templatePath, "new.html"),
|
||||
path.Join(templatePath, "read.html"),
|
||||
|
|
|
@ -169,6 +169,7 @@ func InitRouter(db database.DB, sg *StatsGatherer, assetsPath string) http.Handl
|
|||
r.HandleFunc("/search/", sg.Gather(searchHandler))
|
||||
r.HandleFunc("/upload/", sg.Gather(uploadHandler)).Methods("GET")
|
||||
r.HandleFunc("/upload/", sg.Gather(uploadPostHandler)).Methods("POST")
|
||||
r.HandleFunc("/submission/{id:"+idPattern+"}", sg.Gather(submissionHandler))
|
||||
r.HandleFunc("/read/{id:"+idPattern+"}", sg.Gather(readStartHandler))
|
||||
r.HandleFunc("/read/{id:"+idPattern+"}/{file:.*}", sg.Gather(readHandler))
|
||||
r.HandleFunc("/content/{id:"+idPattern+"}/{file:.*}", sg.Gather(contentHandler))
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/meskio/epubgo"
|
||||
"gitlab.com/trantor/trantor/lib/database"
|
||||
"gitlab.com/trantor/trantor/lib/parser"
|
||||
|
@ -29,6 +30,7 @@ var uploadChannel chan uploadRequest
|
|||
type uploadRequest struct {
|
||||
file multipart.File
|
||||
filename string
|
||||
id int
|
||||
}
|
||||
|
||||
func uploadWorker(database database.DB, store storage.Store) {
|
||||
|
@ -43,6 +45,7 @@ func processFile(req uploadRequest, db database.DB, store storage.Store) {
|
|||
epub, err := openMultipartEpub(req.file)
|
||||
if err != nil {
|
||||
log.Warn("Not valid epub uploaded file ", req.filename, ": ", err)
|
||||
db.UpdateSubmission(req.id, "It is not a valid epub file", nil)
|
||||
return
|
||||
}
|
||||
defer epub.Close()
|
||||
|
@ -54,6 +57,7 @@ func processFile(req uploadRequest, db database.DB, store storage.Store) {
|
|||
size, err := store.Store(book.ID, req.file, epubFile)
|
||||
if err != nil {
|
||||
log.Error("Error storing book (", book.ID, "): ", err)
|
||||
db.UpdateSubmission(req.id, "There was a problem in our server", nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -63,9 +67,11 @@ func processFile(req uploadRequest, db database.DB, store storage.Store) {
|
|||
err = db.AddBook(book)
|
||||
if err != nil {
|
||||
log.Error("Error storing metadata (", book.ID, "): ", err)
|
||||
db.UpdateSubmission(req.id, "There was a problem in our server", nil)
|
||||
return
|
||||
}
|
||||
log.Info("File uploaded: ", req.filename)
|
||||
db.UpdateSubmission(req.id, "Waiting for moderation", &book)
|
||||
}
|
||||
|
||||
func uploadPostHandler(h handler) {
|
||||
|
@ -75,33 +81,41 @@ func uploadPostHandler(h handler) {
|
|||
return
|
||||
}
|
||||
|
||||
problem := false
|
||||
|
||||
h.r.ParseMultipartForm(20000000)
|
||||
filesForm := h.r.MultipartForm.File["epub"]
|
||||
submissionID := genID()
|
||||
for _, f := range filesForm {
|
||||
submission := database.Submission{
|
||||
SubmissionID: submissionID,
|
||||
Filename: f.Filename,
|
||||
Status: "Waiting to be processed",
|
||||
}
|
||||
|
||||
file, err := f.Open()
|
||||
if err != nil {
|
||||
log.Error("Can not open uploaded file ", f.Filename, ": ", err)
|
||||
h.sess.Notify("Upload problem!", "There was a problem with book "+f.Filename, "error")
|
||||
problem = true
|
||||
submission.Status = "It was not possible to read the book"
|
||||
h.db.AddSubmission(submission)
|
||||
continue
|
||||
}
|
||||
uploadChannel <- uploadRequest{file, f.Filename}
|
||||
}
|
||||
|
||||
if !problem {
|
||||
if len(filesForm) > 0 {
|
||||
h.sess.Notify("Upload successful!", "Thank you for your contribution", "success")
|
||||
} else {
|
||||
h.sess.Notify("Upload problem!", "No books where uploaded.", "error")
|
||||
id, err := h.db.AddSubmission(submission)
|
||||
if err != nil {
|
||||
log.Error("Can add submission to db for ", f.Filename, ": ", err)
|
||||
}
|
||||
uploadChannel <- uploadRequest{file, f.Filename, id}
|
||||
}
|
||||
uploadHandler(h)
|
||||
_uploadHandler(h, submissionID)
|
||||
}
|
||||
|
||||
func uploadHandler(h handler) {
|
||||
_uploadHandler(h, "")
|
||||
}
|
||||
|
||||
func _uploadHandler(h handler, submissionID string) {
|
||||
var data uploadData
|
||||
data.SubmissionID = submissionID
|
||||
data.S = GetStatus(h)
|
||||
data.S.Title = "Upload -- " + data.S.Title
|
||||
data.S.Upload = true
|
||||
|
@ -109,7 +123,27 @@ func uploadHandler(h handler) {
|
|||
}
|
||||
|
||||
type uploadData struct {
|
||||
S Status
|
||||
S Status
|
||||
SubmissionID string
|
||||
}
|
||||
|
||||
func submissionHandler(h handler) {
|
||||
var data submissionData
|
||||
var err error
|
||||
|
||||
submissionID := mux.Vars(h.r)["id"]
|
||||
data.Submissions, err = h.db.GetSubmission(submissionID)
|
||||
if err != nil {
|
||||
log.Error("Can get submission ", submissionID, ": ", err)
|
||||
}
|
||||
data.S = GetStatus(h)
|
||||
data.S.Title = "Submission -- " + data.S.Title
|
||||
h.load("submission", data)
|
||||
}
|
||||
|
||||
type submissionData struct {
|
||||
S Status
|
||||
Submissions []database.Submission
|
||||
}
|
||||
|
||||
func openMultipartEpub(file multipart.File) (*epubgo.Epub, error) {
|
||||
|
|
|
@ -19,7 +19,11 @@ function delBook(){
|
|||
|
||||
<header class="row">
|
||||
<div class="span8 offset4">
|
||||
<h1>{{.Title}}</h1>
|
||||
<h1>{{.Title}}
|
||||
{{if not .Active}}
|
||||
<span class="label label-warning">waiting for moderation</span>
|
||||
{{end}}
|
||||
</h1>
|
||||
</div>
|
||||
</header>
|
||||
<div class="row">
|
||||
|
|
|
@ -6,9 +6,13 @@
|
|||
<a href="{{.Prev}}">← Prev</a>
|
||||
</li>
|
||||
{{end}}
|
||||
{{if .Book.Active}}
|
||||
<li class="">
|
||||
<a href="{{.Back}}">Back</a>
|
||||
</li>
|
||||
{{else}}
|
||||
<span class="label label-warning">waiting for moderation</span>
|
||||
{{end}}
|
||||
{{if .Next}}
|
||||
<li class="next">
|
||||
<a href="{{.Next}}">Next →</a>
|
||||
|
|
63
templates/submission.html
Normal file
63
templates/submission.html
Normal file
|
@ -0,0 +1,63 @@
|
|||
{{template "header.html" .S}}
|
||||
|
||||
<div class="row">
|
||||
<h4>The status of your submission:</h4>
|
||||
</div>
|
||||
|
||||
{{$role := .S.Role}}
|
||||
{{range .Submissions}}
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="span9">
|
||||
<p><b>{{.Filename}}</b>
|
||||
{{if .Book.Active}}
|
||||
<span class="label label-success">
|
||||
{{else if not .Book.ID}}
|
||||
<span class="label label-important">
|
||||
{{else}}
|
||||
<span class="label label-warning">
|
||||
{{end}}
|
||||
{{.Status}}</span></p>
|
||||
</div>
|
||||
{{with .Book}}
|
||||
{{if and .ID (not .Active)}}
|
||||
<div class="span2">
|
||||
{{if eq $role "admin" "moderator"}}
|
||||
<div class="btn-group pull-right">
|
||||
<a href="/store/{{.ID}}/" class="btn btn-success"><i class="icon-ok"></i> Save</a>
|
||||
<a href="/edit/{{.ID}}" class="btn btn-primary"><i class="icon-pencil"></i> Edit</a>
|
||||
<a href="/delete/{{.ID}}/" class="btn btn-danger"><i class="icon-remove"></i> Delete</a>
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
{{if .ID}}
|
||||
<div class="span1">
|
||||
<p class="pull-right"><a href="/book/{{.ID}}">{{if .Cover}}<img class="img-rounded" src="/cover/{{.ID}}/small/{{.Title}}.jpg" alt="{{.Title}}" />{{end}}</a></p>
|
||||
</div>
|
||||
<div class="span10 well">
|
||||
<div class="row">
|
||||
<div class="span7">
|
||||
<p>
|
||||
<span class="muted">[{{if .Lang}}{{.Lang}}{{end}}]</span>
|
||||
<a href="/book/{{.ID}}"><strong>{{.Title}}</strong></a>
|
||||
<span class="muted">{{if .Publisher}}{{.Publisher}}{{end}}</span><br />
|
||||
{{range .Authors}}{{.}}, {{end}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="span3">
|
||||
<div class="btn-group pull-right">
|
||||
<a href="/download/{{.ID}}/{{.Title}}.epub" class="btn btn-inverse"><i class="icon-download-alt icon-white"></i> download</a>
|
||||
<a href="/read/{{.ID}}" class="btn btn-warning"><i class="icon-eye-open icon-white"></i> read it!</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{template "footer.html"}}
|
|
@ -1,5 +1,12 @@
|
|||
{{template "header.html" .S}}
|
||||
|
||||
{{if .SubmissionID}}
|
||||
<div class="alert alert-success">
|
||||
<button class="close" data-dismiss="alert">×</button>
|
||||
<strong>Upload successful!</strong> Thank you for your contribution (<a href="/submission/{{.SubmissionID}}">Review the status of your upload</a>)
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<p>Upload your epubs to help the library.</p>
|
||||
|
||||
<form class="well form-inline" method="POST" enctype="multipart/form-data">
|
||||
|
|
Reference in a new issue