1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-20 21:59:42 +02:00

Pinned documents

Closes #278

Pin documents to the top of each space.
This commit is contained in:
sauls8t 2020-02-03 21:00:35 +00:00
parent 2b66d0096a
commit e014f5b5c1
18 changed files with 541 additions and 88 deletions

View file

@ -14,6 +14,7 @@ package document
import (
"database/sql"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"sort"
@ -88,18 +89,12 @@ func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: document.SpaceID,
DocumentID: document.RefID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeRead})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
return
}
ctx.Transaction.Commit()
}
@ -190,10 +185,26 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
}
}
// Sort document list by title.
sort.Sort(doc.ByName(filtered))
sortedDocs := doc.SortedDocs{}
response.WriteJSON(w, filtered)
for j := range filtered {
if filtered[j].Sequence == doc.Unsequenced {
sortedDocs.Unpinned = append(sortedDocs.Unpinned, filtered[j])
} else {
sortedDocs.Pinned = append(sortedDocs.Pinned, filtered[j])
}
}
// Sort document list by title.
sort.Sort(doc.ByName(sortedDocs.Unpinned))
// Sort document list by sequence.
sort.Sort(doc.BySeq(sortedDocs.Pinned))
final := sortedDocs.Pinned
final = append(final, sortedDocs.Unpinned...)
response.WriteJSON(w, final)
}
// Update updates an existing document using the format described
@ -249,7 +260,7 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
}
if oldDoc.SpaceID != d.SpaceID {
h.Store.Category.RemoveDocumentCategories(ctx, d.RefID)
_, _ = h.Store.Category.RemoveDocumentCategories(ctx, d.RefID)
err = h.Store.Document.MoveActivity(ctx, documentID, oldDoc.SpaceID, d.SpaceID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
@ -312,9 +323,9 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
h.Runtime.Commit(ctx.Transaction)
h.Store.Space.SetStats(ctx, d.SpaceID)
_ = h.Store.Space.SetStats(ctx, d.SpaceID)
if oldDoc.SpaceID != d.SpaceID {
h.Store.Space.SetStats(ctx, oldDoc.SpaceID)
_ = h.Store.Space.SetStats(ctx, oldDoc.SpaceID)
}
h.Store.Audit.Record(ctx, audit.EventTypeDocumentUpdate)
@ -480,18 +491,13 @@ func (h *Handler) SearchDocuments(w http.ResponseWriter, r *http.Request) {
return
}
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: "",
DocumentID: "",
Metadata: options.Keywords,
SourceType: activity.SourceTypeSearch,
ActivityType: activity.TypeSearched})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
ctx.Transaction.Commit()
}
}
@ -526,18 +532,13 @@ func (h *Handler) recordSearchActivity(ctx domain.RequestContext, q []search.Que
}
if _, isExisting := prev[q[i].DocumentID]; !isExisting {
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: q[i].SpaceID,
DocumentID: q[i].DocumentID,
Metadata: keywords,
SourceType: activity.SourceTypeSearch,
ActivityType: activity.TypeSearched})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
prev[q[i].DocumentID] = true
}
}
@ -713,16 +714,11 @@ func (h *Handler) FetchDocumentData(w http.ResponseWriter, r *http.Request) {
}
if document.Lifecycle == workflow.LifecycleLive {
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: document.SpaceID,
DocumentID: document.RefID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeRead})
if err != nil {
ctx.Transaction.Rollback()
h.Runtime.Log.Error(method, err)
}
}
ctx.Transaction.Commit()
@ -786,7 +782,7 @@ func (h *Handler) Export(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write([]byte(export))
_, _ = w.Write([]byte(export))
}
// Duplicate makes a copy of a document.
@ -1014,3 +1010,219 @@ func (h *Handler) Duplicate(w http.ResponseWriter, r *http.Request) {
response.WriteEmpty(w)
}
// Pin marks existing document with sequence number so that it
// appears at the top-most space view.
func (h *Handler) Pin(w http.ResponseWriter, r *http.Request) {
method := "document.Pin"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
response.WriteServerError(w, method, errors.New("unable to start transaction"))
return
}
d, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if !permission.CanManageSpace(ctx, *h.Store, d.SpaceID) {
h.Runtime.Rollback(ctx.Transaction)
response.WriteForbiddenError(w)
return
}
// Calculate the next sequence number for this newly pinned document.
seq, err := h.Store.Document.PinSequence(ctx, d.SpaceID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
h.Runtime.Log.Error(method, err)
response.WriteServerError(w, method, err)
return
}
err = h.Store.Document.Pin(ctx, documentID, seq+1)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: d.SpaceID,
DocumentID: documentID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypePinned})
h.Runtime.Commit(ctx.Transaction)
response.WriteEmpty(w)
}
// Unpin removes an existing document from the space pinned list.
func (h *Handler) Unpin(w http.ResponseWriter, r *http.Request) {
method := "document.Unpin"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
response.WriteServerError(w, method, errors.New("unable to start transaction"))
return
}
d, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if !permission.CanManageSpace(ctx, *h.Store, d.SpaceID) {
h.Runtime.Rollback(ctx.Transaction)
response.WriteForbiddenError(w)
return
}
err = h.Store.Document.Unpin(ctx, documentID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: d.SpaceID,
DocumentID: documentID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeUnpinned})
h.Runtime.Commit(ctx.Transaction)
response.WriteEmpty(w)
}
// PinMove moves pinned document up or down in the sequence.
func (h *Handler) PinMove(w http.ResponseWriter, r *http.Request) {
method := "document.PinMove"
ctx := domain.GetRequestContext(r)
documentID := request.Param(r, "documentID")
if len(documentID) == 0 {
response.WriteMissingDataError(w, method, "documentID")
return
}
direction := request.Query(r, "direction")
if len(direction) == 0 {
response.WriteMissingDataError(w, method, "direction")
return
}
var ok bool
ctx.Transaction, ok = h.Runtime.StartTx(sql.LevelReadUncommitted)
if !ok {
h.Runtime.Log.Info("unable to start transaction " + method)
response.WriteServerError(w, method, errors.New("unable to start transaction"))
return
}
d, err := h.Store.Document.Get(ctx, documentID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if !permission.CanManageSpace(ctx, *h.Store, d.SpaceID) {
h.Runtime.Rollback(ctx.Transaction)
response.WriteForbiddenError(w)
return
}
// Get all pinned documents in the space.
pinnedDocs, err := h.Store.Document.Pinned(ctx, d.SpaceID)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
h.Runtime.Log.Error(method, err)
response.WriteServerError(w, method, err)
return
}
// Sort document list by sequence.
sort.Sort(doc.BySeq(pinnedDocs))
// Resequence the documents.
for i := range pinnedDocs {
if pinnedDocs[i].RefID == documentID {
if direction == "u" {
if i-1 >= 0 {
me := pinnedDocs[i].Sequence
target := pinnedDocs[i-1].Sequence
pinnedDocs[i-1].Sequence = me
pinnedDocs[i].Sequence = target
}
}
if direction == "d" {
if i+1 < len(pinnedDocs) {
me := pinnedDocs[i].Sequence
target := pinnedDocs[i+1].Sequence
pinnedDocs[i+1].Sequence = me
pinnedDocs[i].Sequence = target
}
}
break
}
}
// Sort document list by sequence.
sort.Sort(doc.BySeq(pinnedDocs))
// Save the resequenced documents.
for i := range pinnedDocs {
err = h.Store.Document.Pin(ctx, pinnedDocs[i].RefID, i+1)
if err != nil {
h.Runtime.Rollback(ctx.Transaction)
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: d.SpaceID,
DocumentID: documentID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypePinSequence})
h.Runtime.Commit(ctx.Transaction)
response.WriteEmpty(w)
}