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

using all new permissions for securing spaces and documents

WIP
This commit is contained in:
Harvey Kandola 2017-09-18 13:02:15 +01:00
parent ef285c91de
commit 300b617583
51 changed files with 517 additions and 868 deletions

View file

@ -135,24 +135,24 @@ func (h *Handler) DocumentLinks(w http.ResponseWriter, r *http.Request) {
response.WriteJSON(w, l) response.WriteJSON(w, l)
} }
// BySpace is an endpoint that returns the documents in a given folder. // BySpace is an endpoint that returns the documents for given space.
func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) { func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
method := "document.space" method := "document.space"
ctx := domain.GetRequestContext(r) ctx := domain.GetRequestContext(r)
folderID := request.Query(r, "folder") spaceID := request.Query(r, "space")
if len(folderID) == 0 { if len(spaceID) == 0 {
response.WriteMissingDataError(w, method, "folder") response.WriteMissingDataError(w, method, "space")
return return
} }
if !space.CanViewSpace(ctx, *h.Store, folderID) { if !space.CanViewSpace(ctx, *h.Store, spaceID) {
response.WriteForbiddenError(w) response.WriteForbiddenError(w)
return return
} }
documents, err := h.Store.Document.GetBySpace(ctx, folderID) documents, err := h.Store.Document.GetBySpace(ctx, spaceID)
if len(documents) == 0 { if len(documents) == 0 {
documents = []doc.Document{} documents = []doc.Document{}

View file

@ -134,20 +134,19 @@ func (s Scope) GetBySpace(ctx domain.RequestContext, folderID string) (documents
func (s Scope) GetByTag(ctx domain.RequestContext, tag string) (documents []doc.Document, err error) { func (s Scope) GetByTag(ctx domain.RequestContext, tag string) (documents []doc.Document, err error) {
tagQuery := "tags LIKE '%#" + tag + "#%'" tagQuery := "tags LIKE '%#" + tag + "#%'"
err = s.Runtime.Db.Select(&documents, err = s.Runtime.Db.Select(&documents, `
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0 AND `+tagQuery+` AND labelid IN SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0 AND `+tagQuery+`
(SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY title
ctx.OrgID, `, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, "select documents by tag") err = errors.Wrap(err, "select documents by tag")
@ -160,19 +159,17 @@ func (s Scope) GetByTag(ctx domain.RequestContext, tag string) (documents []doc.
// Templates returns a slice containing the documents available as templates to the client's organisation, in title order. // Templates returns a slice containing the documents available as templates to the client's organisation, in title order.
func (s Scope) Templates(ctx domain.RequestContext) (documents []doc.Document, err error) { func (s Scope) Templates(ctx domain.RequestContext) (documents []doc.Document, err error) {
err = s.Runtime.Db.Select(&documents, err = s.Runtime.Db.Select(&documents,
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=1 AND labelid IN `SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=1
(SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, "select document templates") err = errors.Wrap(err, "select document templates")
@ -185,20 +182,17 @@ func (s Scope) Templates(ctx domain.RequestContext) (documents []doc.Document, e
// TemplatesBySpace returns a slice containing the documents available as templates for given space. // TemplatesBySpace returns a slice containing the documents available as templates for given space.
func (s Scope) TemplatesBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) { func (s Scope) TemplatesBySpace(ctx domain.RequestContext, spaceID string) (documents []doc.Document, err error) {
err = s.Runtime.Db.Select(&documents, err = s.Runtime.Db.Select(&documents,
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND labelid=? AND template=1 AND labelid IN `SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND labelid=? AND template=1
(SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
spaceID, UNION ALL
ctx.OrgID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.UserID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY title`, ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.OrgID,
ctx.UserID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil
@ -233,19 +227,17 @@ func (s Scope) PublicDocuments(ctx domain.RequestContext, orgID string) (documen
// DocumentList returns a slice containing the documents available as templates to the client's organisation, in title order. // DocumentList returns a slice containing the documents available as templates to the client's organisation, in title order.
func (s Scope) DocumentList(ctx domain.RequestContext) (documents []doc.Document, err error) { func (s Scope) DocumentList(ctx domain.RequestContext) (documents []doc.Document, err error) {
err = s.Runtime.Db.Select(&documents, err = s.Runtime.Db.Select(&documents,
`SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0 AND labelid IN `SELECT id, refid, orgid, labelid, userid, job, location, title, excerpt, slug, tags, template, layout, created, revised FROM document WHERE orgid=? AND template=0
(SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.UserID)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
err = nil err = nil

View file

@ -169,21 +169,19 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
keywords = strings.TrimSpace(strings.ToLower(keywords)) keywords = strings.TrimSpace(strings.ToLower(keywords))
likeQuery := "LOWER(title) LIKE '%" + keywords + "%'" likeQuery := "LOWER(title) LIKE '%" + keywords + "%'"
err = s.Runtime.Db.Select(&temp, err = s.Runtime.Db.Select(&temp, `
`SELECT d.refid as documentid, d. labelid as folderid, d.title, l.label as context SELECT d.refid as documentid, d. labelid as folderid, d.title, l.label as context
FROM document d LEFT JOIN label l ON d.labelid=l.refid WHERE l.orgid=? AND `+likeQuery+` AND d.labelid IN FROM document d LEFT JOIN label l ON d.labelid=l.refid WHERE l.orgid=? AND `+likeQuery+`
(SELECT refid FROM label WHERE orgid=? AND type=2 AND userid=? AND d.labelid IN
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=1 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=3 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute search links 1") err = errors.Wrap(err, "execute search links 1")
@ -210,19 +208,17 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
err = s.Runtime.Db.Select(&temp, err = s.Runtime.Db.Select(&temp,
`SELECT p.refid as targetid, p.documentid as documentid, p.title as title, p.pagetype as linktype, d.title as context, d.labelid as folderid `SELECT p.refid as targetid, p.documentid as documentid, p.title as title, p.pagetype as linktype, d.title as context, d.labelid as folderid
FROM page p LEFT JOIN document d ON d.refid=p.documentid WHERE p.orgid=? AND `+likeQuery+` AND d.labelid IN FROM page p LEFT JOIN document d ON d.refid=p.documentid WHERE p.orgid=? AND `+likeQuery+`
(SELECT refid FROM label WHERE orgid=? AND type=2 AND userid=? AND d.labelid IN
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=1 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=3 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY p.title`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY p.title`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute search links 2") err = errors.Wrap(err, "execute search links 2")
@ -249,19 +245,17 @@ func (s Scope) SearchCandidates(ctx domain.RequestContext, keywords string) (doc
err = s.Runtime.Db.Select(&temp, err = s.Runtime.Db.Select(&temp,
`SELECT a.refid as targetid, a.documentid as documentid, a.filename as title, a.extension as context, d.labelid as folderid `SELECT a.refid as targetid, a.documentid as documentid, a.filename as title, a.extension as context, d.labelid as folderid
FROM attachment a LEFT JOIN document d ON d.refid=a.documentid WHERE a.orgid=? AND `+likeQuery+` AND d.labelid IN FROM attachment a LEFT JOIN document d ON d.refid=a.documentid WHERE a.orgid=? AND `+likeQuery+`
(SELECT refid FROM label WHERE orgid=? AND type=2 AND userid=? AND d.labelid IN
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=1 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a WHERE orgid=? AND type=3 AND refid IN (SELECT labelid FROM labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
ORDER BY a.filename`, AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
ctx.OrgID, SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
ctx.OrgID, UNION ALL
ctx.UserID, SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
ctx.OrgID, ))
ctx.OrgID, )
ctx.OrgID, ORDER BY a.filename`, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
ctx.OrgID,
ctx.UserID)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute search links 3") err = errors.Wrap(err, "execute search links 3")

View file

@ -265,9 +265,15 @@ func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType strin
AND s.itemtype = ? AND s.itemtype = ?
AND s.documentid = d.refid AND s.documentid = d.refid
-- AND d.template = 0 -- AND d.template = 0
AND d.labelid IN (SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND d.labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
UNION ALL
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
))
)
AND MATCH(s.content) AGAINST(? IN BOOLEAN MODE)` AND MATCH(s.content) AGAINST(? IN BOOLEAN MODE)`
err = s.Runtime.Db.Select(&r, err = s.Runtime.Db.Select(&r,
@ -275,11 +281,10 @@ func (s Scope) matchFullText(ctx domain.RequestContext, keywords, itemType strin
ctx.OrgID, ctx.OrgID,
itemType, itemType,
ctx.OrgID, ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.UserID, ctx.UserID,
ctx.OrgID, ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.UserID, ctx.UserID,
keywords) keywords)
@ -318,9 +323,15 @@ func (s Scope) matchLike(ctx domain.RequestContext, keywords, itemType string) (
AND s.itemtype = ? AND s.itemtype = ?
AND s.documentid = d.refid AND s.documentid = d.refid
-- AND d.template = 0 -- AND d.template = 0
AND d.labelid IN (SELECT refid from label WHERE orgid=? AND type=2 AND userid=? AND d.labelid IN
UNION ALL SELECT refid FROM label a where orgid=? AND type=1 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid='' AND (canedit=1 OR canview=1)) (
UNION ALL SELECT refid FROM label a where orgid=? AND type=3 AND refid IN (SELECT labelid from labelrole WHERE orgid=? AND userid=? AND (canedit=1 OR canview=1))) SELECT refid FROM label WHERE orgid=?
AND refid IN (SELECT refid FROM permission WHERE orgid=? AND location='space' AND refid IN (
SELECT refid from permission WHERE orgid=? AND who='user' AND whoid=? AND location='space'
UNION ALL
SELECT p.refid from permission p LEFT JOIN rolemember r ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.location='space' AND p.action='view' AND r.userid=?
))
)
AND s.content LIKE ?` AND s.content LIKE ?`
err = s.Runtime.Db.Select(&r, err = s.Runtime.Db.Select(&r,
@ -328,11 +339,10 @@ func (s Scope) matchLike(ctx domain.RequestContext, keywords, itemType string) (
ctx.OrgID, ctx.OrgID,
itemType, itemType,
ctx.OrgID, ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.UserID, ctx.UserID,
ctx.OrgID, ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.OrgID,
ctx.UserID, ctx.UserID,
keywords) keywords)

View file

@ -75,7 +75,7 @@ type UserStorer interface {
GetBySerial(ctx RequestContext, serial string) (u user.User, err error) GetBySerial(ctx RequestContext, serial string) (u user.User, err error)
GetActiveUsersForOrganization(ctx RequestContext) (u []user.User, err error) GetActiveUsersForOrganization(ctx RequestContext) (u []user.User, err error)
GetUsersForOrganization(ctx RequestContext) (u []user.User, err error) GetUsersForOrganization(ctx RequestContext) (u []user.User, err error)
GetSpaceUsers(ctx RequestContext, folderID string) (u []user.User, err error) GetSpaceUsers(ctx RequestContext, spaceID string) (u []user.User, err error)
GetVisibleUsers(ctx RequestContext) (u []user.User, err error) GetVisibleUsers(ctx RequestContext) (u []user.User, err error)
UpdateUser(ctx RequestContext, u user.User) (err error) UpdateUser(ctx RequestContext, u user.User) (err error)
UpdateUserPassword(ctx RequestContext, userID, salt, password string) (err error) UpdateUserPassword(ctx RequestContext, userID, salt, password string) (err error)

View file

@ -184,14 +184,16 @@ func (s Scope) GetUsersForOrganization(ctx domain.RequestContext) (u []user.User
} }
// GetSpaceUsers returns a slice containing all user records for given folder. // GetSpaceUsers returns a slice containing all user records for given folder.
func (s Scope) GetSpaceUsers(ctx domain.RequestContext, folderID string) (u []user.User, err error) { func (s Scope) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []user.User, err error) {
err = s.Runtime.Db.Select(&u, err = s.Runtime.Db.Select(&u, `
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised
FROM user u, account a FROM user u, account a
WHERE u.refid IN (SELECT userid from labelrole WHERE orgid=? AND labelid=?) WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN (
AND a.orgid=? AND u.refid = a.userid AND a.active=1 SELECT whoid from permission WHERE orgid=? AND who='user' AND scope='object' AND location='space' AND refid=? UNION ALL
ORDER BY u.firstname, u.lastname`, SELECT r.userid from rolemember r LEFT JOIN permission p ON p.whoid=r.roleid WHERE p.orgid=? AND p.who='role' AND p.scope='object' AND p.location='space' AND p.refid=?
ctx.OrgID, folderID, ctx.OrgID) )
ORDER BY u.firstname, u.lastname;
`, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, spaceID)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("get space users for org %s", ctx.OrgID)) err = errors.Wrap(err, fmt.Sprintf("get space users for org %s", ctx.OrgID))

View file

@ -33,7 +33,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
} }
this.set('meta', meta); this.set('meta', meta);
if (this.get('toEdit') === this.get('page.id') && this.get('isEditor')) { if (this.get('toEdit') === this.get('page.id') && this.get('permissions.documentEdit')) {
this.send('onEdit'); this.send('onEdit');
} }
}); });

View file

@ -72,6 +72,13 @@ export default Ember.Component.extend(TooltipMixin, {
return `move-dialog-${id}`; return `move-dialog-${id}`;
}), }),
hasMenuPermissions: computed('permissions', function() {
let permissions = this.get('permissions');
return permissions.get('documentDelete') || permissions.get('documentCopy') ||
permissions.get('documentMove') || permissions.get('documentTemplate');;
}),
didRender() { didRender() {
$("#" + this.get('blockTitleId')).removeClass('error'); $("#" + this.get('blockTitleId')).removeClass('error');
$("#" + this.get('blockExcerptId')).removeClass('error'); $("#" + this.get('blockExcerptId')).removeClass('error');

View file

@ -77,7 +77,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
let page = _.findWhere(toc, { id: pageId }); let page = _.findWhere(toc, { id: pageId });
let state = tocUtil.getState(toc, page); let state = tocUtil.getState(toc, page);
if (!this.get('isEditor') || is.empty(pageId)) { if (!this.get('permissions.documentEdit') || is.empty(pageId)) {
state.actionablePage = false; state.actionablePage = false;
state.upDisabled = state.downDisabled = state.indentDisabled = state.outdentDisabled = true; state.upDisabled = state.downDisabled = state.indentDisabled = state.outdentDisabled = true;
} }

View file

@ -73,6 +73,13 @@ export default Ember.Component.extend(TooltipMixin, {
return `move-dialog-${id}`; return `move-dialog-${id}`;
}), }),
hasMenuPermissions: computed('permissions', function() {
let permissions = this.get('permissions');
return permissions.get('documentDelete') || permissions.get('documentCopy') ||
permissions.get('documentMove') || permissions.get('documentTemplate');;
}),
didRender() { didRender() {
$("#" + this.get('blockTitleId')).removeClass('error'); $("#" + this.get('blockTitleId')).removeClass('error');
$("#" + this.get('blockExcerptId')).removeClass('error'); $("#" + this.get('blockExcerptId')).removeClass('error');

View file

@ -14,7 +14,6 @@ import Ember from 'ember';
export default Ember.Component.extend({ export default Ember.Component.extend({
documentTags: [], documentTags: [],
tagz: [], tagz: [],
isEditor: false,
newTag: "", newTag: "",
maxTags: 3, maxTags: 3,
canAdd: false, canAdd: false,

View file

@ -14,22 +14,36 @@ import NotifierMixin from '../../mixins/notifier';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
import AuthMixin from '../../mixins/auth'; import AuthMixin from '../../mixins/auth';
const {
inject: { service }
} = Ember;
export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, { export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, {
folderService: Ember.inject.service('folder'), folderService: service('folder'),
session: Ember.inject.service(), session: service(),
appMeta: Ember.inject.service(), appMeta: service(),
pinned: service(),
showToolbar: false, showToolbar: false,
folder: {}, folder: {},
busy: false, busy: false,
moveFolderId: "", moveFolderId: "",
drop: null, drop: null,
pinState : {
isPinned: false,
pinId: '',
newName: '',
},
didReceiveAttrs() { didReceiveAttrs() {
console.log(this.get('permissions'));
let targets = _.reject(this.get('folders'), { let targets = _.reject(this.get('folders'), {
id: this.get('folder').get('id') id: this.get('folder').get('id')
}); });
let folder = this.get('folder');
this.set('pinState.pinId', this.get('pinned').isSpacePinned(folder.get('id')));
this.set('pinState.isPinned', this.get('pinState.pinId') !== '');
this.set('pinState.newName', folder.get('name').substring(0,3).toUpperCase());
this.set('movedFolderOptions', targets); this.set('movedFolderOptions', targets);
}, },
@ -61,6 +75,35 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, AuthMixin, {
}, },
actions: { actions: {
onUnpin() {
this.get('pinned').unpinItem(this.get('pinState.pinId')).then(() => {
this.set('pinState.isPinned', false);
this.set('pinState.pinId', '');
this.eventBus.publish('pinChange');
});
},
onPin() {
let pin = {
pin: this.get('pinState.newName'),
documentId: '',
folderId: this.get('folder.id')
};
if (is.empty(pin.pin)) {
$('#pin-space-name').addClass('error').focus();
return false;
}
this.get('pinned').pinItem(pin).then((pin) => {
this.set('pinState.isPinned', true);
this.set('pinState.pinId', pin.get('id'));
this.eventBus.publish('pinChange');
});
return true;
},
deleteDocuments() { deleteDocuments() {
this.attrs.onDeleteDocument(); this.attrs.onDeleteDocument();
}, },

View file

@ -14,28 +14,7 @@ import TooltipMixin from '../../mixins/tooltip';
import NotifierMixin from '../../mixins/notifier'; import NotifierMixin from '../../mixins/notifier';
import AuthMixin from '../../mixins/auth'; import AuthMixin from '../../mixins/auth';
const {
inject: { service }
} = Ember;
export default Ember.Component.extend(TooltipMixin, NotifierMixin, AuthMixin, { export default Ember.Component.extend(TooltipMixin, NotifierMixin, AuthMixin, {
folderService: service('folder'),
templateService: service('template'),
appMeta: service(),
pinned: service(),
publicFolders: [],
protectedFolders: [],
privateFolders: [],
hasPublicFolders: false,
hasProtectedFolders: false,
hasPrivateFolders: false,
newFolder: "",
menuOpen: false,
pinState : {
isPinned: false,
pinId: '',
newName: '',
},
tab: '', tab: '',
init() { init() {
@ -46,15 +25,6 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, AuthMixin, {
} }
}, },
didReceiveAttrs() {
if (!this.get('noFolder')) {
let folder = this.get('folder');
this.set('pinState.pinId', this.get('pinned').isSpacePinned(folder.get('id')));
this.set('pinState.isPinned', this.get('pinState.pinId') !== '');
this.set('pinState.newName', folder.get('name').substring(0,3).toUpperCase());
}
},
actions: { actions: {
onAddSpace(m) { onAddSpace(m) {
this.attrs.onAddSpace(m); this.attrs.onAddSpace(m);
@ -64,38 +34,5 @@ export default Ember.Component.extend(TooltipMixin, NotifierMixin, AuthMixin, {
onChangeTab(tab) { onChangeTab(tab) {
this.set('tab', tab); this.set('tab', tab);
}, },
onMenuOpen() {
this.set('menuOpen', !this.get('menuOpen'));
},
onUnpin() {
this.get('pinned').unpinItem(this.get('pinState.pinId')).then(() => {
this.set('pinState.isPinned', false);
this.set('pinState.pinId', '');
this.eventBus.publish('pinChange');
});
},
onPin() {
let pin = {
pin: this.get('pinState.newName'),
documentId: '',
folderId: this.get('folder.id')
};
if (is.empty(pin.pin)) {
$('#pin-space-name').addClass('error').focus();
return false;
}
this.get('pinned').pinItem(pin).then((pin) => {
this.set('pinState.isPinned', true);
this.set('pinState.pinId', pin.get('id'));
this.eventBus.publish('pinChange');
});
return true;
},
} }
}); });

View file

@ -1,8 +1,6 @@
{{layout/zone-navigation}} {{layout/zone-navigation}}
{{#layout/zone-container}} {{#layout/zone-container}}
{{#layout/zone-sidebar}} {{#layout/zone-sidebar}}
<div class="sidebar-toolbar">
</div>
<div class="sidebar-common"> <div class="sidebar-common">
{{layout/sidebar-intro title='Settings' message='Documize application settings'}} {{layout/sidebar-intro title='Settings' message='Documize application settings'}}
</div> </div>

View file

@ -7,7 +7,7 @@
<i class="material-icons">arrow_back</i>&nbsp;{{model.document.name}} <i class="material-icons">arrow_back</i>&nbsp;{{model.document.name}}
{{/link-to}} {{/link-to}}
</div> </div>
{{document/document-heading document=model.document isEditor=false}} {{document/document-heading document=model.document}}
{{document/block-editor document=model.document folder=model.folder block=model.block onCancel=(action 'onCancel') onAction=(action 'onAction')}} {{document/block-editor document=model.document folder=model.folder block=model.block onCancel=(action 'onCancel') onAction=(action 'onAction')}}
</div> </div>
</div> </div>

View file

@ -7,7 +7,7 @@
<i class="material-icons">arrow_back</i>&nbsp;{{model.document.name}} <i class="material-icons">arrow_back</i>&nbsp;{{model.document.name}}
{{/link-to}} {{/link-to}}
</div> </div>
{{document/document-heading document=model.document isEditor=false}} {{document/document-heading document=model.document}}
{{#if hasRevisions}} {{#if hasRevisions}}
{{document/document-history document=model.document folder=model.folder pages=model.pages {{document/document-history document=model.document folder=model.folder pages=model.pages
revisions=model.revisions diff=model.diff onFetchDiff=(action 'onFetchDiff') onRollback=(action 'onRollback')}} revisions=model.revisions diff=model.diff onFetchDiff=(action 'onFetchDiff') onRollback=(action 'onRollback')}}

View file

@ -30,7 +30,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
pages: this.get('documentService').getPages(this.modelFor('document').document.get('id')), pages: this.get('documentService').getPages(this.modelFor('document').document.get('id')),
links: this.modelFor('document').links, links: this.modelFor('document').links,
sections: this.modelFor('document').sections, sections: this.modelFor('document').sections,
permissions: this.get('folderService').get('permissions') permissions: this.modelFor('document').permissions
}); });
} }
}); });

View file

@ -18,7 +18,6 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
return Ember.RSVP.hash({ return Ember.RSVP.hash({
folder: this.modelFor('folder').folder, folder: this.modelFor('folder').folder,
isEditor: this.modelFor('folder').isEditor,
permissions: this.modelFor('folder').permissions, permissions: this.modelFor('folder').permissions,
folders: this.modelFor('folder').folders, folders: this.modelFor('folder').folders,
documents: this.modelFor('folder').documents, documents: this.modelFor('folder').documents,

View file

@ -39,7 +39,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
folder: this.get('folder'), folder: this.get('folder'),
permissions: this.get('permissions'), permissions: this.get('permissions'),
folders: this.get('folderService').getAll(), folders: this.get('folderService').getAll(),
documents: this.get('documentService').getAllByFolder(params.folder_id), documents: this.get('documentService').getAllBySpace(params.folder_id),
templates: this.get('templateService').getSavedTemplates(params.folder_id) templates: this.get('templateService').getSavedTemplates(params.folder_id)
}); });
}, },

View file

@ -0,0 +1,15 @@
// 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 Ember from 'ember';
export default Ember.Controller.extend({
});

View file

@ -0,0 +1,25 @@
// 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 Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model() {
this.get('browser').setTitle(this.modelFor('folder').folder.get('name'));
return Ember.RSVP.hash({
folder: this.modelFor('folder').folder,
permissions: this.modelFor('folder').permissions,
folders: this.modelFor('folder').folders
});
}
});

View file

@ -0,0 +1 @@
{{folder/invite-user folders=model.folders folder=model.folder}}

View file

@ -13,6 +13,12 @@ import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin'; import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, { export default Ember.Route.extend(AuthenticatedRouteMixin, {
beforeModel: function (transition) {
if (is.equal(transition.targetName, 'folder.settings.index')) {
this.transitionTo('folder.settings.security');
}
},
model() { model() {
this.get('browser').setTitle(this.modelFor('folder').folder.get('name')); this.get('browser').setTitle(this.modelFor('folder').folder.get('name'));

View file

@ -0,0 +1,15 @@
// 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 Ember from 'ember';
export default Ember.Controller.extend({
});

View file

@ -0,0 +1,25 @@
// 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 Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
export default Ember.Route.extend(AuthenticatedRouteMixin, {
model() {
this.get('browser').setTitle(this.modelFor('folder').folder.get('name'));
return Ember.RSVP.hash({
folder: this.modelFor('folder').folder,
permissions: this.modelFor('folder').permissions,
folders: this.modelFor('folder').folders
});
}
});

View file

@ -0,0 +1 @@
{{folder/permission-admin folders=model.folders folder=model.folder}}

View file

@ -1,36 +1,33 @@
{{#layout/zone-container}} {{#layout/zone-container}}
{{#layout/zone-sidebar}} {{#layout/zone-sidebar}}
<div class="sidebar-toolbar"></div>
<div class="sidebar-common"> <div class="sidebar-common">
{{layout/sidebar-intro title="Space Settings" message="Invite users and configure space permissions. Set up categories to sub-divide the space."}} {{layout/sidebar-intro title="Space Settings" message="Invite users, configure space permissions, and set up categories to sub-divide the space."}}
</div> </div>
<div class="sidebar-wrapper"> <div class="sidebar-wrapper">
<div class="sidebar-menu"> <div class="sidebar-menu">
<ul class="options"> <ul class="options">
<div class="option selected">Users</div> {{#if isAuthProviderDocumize}}
<div class="option ">Categories</div> {{#link-to 'folder.settings.invitation' activeClass='selected' class="option" tagName="li"}}Invite{{/link-to}}
{{/if}}
{{#link-to 'folder.settings.security' activeClass='selected' class="option" tagName="li"}}Secure{{/link-to}}
{{#link-to 'folder.settings.category' activeClass='selected' class="option" tagName="li"}}Organize{{/link-to}}
</ul> </ul>
</div> </div>
</div> </div>
{{/layout/zone-sidebar}} {{/layout/zone-sidebar}}
{{#layout/zone-content}} {{#layout/zone-content}}
<div class="folder-heading"> <div class="back-to-space">
<h1 class="folder-title">{{model.folder.name}}</h1> {{#link-to 'folder' model.folder.id model.folder.slug}}
<div class="regular-button button-gray">
<i class="material-icons">arrow_back</i>
<div class="name">{{model.folder.name}}</div>
</div> </div>
{{#link-to 'folder' model.folder.id model.folder.slug class="vertical-top"}}
<i class="material-icons">arrow_back</i>&nbsp;back to space
{{/link-to}} {{/link-to}}
</div>
<div class="margin-top-30" /> <div class="margin-top-30" />
{{#if isAuthProviderDocumize}} {{outlet}}
{{folder/invite-user folders=model.folders folder=model.folder}}
<div class="margin-top-50" />
{{/if}}
{{folder/permission-admin folders=model.folders folder=model.folder}}
<div class="margin-top-50" /> <div class="margin-top-50" />
{{/layout/zone-content}} {{/layout/zone-content}}
{{/layout/zone-container}} {{/layout/zone-container}}

View file

@ -1,8 +1,7 @@
{{layout/zone-navigation}} {{layout/zone-navigation}}
{{#layout/zone-container}} {{#layout/zone-container}}
{{#layout/zone-sidebar}} {{#layout/zone-sidebar}}
{{folder/sidebar-zone folders=model noFolder=true isFolderOwner=false isEditor=false {{folder/sidebar-zone folders=model noFolder=true onAddSpace=(action 'onAddSpace')}}
onAddSpace=(action 'onAddSpace')}}
{{/layout/zone-sidebar}} {{/layout/zone-sidebar}}
{{#layout/zone-content}} {{#layout/zone-content}}
{{/layout/zone-content}} {{/layout/zone-content}}

View file

@ -1,8 +1,6 @@
{{layout/zone-navigation}} {{layout/zone-navigation}}
{{#layout/zone-container}} {{#layout/zone-container}}
{{#layout/zone-sidebar}} {{#layout/zone-sidebar}}
<div class="sidebar-toolbar">
</div>
<div class="sidebar-common"> <div class="sidebar-common">
{{layout/sidebar-intro title="Profile" message="Your user profile"}} {{layout/sidebar-intro title="Profile" message="Your user profile"}}
</div> </div>

View file

@ -2,8 +2,6 @@
{{#layout/zone-container}} {{#layout/zone-container}}
{{#layout/zone-sidebar}} {{#layout/zone-sidebar}}
<div class="sidebar-toolbar">
</div>
<div class="sidebar-common"> <div class="sidebar-common">
{{layout/sidebar-intro title="Search" message='Search across document name, contents, tags and attachment filenames'}} {{layout/sidebar-intro title="Search" message='Search across document name, contents, tags and attachment filenames'}}
</div> </div>

View file

@ -26,7 +26,17 @@ export default Router.map(function () {
}, function() { }, function() {
this.route('settings', { this.route('settings', {
path: 'settings' path: 'settings'
}, function () {
this.route('security', {
path: 'security'
}); });
this.route('invitation', {
path: 'invitation'
});
this.route('category', {
path: 'category'
});
})
}); });
this.route('document', { this.route('document', {

View file

@ -33,9 +33,9 @@ export default Ember.Service.extend({
}); });
}, },
// Returns all documents for specified folder. // Returns all documents for specified space.
getAllByFolder(folderId) { getAllBySpace(spaceId) {
return this.get('ajax').request(`documents?folder=${folderId}`, { return this.get('ajax').request(`documents?space=${spaceId}`, {
method: "GET" method: "GET"
}).then((response) => { }).then((response) => {
let documents = Ember.ArrayProxy.create({ let documents = Ember.ArrayProxy.create({

View file

@ -1,411 +0,0 @@
**********************************
**********************************
***** document
**********************************
**********************************
{{layout/zone-navigation}}
{{#layout/zone-sidebar}}
{{document/document-sidebar document=model.document folder=model.folder pages=model.pages page=model.page isEditor=model.isEditor sections=model.sections
onAddSection=(action 'onAddSection') onInsertBlock=(action 'onInsertBlock') onDeleteBlock=(action 'onDeleteBlock') changePageSequence=(action 'onPageSequenceChange') changePageLevel=(action 'onPageLevelChange') gotoPage=(action 'gotoPage')}}
{{/layout/zone-sidebar}}
{{#layout/zone-content}}
{{document/document-toolbar document=model.document pages=model.pages tabs=model.tabs folder=model.folder isEditor=model.isEditor
onSaveTemplate=(action 'onSaveTemplate') onSaveMeta=(action 'onSaveMeta') onDocumentDelete=(action 'onDocumentDelete')}}
{{outlet}}
{{/layout/zone-content}}
// 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 Ember from 'ember';
import NotifierMixin from '../../mixins/notifier';
export default Ember.Controller.extend(NotifierMixin, {
documentService: Ember.inject.service('document'),
templateService: Ember.inject.service('template'),
sectionService: Ember.inject.service('section'),
page: null,
folder: {},
pages: [],
// Jump to the right part of the document.
scrollToPage(pageId) {
Ember.run.schedule('afterRender', function () {
let dest;
let target = "#page-title-" + pageId;
let targetOffset = $(target).offset();
if (is.undefined(targetOffset)) {
return;
}
dest = targetOffset.top > $(document).height() - $(window).height() ? $(document).height() - $(window).height() : targetOffset.top;
// small correction to ensure we also show page title
dest = dest > 50 ? dest - 74 : dest;
$("html,body").animate({
scrollTop: dest
}, 500, "linear");
$(".toc-index-item").removeClass("selected");
$("#index-" + pageId).addClass("selected");
});
},
actions: {
gotoPage(pageId) {
if (is.null(pageId)) {
return;
}
this.scrollToPage(pageId);
},
onPageSequenceChange(changes) {
this.get('documentService').changePageSequence(this.get('model.document.id'), changes).then(() => {
_.each(changes, (change) => {
let pageContent = _.findWhere(this.get('model.pages'), {
id: change.pageId
});
if (is.not.undefined(pageContent)) {
pageContent.set('sequence', change.sequence);
}
});
this.set('model.pages', this.get('model.pages').sortBy('sequence'));
this.get('target.router').refresh();
});
},
onPageLevelChange(changes) {
this.get('documentService').changePageLevel(this.get('model.document.id'), changes).then(() => {
_.each(changes, (change) => {
let pageContent = _.findWhere(this.get('model.pages'), {
id: change.pageId
});
if (is.not.undefined(pageContent)) {
pageContent.set('level', change.level);
}
});
let pages = this.get('model.pages');
pages = pages.sortBy('sequence');
this.set('model.pages', []);
this.set('model.pages', pages);
this.get('target.router').refresh();
});
},
onSaveTemplate(name, desc) {
this.get('templateService').saveAsTemplate(this.get('model.document.id'), name, desc).then(function () {});
},
onSaveMeta(doc) {
this.get('documentService').save(doc).then(() => {
this.transitionToRoute('document.index');
});
},
onAddSection(section) {
this.audit.record("added-section-" + section.get('contentType'));
let page = {
documentId: this.get('model.document.id'),
title: `${section.get('title')}`,
level: 1,
sequence: 0,
body: "",
contentType: section.get('contentType'),
pageType: section.get('pageType')
};
let meta = {
documentId: this.get('model.document.id'),
rawBody: "",
config: ""
};
let model = {
page: page,
meta: meta
};
this.get('documentService').addPage(this.get('model.document.id'), model).then((newPage) => {
let data = this.get('store').normalize('page', newPage);
this.get('store').push(data);
this.get('documentService').getPages(this.get('model.document.id')).then((pages) => {
this.set('model.pages', pages.filterBy('pageType', 'section'));
this.set('model.tabs', pages.filterBy('pageType', 'tab'));
this.get('documentService').getPageMeta(this.get('model.document.id'), newPage.id).then(() => {
this.transitionToRoute('document.edit',
this.get('model.folder.id'),
this.get('model.folder.slug'),
this.get('model.document.id'),
this.get('model.document.slug'),
newPage.id);
});
});
});
},
onInsertBlock(block) {
this.audit.record("added-content-block-" + block.get('contentType'));
let page = {
documentId: this.get('model.document.id'),
title: `${block.get('title')}`,
level: 1,
sequence: 0,
body: block.get('body'),
contentType: block.get('contentType'),
pageType: block.get('pageType'),
blockId: block.get('id')
};
let meta = {
documentId: this.get('model.document.id'),
rawBody: block.get('rawBody'),
config: block.get('config'),
externalSource: block.get('externalSource')
};
let model = {
page: page,
meta: meta
};
this.get('documentService').addPage(this.get('model.document.id'), model).then((newPage) => {
let data = this.get('store').normalize('page', newPage);
this.get('store').push(data);
this.get('documentService').getPages(this.get('model.document.id')).then((pages) => {
this.set('model.pages', pages.filterBy('pageType', 'section'));
this.set('model.tabs', pages.filterBy('pageType', 'tab'));
this.get('documentService').getPageMeta(this.get('model.document.id'), newPage.id).then(() => {
this.transitionToRoute('document.edit',
this.get('model.folder.id'),
this.get('model.folder.slug'),
this.get('model.document.id'),
this.get('model.document.slug'),
newPage.id);
});
});
});
},
onDeleteBlock(blockId) {
this.get('sectionService').deleteBlock(blockId).then(() => {
this.audit.record("deleted-block");
this.send("showNotification", "Deleted");
this.transitionToRoute('document.index');
});
},
onDocumentDelete() {
this.get('documentService').deleteDocument(this.get('model.document.id')).then(() => {
this.audit.record("deleted-page");
this.send("showNotification", "Deleted");
this.transitionToRoute('folder', this.get('model.folder.id'), this.get('model.folder.slug'));
});
}
}
});
**********************************
**********************************
***** document/index
**********************************
**********************************
{{document/document-view document=model.document links=model.links allPages=model.allPages tabs=model.tabs pages=model.pages folder=model.folder folders=model.folders isEditor=model.isEditor gotoPage=(action 'gotoPage') onAddBlock=(action 'onAddBlock') onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onPageDeleted')}}
// 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 Ember from 'ember';
import NotifierMixin from '../../../mixins/notifier';
export default Ember.Controller.extend(NotifierMixin, {
documentService: Ember.inject.service('document'),
sectionService: Ember.inject.service('section'),
queryParams: ['page'],
// Jump to the right part of the document.
scrollToPage(pageId) {
Ember.run.schedule('afterRender', function () {
let dest;
let target = "#page-title-" + pageId;
let targetOffset = $(target).offset();
if (is.undefined(targetOffset)) {
return;
}
dest = targetOffset.top > $(document).height() - $(window).height() ? $(document).height() - $(window).height() : targetOffset.top;
// small correction to ensure we also show page title
dest = dest > 50 ? dest - 74 : dest;
$("html,body").animate({
scrollTop: dest
}, 500, "linear");
$(".toc-index-item").removeClass("selected");
$("#index-" + pageId).addClass("selected");
});
},
actions: {
gotoPage(pageId) {
if (is.null(pageId)) {
return;
}
this.scrollToPage(pageId);
},
onPageSequenceChange(changes) {
this.get('documentService').changePageSequence(this.get('model.document.id'), changes).then(() => {
_.each(changes, (change) => {
let pageContent = _.findWhere(this.get('model.pages'), {
id: change.pageId
});
if (is.not.undefined(pageContent)) {
pageContent.set('sequence', change.sequence);
}
});
this.set('model.pages', this.get('model.pages').sortBy('sequence'));
this.get('target.router').refresh();
});
},
onPageLevelChange(changes) {
this.get('documentService').changePageLevel(this.get('model.document.id'), changes).then(() => {
_.each(changes, (change) => {
let pageContent = _.findWhere(this.get('model.pages'), {
id: change.pageId
});
if (is.not.undefined(pageContent)) {
pageContent.set('level', change.level);
}
});
let pages = this.get('model.pages');
pages = pages.sortBy('sequence');
this.set('model.pages', pages);
this.get('target.router').refresh();
});
},
onAddBlock(block) {
this.get('sectionService').addBlock(block).then(() => {
this.showNotification("Published");
});
},
onCopyPage(pageId, targetDocumentId) {
let documentId = this.get('model.document.id');
this.get('documentService').copyPage(documentId, pageId, targetDocumentId).then(() => {
this.showNotification("Copied");
// refresh data if copied to same document
if (documentId === targetDocumentId) {
this.get('target.router').refresh();
}
});
},
onMovePage(pageId, targetDocumentId) {
let documentId = this.get('model.document.id');
this.get('documentService').copyPage(documentId, pageId, targetDocumentId).then(() => {
this.showNotification("Moved");
this.send('onPageDeleted', { id: pageId, children: false });
});
},
onPageDeleted(deletePage) {
let documentId = this.get('model.document.id');
let pages = this.get('model.pages');
let deleteId = deletePage.id;
let deleteChildren = deletePage.children;
let page = _.findWhere(pages, {
id: deleteId
});
let pageIndex = _.indexOf(pages, page, false);
let pendingChanges = [];
this.audit.record("deleted-page");
// select affected pages
for (var i = pageIndex + 1; i < pages.get('length'); i++) {
if (pages[i].get('level') <= page.get('level')) {
break;
}
pendingChanges.push({
pageId: pages[i].get('id'),
level: pages[i].get('level') - 1
});
}
if (deleteChildren) {
// nuke of page tree
pendingChanges.push({
pageId: deleteId
});
this.get('documentService').deletePages(documentId, deleteId, pendingChanges).then(() => {
// update our models so we don't have to reload from db
for (var i = 0; i < pendingChanges.length; i++) {
let pageId = pendingChanges[i].pageId;
this.set('model.pages', _.reject(pages, function (p) { //jshint ignore: line
return p.get('id') === pageId;
}));
}
this.set('model.pages', _.sortBy(pages, "sequence"));
this.get('target.router').refresh();
});
} else {
// page delete followed by re-leveling child pages
this.get('documentService').deletePage(documentId, deleteId).then(() => {
this.set('model.pages', _.reject(pages, function (p) {
return p.get('id') === deleteId;
}));
this.send('onPageLevelChange', pendingChanges);
});
}
}
}
});

View file

@ -107,7 +107,6 @@ video.responsive-video {
html { html {
overflow-y: scroll; overflow-y: scroll;
font-family: $font-regular; font-family: $font-regular;
background-color: $color-off-white;
font-size: 14px; font-size: 14px;
height: 100%; height: 100%;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;

View file

@ -13,3 +13,16 @@
color: $color-gray; color: $color-gray;
} }
} }
.back-to-space {
margin: 10px 0;
> a {
> .regular-button {
> .name {
// max-width: 150px;
// @extend .truncate;
}
}
}
}

View file

@ -1,17 +1,4 @@
.zone-document-content { .zone-document-content {
> .back-to-space {
margin: 10px 0;
> a {
> .regular-button {
> .name {
// max-width: 150px;
// @extend .truncate;
}
}
}
}
.doc-title { .doc-title {
font-size: 2rem; font-size: 2rem;
margin: 30px 0 10px; margin: 30px 0 10px;

View file

@ -14,16 +14,13 @@
padding: 8px 0; padding: 8px 0;
> .permission-name-cell { > .permission-name-cell {
padding: 10px 5px;
font-size: 1.1rem; font-size: 1.1rem;
background-color: $color-off-white;
padding: 10px 10px;
} }
> .permission-roles-cell { > .permission-roles-cell {
background-color: $color-off-white; margin: 15px 0 20px 40px;
margin-top: 10px;
margin-left: 40px;
padding: 10px 10px;
display: inline-block;
> .role-category { > .role-category {
color: $color-gray; color: $color-gray;

View file

@ -10,6 +10,7 @@ $sidebar-width: 400px;
} }
#sidebar-wrapper { #sidebar-wrapper {
background-color: $color-off-white;
z-index: 888; z-index: 888;
position: fixed; position: fixed;
overflow-x: hidden; overflow-x: hidden;
@ -105,7 +106,7 @@ $sidebar-width: 400px;
display: inline-block; display: inline-block;
width: 340px; width: 340px;
padding: 40px 20px 0px 20px; padding: 40px 20px 0px 20px;
margin-left: 60px; margin-left: 20px;
> .pinner { > .pinner {
cursor: pointer; cursor: pointer;
@ -138,7 +139,7 @@ $sidebar-width: 400px;
.sidebar-wrapper { .sidebar-wrapper {
padding: 40px 20px 40px 20px; padding: 40px 20px 40px 20px;
margin-left: 60px; margin-left: 20px;
.sidebar-panel { .sidebar-panel {
width: 300px; width: 300px;

View file

@ -2,7 +2,7 @@
{{#if editMode}} {{#if editMode}}
{{document/document-editor document=document folder=folder page=page meta=meta onCancel=(action 'onCancelEdit') onAction=(action 'onSavePage')}} {{document/document-editor document=document folder=folder page=page meta=meta onCancel=(action 'onCancelEdit') onAction=(action 'onSavePage')}}
{{else}} {{else}}
{{document/page-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor tabMode=tabMode {{document/page-heading tagName=page.tagName document=document folder=folder page=page permissions=permissions tabMode=tabMode
onEdit=(action 'onEdit') onSavePageAsBlock=(action 'onSavePageAsBlock') onEdit=(action 'onEdit') onSavePageAsBlock=(action 'onSavePageAsBlock')
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}} onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
{{section/base-renderer page=page}} {{section/base-renderer page=page}}

View file

@ -1,5 +1,5 @@
<div id="page-{{ page.id }}" class="is-a-tab wysiwyg non-printable {{if expanded 'tab-max' 'tab-min'}}" data-id="{{ page.id }}" data-type="{{ page.contentType }}"> <div id="page-{{ page.id }}" class="is-a-tab wysiwyg non-printable {{if expanded 'tab-max' 'tab-min'}}" data-id="{{ page.id }}" data-type="{{ page.contentType }}">
{{document/tab-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor {{document/tab-heading tagName=page.tagName document=document folder=folder page=page permissions=permissions
onExpand=(action 'onExpand') onSavePageAsBlock=(action 'onSavePageAsBlock') onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}} onExpand=(action 'onExpand') onSavePageAsBlock=(action 'onSavePageAsBlock') onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
{{#if expanded}} {{#if expanded}}
{{section/base-renderer page=page}} {{section/base-renderer page=page}}

View file

@ -2,7 +2,7 @@
{{#if hasPages}} {{#if hasPages}}
{{#each pages key="id" as |page index|}} {{#each pages key="id" as |page index|}}
{{#if isEditor}} {{#if permissions.documentEdit}}
<div class="start-section" data-index={{index}} data-before-id={{page.id}} id="add-section-button-{{page.id}}" {{action 'onShowSectionWizard' page}}> <div class="start-section" data-index={{index}} data-before-id={{page.id}} id="add-section-button-{{page.id}}" {{action 'onShowSectionWizard' page}}>
<div class="start-button"> <div class="start-button">
<div class="round-button round-button-small button-green"> <div class="round-button round-button-small button-green">
@ -15,19 +15,19 @@
<div class="section-divider" /> <div class="section-divider" />
{{/if}} {{/if}}
{{#if (is-equal page.pageType 'section')}} {{#if (is-equal page.pageType 'section')}}
{{#document/document-page document=document folder=folder page=page isEditor=isEditor toEdit=toEdit pageId=pageId {{#document/document-page document=document folder=folder page=page permissions=permissions toEdit=toEdit pageId=pageId
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock') onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}} onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
{{/document/document-page}} {{/document/document-page}}
{{/if}} {{/if}}
{{#if (is-equal page.pageType 'tab')}} {{#if (is-equal page.pageType 'tab')}}
{{#document/document-tab document=document folder=folder page=page isEditor=isEditor pageId=pageId {{#document/document-tab document=document folder=folder page=page permissions=permissions pageId=pageId
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock') onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}} onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
{{/document/document-tab}} {{/document/document-tab}}
{{/if}} {{/if}}
{{/each}} {{/each}}
{{#if isEditor}} {{#if permissions.documentEdit}}
<div class="start-section" data-index="0" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}> <div class="start-section" data-index="0" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}>
<div class="start-button"> <div class="start-button">
<div class="round-button round-button-small button-green"> <div class="round-button round-button-small button-green">
@ -38,7 +38,7 @@
</div> </div>
{{/if}} {{/if}}
{{else}} {{else}}
{{#if isEditor}} {{#if permissions.documentEdit}}
<div class="start-section start-section-empty-state" data-index="-1" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}> <div class="start-section start-section-empty-state" data-index="-1" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}>
<div class="start-button"> <div class="start-button">
<div class="round-button round-button-small button-green"> <div class="round-button round-button-small button-green">

View file

@ -1,25 +1,34 @@
<div class="page-title"> <div class="page-title">
<span id="page-title-{{ page.id }}">{{ page.title }}</span> <span id="page-title-{{ page.id }}">{{ page.title }}</span>
<div id="page-toolbar-{{ page.id }}" class="pull-right page-toolbar hidden-xs hidden-sm"> <div id="page-toolbar-{{ page.id }}" class="pull-right page-toolbar hidden-xs hidden-sm">
{{#if isEditor}} {{#if permissions.documentEdit}}
<div class="round-button-mono" {{action 'onEdit'}}> <div class="round-button-mono" {{action 'onEdit'}}>
<i class="material-icons color-gray">mode_edit</i> <i class="material-icons color-gray">mode_edit</i>
</div> </div>
{{#if hasMenuPermissions}}
<div class="round-button-mono" id="page-menu-{{page.id}}"> <div class="round-button-mono" id="page-menu-{{page.id}}">
<i class="material-icons color-gray">more_vert</i> <i class="material-icons color-gray">more_vert</i>
</div> </div>
{{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}} {{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
<ul class="menu"> <ul class="menu">
{{#if permissions.documentCopy}}
<li class="item" id={{copyButtonId}}>Copy</li> <li class="item" id={{copyButtonId}}>Copy</li>
{{/if}}
{{#if permissions.documentMove}}
<li class="item" id={{moveButtonId}}>Move</li> <li class="item" id={{moveButtonId}}>Move</li>
{{/if}}
{{#if permissions.documentTemplate}}
<li class="item" id={{publishButtonId}}>Publish</li> <li class="item" id={{publishButtonId}}>Publish</li>
<li class="divider"></li> {{/if}}
{{#if permissions.documentDelete}}
<li class="item danger" id={{deleteButtonId}}>Delete</li> <li class="item danger" id={{deleteButtonId}}>Delete</li>
{{/if}}
</ul> </ul>
{{/dropdown-menu}} {{/dropdown-menu}}
{{/if}}
{{#if menuOpen}} {{#if menuOpen}}
{{#if permissions.documentDelete}}
{{#dropdown-dialog target=deleteButtonId position="bottom right" button="Delete" color="flat-red" onAction=(action 'deletePage')}} {{#dropdown-dialog target=deleteButtonId position="bottom right" button="Delete" color="flat-red" onAction=(action 'deletePage')}}
<p>Are you sure you want to delete <span class="bold">{{page.title}}?</span></p> <p>Are you sure you want to delete <span class="bold">{{page.title}}?</span></p>
<p> <p>
@ -27,6 +36,8 @@
<label for="{{checkId}}">&nbsp;Delete child sections</label> <label for="{{checkId}}">&nbsp;Delete child sections</label>
</p> </p>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentTemplate}}
{{#dropdown-dialog id=publishDialogId target=publishButtonId position="bottom right" button="Publish" color="flat-green" focusOn=blockTitleId onAction=(action 'onSavePageAsBlock')}} {{#dropdown-dialog id=publishDialogId target=publishButtonId position="bottom right" button="Publish" color="flat-green" focusOn=blockTitleId onAction=(action 'onSavePageAsBlock')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -44,6 +55,8 @@
{{textarea rows="3" value=blockExcerpt id=blockExcerptId}} {{textarea rows="3" value=blockExcerpt id=blockExcerptId}}
</div> </div>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentCopy}}
{{#dropdown-dialog id=copyDialogId target=copyButtonId position="bottom right" button="Copy" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onCopyPage')}} {{#dropdown-dialog id=copyDialogId target=copyButtonId position="bottom right" button="Copy" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onCopyPage')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -56,6 +69,8 @@
{{ui-select cssClass="dropdown-page-toolbar" content=documentList action=(action 'onTargetChange') optionValuePath="id" optionLabelPath="name" selection=document}} {{ui-select cssClass="dropdown-page-toolbar" content=documentList action=(action 'onTargetChange') optionValuePath="id" optionLabelPath="name" selection=document}}
</div> </div>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentMove}}
{{#dropdown-dialog id=moveDialogId target=moveButtonId position="bottom right" button="Move" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onMovePage')}} {{#dropdown-dialog id=moveDialogId target=moveButtonId position="bottom right" button="Move" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onMovePage')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -70,5 +85,6 @@
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}} {{/if}}
{{/if}} {{/if}}
{{/if}}
</div> </div>
</div> </div>

View file

@ -1,7 +1,7 @@
<div class="sidebar-panel"> <div class="sidebar-panel">
<div class="title">Attachments</div> <div class="title">Attachments</div>
<div class="document-sidebar-view-attachments"> <div class="document-sidebar-view-attachments">
{{#if isEditor}} {{#if permissions.documentEdit}}
<div id="upload-document-files" class="upload-document-files"> <div id="upload-document-files" class="upload-document-files">
Drag-drop files or click to select files Drag-drop files or click to select files
</div> </div>

View file

@ -9,30 +9,39 @@
<i class="material-icons color-gray">expand_more</i> <i class="material-icons color-gray">expand_more</i>
</div> </div>
{{else}} {{else}}
{{#if isEditor}} {{#if permissions.documentEdit}}
{{#link-to 'document.section' page.id}} {{#link-to 'document.section' page.id}}
<div class="round-button-mono"> <div class="round-button-mono">
<i class="material-icons color-gray">mode_edit</i> <i class="material-icons color-gray">mode_edit</i>
</div> </div>
{{/link-to}} {{/link-to}}
{{#if hasMenuPermissions}}
<div class="round-button-mono" id="page-menu-{{page.id}}"> <div class="round-button-mono" id="page-menu-{{page.id}}">
<i class="material-icons color-gray">more_vert</i> <i class="material-icons color-gray">more_vert</i>
</div> </div>
{{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
<ul class="menu">
{{#if permissions.documentCopy}}
<li class="item" id={{copyButtonId}}>Copy</li>
{{/if}}
{{#if permissions.documentMove}}
<li class="item" id={{moveButtonId}}>Move</li>
{{/if}}
{{#if permissions.documentTemplate}}
<li class="item" id={{publishButtonId}}>Publish</li>
{{/if}}
{{#if permissions.documentDelete}}
<li class="item danger" id={{deleteButtonId}}>Delete</li>
{{/if}}
</ul>
{{/dropdown-menu}}
{{/if}}
<div class="round-button-mono" {{action 'toggleExpand'}}> <div class="round-button-mono" {{action 'toggleExpand'}}>
<i class="material-icons color-gray">expand_less</i> <i class="material-icons color-gray">expand_less</i>
</div> </div>
{{#dropdown-menu target=menuTarget position="top right" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
<ul class="menu">
<li class="item" id={{copyButtonId}}>Copy</li>
<li class="item" id={{moveButtonId}}>Move</li>
<li class="item" id={{publishButtonId}}>Publish</li>
<li class="divider"></li>
<li class="item danger" id={{deleteButtonId}}>Delete</li>
</ul>
{{/dropdown-menu}}
{{#if menuOpen}} {{#if menuOpen}}
{{#if permissions.documentDelete}}
{{#dropdown-dialog target=deleteButtonId position="bottom right" button="Delete" color="flat-red" onAction=(action 'deletePage')}} {{#dropdown-dialog target=deleteButtonId position="bottom right" button="Delete" color="flat-red" onAction=(action 'deletePage')}}
<p>Are you sure you want to delete <span class="bold">{{page.title}}?</span></p> <p>Are you sure you want to delete <span class="bold">{{page.title}}?</span></p>
<p> <p>
@ -40,6 +49,8 @@
<label for="{{checkId}}">&nbsp;Delete child sections</label> <label for="{{checkId}}">&nbsp;Delete child sections</label>
</p> </p>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentTemplate}}
{{#dropdown-dialog id=publishDialogId target=publishButtonId position="bottom right" button="Publish" color="flat-green" focusOn=blockTitleId onAction=(action 'onSavePageAsBlock')}} {{#dropdown-dialog id=publishDialogId target=publishButtonId position="bottom right" button="Publish" color="flat-green" focusOn=blockTitleId onAction=(action 'onSavePageAsBlock')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -57,6 +68,8 @@
{{textarea rows="3" value=blockExcerpt id=blockExcerptId}} {{textarea rows="3" value=blockExcerpt id=blockExcerptId}}
</div> </div>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentCopy}}
{{#dropdown-dialog id=copyDialogId target=copyButtonId position="bottom right" button="Copy" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onCopyPage')}} {{#dropdown-dialog id=copyDialogId target=copyButtonId position="bottom right" button="Copy" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onCopyPage')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -69,6 +82,8 @@
{{ui-select cssClass="dropdown-page-toolbar" content=documentList action=(action 'onTargetChange') optionValuePath="id" optionLabelPath="name" selection=document}} {{ui-select cssClass="dropdown-page-toolbar" content=documentList action=(action 'onTargetChange') optionValuePath="id" optionLabelPath="name" selection=document}}
</div> </div>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}}
{{#if permissions.documentMove}}
{{#dropdown-dialog id=moveDialogId target=moveButtonId position="bottom right" button="Move" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onMovePage')}} {{#dropdown-dialog id=moveDialogId target=moveButtonId position="bottom right" button="Move" color="flat-green" onOpenCallback=(action 'onCopyDialogOpen') onAction=(action 'onMovePage')}}
<div class="form-header"> <div class="form-header">
<div class="tip"> <div class="tip">
@ -82,6 +97,7 @@
</div> </div>
{{/dropdown-dialog}} {{/dropdown-dialog}}
{{/if}} {{/if}}
{{/if}}
{{else}} {{else}}
<div class="round-button-mono" {{action 'toggleExpand'}}> <div class="round-button-mono" {{action 'toggleExpand'}}>
<i class="material-icons color-gray">expand_less</i> <i class="material-icons color-gray">expand_less</i>

View file

@ -2,7 +2,7 @@
{{#each tagz as |tg|}} {{#each tagz as |tg|}}
<div class="chip"> <div class="chip">
<span class="chip-text">#{{tg}}</span> <span class="chip-text">#{{tg}}</span>
{{#if isEditor}} {{#if permissions.documentEdit}}
<i class="material-icons pull-right" {{action 'removeTag' tg}}>close</i> <i class="material-icons pull-right" {{action 'removeTag' tg}}>close</i>
{{/if}} {{/if}}
</div> </div>

View file

@ -37,7 +37,7 @@
{{#if permissions.spaceManage}} {{#if permissions.spaceManage}}
{{#link-to 'folder.settings' folder.id folder.slug}}{{model.document.name}} {{#link-to 'folder.settings' folder.id folder.slug}}{{model.document.name}}
<div class="round-button button-gray" id="space-settings-button" data-tooltip="Manage permissions" data-tooltip-position="top center"> <div class="round-button button-blue" id="space-settings-button" data-tooltip="Manage permissions" data-tooltip-position="top center">
<i class="material-icons">settings</i> <i class="material-icons">settings</i>
</div> </div>
{{/link-to}} {{/link-to}}
@ -45,11 +45,30 @@
{{#if permissions.spaceOwner}} {{#if permissions.spaceOwner}}
<div class="button-gap"></div> <div class="button-gap"></div>
<div class="round-button button-gray" id="space-delete-button" data-tooltip="Delete everything" data-tooltip-position="top center"> <div class="round-button button-red" id="space-delete-button" data-tooltip="Delete everything" data-tooltip-position="top center">
<i class="material-icons">delete</i> <i class="material-icons">delete</i>
</div> </div>
{{/if}} {{/if}}
{{#if pinState.isPinned}}
<div class="button-gap"></div>
<div class="round-button button-gray" id="space-unpin-button" data-tooltip="Pin space" data-tooltip-position="top center" {{action 'onUnpin'}}>
<i class="material-icons">favorite</i>
</div>
{{else}}
<div class="button-gap"></div>
<div class="round-button button-gray" id="space-pin-button" data-tooltip="Pin space" data-tooltip-position="top center">
<i class="material-icons">favorite_border</i>
</div>
{{#dropdown-dialog target="space-pin-button" position="bottom right" button="Pin" color="flat-green" onAction=(action 'onPin') focusOn="pin-space-name" }}
<div class="input-control">
<label>Pin Space</label>
<div class="tip">A 3 or 4 character name</div>
{{input type='text' id="pin-space-name" value=pinState.newName}}
</div>
{{/dropdown-dialog}}
{{/if}}
{{/if}} {{/if}}
</div> </div>

View file

@ -1,53 +1,3 @@
<div class="sidebar-toolbar">
{{#unless noFolder}}
<div class="margin-top-20"></div>
<div class="round-button-mono {{if (is-equal tab 'index') 'selected'}}" {{action 'onChangeTab' 'index'}}>
<i class="material-icons">view_headline</i>
</div>
<div class="margin-top-20"></div>
{{#if session.authenticated}}
{{#if permissions.spaceManage}}
{{#if isAuthProviderDocumize}}
<div class="round-button-mono {{if (is-equal tab 'share') 'selected'}}" {{action 'onChangeTab' 'share'}}>
<i class="material-icons">person_add</i>
</div>
<div class="margin-top-20"></div>
{{/if}}
<div class="round-button-mono {{if (is-equal tab 'permissions') 'selected'}}" {{action 'onChangeTab' 'permissions'}}>
<i class="material-icons">group</i>
</div>
<div class="margin-top-20"></div>
{{/if}}
<div class="round-button-mono" id="space-more-button">
<i class="material-icons">more_horiz</i>
</div>
{{#dropdown-menu target="space-more-button" position="bottom left" open="click" onOpenCallback=(action 'onMenuOpen') onCloseCallback=(action 'onMenuOpen')}}
<ul class="menu">
{{#if pinState.isPinned}}
<li class="item" {{action 'onUnpin'}}>Unpin</li>
{{else}}
<li class="item" id="pin-space-button">Pin</li>
{{/if}}
</ul>
{{/dropdown-menu}}
{{#if menuOpen}}
{{#unless pinState.isPinned}}
{{#dropdown-dialog target="pin-space-button" position="bottom left" button="Pin" color="flat-green" onAction=(action 'onPin') focusOn="pin-space-name" }}
<div class="input-control">
<label>Pin Space</label>
<div class="tip">A 3 or 4 character name</div>
{{input type='text' id="pin-space-name" value=pinState.newName}}
</div>
{{/dropdown-dialog}}
{{/unless}}
{{/if}}
{{/if}}
{{/unless}}
</div>
<div class="sidebar-common"> <div class="sidebar-common">
{{layout/sidebar-intro title=appMeta.title message=appMeta.message}} {{layout/sidebar-intro title=appMeta.title message=appMeta.message}}
</div> </div>
@ -56,12 +6,4 @@
{{#if (is-equal tab 'index')}} {{#if (is-equal tab 'index')}}
{{folder/sidebar-folders-list folders=folders folder=folder permissions=permissions onAddSpace=(action 'onAddSpace')}} {{folder/sidebar-folders-list folders=folders folder=folder permissions=permissions onAddSpace=(action 'onAddSpace')}}
{{/if}} {{/if}}
{{#if (is-equal tab 'share')}}
{{folder/sidebar-share folders=folders folder=folder}}
{{/if}}
{{#if (is-equal tab 'permissions')}}
{{folder/sidebar-permissions folders=folders folder=folder}}
{{/if}}
</div> </div>

View file

@ -23,15 +23,6 @@ let BaseModel = Ember.Object.extend({
} }
}); });
let FolderPermissionModel = Ember.Object.extend({
orgId: "",
folderId: "",
userId: "",
fullname: "",
canView: false,
canEdit: false
});
// ProtectedFolderParticipant used to display folder participants that can // ProtectedFolderParticipant used to display folder participants that can
// then be marked as folder owner. // then be marked as folder owner.
let ProtectedFolderParticipant = Ember.Object.extend({ let ProtectedFolderParticipant = Ember.Object.extend({

View file

@ -16,7 +16,7 @@ export default function(name, options = {}) {
server.createList('user', 2); server.createList('user', 2);
server.createList('document', 2); server.createList('document', 2);
server.createList('permission', 4); server.createList('permission', 4);
server.createList('folder-permission', 2); // server.createList('folder-permission', 2);
server.createList('organization', 1); server.createList('organization', 1);
if (options.beforeEach) { if (options.beforeEach) {

View file

@ -117,6 +117,7 @@ func EncodeRecord(r PermissionRecord, a PermissionAction) (p Permission) {
p.Location = "space" p.Location = "space"
p.RefID = r.SpaceID p.RefID = r.SpaceID
p.Action = a p.Action = a
p.Scope = "object" // default to row level permission
return return
} }