1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-26 00:29:47 +02:00

Merge pull request #282 from documize/core-0619

The all new v3 is here sporting a new layout, better theming, quick-jump to spaces and content, tonnes of document view improvements, comment replies, expand/collapse doc views and much more.
This commit is contained in:
Harvey Kandola 2019-06-12 14:19:12 +01:00 committed by GitHub
commit 8f4cd755de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
153 changed files with 7479 additions and 7278 deletions

View file

@ -13,9 +13,9 @@ All you need to provide is PostgreSQL, Microsoft SQL Server or any MySQL variant
## Latest Release ## Latest Release
[Community Edition: v2.5.1](https://github.com/documize/community/releases) [Community Edition: v3.0.0](https://github.com/documize/community/releases)
[Enterprise Edition: v2.5.1](https://www.documize.com/downloads) [Enterprise Edition: v3.0.0](https://www.documize.com/downloads)
> *We provide frequent product updates for both cloud and self-hosted customers.* > *We provide frequent product updates for both cloud and self-hosted customers.*
> >

View file

@ -0,0 +1,5 @@
/* Enterprise edition */
-- Feedback feature: support threaded comments and section references
ALTER TABLE dmz_doc_comment ADD COLUMN `c_replyto` VARCHAR(20) NOT NULL DEFAULT '' COLLATE utf8_bin AFTER `c_docid`;
ALTER TABLE dmz_doc_comment ADD COLUMN `c_sectionid` VARCHAR(20) NOT NULL DEFAULT '' COLLATE utf8_bin AFTER `c_docid`;

View file

@ -0,0 +1,5 @@
/* Enterprise edition */
-- Feedback feature: support threaded comments and section references
ALTER TABLE dmz_doc_comment ADD COLUMN c_replyto VARCHAR(20) NOT NULL DEFAULT '' COLLATE ucs_basic;
ALTER TABLE dmz_doc_comment ADD COLUMN c_sectionid VARCHAR(20) NOT NULL DEFAULT '' COLLATE ucs_basic;

View file

@ -0,0 +1,5 @@
/* Enterprise edition */
-- Feedback feature: support threaded comments and section references
ALTER TABLE dmz_doc_comment ADD c_replyto NVARCHAR(20) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '';
ALTER TABLE dmz_doc_comment ADD c_sectionid NVARCHAR(20) COLLATE Latin1_General_CS_AS NOT NULL DEFAULT '';

View file

@ -14,27 +14,25 @@ package attachment
import ( import (
"bytes" "bytes"
"database/sql" "database/sql"
"fmt"
"io" "io"
"mime" "mime"
"net/http" "net/http"
"strings" "strings"
"github.com/documize/community/domain/auth"
"github.com/documize/community/model/space"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/uniqueid" "github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/auth"
"github.com/documize/community/domain/organization" "github.com/documize/community/domain/organization"
"github.com/documize/community/domain/permission" "github.com/documize/community/domain/permission"
indexer "github.com/documize/community/domain/search" indexer "github.com/documize/community/domain/search"
"github.com/documize/community/domain/store" "github.com/documize/community/domain/store"
"github.com/documize/community/model/attachment" "github.com/documize/community/model/attachment"
"github.com/documize/community/model/audit" "github.com/documize/community/model/audit"
"github.com/documize/community/model/space"
"github.com/documize/community/model/workflow" "github.com/documize/community/model/workflow"
uuid "github.com/nu7hatch/gouuid" uuid "github.com/nu7hatch/gouuid"
) )
@ -89,7 +87,7 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
return return
} }
// Get the space for this attachment // Get the space for this attachment.
sp, err := h.Store.Space.Get(ctx, doc.SpaceID) sp, err := h.Store.Space.Get(ctx, doc.SpaceID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, a.DocumentID) response.WriteNotFoundError(w, method, a.DocumentID)
@ -101,8 +99,7 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
return return
} }
// Get the organization for this request // Get the organization for this request.
// Get the space for this attachment
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
response.WriteNotFoundError(w, method, a.DocumentID) response.WriteNotFoundError(w, method, a.DocumentID)
@ -181,16 +178,20 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
typ = "application/octet-stream" typ = "application/octet-stream"
} }
dataSize := len(a.Data)
w.Header().Set("Content-Type", typ) w.Header().Set("Content-Type", typ)
w.Header().Set("Content-Disposition", `Attachment; filename="`+a.Filename+`" ; `+`filename*="`+a.Filename+`"`) w.Header().Set("Content-Disposition", `Attachment; filename="`+a.Filename+`" ; `+`filename*="`+a.Filename+`"`)
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(a.Data))) if dataSize != 0 {
w.WriteHeader(http.StatusOK) w.Header().Set("Content-Length", string(dataSize))
}
_, err = w.Write(a.Data) _, err = w.Write(a.Data)
if err != nil { if err != nil {
h.Runtime.Log.Error("write attachment", err) h.Runtime.Log.Error("write attachment", err)
return return
} }
w.WriteHeader(http.StatusOK)
h.Store.Audit.Record(ctx, audit.EventTypeAttachmentDownload) h.Store.Audit.Record(ctx, audit.EventTypeAttachmentDownload)
} }

View file

@ -34,8 +34,10 @@ func (s Store) Add(ctx domain.RequestContext, a attachment.Attachment) (err erro
a.OrgID = ctx.OrgID a.OrgID = ctx.OrgID
a.Created = time.Now().UTC() a.Created = time.Now().UTC()
a.Revised = time.Now().UTC() a.Revised = time.Now().UTC()
bits := strings.Split(a.Filename, ".") if len(a.Extension) == 0 {
a.Extension = bits[len(bits)-1] bits := strings.Split(a.Filename, ".")
a.Extension = bits[len(bits)-1]
}
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_doc_attachment (c_refid, c_orgid, c_docid, c_sectionid, c_job, c_fileid, c_filename, c_data, c_extension, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), _, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_doc_attachment (c_refid, c_orgid, c_docid, c_sectionid, c_job, c_fileid, c_filename, c_data, c_extension, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
a.RefID, a.OrgID, a.DocumentID, a.SectionID, a.Job, a.FileID, a.Filename, a.Data, a.Extension, a.Created, a.Revised) a.RefID, a.OrgID, a.DocumentID, a.SectionID, a.Job, a.FileID, a.Filename, a.Data, a.Extension, a.Created, a.Revised)

View file

@ -733,7 +733,8 @@ func (b backerHandler) dmzDocument(files *[]backupItem) (err error) {
err = b.Runtime.Db.Select(&cm, ` err = b.Runtime.Db.Select(&cm, `
SELECT c_refid AS refid, c_orgid AS orgid, c_docid AS documentid, SELECT c_refid AS refid, c_orgid AS orgid, c_docid AS documentid,
c_userid AS userid, c_email AS email, c_userid AS userid, c_email AS email,
c_feedback AS feedback, c_created AS created c_feedback AS feedback, c_sectionid AS sectionid, c_replyto AS replyto,
c_created AS created
FROM dmz_doc_comment`+w) FROM dmz_doc_comment`+w)
if err != nil { if err != nil {
return errors.Wrap(err, "select.doccomment") return errors.Wrap(err, "select.doccomment")

View file

@ -59,6 +59,8 @@ type comment struct {
UserID string `json:"userId"` UserID string `json:"userId"`
Email string `json:"email"` Email string `json:"email"`
Feedback string `json:"feedback"` Feedback string `json:"feedback"`
SectionID string `json:"sectionId"`
ReplyTo string `json:"replyTo"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
} }

View file

@ -1551,15 +1551,6 @@ func (r *restoreHandler) dmzDocAttachment() (err error) {
func (r *restoreHandler) dmzDocComment() (err error) { func (r *restoreHandler) dmzDocComment() (err error) {
filename := "dmz_doc_comment.json" filename := "dmz_doc_comment.json"
type comment struct {
RefID string `json:"feedbackId"`
OrgID string `json:"orgId"`
DocumentID string `json:"documentId"`
UserID string `json:"userId"`
Email string `json:"email"`
Feedback string `json:"feedback"`
Created time.Time `json:"created"`
}
cm := []comment{} cm := []comment{}
err = r.fileJSON(filename, &cm) err = r.fileJSON(filename, &cm)
if err != nil { if err != nil {
@ -1590,10 +1581,10 @@ func (r *restoreHandler) dmzDocComment() (err error) {
for i := range cm { for i := range cm {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_doc_comment INSERT INTO dmz_doc_comment
(c_refid, c_orgid, c_userid, c_docid, c_email, c_feedback, c_created) (c_refid, c_orgid, c_userid, c_docid, c_email, c_feedback, c_replyto, c_sectionid, c_created)
VALUES (?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
cm[i].RefID, r.remapOrg(cm[i].OrgID), r.remapUser(cm[i].UserID), cm[i].DocumentID, cm[i].RefID, r.remapOrg(cm[i].OrgID), r.remapUser(cm[i].UserID), cm[i].DocumentID,
cm[i].Email, cm[i].Feedback, cm[i].Created) cm[i].Email, cm[i].Feedback, cm[i].ReplyTo, cm[i].SectionID, cm[i].Created)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -1727,6 +1718,7 @@ func (r *restoreHandler) dmzUser() (err error) {
insert = true insert = true
} }
if err != nil { if err != nil {
r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to check email %s", u[i].Email)) err = errors.Wrap(err, fmt.Sprintf("unable to check email %s", u[i].Email))
return return
} }

View file

@ -24,6 +24,7 @@ import (
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
"github.com/documize/community/core/uniqueid"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/organization" "github.com/documize/community/domain/organization"
"github.com/documize/community/domain/permission" "github.com/documize/community/domain/permission"
@ -34,6 +35,7 @@ import (
"github.com/documize/community/model/audit" "github.com/documize/community/model/audit"
"github.com/documize/community/model/doc" "github.com/documize/community/model/doc"
"github.com/documize/community/model/link" "github.com/documize/community/model/link"
"github.com/documize/community/model/page"
pm "github.com/documize/community/model/permission" pm "github.com/documize/community/model/permission"
"github.com/documize/community/model/search" "github.com/documize/community/model/search"
"github.com/documize/community/model/space" "github.com/documize/community/model/space"
@ -145,19 +147,9 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
return return
} }
// Get the space as we need to check settings.
space, err := h.Store.Space.Get(ctx, spaceID)
// Can user view drafts? // Can user view drafts?
viewDrafts := permission.CanViewDrafts(ctx, *h.Store, spaceID) viewDrafts := permission.CanViewDrafts(ctx, *h.Store, spaceID)
// If space defaults to drfat documents, then this means
// user can view drafts as long as they have edit rights.
canEdit := permission.HasPermission(ctx, *h.Store, spaceID, pm.DocumentEdit)
if space.Lifecycle == workflow.LifecycleDraft && canEdit {
viewDrafts = true
}
// Get complete list of documents regardless of category permission // Get complete list of documents regardless of category permission
// and versioning. // and versioning.
documents, err := h.Store.Document.GetBySpace(ctx, spaceID) documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
@ -766,3 +758,229 @@ func (h *Handler) Export(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
w.Write([]byte(export)) w.Write([]byte(export))
} }
// Duplicate makes a copy of a document.
// Name of new document is required.
func (h *Handler) Duplicate(w http.ResponseWriter, r *http.Request) {
method := "document.Duplicate"
ctx := domain.GetRequestContext(r)
// Holds old to new ref ID values.
pageRefMap := make(map[string]string)
// Parse payload
defer streamutil.Close(r.Body)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
m := doc.DuplicateModel{}
err = json.Unmarshal(body, &m)
if err != nil {
response.WriteBadRequestError(w, method, err.Error())
h.Runtime.Log.Error(method, err)
return
}
// Check permissions
if !permission.CanViewDocument(ctx, *h.Store, m.DocumentID) {
response.WriteForbiddenError(w)
return
}
if !permission.CanUploadDocument(ctx, *h.Store, m.SpaceID) {
response.WriteForbiddenError(w)
return
}
ctx.Transaction, err = h.Runtime.Db.Beginx()
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// Get document to be duplicated.
d, err := h.Store.Document.Get(ctx, m.DocumentID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// Assign new ID and remove versioning info.
d.RefID = uniqueid.Generate()
d.GroupID = ""
d.Name = m.Name
// Fetch doc attachments, links.
da, err := h.Store.Attachment.GetAttachmentsWithData(ctx, m.DocumentID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
dl, err := h.Store.Link.GetDocumentOutboundLinks(ctx, m.DocumentID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// Fetch published and unpublished sections.
pages, err := h.Store.Page.GetPages(ctx, m.DocumentID)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(pages) == 0 {
pages = []page.Page{}
}
unpublished, err := h.Store.Page.GetUnpublishedPages(ctx, m.DocumentID)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(unpublished) == 0 {
unpublished = []page.Page{}
}
pages = append(pages, unpublished...)
meta, err := h.Store.Page.GetDocumentPageMeta(ctx, m.DocumentID, false)
if err != nil && err != sql.ErrNoRows {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
if len(meta) == 0 {
meta = []page.Meta{}
}
// Duplicate the complete document starting with the document.
err = h.Store.Document.Add(ctx, d)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// Attachments
for i := range da {
da[i].RefID = uniqueid.Generate()
da[i].DocumentID = d.RefID
err = h.Store.Attachment.Add(ctx, da[i])
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
// Sections
for j := range pages {
// Create mapping between old and new section IDs.
pageRefMap[pages[j].RefID] = uniqueid.Generate()
// Get meta for section
sm := page.Meta{}
for k := range meta {
if meta[k].SectionID == pages[j].RefID {
sm = meta[k]
break
}
}
// Get attachments for section.
sa, err := h.Store.Attachment.GetSectionAttachments(ctx, pages[j].RefID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
pages[j].RefID = pageRefMap[pages[j].RefID]
pages[j].DocumentID = d.RefID
sm.DocumentID = d.RefID
sm.SectionID = pages[j].RefID
err = h.Store.Page.Add(ctx, page.NewPage{Page: pages[j], Meta: sm})
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// Now add any section attachments.
for n := range sa {
sa[n].RefID = uniqueid.Generate()
sa[n].DocumentID = d.RefID
sa[n].SectionID = pages[j].RefID
err = h.Store.Attachment.Add(ctx, sa[n])
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
}
// Links
for l := range dl {
// Update common meta for all links.
dl[l].RefID = uniqueid.Generate()
dl[l].SourceDocumentID = d.RefID
// Remap section ID.
if len(dl[l].SourceSectionID) > 0 && len(pageRefMap[dl[l].SourceSectionID]) > 0 {
dl[l].SourceSectionID = pageRefMap[dl[l].SourceSectionID]
}
err = h.Store.Link.Add(ctx, dl[l])
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
// Record activity and finish.
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: d.SpaceID,
DocumentID: d.RefID,
SourceType: activity.SourceTypeDocument,
ActivityType: activity.TypeCreated})
ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeDocumentAdd)
// Update search index if published.
if d.Lifecycle == workflow.LifecycleLive {
a, _ := h.Store.Attachment.GetAttachments(ctx, d.RefID)
go h.Indexer.IndexDocument(ctx, d, a)
pages, _ := h.Store.Page.GetPages(ctx, d.RefID)
for i := range pages {
go h.Indexer.IndexContent(ctx, pages[i])
}
} else {
go h.Indexer.DeleteDocument(ctx, d.RefID)
}
response.WriteEmpty(w)
}

File diff suppressed because one or more lines are too long

View file

@ -76,13 +76,13 @@ func (s Store) GetDocumentOutboundLinks(ctx domain.RequestContext, documentID st
WHERE c_orgid=? AND c_sourcedocid=?`), WHERE c_orgid=? AND c_sourcedocid=?`),
ctx.OrgID, documentID) ctx.OrgID, documentID)
if err != nil && err != sql.ErrNoRows { if err == sql.ErrNoRows || len(links) == 0 {
err = errors.Wrap(err, "select document oubound links") err = nil
return
}
if len(links) == 0 {
links = []link.Link{} links = []link.Link{}
} }
if err != nil {
err = errors.Wrap(err, "select document oubound links")
}
return return
} }
@ -98,13 +98,13 @@ func (s Store) GetPageLinks(ctx domain.RequestContext, documentID, pageID string
WHERE c_orgid=? AND c_sourcedocid=? AND c_sourcesectionid=?`), WHERE c_orgid=? AND c_sourcedocid=? AND c_sourcesectionid=?`),
ctx.OrgID, documentID, pageID) ctx.OrgID, documentID, pageID)
if err != nil && err != sql.ErrNoRows { if err == sql.ErrNoRows || len(links) == 0 {
err = errors.Wrap(err, "get page links") err = nil
return
}
if len(links) == 0 {
links = []link.Link{} links = []link.Link{}
} }
if err != nil {
err = errors.Wrap(err, "get page links")
}
return return
} }

View file

@ -911,16 +911,28 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
return return
} }
// fetch data // Get both source and target documents.
doc, err := h.Store.Document.Get(ctx, documentID) doc, err := h.Store.Document.Get(ctx, documentID)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err) h.Runtime.Log.Error(method, err)
return return
} }
targetDoc, err := h.Store.Document.Get(ctx, targetID)
if err != nil {
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
// workflow check // Workflow check for target (receiving) doc.
if doc.Protection == workflow.ProtectionLock || doc.Protection == workflow.ProtectionReview { if targetDoc.Protection == workflow.ProtectionLock || targetDoc.Protection == workflow.ProtectionReview {
response.WriteForbiddenError(w)
return
}
// Check permissions for target document and copy permission.
if !permission.CanChangeDocument(ctx, *h.Store, targetDoc.RefID) {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
@ -951,8 +963,9 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
newPageID := uniqueid.Generate() newPageID := uniqueid.Generate()
p.RefID = newPageID p.RefID = newPageID
p.Level = 1 p.Level = p.Level
p.Sequence = 0 // p.Sequence = p.Sequence
p.Sequence, _ = h.Store.Page.GetNextPageSequence(ctx, targetDoc.RefID)
p.DocumentID = targetID p.DocumentID = targetID
p.UserID = ctx.UserID p.UserID = ctx.UserID
pageMeta.DocumentID = targetID pageMeta.DocumentID = targetID
@ -1003,6 +1016,33 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
} }
} }
// Copy section links.
links, err := h.Store.Link.GetPageLinks(ctx, documentID, pageID)
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
for lindex := range links {
links[lindex].RefID = uniqueid.Generate()
links[lindex].SourceSectionID = newPageID
links[lindex].SourceDocumentID = targetID
err = h.Store.Link.Add(ctx, links[lindex])
if err != nil {
ctx.Transaction.Rollback()
response.WriteServerError(w, method, err)
h.Runtime.Log.Error(method, err)
return
}
}
// Update doc revised.
h.Store.Document.UpdateRevised(ctx, targetID)
// If document is published, we record activity and
// index content for search.
if doc.Lifecycle == workflow.LifecycleLive { if doc.Lifecycle == workflow.LifecycleLive {
h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{ h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
SpaceID: doc.SpaceID, SpaceID: doc.SpaceID,
@ -1010,17 +1050,18 @@ func (h *Handler) Copy(w http.ResponseWriter, r *http.Request) {
SectionID: newPageID, SectionID: newPageID,
SourceType: activity.SourceTypePage, SourceType: activity.SourceTypePage,
ActivityType: activity.TypeCreated}) ActivityType: activity.TypeCreated})
}
// Update doc revised. go h.Indexer.IndexContent(ctx, p)
h.Store.Document.UpdateRevised(ctx, targetID) }
ctx.Transaction.Commit() ctx.Transaction.Commit()
h.Store.Audit.Record(ctx, audit.EventTypeSectionCopy) h.Store.Audit.Record(ctx, audit.EventTypeSectionCopy)
np, _ := h.Store.Page.Get(ctx, pageID) // Re-level all pages in document.
h.LevelizeDocument(ctx, targetID)
np, _ := h.Store.Page.Get(ctx, pageID)
response.WriteJSON(w, np) response.WriteJSON(w, np)
} }

View file

@ -344,13 +344,9 @@ func (h *Handler) GetSpacePermissions(w http.ResponseWriter, r *http.Request) {
records[i].Name = user.EveryoneUserName records[i].Name = user.EveryoneUserName
} else { } else {
u, err := h.Store.User.Get(ctx, records[i].WhoID) u, err := h.Store.User.Get(ctx, records[i].WhoID)
if err != nil { if err == nil {
h.Runtime.Log.Info(fmt.Sprintf("user not found %s", records[i].WhoID)) records[i].Name = u.Fullname()
h.Runtime.Log.Error(method, err)
continue
} }
records[i].Name = u.Fullname()
} }
} }
} }

View file

@ -39,10 +39,10 @@ func main() {
// Specify the product edition. // Specify the product edition.
rt.Product = domain.Product{} rt.Product = domain.Product{}
rt.Product.Major = "2" rt.Product.Major = "3"
rt.Product.Minor = "5" rt.Product.Minor = "0"
rt.Product.Patch = "1" rt.Product.Patch = "0"
rt.Product.Revision = "190516105625" rt.Product.Revision = "190611110527"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = domain.CommunityEdition rt.Product.Edition = domain.CommunityEdition
rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition) rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)

File diff suppressed because one or more lines are too long

View file

@ -75,5 +75,6 @@ module.exports = {
"iziToast": true, "iziToast": true,
"Papa": true, "Papa": true,
"Popper": true, "Popper": true,
"ClipboardJS": true
} }
}; };

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import { set } from '@ember/object'; import { set } from '@ember/object';
@ -169,27 +170,27 @@ export default Component.extend(ModalMixin, Notifier, {
case constants.AuthProvider.Keycloak: case constants.AuthProvider.Keycloak:
if (this.get('KeycloakUrlError')) { if (this.get('KeycloakUrlError')) {
this.$("#keycloak-url").focus(); $("#keycloak-url").focus();
return; return;
} }
if (this.get('KeycloakRealmError')) { if (this.get('KeycloakRealmError')) {
this.$("#keycloak-realm").focus(); $("#keycloak-realm").focus();
return; return;
} }
if (this.get('KeycloakClientIdError')) { if (this.get('KeycloakClientIdError')) {
this.$("#keycloak-clientId").focus(); $("#keycloak-clientId").focus();
return; return;
} }
if (this.get('KeycloakPublicKeyError')) { if (this.get('KeycloakPublicKeyError')) {
this.$("#keycloak-publicKey").focus(); $("#keycloak-publicKey").focus();
return; return;
} }
if (this.get('KeycloakAdminUserError')) { if (this.get('KeycloakAdminUserError')) {
this.$("#keycloak-admin-user").focus(); $("#keycloak-admin-user").focus();
return; return;
} }
if (this.get('KeycloakAdminPasswordError')) { if (this.get('KeycloakAdminPasswordError')) {
this.$("#keycloak-admin-password").focus(); $("#keycloak-admin-password").focus();
return; return;
} }
@ -213,11 +214,11 @@ export default Component.extend(ModalMixin, Notifier, {
case constants.AuthProvider.LDAP: case constants.AuthProvider.LDAP:
if (this.get('ldapErrorServerHost')) { if (this.get('ldapErrorServerHost')) {
this.$("#ldap-host").focus(); $("#ldap-host").focus();
return; return;
} }
if (this.get('ldapErrorServerPort')) { if (this.get('ldapErrorServerPort')) {
this.$("#ldap-port").focus(); $("#ldap-port").focus();
return; return;
} }
@ -226,7 +227,7 @@ export default Component.extend(ModalMixin, Notifier, {
config.serverPort = parseInt(this.get('ldapConfig.serverPort')); config.serverPort = parseInt(this.get('ldapConfig.serverPort'));
if (!_.isEmpty(config.groupFilter) && _.isEmpty(config.attributeGroupMember)) { if (!_.isEmpty(config.groupFilter) && _.isEmpty(config.attributeGroupMember)) {
this.$('#ldap-attributeGroupMember').focus(); $('#ldap-attributeGroupMember').focus();
return; return;
} }

View file

@ -53,7 +53,7 @@ export default Component.extend(Notifier, Modal, {
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.$('#restore-file').on('change', function(){ $('#restore-file').on('change', function(){
var fileName = document.getElementById("restore-file").files[0].name; var fileName = document.getElementById("restore-file").files[0].name;
$(this).next('.custom-file-label').html(fileName); $(this).next('.custom-file-label').html(fileName);
}); });

View file

@ -84,7 +84,7 @@ export default Component.extend(Notifier, {
actions: { actions: {
change() { change() {
const selectEl = this.$('#maxTags')[0]; const selectEl = $('#maxTags')[0];
const selection = selectEl.selectedOptions[0].value; const selection = selectEl.selectedOptions[0].value;
this.set('maxTags', parseInt(selection)); this.set('maxTags', parseInt(selection));

View file

@ -63,9 +63,9 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.set('showPermExplain', !this.get('showPermExplain')); this.set('showPermExplain', !this.get('showPermExplain'));
if (this.showPermExplain) { if (this.showPermExplain) {
this.$(".perms").show(); $(".perms").show();
} else { } else {
this.$(".perms").hide(); $(".perms").hide();
} }
}, },

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { debounce } from '@ember/runloop'; import { debounce } from '@ember/runloop';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
@ -83,7 +84,7 @@ export default Component.extend(ModalMixin, {
didRender() { didRender() {
this._super(...arguments); this._super(...arguments);
this.$('#content-linker-networklocation').removeClass('is-invalid'); $('#content-linker-networklocation').removeClass('is-invalid');
}, },
willDestroyElement() { willDestroyElement() {
@ -149,7 +150,7 @@ export default Component.extend(ModalMixin, {
} }
if (_.isNull(selection)) { if (_.isNull(selection)) {
if (this.get('tab4Selected')) this.$('#content-linker-networklocation').addClass('is-invalid').focus(); if (this.get('tab4Selected')) $('#content-linker-networklocation').addClass('is-invalid').focus();
return; return;
} }

View file

@ -17,6 +17,8 @@ import Modals from '../../mixins/modal';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend(Modals, { export default Component.extend(Modals, {
classNames: ['document-meta-wrapper', 'non-printable'],
appMeta: service(),
documentService: service('document'), documentService: service('document'),
sessionService: service('session'), sessionService: service('session'),
categoryService: service('category'), categoryService: service('category'),
@ -52,10 +54,22 @@ export default Component.extend(Modals, {
}, },
actions: { actions: {
onEdit() {
if (!this.get('permissions.documentEdit')) return;
this.get('router').transitionTo('document.settings');
},
onEditCategory() { onEditCategory() {
if (!this.get('permissions.documentEdit')) return; if (!this.get('permissions.documentEdit')) return;
this.get('router').transitionTo('document.settings', {queryParams: {tab: 'category'}}); this.get('router').transitionTo('document.settings', {queryParams: {tab: 'category'}});
},
onSelectVersion(version) {
let space = this.get('space');
this.get('router').transitionTo('document', space.get('id'), space.get('slug'), version.documentId, this.get('document.slug'));
} }
} }
}); });

View file

@ -18,17 +18,23 @@ export default Component.extend({
editMode: false, editMode: false,
editPage: null, editPage: null,
editMeta: null, editMeta: null,
expanded: true,
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
if (this.get('isDestroyed') || this.get('isDestroying')) return; if (this.get('isDestroyed') || this.get('isDestroying')) return;
let pageId = this.get('page.id');
if (this.get('session.authenticated')) { if (this.get('session.authenticated')) {
this.workflow(); this.workflow();
} }
if (this.get('toEdit') === this.get('page.id') && this.get('permissions.documentEdit')) this.send('onEdit'); if (this.get('toEdit') === pageId && this.get('permissions.documentEdit')) this.send('onEdit');
// Work out if this section is expanded by default (state stored in browser local storage).
this.set('expanded', !_.includes(this.get('expandState'), pageId));
}, },
workflow() { workflow() {

View file

@ -48,6 +48,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
this.get('permissions.documentEdit')) return true; this.get('permissions.documentEdit')) return true;
}), }),
duplicateName: '',
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -88,6 +89,10 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
this.modalOpen("#document-template-modal", {show:true}, "#new-template-name"); this.modalOpen("#document-template-modal", {show:true}, "#new-template-name");
}, },
onShowDuplicateModal() {
this.modalOpen("#document-duplicate-modal", {show:true}, "#duplicate-name");
},
onShowDeleteModal() { onShowDeleteModal() {
this.modalOpen("#document-delete-modal", {show:true}); this.modalOpen("#document-delete-modal", {show:true});
}, },
@ -99,7 +104,35 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
cb(); cb();
}, },
onPrintDocument() { onShowPrintModal() {
let pages = this.get('pages');
// By default we select everything for print.
pages.forEach((item) => {
item.set('printSelected', true);
});
this.set('pages', pages);
this.modalOpen("#document-print-modal", {show:true});
},
onPrintSelection() {
this.modalClose('#document-print-modal');
let pages = this.get('pages');
pages.forEach((item) => {
let pageId = item.get('page.id');
let selected = item.get('printSelected');
$(`#page-${pageId}`).addClass('non-printable');
$(`#page-spacer-${pageId}`).addClass('non-printable');
if (selected) {
$(`#page-${pageId}`).removeClass('non-printable');
$(`#page-spacer-${pageId}`).removeClass('non-printable');
}
});
window.print(); window.print();
}, },
@ -155,6 +188,25 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
return true; return true;
}, },
onDuplicate() {
let name = this.get('duplicateName');
if (_.isEmpty(name)) {
$("#duplicate-name").addClass("is-invalid").focus();
return;
}
$("#duplicate-name").removeClass("is-invalid");
this.set('duplicateName', '');
this.get('onDuplicate')(name);
this.modalClose('#document-duplicate-modal');
return true;
},
onExport() { onExport() {
let spec = { let spec = {
spaceId: this.get('document.spaceId'), spaceId: this.get('document.spaceId'),

View file

@ -0,0 +1,36 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { inject as service } from '@ember/service';
import AuthMixin from '../../mixins/auth';
import Component from '@ember/component';
export default Component.extend(AuthMixin, {
router: service(),
documentSvc: service('document'),
docs: null,
space: null,
didReceiveAttrs() {
this._super(...arguments);
this.get('documentSvc').getAllBySpace(this.get('space.id')).then((docs) => {
this.set('docs', docs);
this.classNames = ['dicon', this.get('constants').Icon.ArrowSmallDown];
});
},
actions: {
onSpace() {
this.router.transitionTo('folder.index', this.space.id, this.space.slug);
}
}
});

View file

@ -13,21 +13,22 @@ import $ from 'jquery';
import { computed, observer } from '@ember/object'; import { computed, observer } from '@ember/object';
import { debounce } from '@ember/runloop'; import { debounce } from '@ember/runloop';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Notifier from '../../mixins/notifier';
import ModalMixin from '../../mixins/modal'; import ModalMixin from '../../mixins/modal';
import tocUtil from '../../utils/toc'; import tocUtil from '../../utils/toc';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend(ModalMixin, { export default Component.extend(Notifier, ModalMixin, {
documentService: service('document'), documentService: service('document'),
searchService: service('search'), searchService: service('search'),
router: service(), router: service(),
appMeta: service(),
deleteChildren: false, deleteChildren: false,
blockTitle: "", blockTitle: "",
blockExcerpt: "", blockExcerpt: "",
// canEdit: false, targetSpace: null,
canDelete: false, targetDocs: null,
canMove: false, targetDoc: null,
docSearchFilter: '',
// eslint-disable-next-line ember/no-observers // eslint-disable-next-line ember/no-observers
onKeywordChange: observer('docSearchFilter', function() { onKeywordChange: observer('docSearchFilter', function() {
@ -37,12 +38,6 @@ export default Component.extend(ModalMixin, {
emptySearch: computed('docSearchResults', function() { emptySearch: computed('docSearchResults', function() {
return this.get('docSearchResults.length') === 0; return this.get('docSearchResults.length') === 0;
}), }),
hasMenuPermissions: computed('permissions.{documentCopy,documentTemplate}', 'userPendingItem', 'canEdit', 'canMove', 'canDelete', function() {
let permissions = this.get('permissions');
return permissions.get('documentCopy') || permissions.get('documentTemplate') ||
this.get('canEdit') || this.get('canMove') || this.get('canDelete');
}),
canEdit: computed('permissions', 'document', 'pages', function() { canEdit: computed('permissions', 'document', 'pages', function() {
let constants = this.get('constants'); let constants = this.get('constants');
let permissions = this.get('permissions'); let permissions = this.get('permissions');
@ -57,7 +52,7 @@ export default Component.extend(ModalMixin, {
init() { init() {
this._super(...arguments); this._super(...arguments);
this.docSearchResults = [];
this.state = { this.state = {
actionablePage: false, actionablePage: false,
upDisabled: true, upDisabled: true,
@ -72,21 +67,32 @@ export default Component.extend(ModalMixin, {
this._super(...arguments); this._super(...arguments);
this.modalInputFocus('#publish-page-modal-' + this.get('page.id'), '#block-title-' + this.get('page.id')); this.modalInputFocus('#publish-page-modal-' + this.get('page.id'), '#block-title-' + this.get('page.id'));
let permissions = this.get('permissions');
// this.set('canEdit', permissions.get('documentEdit'));
this.set('canDelete', permissions.get('documentDelete'));
this.set('canMove', permissions.get('documentMove'));
this.setState(this.get('page.id')); this.setState(this.get('page.id'));
}, },
searchDocs() { didInsertElement(){
let payload = { keywords: this.get('docSearchFilter').trim(), doc: true }; this._super(...arguments);
if (payload.keywords.length == 0) return;
this.get('searchService').find(payload).then((response)=> { let pageId = this.get('page.id');
this.set('docSearchResults', response); let url = this.get('appMeta.appHost') +
this.get('router').generate('document.index', {queryParams: {currentPageId: pageId}});
let self = this;
let clip = new ClipboardJS('#page-copy-link-' + pageId, {
text: function() {
self.notifySuccess('Link copied to clipboard');
return url;
}
}); });
this.set('clip', clip);
},
willDestroyElement() {
this._super(...arguments);
let clip = this.get('clip');
if (!_.isUndefined(clip)) clip.destroy();
}, },
// Controls what user can do with the toc enty for this page // Controls what user can do with the toc enty for this page
@ -126,6 +132,10 @@ export default Component.extend(ModalMixin, {
this.modalClose('#delete-page-modal-' + this.get('page.id')); this.modalClose('#delete-page-modal-' + this.get('page.id'));
}, },
onShowPublishModal() {
this.modalOpen('#publish-page-modal-' + this.get('page.id'), {"show": true}, '#block-title-' + this.get('page.id'));
},
onSavePageAsBlock() { onSavePageAsBlock() {
let page = this.get('page'); let page = this.get('page');
let titleElem = '#block-title-' + page.get('id'); let titleElem = '#block-title-' + page.get('id');
@ -171,45 +181,55 @@ export default Component.extend(ModalMixin, {
}); });
}, },
onSelectSearchResult(documentId) { onShowCopyModal() {
let results = this.get('docSearchResults'); this.send('onSelectSpace', this.get('folder'));
results.forEach((d) => { this.modalOpen('#copy-page-modal-' + this.get('page.id'), {show:true});
d.set('selected', d.get('documentId') === documentId); },
});
this.set('docSearchResults', results); onShowMoveModal() {
this.send('onSelectSpace', this.get('folder'));
this.modalOpen('#move-page-modal-' + this.get('page.id'), {show:true});
}, },
onCopyPage() { onCopyPage() {
let item = this.get('docSearchResults').findBy('selected', true); let targetDoc = this.get('targetDoc');
let documentId = !_.isUndefined(item) ? item.get('documentId') : ''; if (_.isNull(targetDoc)) return;
if (_.isEmpty(documentId)) return;
this.modalClose('#copy-page-modal-' + this.get('page.id')); this.modalClose('#copy-page-modal-' + this.get('page.id'));
let cb = this.get('onCopyPage'); this.get('onCopyPage')(targetDoc.get('id'));
cb(documentId); this.get('refresh')();
let refresh = this.get('refresh');
refresh();
}, },
onMovePage() { onMovePage() {
let item = this.get('docSearchResults').findBy('selected', true); let targetDoc = this.get('targetDoc');
let documentId = !_.isUndefined(item) ? item.get('documentId') : ''; if (_.isNull(targetDoc)) return;
if (_.isEmpty(documentId)) return;
// can't move into self
if (documentId === this.get('document.id')) return;
this.modalClose('#move-page-modal-' + this.get('page.id')); this.modalClose('#move-page-modal-' + this.get('page.id'));
let cb = this.get('onMovePage'); this.get('onMovePage')(targetDoc.get('id'));
cb(documentId); this.get('refresh')();
},
let refresh = this.get('refresh'); // Load up documents for selected space and select the first one.
refresh(); onSelectSpace(space) {
this.set('targetSpace', space);
this.get('documentService').getAllBySpace(space.get('id')).then((docs) => {
this.set('targetDocs', docs);
if (space.get('id') === this.get('folder.id')) {
this.set('targetDoc', this.get('document'));
} else {
if (docs.length > 0) {
this.set('targetDoc', docs[0]);
}
}
});
},
onSelectDoc(doc) {
this.set('targetDoc', doc);
}, },
// Page up -- above pages shunt down // Page up -- above pages shunt down
@ -277,6 +297,15 @@ export default Component.extend(ModalMixin, {
let cb = this.get('onPageLevelChange'); let cb = this.get('onPageLevelChange');
cb(state.pageId, pendingChanges); cb(state.pageId, pendingChanges);
} }
},
onExpand() {
this.set('expanded', !this.get('expanded'));
this.get('onExpand')(this.get('page.id'), this.get('expanded'));
},
onCopyLink() {
this.set('currentPageId', this.get('page.id'));
} }
} }
}); });

View file

@ -167,9 +167,9 @@ export default Component.extend(Notifier, {
if (tag.length> 0) { if (tag.length> 0) {
if (!_.includes(tagzToSave, tag) && !_.startsWith(tag, '-')) { if (!_.includes(tagzToSave, tag) && !_.startsWith(tag, '-')) {
tagzToSave.push(tag); tagzToSave.push(tag);
this.$('#add-tag-field-' + t.number).removeClass('is-invalid'); $('#add-tag-field-' + t.number).removeClass('is-invalid');
} else { } else {
this.$('#add-tag-field-' + t.number).addClass('is-invalid'); $('#add-tag-field-' + t.number).addClass('is-invalid');
} }
} }
}); });

View file

@ -168,9 +168,9 @@ export default Component.extend(Notifier, {
if (tag.length> 0) { if (tag.length> 0) {
if (!_.includes(tagzToSave, tag) && !_.startsWith(tag, '-')) { if (!_.includes(tagzToSave, tag) && !_.startsWith(tag, '-')) {
tagzToSave.push(tag); tagzToSave.push(tag);
this.$('#add-tag-field-' + t.number).removeClass('is-invalid'); $('#add-tag-field-' + t.number).removeClass('is-invalid');
} else { } else {
this.$('#add-tag-field-' + t.number).addClass('is-invalid'); $('#add-tag-field-' + t.number).addClass('is-invalid');
} }
} }
}); });

View file

@ -17,7 +17,7 @@ import Notifier from '../../mixins/notifier';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend(Modals, Notifier, { export default Component.extend(Modals, Notifier, {
classNames: ["section"], classNames: ["document-meta", ' non-printable'],
documentService: service('document'), documentService: service('document'),
browserSvc: service('browser'), browserSvc: service('browser'),
appMeta: service(), appMeta: service(),
@ -37,6 +37,15 @@ export default Component.extend(Modals, Notifier, {
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
// For authenticated users we send server auth token.
let qry = '';
if (this.get('session.hasSecureToken')) {
qry = '?secure=' + this.get('session.secureToken');
} else if (this.get('session.authenticated')) {
qry = '?token=' + this.get('session.authToken');
}
this.set('downloadQuery', qry);
if (!this.get('permissions.documentEdit') || this.get('document.protection') === this.get('constants').ProtectionType.Lock) { if (!this.get('permissions.documentEdit') || this.get('document.protection') === this.get('constants').ProtectionType.Lock) {
return; return;
} }
@ -47,7 +56,7 @@ export default Component.extend(Modals, Notifier, {
let uploadUrl = `${url}/documents/${documentId}/attachments`; let uploadUrl = `${url}/documents/${documentId}/attachments`;
// Handle upload clicks on button and anything inside that button. // Handle upload clicks on button and anything inside that button.
let sel = ['#upload-document-files ', '#upload-document-files > div']; let sel = ['#upload-document-files ', '#upload-document-files > span'];
for (var i=0; i < 2; i++) { for (var i=0; i < 2; i++) {
let dzone = new Dropzone(sel[i], { let dzone = new Dropzone(sel[i], {
headers: { headers: {
@ -86,15 +95,6 @@ export default Component.extend(Modals, Notifier, {
dzone.removeFile(file); dzone.removeFile(file);
}); });
} }
// For authenticated users we send server auth token.
let qry = '';
if (this.get('session.hasSecureToken')) {
qry = '?secure=' + this.get('session.secureToken');
} else if (this.get('session.authenticated')) {
qry = '?token=' + this.get('session.authToken');
}
this.set('downloadQuery', qry);
}, },
getAttachments() { getAttachments() {
@ -109,15 +109,6 @@ export default Component.extend(Modals, Notifier, {
this.notifySuccess('File deleted'); this.notifySuccess('File deleted');
this.getAttachments(); this.getAttachments();
}); });
},
onExport() {
this.get('documentSvc').export({}).then((htmlExport) => {
this.get('browserSvc').downloadFile(htmlExport, this.get('space.slug') + '.html');
this.notifySuccess('Exported');
});
this.modalClose("#space-export-modal");
} }
} }
}); });

View file

@ -1,41 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { computed } from '@ember/object';
import { notEmpty } from '@ember/object/computed';
import { inject as service } from '@ember/service';
import Modals from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(Modals, {
appMeta: service(),
documentService: service('document'),
sessionService: service('session'),
router: service(),
userChanges: notEmpty('contributorMsg'),
unassigned: computed('selectedCategories', 'tagz', function() {
return this.get('selectedCategories').length === 0 && this.get('tagz').length === 0;
}),
actions: {
onEditStatus() {
if (!this.get('permissions.documentEdit')) return;
this.get('router').transitionTo('document.settings', {queryParams: {tab: 'general'}});
},
onSelectVersion(version) {
let space = this.get('space');
this.get('router').transitionTo('document', space.get('id'), space.get('slug'), version.documentId, this.get('document.slug'));
}
}
});

View file

@ -38,6 +38,7 @@ export default Component.extend(Notifier, {
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
// Show/allow liking if space allows it and document is published.
this.set('showLikes', this.get('folder.allowLikes') && this.get('document.isLive')); this.set('showLikes', this.get('folder.allowLikes') && this.get('document.isLive'));
}, },

View file

@ -9,6 +9,7 @@
// //
// https://documize.com // https://documize.com
import $ from 'jquery';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { A } from '@ember/array'; import { A } from '@ember/array';
import { debounce } from '@ember/runloop'; import { debounce } from '@ember/runloop';
@ -152,9 +153,9 @@ export default Component.extend(Notifier, Modals, {
this.set('showSpacePermExplain', !this.get('showSpacePermExplain')); this.set('showSpacePermExplain', !this.get('showSpacePermExplain'));
if (this.showSpacePermExplain) { if (this.showSpacePermExplain) {
this.$(".space-perms").show(); $(".space-perms").show();
} else { } else {
this.$(".space-perms").hide(); $(".space-perms").hide();
} }
}, },
@ -162,9 +163,9 @@ export default Component.extend(Notifier, Modals, {
this.set('showDocumentPermExplain', !this.get('showDocumentPermExplain')); this.set('showDocumentPermExplain', !this.get('showDocumentPermExplain'));
if (this.showDocumentPermExplain) { if (this.showDocumentPermExplain) {
this.$(".document-perms").show(); $(".document-perms").show();
} else { } else {
this.$(".document-perms").hide(); $(".document-perms").hide();
} }
}, },
@ -257,7 +258,7 @@ export default Component.extend(Notifier, Modals, {
} }
if (email.length === 0) { if (email.length === 0) {
this.$('#space-invite-email').addClass('is-invalid').focus(); $('#space-invite-email').addClass('is-invalid').focus();
return; return;
} }
@ -283,7 +284,7 @@ export default Component.extend(Notifier, Modals, {
this.get('spaceSvc').share(this.get('folder.id'), result).then(() => { this.get('spaceSvc').share(this.get('folder.id'), result).then(() => {
this.notifySuccess('Invites sent'); this.notifySuccess('Invites sent');
this.$('#space-invite-email').removeClass('is-invalid'); $('#space-invite-email').removeClass('is-invalid');
this.modalClose("#space-invite-user-modal"); this.modalClose("#space-invite-user-modal");
this.load(); this.load();
}); });

View file

@ -17,7 +17,9 @@ import AuthMixin from '../../mixins/auth';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend(AuthMixin, { export default Component.extend(AuthMixin, {
classNames: ["section"], tagName: 'div',
classNames: ['master-sidebar'],
router: service(), router: service(),
documentService: service('document'), documentService: service('document'),
folderService: service('folder'), folderService: service('folder'),

View file

@ -19,7 +19,6 @@ import Notifier from '../../mixins/notifier';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend(ModalMixin, AuthMixin, Notifier, { export default Component.extend(ModalMixin, AuthMixin, Notifier, {
classNames: ["display-inline-block"],
spaceService: service('folder'), spaceService: service('folder'),
localStorage: service(), localStorage: service(),
templateService: service('template'), templateService: service('template'),

View file

@ -0,0 +1,17 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['master-grid-container'],
});

View file

@ -0,0 +1,17 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['master-content'],
});

View file

@ -0,0 +1,17 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['custom-action'],
});

View file

@ -0,0 +1,23 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['goto-top'],
actions: {
onClick() {
this.get('browser').scrollTo('html');
}
}
});

View file

@ -0,0 +1,17 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['master-sidebar', 'non-printable'],
});

View file

@ -17,7 +17,7 @@ import Component from '@ember/component';
export default Component.extend(Modals, { export default Component.extend(Modals, {
tagName: 'div', tagName: 'div',
classNames: ['master-sidebar-container', 'non-printable'], classNames: ['master-navigation', 'non-printable'],
selectedItem: '', selectedItem: '',
folderService: service('folder'), folderService: service('folder'),
appMeta: service(), appMeta: service(),

View file

@ -0,0 +1,18 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Modals from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(Modals, {
tagName: 'div',
classNames: ['master-navigation', 'non-printable'],
});

View file

@ -19,7 +19,9 @@ export default Component.extend({
size: 500, size: 500,
calcClass: computed(function() { calcClass: computed(function() {
switch(this.size) { let size = parseInt(this.size, 10);
switch(size) {
case 100: case 100:
return 'spacer-100'; return 'spacer-100';

View file

@ -0,0 +1,84 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { computed } from '@ember/object';
import Component from '@ember/component';
export default Component.extend({
tagName: 'button',
classNames: [],
classNameBindings: ['calcClass'],
attributeBindings: ['calcAttrs:data-dismiss', 'submitAttrs:type'],
label: '',
icon: '',
color: '',
light: false,
themed: false,
dismiss: false,
truncate: false,
stretch: false,
uppercase: true,
iconClass: '',
hasIcon: computed('iconClass', function() {
return this.iconClass.trim() != '';
}),
calcClass: computed(function() {
// Prepare icon class name
this.iconClass = this.icon;
// Prepare button class name
let bc = 'button';
if (this.themed) {
bc += '-theme';
} else {
bc += '-' + this.color;
}
if (this.light) {
bc += '-light';
}
if (!this.uppercase) {
bc += ' text-case-normal';
}
if (this.truncate) {
bc += ' text-truncate';
}
if (this.stretch) {
bc += ' max-width-100 text-left';
}
return bc;
}),
calcAttrs: computed(function() {
if (this.dismiss) {
return 'modal';
}
return null;
}),
submitAttrs: computed(function() {
return this.submit ? "submit": null;
}),
click(e) {
if (!_.isUndefined(this.onClick)) {
e.preventDefault();
this.onClick(e);
}
}
});

View file

@ -0,0 +1,17 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import Component from '@ember/component';
export default Component.extend({
tagName: 'div',
classNames: ['divider'],
});

View file

@ -0,0 +1,55 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { computed } from '@ember/object';
import Component from '@ember/component';
export default Component.extend({
classNames: [],
classNameBindings: ['calcClass'],
label: '',
color: '',
arrow: true,
iconClass: '',
calcClass: computed(function() {
// Prepare icon class name
this.iconClass = this.get('constants').Icon.ArrowSmallDown;
// Prepare button class name
let bc = 'dropdown';
if (!this.themed) {
bc += ' dropdown-' + this.color;
}
return bc;
}),
calcAttrs: computed(function() {
if (this.dismiss) {
return 'modal';
}
return null;
}),
submitAttrs: computed(function() {
return this.submit ? "submit": null;
}),
click(e) {
if (!_.isUndefined(this.onClick)) {
e.preventDefault();
this.onClick(e);
}
}
});

View file

@ -12,15 +12,13 @@
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
classNames: ['dmz-toolbar', 'non-printable'], classNames: ['dmz-toolbar'],
classNameBindings: classNameBindings:
['raised:dmz-toolbar-raised', ['raised:dmz-toolbar-raised',
'large:dmz-toolbar-large',
'bordered:dmz-toolbar-bordered', 'bordered:dmz-toolbar-bordered',
'light:dmz-toolbar-light', 'light:dmz-toolbar-light',
'dark:dmz-toolbar-dark'], 'dark:dmz-toolbar-dark'],
raised: false, raised: false,
large: false,
bordered: false, bordered: false,
dark: false, dark: false,
light: false, light: false,

View file

@ -228,16 +228,19 @@ let constants = EmberObject.extend({
Delete: 'dicon-bin', Delete: 'dicon-bin',
Edit: 'dicon-pen-2', Edit: 'dicon-pen-2',
Email: 'dicon-email', Email: 'dicon-email',
Expand: 'dicon-enlarge',
Export: 'dicon-data-upload', Export: 'dicon-data-upload',
Export2: 'dicon-upload', Export2: 'dicon-upload',
Filter: 'dicon-sort-tool', Filter: 'dicon-sort-tool',
Grid: 'dicon-grid-interface', Grid: 'dicon-grid-interface',
GoTop: 'dicon-move-layer-up',
Handshake: 'dicon-handshake', Handshake: 'dicon-handshake',
Index: 'dicon-menu-8', Index: 'dicon-menu-8',
Integrations: 'dicon-geometry', Integrations: 'dicon-geometry',
Link: 'dicon-link', Link: 'dicon-link',
ListBullet: 'dicon-list-bullet-2', ListBullet: 'dicon-list-bullet-2',
Locked: 'dicon-lock', Locked: 'dicon-lock',
MoreHorizontal: 'dicon-menu-5',
NotAllowed: 'dicon-ban', NotAllowed: 'dicon-ban',
PDF: 'dicon-pdf', PDF: 'dicon-pdf',
Print: 'dicon-print', Print: 'dicon-print',
@ -252,6 +255,7 @@ let constants = EmberObject.extend({
Send: 'dicon-send', Send: 'dicon-send',
Settings: 'dicon-settings-gear', Settings: 'dicon-settings-gear',
Share: 'dicon-network-connection', Share: 'dicon-network-connection',
Sort: 'dicon-alpha-order',
Split: 'dicon-split-37', Split: 'dicon-split-37',
Tag: 'dicon-delete-key', Tag: 'dicon-delete-key',
Tick: 'dicon-check', Tick: 'dicon-check',
@ -333,6 +337,7 @@ let constants = EmberObject.extend({
Close: 'Close', Close: 'Close',
Copy: 'Copy', Copy: 'Copy',
Delete: 'Delete', Delete: 'Delete',
Duplicate: 'Duplicate',
Edit: 'Edit', Edit: 'Edit',
Export: 'Export', Export: 'Export',
File: 'File', File: 'File',
@ -341,13 +346,16 @@ let constants = EmberObject.extend({
Invite: 'Invite', Invite: 'Invite',
Join: 'Join', Join: 'Join',
Leave: 'Leave', Leave: 'Leave',
Login: 'Login',
Move: 'Move', Move: 'Move',
Next: 'Next', Next: 'Next',
OK: 'OK', OK: 'OK',
Preview: 'Preview', Preview: 'Preview',
Print: 'Print',
Publish: 'Publish', Publish: 'Publish',
Reject: 'Reject', Reject: 'Reject',
Remove: 'Remove', Remove: 'Remove',
Reply: 'Reply',
Reset: 'Reset', Reset: 'Reset',
Restore: 'Restore', Restore: 'Restore',
Request: 'Request', Request: 'Request',
@ -357,6 +365,8 @@ let constants = EmberObject.extend({
Share: 'Share', Share: 'Share',
SignIn: 'Sign In', SignIn: 'Sign In',
Sort: 'Sort', Sort: 'Sort',
Space: 'Space',
Spaces: 'Spaces',
Unassigned: 'Unassigned', Unassigned: 'Unassigned',
Update: 'Update', Update: 'Update',
Upload: 'Upload', Upload: 'Upload',

View file

@ -1,20 +1,25 @@
{{#layout/master-sidebar hideNavigation=true}} <Layout::MasterNavigation @hideNavigation={{true}} />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar />
<div class="section">
<div class="title">welcome to documize</div>
<p>Let's set up your account and you started</p>
</div>
{{/layout/master-sidebar}}
{{#layout/master-content}} <Layout::Grid::Container>
{{layout/logo-heading <Layout::Grid::Sidebar>
title=appMeta.title <div class="sidebar-content">
desc=appMeta.message <div class="section">
logo=true}} <div class="title">welcome to documize</div>
<p>Let's set up your account and get you started</p>
</div>
</div>
</Layout::Grid::Sidebar>
{{onboard/share-folder <Layout::Grid::Content>
serial=serial {{layout/logo-heading
folderId=folderId title=appMeta.title
slug=slug}} desc=appMeta.message
{{/layout/master-content}} logo=true}}
{{onboard/share-folder
serial=serial
folderId=folderId
slug=slug}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -1,71 +1,83 @@
{{#layout/master-sidebar selectedItem="settings"}} <Layout::MasterNavigation @selectedItem="settings" />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1" />
<div class="section"> <div class="zone-2">
<div class="title">administration</div> <div class="label color-gray-700">
<div class="list"> Documize {{appMeta.edition}} Edition {{appMeta.version}} (build {{appMeta.revision}})
{{#link-to "customize.general" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">General</div>
{{/link-to}}
{{#link-to "customize.labels" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Checkbox}} />
<div class="name">Labels</div>
{{/link-to}}
{{#link-to "customize.folders" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Grid}} />
<div class="name">Spaces</div>
{{/link-to}}
{{#link-to "customize.users" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Person}} />
<div class="name">User Management</div>
{{/link-to}}
{{#link-to "customize.groups" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.People}} />
<div class="name">User Groups</div>
{{/link-to}}
{{#link-to "customize.integrations" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Integrations}} />
<div class="name">Integrations</div>
{{/link-to}}
{{#if session.isGlobalAdmin}}
{{#link-to "customize.smtp" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Send}} />
<div class="name">Mail Server</div>
{{/link-to}}
{{#link-to "customize.auth" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Authentication</div>
{{/link-to}}
{{#link-to "customize.search" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Search}} />
<div class="name">Search</div>
{{/link-to}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#link-to "customize.audit" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.ButtonAction}} />
<div class="name">Audit Log</div>
{{/link-to}}
{{/if}}
{{/if}}
{{#link-to "customize.backup" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Database}} />
<div class="name">Backup & Restore</div>
{{/link-to}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#link-to "customize.billing" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Handshake}} />
<div class="name">Billing</div>
{{/link-to}}
{{/if}}
{{#link-to "customize.product" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Announce}} />
<div class="name">Changelog</div>
{{/link-to}}
</div> </div>
</div> </div>
{{/layout/master-sidebar}} <div class="zone-3" />
</Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
{{outlet}} <Layout::Grid::Sidebar>
{{/layout/master-content}} <div class="sidebar-content">
<div class="section">
<div class="title">administration</div>
<div class="list">
{{#link-to "customize.general" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">General</div>
{{/link-to}}
{{#link-to "customize.labels" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Checkbox}} />
<div class="name">Labels</div>
{{/link-to}}
{{#link-to "customize.folders" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Grid}} />
<div class="name">Spaces</div>
{{/link-to}}
{{#link-to "customize.users" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Person}} />
<div class="name">User Management</div>
{{/link-to}}
{{#link-to "customize.groups" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.People}} />
<div class="name">User Groups</div>
{{/link-to}}
{{#link-to "customize.integrations" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Integrations}} />
<div class="name">Integrations</div>
{{/link-to}}
{{#if session.isGlobalAdmin}}
{{#link-to "customize.smtp" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Send}} />
<div class="name">Mail Server</div>
{{/link-to}}
{{#link-to "customize.auth" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Authentication</div>
{{/link-to}}
{{#link-to "customize.search" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Search}} />
<div class="name">Search</div>
{{/link-to}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#link-to "customize.audit" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.ButtonAction}} />
<div class="name">Audit Log</div>
{{/link-to}}
{{/if}}
{{/if}}
{{#link-to "customize.backup" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Database}} />
<div class="name">Backup & Restore</div>
{{/link-to}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#link-to "customize.billing" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Handshake}} />
<div class="name">Billing</div>
{{/link-to}}
{{/if}}
{{#link-to "customize.product" activeClass="selected" class="item" tagName="div"}}
<i class={{concat "dicon " constants.Icon.Announce}} />
<div class="name">Changelog</div>
{{/link-to}}
</div>
</div>
</div>
</Layout::Grid::Sidebar>
<Layout::Grid::Content>
{{outlet}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -9,7 +9,7 @@
// //
// https://documize.com // https://documize.com
import { Promise as EmberPromise } from 'rsvp'; import { Promise as EmberPromise, all } from 'rsvp';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import Notifier from '../../../mixins/notifier'; import Notifier from '../../../mixins/notifier';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
@ -19,6 +19,7 @@ export default Controller.extend(Notifier, {
templateService: service('template'), templateService: service('template'),
sectionService: service('section'), sectionService: service('section'),
linkService: service('link'), linkService: service('link'),
localStore: service('local-storage'),
appMeta: service(), appMeta: service(),
router: service(), router: service(),
sidebarTab: 'toc', sidebarTab: 'toc',
@ -45,8 +46,19 @@ export default Controller.extend(Notifier, {
onCopyPage(pageId, targetDocumentId) { onCopyPage(pageId, targetDocumentId) {
let documentId = this.get('document.id'); let documentId = this.get('document.id');
this.get('documentService').copyPage(documentId, pageId, targetDocumentId).then(() => { let pages = this.get('pages');
// Make list of page ID values including all child pages.
let pagesToProcess = [{ pageId: pageId }].concat(this.get('documentService').getChildren(pages, pageId));
// Copy each page.
let promises = [];
pagesToProcess.forEach((page, index) => {
promises[index] = this.get('documentService').copyPage(documentId, page.pageId, targetDocumentId);
});
// Do post-processing after all copying has completed.
all(promises).then(() => {
// refresh data if copied to same document // refresh data if copied to same document
if (documentId === targetDocumentId) { if (documentId === targetDocumentId) {
this.set('pageId', ''); this.set('pageId', '');
@ -61,9 +73,21 @@ export default Controller.extend(Notifier, {
onMovePage(pageId, targetDocumentId) { onMovePage(pageId, targetDocumentId) {
let documentId = this.get('document.id'); let documentId = this.get('document.id');
let pages = this.get('pages');
this.get('documentService').copyPage(documentId, pageId, targetDocumentId).then(() => { // Make list of page ID values including all child pages.
this.send('onPageDeleted', { id: pageId, children: false }); let pagesToProcess = [{ pageId: pageId }].concat(this.get('documentService').getChildren(pages, pageId));
// Copy each page.
let promises = [];
pagesToProcess.forEach((page, index) => {
promises[index] = this.get('documentService').copyPage(documentId, page.pageId, targetDocumentId);
});
// Do post-processing after all copying has completed.
all(promises).then(() => {
// For move operation we delete all copied pages.
this.send('onPageDeleted', { id: pageId, children: true });
}); });
}, },
@ -108,19 +132,9 @@ export default Controller.extend(Notifier, {
let documentId = this.get('document.id'); let documentId = this.get('document.id');
let deleteId = deletePage.id; let deleteId = deletePage.id;
let deleteChildren = deletePage.children; let deleteChildren = deletePage.children;
let pendingChanges = [];
let pages = this.get('pages'); let pages = this.get('pages');
let pageIndex = _.findIndex(pages, function(i) { return i.get('page.id') === deleteId; }); let pendingChanges = this.get('documentService').getChildren(pages, deleteId);
let item = pages[pageIndex];
// select affected pages
for (var i = pageIndex + 1; i < pages.get('length'); i++) {
if (i === pageIndex + 1 && pages[i].get('page.level') === item.get('page.level')) break;
if (pages[i].get('page.level') <= item.get('page.level')) break;
pendingChanges.push({ pageId: pages[i].get('page.id'), level: pages[i].get('page.level') - 1 });
}
this.set('currentPageId', null); this.set('currentPageId', null);
@ -188,6 +202,12 @@ export default Controller.extend(Notifier, {
}); });
}, },
onDuplicate(name) {
this.get('documentService').duplicate(this.get('folder.id'), this.get('document.id'), name).then(() => {
this.notifySuccess('Duplicated');
});
},
onPageSequenceChange(currentPageId, changes) { onPageSequenceChange(currentPageId, changes) {
this.set('currentPageId', currentPageId); this.set('currentPageId', currentPageId);
@ -263,6 +283,36 @@ export default Controller.extend(Notifier, {
}); });
}); });
}); });
},
// Expand all if nothing is expanded at the moment.
// Collapse all if something is expanded at the moment.
onExpandAll() {
let expandState = this.get('localStore').getDocSectionHide(this.get('document.id'));
if (expandState.length === 0) {
let pages = this.get('pages');
pages.forEach((item) => {
expandState.push(item.get('page.id'));
})
} else {
expandState = [];
}
this.get('localStore').setDocSectionHide(this.get('document.id'), expandState);
this.set('expandState', expandState);
},
onExpand(pageId, show) {
let expandState = this.get('localStore').getDocSectionHide(this.get('document.id'));
if (show) {
expandState = _.without(expandState, pageId)
} else {
expandState.push(pageId);
}
this.get('localStore').setDocSectionHide(this.get('document.id'), expandState);
} }
} }
}); });

View file

@ -19,6 +19,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
linkService: service('link'), linkService: service('link'),
folderService: service('folder'), folderService: service('folder'),
userService: service('user'), userService: service('user'),
localStore: service('local-storage'),
contributionStatus: '', contributionStatus: '',
approvalStatus: '', approvalStatus: '',
@ -69,6 +70,9 @@ export default Route.extend(AuthenticatedRouteMixin, {
controller.set('blocks', model.blocks); controller.set('blocks', model.blocks);
controller.set('versions', model.versions); controller.set('versions', model.versions);
controller.set('attachments', model.attachments); controller.set('attachments', model.attachments);
// For persistence of section expand/collapse state.
controller.set('expandState', this.get('localStore').getDocSectionHide(model.document.id));
}, },
activate: function () { activate: function () {

View file

@ -1,8 +1,12 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=100}} <Layout::MasterToolbar>
<div class="zone-1">
<div class="section"> <Document::DocumentsList @space={{folder}} @docId={{document.id}} />
{{document/sidebar-meta </div>
<div class="zone-2" />
<div class="zone-3">
{{document/document-toolbar
tab=tab
roles=roles roles=roles
pages=pages pages=pages
space=folder space=folder
@ -10,128 +14,93 @@
document=document document=document
versions=versions versions=versions
permissions=permissions permissions=permissions
refresh=(action "refresh")
onSaveTemplate=(action "onSaveTemplate")
onSaveDocument=(action "onSaveDocument")
onDuplicate=(action "onDuplicate")
onDocumentDelete=(action "onDocumentDelete")}}
</div>
</Layout::MasterToolbar>
<Layout::Grid::Container>
<Layout::Grid::Sidebar>
<div class="sidebar-content">
<Layout::Grid::SidebarGoTop />
<Layout::Grid::SidebarCustomAction>
<i class="dicon {{constants.Icon.Expand}} {{if (gt expandState.length 0) "color-green-500"}}" {{action "onExpandAll"}}>
{{#attach-tooltip showDelay=750}}Expand/collapse{{/attach-tooltip}}
</i>
</Layout::Grid::SidebarCustomAction>
<Ui::UiSpacer @size="200" />
{{document/sidebar-toc
page=page
roles=roles
pages=pages
folder=folder
document=document
permissions=permissions
currentPageId=currentPageId
onShowPage=(action "onShowPage")
onPageLevelChange=(action "onPageLevelChange")
onPageSequenceChange=(action "onPageSequenceChange")}}
</div>
</Layout::Grid::Sidebar>
<Layout::Grid::Content>
<div class="document-meta">
<div class="document-heading">
<h1 class="name">{{document.name}}</h1>
<h2 class="desc">{{document.excerpt}}</h2>
<div class="dates">
{{formatted-date document.created}}
{{#if (not-eq document.created document.revised)}}
&mdash; revised {{formatted-date document.revised}}
{{/if}}
</div>
</div>
</div>
{{document/document-meta
tab=tab
roles=roles
pages=pages
space=folder
spaces=folders
document=document
versions=versions
attachments=attachments
permissions=permissions
contributionStatus=contributionStatus contributionStatus=contributionStatus
approvalStatus=approvalStatus}} approvalStatus=approvalStatus}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="text-center"> {{document/view-content
{{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}} expandState=expandState
{{ui/ui-toolbar-icon icon=constants.Icon.Index color=constants.Color.Gray tooltip="Table of contents"
selected=(eq sidebarTab "toc") onClick=(action "onSidebarChange" "toc")}}
{{ui/ui-toolbar-icon icon=constants.Icon.Attachment color=constants.Color.Gray tooltip="Attachments"
selected=(eq sidebarTab "files") onClick=(action "onSidebarChange" "files")}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{ui/ui-toolbar-icon icon=constants.Icon.Chat color=constants.Color.Gray tooltip="Comments & Feedback"
selected=(eq sidebarTab "feedback") onClick=(action "onSidebarChange" "feedback")}}
{{/if}}
{{/ui/ui-toolbar}}
</div>
</div>
{{ui/ui-spacer size=200}}
{{#if (eq sidebarTab "toc")}}
{{document/sidebar-toc
page=page
roles=roles roles=roles
links=links
pages=pages pages=pages
blocks=blocks
folder=folder folder=folder
folders=folders
sections=sections
document=document document=document
permissions=permissions permissions=permissions
attachments=attachments
currentPageId=currentPageId currentPageId=currentPageId
onShowPage=(action "onShowPage") refresh=(action "refresh")
onExpand=(action "onExpand")
onSavePage=(action "onSavePage")
onCopyPage=(action "onCopyPage")
onMovePage=(action "onMovePage")
onDeletePage=(action "onPageDeleted")
onInsertSection=(action "onInsertSection")
onSavePageAsBlock=(action "onSavePageAsBlock")
onPageLevelChange=(action "onPageLevelChange") onPageLevelChange=(action "onPageLevelChange")
onPageSequenceChange=(action "onPageSequenceChange")}} onPageSequenceChange=(action "onPageSequenceChange")
{{/if}} onAttachmentUpload=(action "onAttachmentUpload")
onAttachmentDelete=(action "onAttachmentDelete")}}
{{#if (eq sidebarTab "files")}} </Layout::Grid::Content>
{{document/sidebar-attachment </Layout::Grid::Container>
document=document
permissions=permissions}}
{{/if}}
{{#if (eq sidebarTab "feedback")}}
{{enterprise/sidebar-feedback
document=document
permissions=permissions}}
{{/if}}
{{/layout/master-sidebar}}
{{#layout/master-content}}
{{document/document-toolbar
tab=tab
roles=roles
space=folder
spaces=folders
document=document
versions=versions
permissions=permissions
refresh=(action "refresh")
onSaveTemplate=(action "onSaveTemplate")
onSaveDocument=(action "onSaveDocument")
onDocumentDelete=(action "onDocumentDelete")}}
{{document/document-meta
tab=tab
roles=roles
pages=pages
space=folder
spaces=folders
document=document
versions=versions
permissions=permissions
contributionStatus=contributionStatus
approvalStatus=approvalStatus}}
{{#if contributionStatus}}
{{ui/ui-spacer size=200}}
<div class="document-meta">
<div class="label-workflow-status">
{{contributionStatus}}
</div>
</div>
{{else}}
{{#if approvalStatus}}
{{ui/ui-spacer size=200}}
<div class="document-meta">
<div class="label-workflow-status">
{{approvalStatus}}
</div>
</div>
{{/if}}
{{/if}}
{{ui/ui-spacer size=300}}
<div class="document-meta {{if permissions.documentEdit "cursor-pointer"}}" {{action "onEditMeta"}}>
<div class="document-heading">
<h1 class="name">{{document.name}}</h1>
<h2 class="desc">{{document.excerpt}}</h2>
</div>
</div>
{{document/view-content
roles=roles
links=links
pages=pages
blocks=blocks
folder=folder
folders=folders
sections=sections
document=document
permissions=permissions
attachments=attachments
currentPageId=currentPageId
refresh=(action "refresh")
onSavePage=(action "onSavePage")
onCopyPage=(action "onCopyPage")
onMovePage=(action "onMovePage")
onDeletePage=(action "onPageDeleted")
onInsertSection=(action "onInsertSection")
onSavePageAsBlock=(action "onSavePageAsBlock")
onPageLevelChange=(action "onPageLevelChange")
onPageSequenceChange=(action "onPageSequenceChange")
onAttachmentUpload=(action "onAttachmentUpload")
onAttachmentDelete=(action "onAttachmentDelete")}}
{{/layout/master-content}}

View file

@ -19,6 +19,10 @@ export default Controller.extend(Notifier, {
selectedRevision: null, selectedRevision: null,
actions: { actions: {
onBack() {
this.get('router').transitionTo('document.index');
},
onRevision(revision) { onRevision(revision) {
this.set('selectedRevision', revision); this.set('selectedRevision', revision);
}, },

View file

@ -1,38 +1,47 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1">
<div class="section"> {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{#link-to "document.index"}} {{ui/ui-toolbar-button themed=true uppercase=false
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.ArrowLeft label="Document"}} icon=constants.Icon.ArrowLeft label=document.name onClick=(action "onBack")}}
{{/link-to}} {{/ui/ui-toolbar}}
{{ui/ui-spacer size=400}}
<div class="title">REVISIONS</div>
<div class="list">
{{#each revisions as |revision|}}
<div class="item {{if (eq selectedRevision revision) "selected"}}" {{action "onRevision" revision}}>
<i class={{concat "dicon " constants.Icon.TriangleSmallRight}} />
<div class="name">{{formatted-date revision.created}}</div>
</div>
{{/each}}
</div>
</div> </div>
{{/layout/master-sidebar}} <div class="zone-2" />
<div class="zone-3" />
</Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
{{layout/logo-heading <Layout::Grid::Sidebar>
title="Content Revisions" <div class="sidebar-content">
desc="Review previous content changes and roll back edits" <div class="section">
icon=constants.Icon.TimeBack}} <div class="title">REVISIONS</div>
<div class="list">
{{#each revisions as |revision|}}
<div class="item {{if (eq selectedRevision revision) "selected"}}" {{action "onRevision" revision}}>
<i class={{concat "dicon " constants.Icon.TriangleSmallRight}} />
<div class="name">{{formatted-date revision.created}}</div>
</div>
{{/each}}
</div>
</div>
</div>
</Layout::Grid::Sidebar>
<Layout::Grid::Content>
{{layout/logo-heading
title="Content Revisions"
desc="Review previous content changes and roll back edits"
icon=constants.Icon.TimeBack}}
{{document/view-revision
pages=pages
folder=folder
document=document
permissions=permissions
revisions=revisions
revision=selectedRevision
onRollback=(action "onRollback")}}
</Layout::Grid::Content>
</Layout::Grid::Container>
{{document/view-revision
pages=pages
folder=folder
document=document
permissions=permissions
revisions=revisions
revision=selectedRevision
onRollback=(action "onRollback")}}
{{/layout/master-content}}

View file

@ -13,6 +13,7 @@ import { inject as service } from '@ember/service';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
export default Controller.extend({ export default Controller.extend({
router: service(),
documentService: service('document'), documentService: service('document'),
actions: { actions: {

View file

@ -1,14 +1,16 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1">
<div class="section"> {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{#link-to "document.index" model.folder.id model.folder.slug model.document.id model.document.slug}} {{ui/ui-toolbar-button themed=true uppercase=false
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.ArrowLeft label="Document"}} icon=constants.Icon.ArrowLeft label=model.document.name onClick=(action "onCancel")}}
{{/link-to}} {{/ui/ui-toolbar}}
</div> </div>
{{/layout/master-sidebar}} <div class="zone-2" />
<div class="zone-3" />
</Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Container>
{{document/document-editor {{document/document-editor
document=model.document document=model.document
folder=model.folder folder=model.folder
@ -22,4 +24,4 @@
files=model.attachments files=model.attachments
onAttachmentUpload=(action "onAttachmentUpload") onAttachmentUpload=(action "onAttachmentUpload")
onAttachmentDelete=(action "onAttachmentDelete")}} onAttachmentDelete=(action "onAttachmentDelete")}}
{{/layout/master-content}} </Layout::Container>

View file

@ -23,6 +23,10 @@ export default Controller.extend(Notifier, {
tab: 'general', tab: 'general',
actions: { actions: {
onBack() {
this.get('router').transitionTo('document.index');
},
onTab(view) { onTab(view) {
this.set('tab', view); this.set('tab', view);
}, },

View file

@ -1,87 +1,96 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="section"> <div class="zone-1">
{{#link-to "document.index"}} {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.ArrowLeft label="Document"}} {{ui/ui-toolbar-button themed=true uppercase=false
{{/link-to}} icon=constants.Icon.ArrowLeft label=model.document.name onClick=(action "onBack")}}
{{/ui/ui-toolbar}}
{{ui/ui-spacer size=400}}
<div class="title">Document Options</div>
<div class="list">
<div class="item {{if (eq tab "general") "selected"}}" {{action "onTab" "general"}}>
<i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">Content Settings</div>
</div>
<div class="item {{if (eq tab "category") "selected"}}" {{action "onTab" "category"}}>
<i class={{concat "dicon " constants.Icon.Category}} />
<div class="name">Categories</div>
</div>
<div class="item {{if (eq tab "tag") "selected"}}" {{action "onTab" "tag"}}>
<i class={{concat "dicon " constants.Icon.Tag}} />
<div class="name">Tags</div>
</div>
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#if model.permissions.documentApprove}}
<div class="item {{if (eq tab "protection") "selected"}}" {{action "onTab" "protection"}}>
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Change Control</div>
</div>
{{/if}}
{{#if model.permissions.documentVersion}}
<div class="item {{if (eq tab "versions") "selected"}}" {{action "onTab" "versions"}}>
<i class={{concat "dicon " constants.Icon.Copy}} />
<div class="name">Versions</div>
</div>
{{/if}}
{{/if}}
</div>
</div> </div>
{{/layout/master-sidebar}} <div class="zone-2" />
<div class="zone-3" />
</Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
{{#if (eq tab "general")}} <Layout::Grid::Sidebar>
{{document/settings-general <div class="sidebar-content">
space=model.folder <div class="section">
document=model.document <div class="title">Options</div>
permissions=model.permissions <div class="list">
onSaveDocument=(action "onSaveDocument")}} <div class="item {{if (eq tab "general") "selected"}}" {{action "onTab" "general"}}>
{{/if}} <i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">Content Settings</div>
</div>
<div class="item {{if (eq tab "category") "selected"}}" {{action "onTab" "category"}}>
<i class={{concat "dicon " constants.Icon.Category}} />
<div class="name">Categories</div>
</div>
<div class="item {{if (eq tab "tag") "selected"}}" {{action "onTab" "tag"}}>
<i class={{concat "dicon " constants.Icon.Tag}} />
<div class="name">Tags</div>
</div>
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#if model.permissions.documentApprove}}
<div class="item {{if (eq tab "protection") "selected"}}" {{action "onTab" "protection"}}>
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Change Control</div>
</div>
{{/if}}
{{#if model.permissions.documentVersion}}
<div class="item {{if (eq tab "versions") "selected"}}" {{action "onTab" "versions"}}>
<i class={{concat "dicon " constants.Icon.Copy}} />
<div class="name">Versions</div>
</div>
{{/if}}
{{/if}}
</div>
</div>
</div>
</Layout::Grid::Sidebar>
{{#if (eq tab "category")}} <Layout::Grid::Content>
{{document/settings-category {{#if (eq tab "general")}}
space=model.folder {{document/settings-general
document=model.document space=model.folder
permissions=model.permissions document=model.document
onSaveDocument=(action "onSaveDocument")}} permissions=model.permissions
{{/if}} onSaveDocument=(action "onSaveDocument")}}
{{/if}}
{{#if (eq tab "tag")}} {{#if (eq tab "category")}}
{{document/settings-tag {{document/settings-category
space=model.folder space=model.folder
document=model.document document=model.document
permissions=model.permissions permissions=model.permissions
onSaveDocument=(action "onSaveDocument")}} onSaveDocument=(action "onSaveDocument")}}
{{/if}} {{/if}}
{{#if (eq tab "protection")}} {{#if (eq tab "tag")}}
{{document/settings-protection {{document/settings-tag
space=model.folder space=model.folder
spaces=model.folders document=model.document
document=model.document permissions=model.permissions
permissions=model.permissions onSaveDocument=(action "onSaveDocument")}}
onRefresh=(action "onRefresh") {{/if}}
onSaveDocument=(action "onSaveDocument")}}
{{/if}}
{{#if (eq tab "versions")}} {{#if (eq tab "protection")}}
{{enterprise/settings-version {{document/settings-protection
space=model.folder space=model.folder
spaces=model.folders spaces=model.folders
document=model.document document=model.document
permissions=model.permissions permissions=model.permissions
versions=model.versions onRefresh=(action "onRefresh")
onRefresh=(action "onRefresh") onSaveDocument=(action "onSaveDocument")}}
onSaveDocument=(action "onSaveDocument")}} {{/if}}
{{/if}}
{{/layout/master-content}} {{#if (eq tab "versions")}}
{{enterprise/settings-version
space=model.folder
spaces=model.folders
document=model.document
permissions=model.permissions
versions=model.versions
onRefresh=(action "onRefresh")
onSaveDocument=(action "onSaveDocument")}}
{{/if}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -18,6 +18,10 @@ export default Controller.extend(Notifier, {
sectionSvc: service('section'), sectionSvc: service('section'),
actions: { actions: {
onBack() {
this.get('router').transitionTo('folder.settings');
},
onCancel( /*page*/ ) { onCancel( /*page*/ ) {
this.get('router').transitionTo('folder.settings', {queryParams: {tab: 'blocks'}}); this.get('router').transitionTo('folder.settings', {queryParams: {tab: 'blocks'}});
}, },

View file

@ -1,18 +1,30 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="section"> <div class="zone-1">
{{#link-to "folder.settings"}} {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.ArrowLeft label="Space Settings"}} {{ui/ui-toolbar-button themed=true uppercase=false
{{/link-to}} icon=constants.Icon.ArrowLeft label=model.space.name onClick=(action "onBack")}}
{{/ui/ui-toolbar}}
</div> </div>
{{/layout/master-sidebar}} </Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
{{layout/logo-heading <Layout::Grid::Sidebar>
title="Content Blocks" <div class="sidebar-content">
desc="Content blocks provide re-usable content that can be inserted into any document" <div class="section">
icon=constants.Icon.Integrations}} <div class="title">about</div>
<div class="text">Manage reusable snippets of content for this space</div>
</div>
</div>
</Layout::Grid::Sidebar>
{{document/block-editor document=model.document folder=model.folder block=model.block <Layout::Grid::Content>
onCancel=(action "onCancel") onAction=(action "onAction")}} {{layout/logo-heading
{{/layout/master-content}} title="Content Blocks"
desc="Content blocks provide re-usable content that can be inserted into any document"
icon=constants.Icon.Integrations}}
{{document/block-editor document=model.document folder=model.space block=model.block
onCancel=(action "onCancel") onAction=(action "onAction")}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -15,6 +15,7 @@ import NotifierMixin from '../../../mixins/notifier';
import Controller from '@ember/controller'; import Controller from '@ember/controller';
export default Controller.extend(NotifierMixin, { export default Controller.extend(NotifierMixin, {
router: service(),
documentService: service('document'), documentService: service('document'),
folderService: service('folder'), folderService: service('folder'),
localStorage: service('localStorage'), localStorage: service('localStorage'),
@ -33,6 +34,10 @@ export default Controller.extend(NotifierMixin, {
}, },
actions: { actions: {
onBack() {
this.get('router').transitionTo('folders');
},
onRefresh() { onRefresh() {
this.get('target._routerMicrolib').refresh(); this.get('target._routerMicrolib').refresh();
}, },

View file

@ -44,11 +44,14 @@ export default Route.extend(AuthenticatedRouteMixin, {
d.set('selected', false); d.set('selected', false);
}); });
let labelId = this.modelFor('folder').folder.get('labelId');
return hash({ return hash({
folder: this.modelFor('folder').folder, folder: this.modelFor('folder').folder,
permissions: this.modelFor('folder').permissions, permissions: this.modelFor('folder').permissions,
label: _.find(this.modelFor('folder').labels, {id: this.modelFor('folder').folder.get('labelId')}), label: _.find(this.modelFor('folder').labels, {id: labelId}),
labels: this.modelFor('folder').labels, labels: this.modelFor('folder').labels,
labelSpaces: _.filter(folders, function(s) { return s.get('labelId') === labelId; }),
folders: folders, folders: folders,
documents: documents, documents: documents,
documentsDraft: _.filter(documents, function(d) { return d.get('lifecycle') === constants.Lifecycle.Draft; }), documentsDraft: _.filter(documents, function(d) { return d.get('lifecycle') === constants.Lifecycle.Draft; }),

View file

@ -1,4 +1,26 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
<Layout::MasterToolbar>
<div class="zone-1">
{{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{ui/ui-toolbar-button themed=true uppercase=true
icon=constants.Icon.ArrowLeft label=constants.Label.Spaces onClick=(action "onBack")}}
{{/ui/ui-toolbar}}
</div>
<div class="zone-2" />
<div class="zone-3">
{{folder/space-toolbar
spaces=model.folders
space=model.folder
permissions=model.permissions
templates=model.templates
category=category
categories=model.categories
documents=filteredDocs
onRefresh=(action "onRefresh")}}
</div>
</Layout::MasterToolbar>
<Layout::Grid::Container>
{{folder/space-sidebar {{folder/space-sidebar
spaces=model.folders spaces=model.folders
space=model.folder space=model.folder
@ -17,43 +39,42 @@
categoryFilter=category categoryFilter=category
onFiltered=(action "onFiltered") onFiltered=(action "onFiltered")
onRefresh=(action "onRefresh")}} onRefresh=(action "onRefresh")}}
{{/layout/master-sidebar}}
{{#layout/master-content}} <Layout::Grid::Content>
<div class="grid-container-6-4"> {{#if (eq model.folder.labelId "")}}
<div class="grid-cell-1"> <div class="space-label">Unclassified</div>
{{#if (eq model.folder.labelId "")}} {{else}}
<div class="space-label">Unclassified</div> <div class="space-label" style={{{model.label.bgColor}}}>{{model.label.name}}</div>
{{else}} {{#if (gt model.labelSpaces.length 1)}}
<div class="space-label" style={{{model.label.bgColor}}}>{{model.label.name}}</div> <i class="space-label-dropdown dicon {{constants.Icon.ArrowSmallDown}}">
{{#attach-popover class="ember-attacher-popper" hideOn="click clickout" showOn="click" isShown=false placement="bottom-middle"}}
<ul class="menu">
{{#each model.labelSpaces as |space|}}
{{#if (not-eq space.id model.folder.id)}}
{{#link-to "folder.index" space.id space.slug class="item"}}{{space.name}}{{/link-to}}
{{/if}}
{{/each}}
</ul>
{{/attach-popover}}
</i>
{{/if}} {{/if}}
{{layout/logo-heading {{/if}}
title=model.folder.name
desc=model.folder.desc
meta=model.folder.icon}}
</div>
<div class="grid-cell-2 grid-cell-right">
{{folder/space-toolbar
spaces=model.folders
space=model.folder
permissions=model.permissions
templates=model.templates
category=category
categories=model.categories
documents=filteredDocs
onRefresh=(action "onRefresh")}}
</div>
</div>
{{folder/documents-list {{layout/logo-heading
documents=filteredDocs title=model.folder.name
spaces=model.folders desc=model.folder.desc
space=model.folder meta=model.folder.icon}}
templates=model.templates
permissions=model.permissions {{folder/documents-list
sortBy=sortBy documents=filteredDocs
onFiltered=(action "onFiltered") spaces=model.folders
onExportDocument=(action "onExportDocument") space=model.folder
onDeleteDocument=(action "onDeleteDocument") templates=model.templates
onMoveDocument=(action "onMoveDocument")}} permissions=model.permissions
{{/layout/master-content}} sortBy=sortBy
onFiltered=(action "onFiltered")
onExportDocument=(action "onExportDocument")
onDeleteDocument=(action "onDeleteDocument")
onMoveDocument=(action "onMoveDocument")}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -22,6 +22,10 @@ export default Controller.extend(NotifierMixin, {
tab: 'general', tab: 'general',
actions: { actions: {
onBack() {
this.get('router').transitionTo('folder.index');
},
onTab(view) { onTab(view) {
this.set('tab', view); this.set('tab', view);
}, },

View file

@ -1,75 +1,81 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1">
<div class="section"> {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
{{#link-to "folder.index"}} {{ui/ui-toolbar-button themed=true uppercase=false
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.ArrowLeft label="Space"}} icon=constants.Icon.ArrowLeft label=model.folder.name onClick=(action "onBack")}}
{{/link-to}} {{/ui/ui-toolbar}}
{{ui/ui-spacer size=400}}
<div class="title">space management</div>
<div class="list">
<div class="item {{if (eq tab "general") "selected"}}" {{action "onTab" "general"}}>
<i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">Meta</div>
</div>
<div class="item {{if (eq tab "categories") "selected"}}" {{action "onTab" "categories"}}>
<i class={{concat "dicon " constants.Icon.Category}} />
<div class="name">Categories</div>
</div>
<div class="item {{if (eq tab "permissions") "selected"}}" {{action "onTab" "permissions"}}>
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Permissions</div>
</div>
<div class="item {{if (eq tab "blocks") "selected"}}" {{action "onTab" "blocks"}}>
<i class={{concat "dicon " constants.Icon.Blocks}} />
<div class="name">Content Blocks</div>
</div>
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
<div class="item {{if (eq tab "archived") "selected"}}" {{action "onTab" "archived"}}>
<i class={{concat "dicon " constants.Icon.Archive}} />
<div class="name">Archived Content</div>
</div>
{{/if}}
{{#if model.permissions.spaceOwner}}
<div class="item {{if (eq tab "deletion") "selected"}}" {{action "onTab" "deletion"}}>
<i class={{concat "dicon " constants.Icon.Delete}} />
<div class="name">Deletion</div>
</div>
{{/if}}
</div>
</div> </div>
{{/layout/master-sidebar}} </Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
{{#if (eq tab "general")}} <Layout::Grid::Sidebar>
{{folder/settings-general permissions=model.permissions space=model.folder labels=model.labels}} <div class="sidebar-content">
{{/if}} <div class="section">
<div class="title">space management</div>
<div class="list">
<div class="item {{if (eq tab "general") "selected"}}" {{action "onTab" "general"}}>
<i class={{concat "dicon " constants.Icon.Settings}} />
<div class="name">Meta</div>
</div>
<div class="item {{if (eq tab "categories") "selected"}}" {{action "onTab" "categories"}}>
<i class={{concat "dicon " constants.Icon.Category}} />
<div class="name">Categories</div>
</div>
<div class="item {{if (eq tab "permissions") "selected"}}" {{action "onTab" "permissions"}}>
<i class={{concat "dicon " constants.Icon.Locked}} />
<div class="name">Permissions</div>
</div>
<div class="item {{if (eq tab "blocks") "selected"}}" {{action "onTab" "blocks"}}>
<i class={{concat "dicon " constants.Icon.Blocks}} />
<div class="name">Content Blocks</div>
</div>
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
<div class="item {{if (eq tab "archived") "selected"}}" {{action "onTab" "archived"}}>
<i class={{concat "dicon " constants.Icon.Archive}} />
<div class="name">Archived Content</div>
</div>
{{/if}}
{{#if model.permissions.spaceOwner}}
<div class="item {{if (eq tab "deletion") "selected"}}" {{action "onTab" "deletion"}}>
<i class={{concat "dicon " constants.Icon.Delete}} />
<div class="name">Deletion</div>
</div>
{{/if}}
</div>
</div>
</div>
</Layout::Grid::Sidebar>
{{#if (eq tab "permissions")}} <Layout::Grid::Content>
{{folder/settings-permissions permissions=model.permissions folders=model.folders folder=model.folder onRefresh=(action "onRefresh")}} {{#if (eq tab "general")}}
{{/if}} {{folder/settings-general permissions=model.permissions space=model.folder labels=model.labels}}
{{#if (eq tab "templates")}}
{{folder/settings-templates permissions=model.permissions space=model.folder templates=model.templates}}
{{/if}}
{{#if (eq tab "blocks")}}
{{folder/settings-blocks permissions=model.permissions space=model.folder}}
{{/if}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#if (eq tab "archived")}}
{{enterprise/space-archived permissions=model.permissions spaces=model.folder space=model.folder}}
{{/if}} {{/if}}
{{/if}}
{{#if (eq tab "categories")}} {{#if (eq tab "permissions")}}
{{folder/settings-category permissions=model.permissions spaces=model.folder space=model.folder}} {{folder/settings-permissions permissions=model.permissions folders=model.folders folder=model.folder onRefresh=(action "onRefresh")}}
{{/if}} {{/if}}
{{#if (eq tab "deletion")}} {{#if (eq tab "templates")}}
{{folder/settings-delete permissions=model.permissions spaces=model.folder space=model.folder}} {{folder/settings-templates permissions=model.permissions space=model.folder templates=model.templates}}
{{/if}} {{/if}}
{{/layout/master-content}}
{{#if (eq tab "blocks")}}
{{folder/settings-blocks permissions=model.permissions space=model.folder}}
{{/if}}
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#if (eq tab "archived")}}
{{enterprise/space-archived permissions=model.permissions spaces=model.folder space=model.folder}}
{{/if}}
{{/if}}
{{#if (eq tab "categories")}}
{{folder/settings-category permissions=model.permissions spaces=model.folder space=model.folder}}
{{/if}}
{{#if (eq tab "deletion")}}
{{folder/settings-delete permissions=model.permissions spaces=model.folder space=model.folder}}
{{/if}}
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -1,138 +1,142 @@
{{#layout/master-sidebar selectedItem="spaces"}} <Layout::MasterNavigation @selectedItem="spaces" />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1" />
<div class="section"> <div class="zone-2" />
<div class="title">filter</div> <div class="zone-3">
<div class="list"> {{#if (or session.isEditor session.isAdmin)}}
<div class="item {{if (eq selectedView "all") "selected"}}" {{action "onSelect" "all"}}> {{#ui/ui-toolbar dark=false light=false raised=false large=false bordered=false}}
<i class={{concat "dicon " constants.Icon.All}} /> {{#if session.isEditor}}
<div class="name">All ({{model.spaces.length}})</div> {{ui/ui-toolbar-button icon=constants.Icon.Plus color=constants.Color.Green
</div> label=constants.Label.Space onClick=(action "onShowModal")}}
<div class="item {{if (eq selectedView "public") "selected"}}" {{action "onSelect" "public"}}> {{ui/ui-toolbar-divider}}
<i class={{concat "dicon " constants.Icon.World}} /> {{/if}}
<div class="name">Public ({{publicSpaces.length}})</div> {{#if session.isAdmin}}
</div> {{ui/ui-toolbar-icon icon=constants.Icon.Settings color=constants.Color.Green
{{#if session.authenticated}} tooltip="Space settings" linkTo="customize"}}
<div class="item {{if (eq selectedView "protected") "selected"}}" {{action "onSelect" "protected"}}> {{/if}}
<i class={{concat "dicon " constants.Icon.People}} /> {{/ui/ui-toolbar}}
<div class="name">Protected ({{protectedSpaces.length}})</div>
</div>
<div class="item {{if (eq selectedView "personal") "selected"}}" {{action "onSelect" "personal"}}>
<i class={{concat "dicon " constants.Icon.Person}} />
<div class="name">Personal ({{personalSpaces.length}})</div>
</div>
{{/if}}
</div>
</div>
{{ui/ui-spacer size=300}}
<div class="section">
<div class="title">label</div>
{{#if labels}}
<div class="list">
{{#each labels as |label|}}
{{#if (gt label.count 0)}}
<div class="item {{if (eq selectedView label.id) "selected"}}" {{action "onSelect" label.id}}>
<i class={{concat "dicon label-color " constants.Icon.Checkbox}} style={{label.bgfgColor}}/>
<div class="name">{{label.name}} ({{label.count}})</div>
</div>
{{/if}}
{{/each}}
</div>
{{else}}
<div class="empty">No labels</div>
{{/if}} {{/if}}
</div> </div>
{{/layout/master-sidebar}} </Layout::MasterToolbar>
{{#layout/master-content}} <Layout::Grid::Container>
<div class="grid-container-8-2"> <Layout::Grid::Sidebar>
<div class="grid-cell-1"> <div class="sidebar-content">
{{layout/logo-heading <div class="section">
title=appMeta.title <div class="title">filter</div>
desc=appMeta.message <div class="list">
logo=true}} <div class="item {{if (eq selectedView "all") "selected"}}" {{action "onSelect" "all"}}>
</div> <i class={{concat "dicon " constants.Icon.All}} />
<div class="grid-cell-2 grid-cell-right"> <div class="name">All ({{model.spaces.length}})</div>
{{#if (or session.isEditor session.isAdmin)}} </div>
{{#ui/ui-toolbar dark=false light=true raised=true large=true bordered=true tooltip="New space"}} <div class="item {{if (eq selectedView "public") "selected"}}" {{action "onSelect" "public"}}>
{{#if session.isEditor}} <i class={{concat "dicon " constants.Icon.World}} />
{{ui/ui-toolbar-icon icon=constants.Icon.Plus color=constants.Color.Green onClick=(action "onShowModal")}} <div class="name">Public ({{publicSpaces.length}})</div>
{{ui/ui-toolbar-label label="SPACE" color=constants.Color.Green onClick=(action "onShowModal")}} </div>
{{#if session.authenticated}}
<div class="item {{if (eq selectedView "protected") "selected"}}" {{action "onSelect" "protected"}}>
<i class={{concat "dicon " constants.Icon.People}} />
<div class="name">Protected ({{protectedSpaces.length}})</div>
</div>
<div class="item {{if (eq selectedView "personal") "selected"}}" {{action "onSelect" "personal"}}>
<i class={{concat "dicon " constants.Icon.Person}} />
<div class="name">Personal ({{personalSpaces.length}})</div>
</div>
{{/if}} {{/if}}
{{/ui/ui-toolbar}} </div>
{{/if}} </div>
<Ui::UiSpacer @size="300" />
<div class="section">
<div class="title">label</div>
{{#if labels}}
<div class="list">
{{#each labels as |label|}}
{{#if (gt label.count 0)}}
<div class="item {{if (eq selectedView label.id) "selected"}}" {{action "onSelect" label.id}}>
<i class={{concat "dicon label-color " constants.Icon.Checkbox}} style={{label.bgfgColor}}/>
<div class="name">{{label.name}} ({{label.count}})</div>
</div>
{{/if}}
{{/each}}
</div>
{{else}}
<div class="empty">No labels</div>
{{/if}}
</div>
</div> </div>
</div> </Layout::Grid::Sidebar>
{{ui/ui-spacer size=400}} <Layout::Grid::Content>
{{layout/logo-heading title=appMeta.title desc=appMeta.message logo=true}}
{{spaces/space-list spaces=selectedSpaces labels=labels}} {{spaces/space-list spaces=selectedSpaces labels=labels}}
<div class="modal" tabindex="-1" role="dialog" id="add-space-modal"> <div class="modal" tabindex="-1" role="dialog" id="add-space-modal">
<div class="modal-dialog modal-lg" role="document"> <div class="modal-dialog modal-lg" role="document">
<div class="modal-content"> <div class="modal-content">
<div class="modal-header">New Space</div> <div class="modal-header">New Space</div>
<div class="modal-body"> <div class="modal-body">
<form onsubmit={{action "onAddSpace"}}> <form onsubmit={{action "onAddSpace"}}>
<div class="form-group"> <div class="form-group">
<label for="new-space-name">Name</label> <label for="new-space-name">Name</label>
{{input type="text" id="new-space-name" class="form-control mousetrap" placeholder="Space name" value=spaceName}} {{input type="text" id="new-space-name" class="form-control mousetrap" placeholder="Space name" value=spaceName}}
<small class="form-text text-muted">Characters and numbers only</small> <small class="form-text text-muted">Characters and numbers only</small>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Description</label> <label>Description</label>
{{focus-input id="space-desc" type="text" value=spaceDesc class="form-control" placeholder="Space description" autocomplete="off"}} {{focus-input id="space-desc" type="text" value=spaceDesc class="form-control" placeholder="Space description" autocomplete="off"}}
</div> </div>
<div class="form-group"> <div class="form-group">
<label>Icon</label> <label>Icon</label>
<div class="ui-icon-picker"> <div class="ui-icon-picker">
<ul class="list"> <ul class="list">
{{#each iconList as |icon|}} {{#each iconList as |icon|}}
<li class="item {{if (eq spaceIcon icon) "selected"}}" {{action "onSetIcon" icon}}> <li class="item {{if (eq spaceIcon icon) "selected"}}" {{action "onSetIcon" icon}}>
{{ui/ui-icon-meta icon=icon}} {{ui/ui-icon-meta icon=icon}}
</li>
{{/each}}
</ul>
</div>
</div>
<div class="form-group">
<label>Label</label>
<ul class="space-label-picker">
<li class="label none {{if (eq spaceLabel "") "selected"}}" {{action "onSetLabel" ""}}>None</li>
{{#each labels as |label|}}
<li class="label {{if (eq spaceLabel label.id) "selected"}}"
style={{label.bgColor}}
{{action "onSetLabel" label.id}} title={{label.name}}>
{{label.name}}
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>
</div>
<div class="form-group"> <div class="form-group">
<label>Label</label> <label for="clone-space-dropdown">Clone Space</label>
<ul class="space-label-picker"> {{ui/ui-select id="clone-space-dropdown" content=spaces prompt="select space" action=(action "onCloneSpaceSelect") optionValuePath="id" optionLabelPath="name" selection=clonedSpace}}
<li class="label none {{if (eq spaceLabel "") "selected"}}" {{action "onSetLabel" ""}}>None</li> <small id="emailHelp" class="form-text text-muted">Copy templates, permissions, documents from existing space</small>
{{#each labels as |label|}} <div class="margin-top-10" />
<li class="label {{if (eq spaceLabel label.id) "selected"}}" {{#if hasClone}}
style={{label.bgColor}} {{#ui/ui-checkbox selected=copyTemplate}}Copy templates{{/ui/ui-checkbox}}
{{action "onSetLabel" label.id}} title={{label.name}}> {{#ui/ui-checkbox selected=copyPermission}}Copy permissions{{/ui/ui-checkbox}}
{{label.name}} {{#ui/ui-checkbox selected=copyDocument}}Copy documents{{/ui/ui-checkbox}}
</li> {{/if}}
{{/each}} </div>
</ul> </form>
</div> </div>
<div class="modal-footer">
<div class="form-group"> {{ui/ui-button color=constants.Color.Gray light=true label=constants.Label.Cancel dismiss=true}}
<label for="clone-space-dropdown">Clone Space</label> {{ui/ui-button-gap}}
{{ui/ui-select id="clone-space-dropdown" content=spaces prompt="select space" action=(action "onCloneSpaceSelect") optionValuePath="id" optionLabelPath="name" selection=clonedSpace}} {{ui/ui-button color=constants.Color.Green light=true label=constants.Label.Add onClick=(action "onAddSpace")}}
<small id="emailHelp" class="form-text text-muted">Copy templates, permissions, documents from existing space</small> </div>
<div class="margin-top-10" />
{{#if hasClone}}
{{#ui/ui-checkbox selected=copyTemplate}}Copy templates{{/ui/ui-checkbox}}
{{#ui/ui-checkbox selected=copyPermission}}Copy permissions{{/ui/ui-checkbox}}
{{#ui/ui-checkbox selected=copyDocument}}Copy documents{{/ui/ui-checkbox}}
{{/if}}
</div>
</form>
</div>
<div class="modal-footer">
{{ui/ui-button color=constants.Color.Gray light=true label=constants.Label.Cancel dismiss=true}}
{{ui/ui-button-gap}}
{{ui/ui-button color=constants.Color.Green light=true label=constants.Label.Add onClick=(action "onAddSpace")}}
</div> </div>
</div> </div>
</div> </div>
</div> </Layout::Grid::Content>
{{/layout/master-content}} </Layout::Grid::Container>

View file

@ -1,11 +1,26 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{/layout/master-sidebar}} <Layout::MasterToolbar />
{{#layout/master-content}} <Layout::Grid::Container>
{{layout/logo-heading <Layout::Grid::Sidebar>
title=session.user.fullname <div class="sidebar-content">
desc="Manage you profile and password" <div class="section">
icon=constants.Icon.Person}} <div class="title">PROFILE</div>
<div class="text">
Set your personal information or reset your password.
</div>
<div class="text">
Have a product idea, suggestion or some feedback? <a href="mailto:support@documize.com">Get in touch.</a>
</div>
</div>
</div>
</Layout::Grid::Sidebar>
<Layout::Grid::Content>
{{layout/logo-heading
title=session.user.fullname
desc="Manage your profile and password"
icon=constants.Icon.Person}}
{{user/user-profile model=model save=(action "save")}} {{user/user-profile model=model save=(action "save")}}
{{/layout/master-content}} </Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -1,75 +1,79 @@
{{#layout/master-sidebar selectedItem="search"}} <Layout::MasterNavigation @selectedItem="search" />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar />
<div class="section"> <Layout::Grid::Container>
<div class="title">Match Filter</div> <Layout::Grid::Sidebar>
<div class="list"> <div class="sidebar-content">
<div class="item"> <div class="section">
{{input id="search-1" type="checkbox" checked=matchFilter.matchDoc}} <div class="title">Match Filter</div>
<label for="search-1" class="name">Document name</label> <div class="list">
<div class="item">
{{input id="search-1" type="checkbox" checked=matchFilter.matchDoc}}
<label for="search-1" class="name">Document name</label>
</div>
<div class="item">
{{input id="search-2" type="checkbox" checked=matchFilter.matchContent}}
<label for="search-2" class="name">Document content</label>
</div>
<div class="item">
{{input id="search-3" type="checkbox" checked=matchFilter.matchTag}}
<label for="search-3" class="name">Tag name</label>
</div>
<div class="item">
{{input id="search-4" type="checkbox" checked=matchFilter.matchFile}}
<label for="search-4" class="name">Attachment name</label>
</div>
</div>
</div> </div>
<div class="item"> <Ui::UiSpacer @size="200" />
{{input id="search-2" type="checkbox" checked=matchFilter.matchContent}} <div class="section">
<label for="search-2" class="name">Document content</label> <div class="title">query examples</div>
</div> <div class="view-search">
<div class="item"> {{#if (eq appMeta.storageProvider constants.StoreProvider.MySQL)}}
{{input id="search-3" type="checkbox" checked=matchFilter.matchTag}} <div class="syntax">
<label for="search-3" class="name">Tag name</label> <div class="example">apple banana</div>
</div> <div class="explain">Show results that contain at least one of the two words</div>
<div class="item"> <div class="example">+apple +banana</div>
{{input id="search-4" type="checkbox" checked=matchFilter.matchFile}} <div class="explain">Show results that contain both words</div>
<label for="search-4" class="name">Attachment name</label> <div class="example">+apple macintosh</div>
<div class="explain">Show results that contain the word "apple", but rank rows higher if they also contain "macintosh"</div>
<div class="example">+apple -macintosh</div>
<div class="explain">Show results that contain the word "apple" but not "macintosh"</div>
<div class="example">+apple +(&gt;turnover &lt;strudel)</div>
<div class="explain">Show results that contain the words "apple" and "turnover", or "apple" and "strudel" (in any order), but rank "apple turnover" higher than "apple strudel"</div>
<div class="example">apple*</div>
<div class="explain">Show results that contain words such as "apple", "apples", "applesauce", or "applet"</div>
<div class="example">"some words"</div>
<div class="explain">Show results that contain the exact phrase "some words" (for example, rows that contain "some words of wisdom" but not "some noise words")</div>
</div>
{{/if}}
{{#if (eq appMeta.storageProvider constants.StoreProvider.PostgreSQL)}}
<div class="syntax">
<div class="example">apple | banana</div>
<div class="explain">Show results that contain at either word</div>
<div class="example">apple & banana</div>
<div class="explain">Show results that contain both words</div>
<div class="example">apple !macintosh</div>
<div class="explain">Show results that contain the word "apple" but not "macintosh"</div>
<div class="example">google & (apple | microsoft) & !ibm</div>
<div class="explain">Show results that have "google", either "apple" or "microsoft" but not "ibm"</div>
</div>
{{/if}}
{{#if (eq appMeta.storageProvider constants.StoreProvider.SQLServer)}}
{{/if}}
</div>
</div> </div>
</div> </div>
</div> </Layout::Grid::Sidebar>
{{ui/ui-spacer size=200}}
<div class="section"> <Layout::Grid::Content>
<div class="title">query examples</div> {{layout/logo-heading
<div class="view-search"> title="Search"
{{#if (eq appMeta.storageProvider constants.StoreProvider.MySQL)}} desc="Find content"
<div class="syntax"> icon=constants.Icon.Search}}
<div class="example">apple banana</div>
<div class="explain">Show results that contain at least one of the two words</div>
<div class="example">+apple +banana</div>
<div class="explain">Show results that contain both words</div>
<div class="example">+apple macintosh</div>
<div class="explain">Show results that contain the word "apple", but rank rows higher if they also contain "macintosh"</div>
<div class="example">+apple -macintosh</div>
<div class="explain">Show results that contain the word "apple" but not "macintosh"</div>
<div class="example">+apple +(&gt;turnover &lt;strudel)</div>
<div class="explain">Show results that contain the words "apple" and "turnover", or "apple" and "strudel" (in any order), but rank "apple turnover" higher than "apple strudel"</div>
<div class="example">apple*</div>
<div class="explain">Show results that contain words such as "apple", "apples", "applesauce", or "applet"</div>
<div class="example">"some words"</div>
<div class="explain">Show results that contain the exact phrase "some words" (for example, rows that contain "some words of wisdom" but not "some noise words")</div>
</div>
{{/if}}
{{#if (eq appMeta.storageProvider constants.StoreProvider.PostgreSQL)}}
<div class="syntax">
<div class="example">apple | banana</div>
<div class="explain">Show results that contain at either word</div>
<div class="example">apple & banana</div>
<div class="explain">Show results that contain both words</div>
<div class="example">apple !macintosh</div>
<div class="explain">Show results that contain the word "apple" but not "macintosh"</div>
<div class="example">google & (apple | microsoft) & !ibm</div>
<div class="explain">Show results that have "google", either "apple" or "microsoft" but not "ibm"</div>
</div>
{{/if}}
{{#if (eq appMeta.storageProvider constants.StoreProvider.SQLServer)}}
{{/if}}
</div>
</div>
{{/layout/master-sidebar}}
{{#layout/master-content}} {{search/search-view
{{layout/logo-heading filter=filter
title="Search" matchFilter=matchFilter}}
desc="Find content" </Layout::Grid::Content>
icon=constants.Icon.Search}} </Layout::Grid::Container>
{{search/search-view
filter=filter
matchFilter=matchFilter}}
{{/layout/master-content}}

View file

@ -12,4 +12,7 @@
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
export default Route.extend({ export default Route.extend({
activate() {
this.get('browser').setTitle('Product News');
}
}); });

View file

@ -1,26 +1,40 @@
{{#layout/master-sidebar}} <Layout::MasterNavigation />
{{ui/ui-spacer size=300}} <Layout::MasterToolbar>
<div class="zone-1" />
<div class="section"> <div class="zone-2">
<div class="title">SUMMARY</div> <div class="label color-gray-700">
{{ui/ui-spacer size=100}} Documize {{appMeta.edition}} Edition {{appMeta.version}} (build {{appMeta.revision}})
<p>Documize {{appMeta.edition}} Edition</p>
<p>Version {{appMeta.version}}</p>
<p>Build {{appMeta.revision}}</p>
</div>
{{/layout/master-sidebar}}
{{#layout/master-content}}
{{layout/logo-heading
title="Product News"
desc="Latest product news and updates from Documize Inc."
icon=constants.Icon.Announce}}
<div class="product-news">
{{{newsContent}}}
<div class="action">
Have an idea? Suggestion or feedback? <a href="mailto:support@documize.com">Get in touch!</a>
</div> </div>
</div> </div>
{{/layout/master-content}} <div class="zone-3" />
</Layout::MasterToolbar>
<Layout::Grid::Container>
<Layout::Grid::Sidebar>
<div class="sidebar-content">
<div class="section">
<div class="title">ABOUT</div>
<div class="text">
Documize product updates are released frequently for both
cloud and self-hosted customers.
</div>
<div class="text">
Have an idea, suggestion or some feedback? <a href="mailto:support@documize.com">Get in touch.</a>
</div>
</div>
</div>
</Layout::Grid::Sidebar>
<Layout::Grid::Content>
{{layout/logo-heading
title="Product News"
desc="Latest product news and updates"
icon=constants.Icon.Announce}}
<div class="product-news">
{{{newsContent}}}
<div class="action">
Have an idea? Suggestion or feedback? <a href="mailto:support@documize.com">Get in touch!</a>
</div>
</div>
</Layout::Grid::Content>
</Layout::Grid::Container>

View file

@ -78,6 +78,21 @@ export default Service.extend({
}); });
}, },
// Duplicate creates a copy.
duplicate(spaceId, docId, docName) {
let data = {
spaceId: spaceId,
documentId: docId,
documentName: docName
};
return this.get('ajax').request(`document/duplicate`, {
method: 'POST',
data: JSON.stringify(data)
});
},
//************************************************** //**************************************************
// Page // Page
//************************************************** //**************************************************
@ -168,6 +183,22 @@ export default Service.extend({
}); });
}, },
// Given a page ID, return all children of the starting page.
getChildren(pages, pageId) {
let children = [];
let pageIndex = _.findIndex(pages, function(i) { return i.get('page.id') === pageId; });
let item = pages[pageIndex];
for (var i = pageIndex + 1; i < pages.get('length'); i++) {
if (i === pageIndex + 1 && pages[i].get('page.level') === item.get('page.level')) break;
if (pages[i].get('page.level') <= item.get('page.level')) break;
children.push({ pageId: pages[i].get('page.id'), level: pages[i].get('page.level') - 1 });
}
return children;
},
//************************************************** //**************************************************
// Page Revisions // Page Revisions
//************************************************** //**************************************************

View file

@ -26,5 +26,24 @@ export default Service.extend({
clearAll() { clearAll() {
localStorage.clear(); localStorage.clear();
} },
getDocSectionHide(docId) {
let state = localStorage[`doc-hide-${docId}`];
if (_.isUndefined(state) || _.isEmpty(state)) {
return [];
}
return _.split(state, '|');
},
setDocSectionHide(docId, state) {
let key = `doc-hide-${docId}`;
if (state.length === 0) {
delete localStorage[key];
} else {
localStorage[key] = _.join(state, '|');
}
},
}); });

View file

@ -17,7 +17,7 @@
**************************************************************/ **************************************************************/
/************************************************************** /**************************************************************
* Gray, Grey shades * Gray shades
**************************************************************/ **************************************************************/
$gray-shades: ( $gray-shades: (
900: #1F2833, 900: #1F2833,
@ -153,9 +153,11 @@ $color-white-dark-1: #f5f5f5;
* Specialty colors * Specialty colors
**************************************************************/ **************************************************************/
// Documents, spaces card background color // Documents and spaces card background color
$color-card: #F6F4EE; $color-card: #F6F4EE;
$color-sidebar: #f2f8fd; $color-sidebar: #F6F4EE;
// $color-card: #F6F4EE;
// $color-sidebar: #f2f8fd;
/************************************************************** /**************************************************************
* Theme colors. * Theme colors.

View file

@ -160,269 +160,289 @@ icons
-------------------------*/ -------------------------*/
.dicon-delete-key::before { .dicon-delete-key::before {
content: "\ea02"; content: "\ea02";
} }
.dicon-i-remove::before { .dicon-i-remove::before {
content: "\ea03"; content: "\ea03";
} }
.dicon-bin::before { .dicon-bin::before {
content: "\ea04"; content: "\ea04";
} }
.dicon-attachment::before { .dicon-attachment::before {
content: "\ea06"; content: "\ea06";
} }
.dicon-pen-2::before { .dicon-pen-2::before {
content: "\ea08"; content: "\ea08";
} }
.dicon-settings-gear::before { .dicon-settings-gear::before {
content: "\ea0c"; content: "\ea0c";
} }
.dicon-small-down::before { .dicon-small-down::before {
content: "\ea11"; content: "\ea11";
} }
.dicon-small-left::before { .dicon-small-left::before {
content: "\ea12"; content: "\ea12";
} }
.dicon-small-right::before { .dicon-small-right::before {
content: "\ea13"; content: "\ea13";
} }
.dicon-small-up::before { .dicon-small-up::before {
content: "\ea14"; content: "\ea14";
} }
.dicon-small-triangle-down::before { .dicon-small-triangle-down::before {
content: "\ea15"; content: "\ea15";
} }
.dicon-small-triangle-left::before { .dicon-small-triangle-left::before {
content: "\ea16"; content: "\ea16";
} }
.dicon-small-triangle-right::before { .dicon-small-triangle-right::before {
content: "\ea17"; content: "\ea17";
} }
.dicon-small-triangle-up::before { .dicon-small-triangle-up::before {
content: "\ea18"; content: "\ea18";
} }
.dicon-arrow-down-2::before { .dicon-arrow-down-2::before {
content: "\ea19"; content: "\ea19";
} }
.dicon-arrow-left-2::before { .dicon-arrow-left-2::before {
content: "\ea1a"; content: "\ea1a";
} }
.dicon-arrow-right-2::before { .dicon-arrow-right-2::before {
content: "\ea1b"; content: "\ea1b";
} }
.dicon-arrow-up-2::before { .dicon-arrow-up-2::before {
content: "\ea1c"; content: "\ea1c";
} }
.dicon-chart-bar-33::before { .dicon-chart-bar-33::before {
content: "\ea1d"; content: "\ea1d";
} }
.dicon-geometry::before { .dicon-geometry::before {
content: "\ea1e"; content: "\ea1e";
} }
.dicon-bookmark::before { .dicon-bookmark::before {
content: "\ea1f"; content: "\ea1f";
} }
.dicon-bookmark-delete::before { .dicon-bookmark-delete::before {
content: "\ea20"; content: "\ea20";
} }
.dicon-bookmark-add::before { .dicon-bookmark-add::before {
content: "\ea22"; content: "\ea22";
} }
.dicon-pdf::before { .dicon-pdf::before {
content: "\ea23"; content: "\ea23";
} }
.dicon-print::before { .dicon-print::before {
content: "\ea24"; content: "\ea24";
} }
.dicon-list-bullet-2::before { .dicon-list-bullet-2::before {
content: "\ea25"; content: "\ea25";
} }
.dicon-magnifier::before { .dicon-magnifier::before {
content: "\ea26"; content: "\ea26";
} }
.dicon-b-chat::before { .dicon-b-chat::before {
content: "\ea27"; content: "\ea27";
} }
.dicon-filter-tool::before { .dicon-filter-tool::before {
content: "\ea28"; content: "\ea28";
} }
.dicon-grid-interface::before { .dicon-grid-interface::before {
content: "\ea29"; content: "\ea29";
} }
.dicon-lock::before { .dicon-lock::before {
content: "\ea2a"; content: "\ea2a";
} }
.dicon-unlocked::before { .dicon-unlocked::before {
content: "\ea2b"; content: "\ea2b";
} }
.dicon-menu-7::before { .dicon-menu-7::before {
content: "\ea2c"; content: "\ea2c";
} }
.dicon-network-connection::before { .dicon-network-connection::before {
content: "\ea2d"; content: "\ea2d";
} }
.dicon-e-add::before { .dicon-e-add::before {
content: "\ea2e"; content: "\ea2e";
} }
.dicon-data-upload::before { .dicon-data-upload::before {
content: "\ea2f"; content: "\ea2f";
} }
.dicon-upload::before { .dicon-upload::before {
content: "\ea30"; content: "\ea30";
} }
.dicon-flag::before { .dicon-flag::before {
content: "\ea31"; content: "\ea31";
} }
.dicon-globe::before { .dicon-globe::before {
content: "\ea32"; content: "\ea32";
} }
.dicon-single-01::before { .dicon-single-01::before {
content: "\ea33"; content: "\ea33";
} }
.dicon-multiple-19::before { .dicon-multiple-19::before {
content: "\ea34"; content: "\ea34";
} }
.dicon-box::before { .dicon-box::before {
content: "\ea35"; content: "\ea35";
} }
.dicon-time::before { .dicon-time::before {
content: "\ea37"; content: "\ea37";
} }
.dicon-split-37::before { .dicon-split-37::before {
content: "\ea38"; content: "\ea38";
} }
.dicon-sort-tool::before { .dicon-sort-tool::before {
content: "\ea39"; content: "\ea39";
} }
.dicon-button-2::before { .dicon-button-2::before {
content: "\ea3a"; content: "\ea3a";
} }
.dicon-menu-6::before { .dicon-menu-6::before {
content: "\ea40"; content: "\ea40";
} }
.dicon-pulse::before { .dicon-pulse::before {
content: "\ea41"; content: "\ea41";
} }
.dicon-copy::before { .dicon-copy::before {
content: "\ea43"; content: "\ea43";
} }
.dicon-menu-8::before { .dicon-menu-8::before {
content: "\ea48"; content: "\ea48";
} }
.dicon-send::before { .dicon-send::before {
content: "\ea49"; content: "\ea49";
} }
.dicon-email::before { .dicon-email::before {
content: "\ea4a"; content: "\ea4a";
} }
.dicon-download::before { .dicon-download::before {
content: "\ea4b"; content: "\ea4b";
} }
.dicon-database::before { .dicon-database::before {
content: "\ea4c"; content: "\ea4c";
} }
.dicon-notification::before { .dicon-notification::before {
content: "\ea4d"; content: "\ea4d";
} }
.dicon-handshake::before { .dicon-handshake::before {
content: "\ea4e"; content: "\ea4e";
} }
.dicon-add-27::before { .dicon-add-27::before {
content: "\ea4f"; content: "\ea4f";
} }
.dicon-delete-28::before { .dicon-delete-28::before {
content: "\ea50"; content: "\ea50";
} }
.dicon-i-check::before { .dicon-i-check::before {
content: "\ea51"; content: "\ea51";
} }
.dicon-shape-rectangle::before { .dicon-shape-rectangle::before {
content: "\ea52"; content: "\ea52";
} }
.dicon-ban::before { .dicon-ban::before {
content: "\ea53"; content: "\ea53";
} }
.dicon-check-single::before { .dicon-check-single::before {
content: "\ea54"; content: "\ea54";
} }
.dicon-check-double::before { .dicon-check-double::before {
content: "\ea55"; content: "\ea55";
} }
.dicon-check::before { .dicon-check::before {
content: "\ea56"; content: "\ea56";
} }
.dicon-preview::before { .dicon-preview::before {
content: "\ea58"; content: "\ea58";
} }
.dicon-link::before { .dicon-link::before {
content: "\ea59"; content: "\ea59";
} }
.dicon-b-check::before { .dicon-b-check::before {
content: "\ea5a"; content: "\ea5a";
} }
.dicon-e-delete::before {
content: "\ea5b";
}
.dicon-menu-5::before {
content: "\ea5c";
}
.dicon-move-layer-up::before {
content: "\ea5d";
}
.dicon-alpha-order::before {
content: "\ea5e";
}
.dicon-enlarge::before {
content: "\ea5f";
}

View file

@ -1,5 +1,6 @@
@import "grid.scss"; @import "grid.scss";
@import "spacing.scss"; @import "spacing.scss";
@import "headings.scss"; @import "headings.scss";
@import "master-internal.scss"; @import "layout.scss";
@import "toolbar.scss";
@import "sidebar.scss"; @import "sidebar.scss";

View file

@ -0,0 +1,140 @@
// *****************************************************************
// Define mobile-first layout for main content zone with a sidebar.
// *****************************************************************
.master-grid-container {
display: block;
height: auto;
width: 100%;
.master-sidebar {
display: block;
height: auto;
margin: 10px;
}
.master-content {
display: block;
height: auto;
padding: 10px;
}
}
// Tablet starts around 700px
@media (min-width: $display-break-1) {
.master-grid-container {
display: grid;
grid-template-columns: 200px auto;
.master-sidebar {
width: 200px;
margin: 20px 10px 20px 20px;
height: calc(100vh - 90px - 40px);
@include sticky();
top: 0px;
overflow-x: hidden;
overflow-y: auto;
}
.master-content {
grid-column-start: 2;
padding: 20px;
max-width: calc(100vw - 210px);
}
}
}
// Small screen starts around 900px
@media (min-width: $display-break-2) and (min-height: 650px) {
.master-grid-container {
display: grid;
grid-template-columns: 240px auto;
.master-sidebar {
width: 220px;
}
.master-content {
grid-column-start: 2;
padding: 20px;
max-width: calc(100vw - 250px);
}
}
}
// Medium screen starts around 1200px
@media (min-width: $display-break-3) and (min-height: 650px) {
.master-grid-container {
display: grid;
grid-template-columns: 300px auto;
.master-sidebar {
width: 280px;
margin: 40px 10px 40px 20px;
height: calc(100vh - 90px - 80px);
}
.master-content {
grid-column-start: 2;
padding: 40px 30px;
max-width: calc(100vw - 310px);
}
}
}
// Large screen starts around 1600px
@media (min-width: $display-break-4) and (min-height: 650px) {
.master-grid-container {
display: grid;
grid-template-columns: 400px minmax(auto, 1200px);
.master-sidebar {
width: 400px;
margin: 40px 10px 40px 20px;
}
.master-content {
grid-column-start: 2;
padding: 40px 40px;
max-width: 1200px;
}
}
}
// *****************************************************************
// Define mobile-first layout for content without a sidebar.
// *****************************************************************
.master-container {
display: block;
height: auto;
width: 100%;
padding: 20px;
}
// Tablet starts around 700px
@media (min-width: $display-break-1) {
.master-container {
padding: 20px;
}
}
// Small screen starts around 900px
@media (min-width: $display-break-2) and (min-height: 650px) {
.master-container {
padding: 20px;
}
}
// Medium screen starts around 1200px
@media (min-width: $display-break-3) and (min-height: 650px) {
.master-container {
padding: 30px;
}
}
// Large screen starts around 1600px
@media (min-width: $display-break-4) and (min-height: 650px) {
.master-container {
padding: 40px;
// max-width: 1200px;
}
}

View file

@ -1,389 +0,0 @@
// CSS GRID LAYOUT
// Mobile-first layout
.master-container {
display: block;
height: auto;
width: 100%;
.master-content {
display: block;
height: auto;
width: 100%;
padding: 10px;
}
}
.master-sidebar-container {
display: block;
height: auto;
width: 100%;
z-index: 1041; // required if we want to show modals from inside sidebar
.master-navbar {
display: block;
height: auto;
width: 100%;
background-color: $theme-500;
text-align: center;
padding: 0;
z-index: 999;
> .nav-content {
display: flex;
flex-grow: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
> .nav-options {
> .selected {
> .dicon, > .name {
color: $color-white !important;
}
}
> .option {
cursor: pointer;
display: inline-block;
> .dicon {
display: inline-block;
color: $theme-300;
font-size: 20px;
padding: 10px;
}
> .name {
display: none;
color: $theme-300;
}
&:hover {
> .dicon, > .name {
color: $theme-400 !important;
}
}
}
}
> .meta {
> .logo {
display: none;
cursor: pointer;
}
> .bookmarks, > .login, > .invalid-plan {
display: inline-block;
>.dicon {
cursor: pointer;
color: $color-white;
font-size: 20px;
padding: 10px;
}
}
> .invalid-plan {
>.dicon {
color: map-get($yellow-shades, 600) !important;
}
}
> .user-gravatar-container {
display: inline-block;
margin: 0 10px 0 0;
padding: 0;
vertical-align: text-bottom;
> .user-gravatar {
cursor: pointer;
position: relative;
width: 25px;
height: 25px;
line-height: 24px;
padding: 0;
border-radius: 50%;
font-size: 0.75rem;
text-align: center;
color: $theme-500;
font-weight: bold;
background-color: $color-white;
@extend .no-select;
}
}
}
}
}
.master-sidebar {
display: block;
height: auto;
width: 100%;
padding: 5px 10px;
z-index: 888;
// background-color: map-get($gray-shades, 100);
background-color: $color-sidebar;
}
}
// Tablet starts around 700px
@media (min-width: $display-break-1) {
.master-container {
display: grid;
grid-template-columns: 240px auto;
.master-content {
grid-column-start: 2;
padding: 20px;
max-width: calc(100vw - 250px);
}
}
.master-sidebar-container {
position: fixed;
width: 240px;
height: 100vh;
.master-navbar {
position: fixed;
top: 0;
left: 0;
width: 40px;
height: 100vh;
text-align: center;
padding: 10px 0;
> .nav-content {
display: flex;
flex-grow: 1;
flex-direction: column;
align-items: center;
justify-content: space-between;
flex-wrap: nowrap;
height: 100vh;
> .nav-options {
> .option {
display: block;
>.dicon {
display: block;
font-size: 20px;
padding: 20px 0;
}
}
}
> .meta {
padding-bottom: 35px;
> .logo {
display: block;
> img {
width: 32px;
height: 32px;
}
> .documize {
display: none;
}
}
> .bookmarks, > .login, > .invalid-plan {
display: block;
>.dicon {
color: $color-white;
font-size: 20px;
padding: 10px 0;
}
}
> .user-gravatar-container {
display: block;
text-align: center;
margin: 10px 0 15px 4px;
padding: 0;
> .user-gravatar {
display: block;
}
}
}
}
}
.master-sidebar {
position: fixed;
top: 0;
left: 40px;
width: 200px;
height: 100vh;
overflow-x: hidden;
overflow-y: auto;
}
}
}
// Small screen starts around 900px
@media (min-width: $display-break-2) and (min-height: 650px) {
.master-container {
display: grid;
grid-template-columns: 290px auto;
.master-content {
grid-column-start: 2;
padding: 20px;
max-width: calc(100vw - 300px);
}
}
.master-sidebar-container {
position: fixed;
width: 290px;
height: 100vh;
.master-navbar {
position: fixed;
top: 0;
left: 0;
width: 70px;
height: 100vh;
text-align: center;
> .nav-content {
> .nav-options {
> .option {
> .dicon {
display: block;
font-size: 24px;
padding: 15px 0 10px 0;
}
> .name {
display: block;
padding: 0 0 15px 0;
font-size: 0.8rem;
font-weight: 700;
text-transform: uppercase;
}
}
}
> .meta {
> .logo {
> img {
width: 32px;
height: 32px;
}
> .documize {
display: block;
font-size: 0.7rem;
color: white;
text-decoration: none;
}
}
> .user-gravatar-container {
margin: 10px 0 15px 8px;
padding: 0;
> .user-gravatar {
width: 30px;
height: 30px;
line-height: 30px;
font-size: 0.9rem;
}
}
}
}
}
.master-sidebar {
position: fixed;
top: 0;
left: 70px;
width: 220px;
height: 100vh;
}
}
}
// Medium screen starts around 1200px
@media (min-width: $display-break-3) and (min-height: 650px) {
.master-container {
display: grid;
grid-template-columns: 320px auto;
.master-content {
grid-column-start: 2;
padding: 40px 30px;
max-width: calc(100vw - 330px);
}
}
.master-sidebar-container {
position: fixed;
width: 320px;
height: 100vh;
.master-navbar {
position: fixed;
top: 0;
left: 0;
width: 70px;
height: 100vh;
}
.master-sidebar {
position: fixed;
top: 0;
left: 70px;
width: 250px;
height: 100vh;
padding: 20px 10px;
}
}
}
// Large screen starts around 1600px
@media (min-width: $display-break-4) and (min-height: 650px) {
.master-container {
display: grid;
grid-template-columns: 370px minmax(auto, 1200px);
.master-content {
grid-column-start: 2;
padding: 40px 40px;
max-width: 1200px;
}
}
.master-sidebar-container {
position: fixed;
width: 370px;
height: 100vh;
.master-navbar {
position: fixed;
top: 0;
left: 0;
width: 70px;
height: 100vh;
}
.master-sidebar {
position: fixed;
top: 0;
left: 70px;
width: 300px;
height: 100vh;
padding: 20px 15px;
}
}
}

View file

@ -1,7 +1,39 @@
.sidebar-content { .sidebar-content {
display: block; display: block;
position: relative; position: relative;
// @extend .text-truncate; padding: 20px 5px 20px 10px;
// background-color: $theme-100;
background-color: $color-sidebar;
// background-color: map-get($gray-shades, 100);
@include border-radius(6px);
> .goto-top {
position: absolute;
top: 10px;
left: 10px;
cursor: pointer;
color: map-get($gray-shades, 500);
font-size: 1rem;
&:hover {
color: map-get($gray-shades, 800);
}
}
// Allows for icon to be placed top right corner of sidebar zone.
// Expects icon to be placed in the zone.
> .custom-action {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
color: map-get($gray-shades, 500);
font-size: 1rem;
&:hover {
color: map-get($gray-shades, 800);
}
}
> .section { > .section {
margin: 0; margin: 0;

View file

@ -0,0 +1,129 @@
// *****************************************************************
// Define mobile-first layout for top navigation bar and toolbar.
// *****************************************************************
.master-navigation {
display: block;
height: auto;
width: 100%;
> .navbar {
display: block;
height: 40px;
width: 100%;
background-color: $theme-500;
text-align: center;
padding: 0;
z-index: 999;
> .container {
display: flex;
flex-grow: 1;
flex-direction: row;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
width: 100%;
padding: 0 20px;
> .options {
> .selected {
> .dicon {
color: $color-white !important;
}
}
> .option {
cursor: pointer;
display: inline-block;
> .dicon {
display: inline-block;
color: $theme-300;
font-size: 20px;
padding: 10px 10px;
}
&:hover {
> .dicon {
color: $color-white !important;
}
}
}
> a.option {
color: $theme-300;
&:hover {
color: $color-white;
}
}
> .invalid-plan {
> .dicon {
color: map-get($yellow-shades, 600) !important;
}
}
> .user-gravatar-container {
display: inline-block;
margin: 0;
padding: 7px 10px;
vertical-align: top;
> .user-gravatar {
display: inline-block;
cursor: pointer;
position: relative;
width: 26px;
height: 26px;
line-height: 26px;
padding: 0;
border-radius: 50%;
font-size: 0.85rem;
text-align: center;
color: $theme-500;
font-weight: bold;
background-color: $color-white;
@extend .no-select;
&:hover {
background-color: $theme-100;
}
}
}
}
}
}
> .toolbar {
display: block;
width: 100%;
background-color: $color-sidebar;
background-color: $theme-100;
padding: 0;
// z-index: 999;
> .container {
height: 50px;
width: 100%;
display: flex;
flex-grow: 1;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
padding: 0 15px 0 20px;
> div[class^="zone-"], > div[class*=" zone-"] {
margin: 0;
padding: 0;
align-items: center;
align-self: center;
> .label {
font-size: 1rem;
}
}
}
}
}

View file

@ -179,12 +179,12 @@
.dmz-button-theme { .dmz-button-theme {
@extend %dmz-button; @extend %dmz-button;
background-color: $theme-800; background-color: $theme-500;
color: $color-white; color: $color-white;
@include button-shadow(); @include button-shadow();
&:hover { &:hover {
background-color: $theme-600; background-color: $theme-700;
} }
} }
.dmz-button-theme-light { .dmz-button-theme-light {

View file

@ -8,7 +8,7 @@
// -webkit-box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2); // -webkit-box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2);
// box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2); // box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2);
@include border-radius(5px); @include border-radius(3px);
> p { > p {
margin: 4px; margin: 4px;
@ -19,6 +19,9 @@
margin: 0; margin: 0;
padding: 0; padding: 0;
min-width: 140px; min-width: 140px;
max-height: calc(100vh - 200px);
overflow: hidden;
overflow-y: auto;
> .item { > .item {
color: map-get($gray-shades, 800); color: map-get($gray-shades, 800);
@ -36,20 +39,21 @@
&:hover { &:hover {
color: $color-black; color: $color-black;
background-color: map-get($yellow-shades, 100); background-color: map-get($yellow-shades, 100);
@include border-radius(3px);
} }
} }
> .header { > .header {
color: map-get($gray-shades, 800); color: $color-white;
background-color: map-get($gray-shades, 300); background-color: $theme-500;
font-size: 1rem; font-size: 1rem;
font-weight: 600; font-weight: 600;
cursor: auto; cursor: auto;
&:hover { &:hover {
color: map-get($gray-shades, 800); color: $color-white;
background-color: map-get($gray-shades, 300); background-color: $theme-500;
} }
} }
> .divider { > .divider {

View file

@ -1,40 +1,44 @@
%toolbar-shadow { %toolbar-shadow {
box-shadow: 1px 1px 3px 0px map-get($gray-shades, 200); box-shadow: 1px 1px 3px 0px map-get($gray-shades, 200);
box-shadow: none;
} }
.dmz-toolbar { .dmz-toolbar {
display: inline-flex; display: inline-flex;
flex-basis: auto; flex-basis: auto;
align-items: center;
text-align: center; text-align: center;
white-space: nowrap; white-space: nowrap;
vertical-align: middle;
border: 1px solid transparent; border: 1px solid transparent;
height: 30px;
width: auto; width: auto;
padding: 6px 10px; padding: 6px 10px;
line-height: 24px;
@extend .no-select; @extend .no-select;
@include border-radius(6px); @include border-radius(4px);
> .dicon { > .dicon {
font-size: 16px; font-size: 20px;
font-weight: 500; color: $theme-500;
color: map-get($gray-shades, 600);
padding: 0 0.5rem; padding: 0 0.5rem;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: map-get($gray-shades, 700); color: $theme-400;
} }
} }
> .divider {
margin: 0 6px;
width: 1px;
background-color: $theme-200;
}
> .icon-selected { > .icon-selected {
color: map-get($yellow-shades, 600); color: map-get($yellow-shades, 600);
} }
> .label { > .label {
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 400;
color: map-get($gray-shades, 600); color: map-get($gray-shades, 600);
padding: 0 0.5rem; padding: 0 0.5rem;
align-self: center; align-self: center;
@ -68,17 +72,154 @@
color: map-get($green-shades, 600); color: map-get($green-shades, 600);
} }
} }
}
.dmz-toolbar-large { > .dropdown {
height: 40px; display: inline-block;
padding: 9px 10px; cursor: pointer;
line-height: 33px;
> .dicon { &:hover {
font-size: 20px; > .label, > .dicon {
color: $theme-400;
}
}
> .label {
display: inline-block;
font-size: 1rem;
font-weight: 400;
color: $theme-500;
padding: 0 0 0 0.5rem;
vertical-align: text-bottom;
}
> .dicon {
font-size: 20px;
color: $theme-500;
padding: 0 0.3rem 0 0;
vertical-align: bottom;
}
}
> .dropdown-green {
> .label, > .dicon {
color: map-get($green-shades, 500);
&:hover {
color: map-get($green-shades, 600);
}
}
}
> %button {
display: inline-block;
white-space: nowrap;
text-align: center;
padding: 0.375rem 0.75rem;
// margin: 0 0.5rem;
font-weight: 500; font-weight: 500;
padding: 0 0.5rem; font-size: 1rem;
border: 1px solid transparent;
text-transform: uppercase;
cursor: pointer;
@extend .no-select;
@include border-radius(2px);
outline: none;
background-color: map-get($gray-shades, 600);
color: $color-white;
// @include button-shadow();
> .dicon {
font-size: 1rem;
padding: 0.2rem 0.4rem 0 0;
font-weight: 500;
}
> .label {
display: inline-block;
font-size: 1rem;
margin: 0;
padding: 0;
}
&:hover {
color: map-get($gray-shades, 800);
border-color: map-get($gray-shades, 500);
}
}
.button-theme {
@extend %button;
background-color: $theme-500;
color: $color-white;
&:hover {
color: $color-white;
border-color: $theme-600;
background-color: $theme-600;
}
}
.button-green {
@extend %button;
background-color: map-get($green-shades, 600);
color: $color-white;
&:hover {
color: $color-white;
border-color: map-get($green-shades, 500);
background-color: map-get($green-shades, 500);
}
}
.button-green-light {
@extend %button;
background-color: map-get($green-shades, 200);
color: map-get($green-shades, 700);
border: 1px solid map-get($green-shades, 300);
&:hover {
background-color: map-get($green-shades, 300);
}
}
.button-yellow {
@extend %button;
background-color: map-get($yellow-shades, 600);
color: $color-white;
&:hover {
background-color: map-get($yellow-shades, 700);
}
}
.button-yellow-light {
@extend %button;
background-color: map-get($yellow-shades, 200);
color: map-get($yellow-shades, 700);
border: 1px solid map-get($yellow-shades, 300);
&:hover {
background-color: map-get($yellow-shades, 300);
}
}
.button-red {
@extend %button;
background-color: map-get($red-shades, 600);
color: $color-white;
&:hover {
background-color: map-get($red-shades, 700);
}
}
.button-red-light {
@extend %button;
background-color: map-get($red-shades, 100);
color: map-get($red-shades, 700);
border: 1px solid map-get($red-shades, 200);
&:hover {
background-color: map-get($red-shades, 200);
}
} }
} }
@ -88,6 +229,7 @@
.dmz-toolbar-light { .dmz-toolbar-light {
background-color: map-get($gray-shades, 100); background-color: map-get($gray-shades, 100);
background-color: $color-white;
} }
.dmz-toolbar-raised { .dmz-toolbar-raised {

View file

@ -15,8 +15,10 @@ a {
text-decoration: none; text-decoration: none;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
outline: none;
a:focus, a:hover { a:focus, a:hover {
outline: none;
text-decoration: underline; text-decoration: underline;
} }
} }

View file

@ -1,10 +1,16 @@
.document-meta-wrapper {
padding: 20px 20px 0 20px;
@include border-radius(7px);
background-color: map-get($gray-shades, 100);
}
.document-meta { .document-meta {
margin: 0; margin: 0;
padding: 0; padding: 0;
> .title { > .title {
text-transform: uppercase; text-transform: uppercase;
font-size: 1rem; font-size: 0.8rem;
font-weight: 600; font-weight: 600;
color: map-get($gray-shades, 700); color: map-get($gray-shades, 700);
margin-bottom: 5px; margin-bottom: 5px;
@ -138,13 +144,33 @@
@extend .no-select; @extend .no-select;
display: block; display: block;
margin: 0; margin: 0;
font-size: 1.1rem; font-size: 0.9rem;
font-weight: 400; font-weight: 400;
color: map-get($yellow-shades, 700); color: map-get($green-shades, 700);
}
> .label-version {
@include border-radius(3px);
@extend .no-select;
display: inline-block;
margin: 5px 0 5px 0;
padding: 0.3rem 0.7rem;
font-size: 1.1rem;
font-weight: 500;
background-color: map-get($gray-shades, 800);
color: map-get($gray-shades, 100);
}
> .version-dropdown {
color: map-get($gray-shades, 500);
font-size: 25px;
vertical-align: middle;
cursor: pointer;
} }
> .document-heading { > .document-heading {
.name { .name {
margin: 0;
color: map-get($gray-shades, 900); color: map-get($gray-shades, 900);
font-size: 2.2rem; font-size: 2.2rem;
font-weight: 700; font-weight: 700;
@ -155,5 +181,12 @@
font-size: 1.2rem; font-size: 1.2rem;
font-weight: 500; font-weight: 500;
} }
> .dates {
margin-bottom: 2rem;
font-size: 0.9rem;
font-weight: 300;
color: map-get($gray-shades, 700);
}
} }
} }

View file

@ -2,7 +2,7 @@
margin: 0 0 0 0; margin: 0 0 0 0;
> .page-header { > .page-header {
margin: 2rem 0 2rem 0; margin: 0 0 2rem 0;
> .page-number { > .page-number {
color: $theme-500; color: $theme-500;
@ -55,6 +55,14 @@
color: map-get($yellow-shades, 700); color: map-get($yellow-shades, 700);
} }
} }
> i.expand {
color: map-get($green-shades, 500);
&:hover {
color: map-get($green-shades, 700);
}
}
} }
} }

View file

@ -1,51 +1,67 @@
#upload-document-files {
> .dz-preview, .dz-processing {
display: none !important;
}
}
.document-sidebar-attachment { .document-sidebar-attachment {
> .files { > .files {
margin: 0; margin: 0;
padding: 0; padding: 0;
> .file { > .file {
@include card();
list-style-type: none; list-style-type: none;
margin: 10px 0 0 0; margin: 0;
padding: 5px; padding: 0 10px 0 0;
width: 100%;
font-size: 0.9rem; font-size: 0.9rem;
position: relative; position: relative;
display: inline-block;
> a { > a {
display: inline-block; display: inline-block;
font-size: 0.9rem; font-size: 1rem;
vertical-align: text-top;
margin-right: 10px;
width: 90%;
@extend .text-truncate; @extend .text-truncate;
color: map-get($gray-shades, 800);
&:hover {
color: map-get($gray-shades, 900);
}
} }
> .menu { > .menu {
position: absolute; display: inline-block;
right: -10px; color: map-get($gray-shades, 300);
top: 0; font-size: 1.2rem;
&:hover {
color: map-get($gray-shades, 600);
}
} }
} }
} }
} }
#upload-document-files {
list-style-type: none;
margin: 0;
padding: 0;
font-size: 0.9rem;
position: relative;
display: inline-block;
text-transform: uppercase;
vertical-align: super;
> span {
display: inline-block;
color: map-get($gray-shades, 600);
font-size: 1rem;
cursor: pointer;
&:hover {
color: map-get($gray-shades, 800);
}
}
> .dz-preview, .dz-processing {
display: none !important;
}
}
.dz-preview, .dz-processing { .dz-preview, .dz-processing {
display: none !important; display: none !important;
} }
.section-attachments { .section-attachments {
margin: 1.5rem 0 0 0; margin: 1.5rem 0 1.5rem 0;
padding: 0; padding: 0;
> .file { > .file {

View file

@ -26,21 +26,24 @@
padding: 0; padding: 0;
list-style: none; list-style: none;
font-size: 1rem; font-size: 1rem;
overflow-x: hidden;
list-style-type: none; list-style-type: none;
margin: 0 0 0 0; margin: 0 0 0 0;
.item { .item {
@extend .no-select; @extend .no-select;
padding: 5px 0; padding: 5px 0;
text-overflow: ellipsis; // text-overflow: ellipsis;
word-wrap: break-word; // word-wrap: break-word;
white-space: nowrap; // white-space: nowrap;
overflow: hidden; // overflow: hidden;
> .link { > .link {
color: map-get($gray-shades, 800); color: map-get($gray-shades, 800);
font-weight: 500; // font-weight: 300;
// text-overflow: ellipsis;
// word-wrap: break-word;
// white-space: nowrap;
overflow: hidden;
&:hover { &:hover {
color: map-get($yellow-shades, 600); color: map-get($yellow-shades, 600);
@ -48,7 +51,7 @@
> .numbering { > .numbering {
color: map-get($gray-shades, 600); color: map-get($gray-shades, 600);
font-weight: 500; font-weight: 300;
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
font-size: 0.9rem; font-size: 0.9rem;

View file

@ -10,6 +10,12 @@
color: map-get($gray-shades, 100); color: map-get($gray-shades, 100);
} }
.space-label-dropdown {
font-size: 25px;
vertical-align: middle;
cursor: pointer;
}
.view-space { .view-space {
> .documents { > .documents {
margin: 0; margin: 0;
@ -17,7 +23,6 @@
> .document { > .document {
@include card(); @include card();
box-shadow: none;
list-style-type: none; list-style-type: none;
margin: 0 0 2rem 0; margin: 0 0 2rem 0;
padding: 15px 20px; padding: 15px 20px;
@ -70,84 +75,84 @@
margin-top: 0.4rem; margin-top: 0.4rem;
color: $color-black-light-3; color: $color-black-light-3;
} }
}
> .meta { > .meta {
padding: 25px 0 0 0; padding: 25px 0 0 0;
> .lifecycle { > .lifecycle {
display: inline-block;
text-transform: uppercase;
font-size: 0.9rem;
font-weight: 500;
text-align: center;
@include border-radius(5px);
background-color: map-get($gray-shades, 300);
padding: 0.25rem 1rem;
margin-right: 20px;
> .draft {
color: map-get($yellow-shades, 600);
}
> .live {
color: map-get($green-shades, 600);
}
> .archived {
color: map-get($red-shades, 600);
}
}
> .dicon {
color: map-get($gray-shades, 600);
font-size: 20px;
}
.categories {
display: inline-block;
padding: 0;
> .category {
padding: 0 15px 0 0;
display: inline-block; display: inline-block;
text-transform: uppercase;
font-size: 0.9rem;
font-weight: 500;
text-align: center;
@include border-radius(5px);
background-color: map-get($gray-shades, 300);
padding: 0.25rem 1rem;
margin-right: 20px;
> .dicon { > .draft {
color: map-get($gray-shades, 500); color: map-get($yellow-shades, 600);
font-size: 20px;
vertical-align: bottom;
} }
> .name { > .live {
display: inline-block; color: map-get($green-shades, 600);
color: map-get($gray-shades, 800); }
font-size: 1rem;
> .archived {
color: map-get($red-shades, 600);
} }
} }
}
.hashtags { > .dicon {
display: inline-block; color: map-get($gray-shades, 600);
padding: 0; font-size: 20px;
}
> .hashtag { .categories {
padding: 0 15px 0 0; display: inline-block;
padding: 0;
> .dicon { > .category {
color: map-get($gray-shades, 500); padding: 0 15px 0 0;
font-size: 20px;
vertical-align: bottom;
}
> .name {
display: inline-block; display: inline-block;
color: map-get($gray-shades, 800);
font-size: 1rem;
}
&:hover { > .dicon {
> .dicon, > .name { color: map-get($gray-shades, 500);
color: map-get($gray-shades, 600); font-size: 20px;
vertical-align: bottom;
}
> .name {
display: inline-block;
color: map-get($gray-shades, 800);
font-size: 1rem;
}
}
}
.hashtags {
display: inline-block;
padding: 0;
> .hashtag {
padding: 0 15px 0 0;
> .dicon {
color: map-get($gray-shades, 500);
font-size: 20px;
vertical-align: bottom;
}
> .name {
display: inline-block;
color: map-get($gray-shades, 800);
font-size: 1rem;
}
&:hover {
> .dicon, > .name {
color: map-get($gray-shades, 600);
}
} }
} }
} }

View file

@ -26,7 +26,7 @@
</ul> </ul>
</div> </div>
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
{{#if isKeycloakProvider}} {{#if isKeycloakProvider}}
<div class="form-group"> <div class="form-group">

View file

@ -26,7 +26,7 @@
</small> </small>
</div> </div>
</div> </div>
{{ui/ui-spacer size=400}} <Ui::UiSpacer @size="400" />
<div class="change-log"> <div class="change-log">
{{{changelog}}} {{{changelog}}}
</div> </div>

View file

@ -29,7 +29,7 @@
{{ui/ui-button color=constants.Color.Yellow light=true label="Upgrade"}} {{ui/ui-button color=constants.Color.Yellow light=true label="Upgrade"}}
</a> </a>
{{/if}} {{/if}}
{{ui/ui-spacer size=400}} <Ui::UiSpacer @size="400" />
<form> <form>
<div class="form-group"> <div class="form-group">
@ -101,7 +101,7 @@
{{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}} {{#if (eq appMeta.edition constants.Product.EnterpriseEdition)}}
{{#if (eq appMeta.location "cloud")}} {{#if (eq appMeta.location "cloud")}}
{{ui/ui-spacer size=400}} <Ui::UiSpacer @size="400" />
<div class="view-customize"> <div class="view-customize">
<div class="deactivation-zone"> <div class="deactivation-zone">
<p>Let us know if you would like to close your account or cancel your subscription.</p> <p>Let us know if you would like to close your account or cancel your subscription.</p>

View file

@ -1,6 +1,6 @@
{{#if spaces}} {{#if spaces}}
{{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.Export label="Export All Content" onClick=(action "onExport")}} {{ui/ui-button color=constants.Color.Yellow light=true icon=constants.Icon.Export label="Export All Content" onClick=(action "onExport")}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="view-customize"> <div class="view-customize">
<ul class="space-list"> <ul class="space-list">
@ -28,7 +28,7 @@
</div> </div>
<div class="desc">Some description that is to be wired up to the backend</div> <div class="desc">Some description that is to be wired up to the backend</div>
{{/link-to}} {{/link-to}}
{{ui/ui-spacer size=200}} <Ui::UiSpacer @size="200" />
{{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}} {{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}}
{{ui/ui-toolbar-icon icon=constants.Icon.AddUser color=constants.Color.Green tooltip="Add myself as owner" onClick=(action "onOwner" space.id)}} {{ui/ui-toolbar-icon icon=constants.Icon.AddUser color=constants.Color.Green tooltip="Add myself as owner" onClick=(action "onOwner" space.id)}}
{{ui/ui-toolbar-icon icon=constants.Icon.Delete color=constants.Color.Red tooltip="Delete space" onClick=(action "onShow" space.id)}} {{ui/ui-toolbar-icon icon=constants.Icon.Delete color=constants.Color.Red tooltip="Delete space" onClick=(action "onShow" space.id)}}

View file

@ -6,7 +6,7 @@
label=constants.Label.Add label=constants.Label.Add
onClick=(action "onShowAddModal")}} onClick=(action "onShowAddModal")}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<ul class="space-labels"> <ul class="space-labels">
{{#each labels as |label|}} {{#each labels as |label|}}

View file

@ -21,9 +21,9 @@
{{input id="newUserEmail" type="email" class="form-control" placeholder="Email" value=newUser.email}} {{input id="newUserEmail" type="email" class="form-control" placeholder="Email" value=newUser.email}}
</div> </div>
</div> </div>
{{ui/ui-spacer size=200}} <Ui::UiSpacer @size="200" />
{{ui/ui-button color=constants.Color.Green icon=constants.Icon.Person light=true label=constants.Label.Add onClick=(action "onAddUser")}} {{ui/ui-button color=constants.Color.Green icon=constants.Icon.Person light=true label=constants.Label.Add onClick=(action "onAddUser")}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
</form> </form>
<form onsubmit={{action "onAddUser"}}> <form onsubmit={{action "onAddUser"}}>
<div class="form-group"> <div class="form-group">

View file

@ -26,7 +26,7 @@
</div> </div>
</div> </div>
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="groups-list"> <div class="groups-list">
{{#each groups as |group|}} {{#each groups as |group|}}
@ -35,7 +35,7 @@
{{group.name}} ({{group.members}}) {{group.name}} ({{group.members}})
</div> </div>
<div class="desc">{{group.purpose}}</div> <div class="desc">{{group.purpose}}</div>
{{ui/ui-spacer size=200}} <Ui::UiSpacer @size="200" />
{{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}} {{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}}
{{ui/ui-toolbar-icon icon=constants.Icon.AddUser color=constants.Color.Gray tooltip="Add members" onClick=(action "onShowAddMemberModal" group.id)}} {{ui/ui-toolbar-icon icon=constants.Icon.AddUser color=constants.Color.Gray tooltip="Add members" onClick=(action "onShowAddMemberModal" group.id)}}
{{#if (gt group.members 0)}} {{#if (gt group.members 0)}}
@ -146,7 +146,7 @@
{{ui/ui-toolbar-label color=constants.Color.Gray label="ALL" selected=(eq userLimit 99999) onClick=(action "onLimit" 99999)}} {{ui/ui-toolbar-label color=constants.Color.Gray label="ALL" selected=(eq userLimit 99999) onClick=(action "onLimit" 99999)}}
{{/ui/ui-toolbar}} {{/ui/ui-toolbar}}
</div> </div>
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="group-users-members"> <div class="group-users-members">
{{#each users as |user|}} {{#each users as |user|}}
<div class="item"> <div class="item">

View file

@ -1,6 +1,6 @@
<div class="view-customize"> <div class="view-customize">
{{#if isAuthProviderKeycloak}} {{#if isAuthProviderKeycloak}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
{{#if syncInProgress}} {{#if syncInProgress}}
{{ui/ui-button color=constants.Color.Gray light=true icon=constants.Icon.Locked label="Keycloak user sync running..."}} {{ui/ui-button color=constants.Color.Gray light=true icon=constants.Icon.Locked label="Keycloak user sync running..."}}
{{else}} {{else}}
@ -9,7 +9,7 @@
{{/if}} {{/if}}
{{#if (or isAuthProviderLDAP c)}} {{#if (or isAuthProviderLDAP c)}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
{{#if syncInProgress}} {{#if syncInProgress}}
{{ui/ui-button color=constants.Color.Gray light=true icon=constants.Icon.Locked label="LDAP user sync running..."}} {{ui/ui-button color=constants.Color.Gray light=true icon=constants.Icon.Locked label="LDAP user sync running..."}}
{{else}} {{else}}
@ -17,7 +17,7 @@
{{/if}} {{/if}}
{{/if}} {{/if}}
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="explain-user-perms"> <div class="explain-user-perms">
<div class="title" {{action "togglePerms"}}> <div class="title" {{action "togglePerms"}}>
Permissions Explained Permissions Explained
@ -40,7 +40,7 @@
<div class="perm-desc">Can login and use Documize</div> <div class="perm-desc">Can login and use Documize</div>
</div> </div>
</div> </div>
{{ui/ui-spacer size=300}} <Ui::UiSpacer @size="300" />
<div class="form-group"> <div class="form-group">
{{focus-input type="text" class="form-control" placeholder="filter users" value=filter key-up=(action "onFilterChange")}} {{focus-input type="text" class="form-control" placeholder="filter users" value=filter key-up=(action "onFilterChange")}}

View file

@ -1,7 +1,53 @@
{{ui/ui-spacer size=300}} <div class="document-meta non-printable">
<div class="title">STATUS</div>
<div class="{{if (eq document.lifecycle constants.Lifecycle.Draft) "label-draft"}}
{{if (eq document.lifecycle constants.Lifecycle.Live) "label-live"}}
{{if (eq document.lifecycle constants.Lifecycle.Archived) "label-archived"}}">
{{document.lifecycleLabel}}
{{#attach-tooltip showDelay=1000}}Lifecycle: Draft &middot; Live &middot; Archived{{/attach-tooltip}}
</div>
<div class="document-meta"> {{#if (eq document.protection constants.ProtectionType.None)}}
<div class="title {{if permissions.documentEdit "cursor-pointer"}}" {{action "onEditCategory"}}>CATEGORY / TAG</div> <div class="label-open">
OPEN
{{#attach-tooltip showDelay=1000}}Change Control: Open &middot; Protected &middot; Locked{{/attach-tooltip}}
</div>
{{/if}}
{{#if (eq document.protection constants.ProtectionType.Review)}}
<div class="label-protected">
PROTECTED
{{#attach-tooltip showDelay=1000}}Change Control: Open &middot; Protected &middot; Locked{{/attach-tooltip}}
</div>
{{/if}}
{{#if (eq document.protection constants.ProtectionType.Lock)}}
<div class="label-locked">
LOCKED
{{#attach-tooltip showDelay=1000}}Change Control: Open &middot; Protected &middot; Locked{{/attach-tooltip}}
</div>
{{/if}}
{{#if contributionStatus}}
<div class="label-workflow-status">
{{contributionStatus}}
</div>
{{else}}
{{#if approvalStatus}}
<div class="label-workflow-status">
{{approvalStatus}}
</div>
{{/if}}
{{/if}}
{{#if document.template}}
<div class="label-template non-printable">
Template
{{#attach-tooltip showDelay=1000}}This is a template{{/attach-tooltip}}
</div>
{{/if}}
<Ui::UiSpacer @size="200" />
<div class="title">CATEGORY / TAG</div>
{{#each selectedCategories as |cat|}} {{#each selectedCategories as |cat|}}
<div class="meta-label"> <div class="meta-label">
<i class="dicon {{constants.Icon.Category}}"/> <i class="dicon {{constants.Icon.Category}}"/>
@ -16,14 +62,15 @@
{{#attach-tooltip showDelay=1000}}Tag{{/attach-tooltip}} {{#attach-tooltip showDelay=1000}}Tag{{/attach-tooltip}}
</div> </div>
{{/each}} {{/each}}
{{#if unassigned}} {{#if unassigned}}
{{#if permissions.spaceManage}} {{#if permissions.spaceManage}}
{{#ui/ui-toolbar dark=false light=true raised=true large=false bordered=true}} <div class="empty cursor-pointer" {{action "onEditCategory"}}>Unassigned</div>
{{ui/ui-toolbar-icon icon=constants.Icon.Plus color=constants.Color.Gray linkTo="document.settings"}}
{{/ui/ui-toolbar}}
{{else}} {{else}}
<div class="empty">Unassigned</div> <div class="empty">Unassigned</div>
{{/if}} {{/if}}
{{/if}} {{/if}}
</div> </div>
{{document/sidebar-attachment document=document permissions=permissions}}
<Ui::UiSpacer @size="200" />

View file

@ -11,29 +11,37 @@
onAction=(action "onSavePage")}} onAction=(action "onSavePage")}}
</div> </div>
{{else}} {{else}}
{{document/page-heading <div class={{page.tocIndentCss}}>
page=page {{document/page-heading
meta=meta expanded=expanded
pages=pages page=page
roles=roles meta=meta
folder=folder pages=pages
blocks=blocks roles=roles
tabMode=tabMode folder=folder
pending=pending folders=folders
document=document blocks=blocks
permissions=permissions tabMode=tabMode
onEdit=(action "onEdit") pending=pending
refresh=(action refresh) document=document
onCopyPage=(action "onCopyPage") permissions=permissions
onMovePage=(action "onMovePage") currentPageId=currentPageId
onDeletePage=(action "onDeletePage") onEdit=(action "onEdit")
onSavePageAsBlock=(action "onSavePageAsBlock") refresh=(action refresh)
onPageLevelChange=(action onPageLevelChange) onExpand=(action onExpand)
onPageSequenceChange=(action onPageSequenceChange) onCopyPage=(action "onCopyPage")
onShowSectionWizard=(action onShowSectionWizard)}} onMovePage=(action "onMovePage")
onDeletePage=(action "onDeletePage")
onSavePageAsBlock=(action "onSavePageAsBlock")
onPageLevelChange=(action onPageLevelChange)
onPageSequenceChange=(action onPageSequenceChange)
onShowSectionWizard=(action onShowSectionWizard)}}
<div class="wysiwyg"> {{#if expanded}}
{{section/base-renderer page=page}} <div class="wysiwyg">
{{section/base-renderer page=page}}
</div>
{{/if}}
</div> </div>
{{/if}} {{/if}}

Some files were not shown because too many files have changed in this diff Show more