mirror of
https://github.com/documize/community.git
synced 2025-07-19 05:09:42 +02:00
Enable custom logo upload and rendering
This commit is contained in:
parent
a211ba051a
commit
036f36ba1d
36 changed files with 574 additions and 211 deletions
|
@ -46,7 +46,6 @@ import (
|
||||||
"github.com/documize/community/model/doc"
|
"github.com/documize/community/model/doc"
|
||||||
"github.com/documize/community/model/group"
|
"github.com/documize/community/model/group"
|
||||||
"github.com/documize/community/model/link"
|
"github.com/documize/community/model/link"
|
||||||
"github.com/documize/community/model/org"
|
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
"github.com/documize/community/model/permission"
|
"github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/pin"
|
"github.com/documize/community/model/pin"
|
||||||
|
@ -231,14 +230,14 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
|
||||||
w = fmt.Sprintf(" WHERE c_refid='%s' ", b.Spec.OrgID)
|
w = fmt.Sprintf(" WHERE c_refid='%s' ", b.Spec.OrgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
o := []org.Organization{}
|
o := []orgExtended{}
|
||||||
err = b.Runtime.Db.Select(&o, `SELECT id, c_refid AS refid,
|
err = b.Runtime.Db.Select(&o, `SELECT id, c_refid AS refid,
|
||||||
c_title AS title, c_message AS message, c_domain AS domain,
|
c_title AS title, c_message AS message, c_domain AS domain,
|
||||||
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
c_service AS conversionendpoint, c_email AS email, c_serial AS serial, c_active AS active,
|
||||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||||
coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription,
|
coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription,
|
||||||
coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags,
|
coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags,
|
||||||
c_created AS created, c_revised AS revised
|
c_theme AS theme, c_logo AS logo, c_created AS created, c_revised AS revised
|
||||||
FROM dmz_org`+w)
|
FROM dmz_org`+w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -255,10 +254,6 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
|
||||||
|
|
||||||
// Config, User Config.
|
// Config, User Config.
|
||||||
func (b backerHandler) dmzConfig(files *[]backupItem) (err error) {
|
func (b backerHandler) dmzConfig(files *[]backupItem) (err error) {
|
||||||
type config struct {
|
|
||||||
ConfigKey string `json:"key"`
|
|
||||||
ConfigValue string `json:"config"`
|
|
||||||
}
|
|
||||||
c := []config{}
|
c := []config{}
|
||||||
err = b.Runtime.Db.Select(&c, `SELECT c_key AS configkey, c_config AS configvalue FROM dmz_config`)
|
err = b.Runtime.Db.Select(&c, `SELECT c_key AS configkey, c_config AS configvalue FROM dmz_config`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -279,14 +274,7 @@ func (b backerHandler) dmzConfig(files *[]backupItem) (err error) {
|
||||||
w = fmt.Sprintf(" where c_orgid='%s' ", b.Spec.OrgID)
|
w = fmt.Sprintf(" where c_orgid='%s' ", b.Spec.OrgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userConfig struct {
|
|
||||||
OrgID string `json:"orgId"`
|
|
||||||
UserID string `json:"userId"`
|
|
||||||
ConfigKey string `json:"key"`
|
|
||||||
ConfigValue string `json:"config"`
|
|
||||||
}
|
|
||||||
uc := []userConfig{}
|
uc := []userConfig{}
|
||||||
|
|
||||||
err = b.Runtime.Db.Select(&uc, `select c_orgid AS orgid, c_userid AS userid,
|
err = b.Runtime.Db.Select(&uc, `select c_orgid AS orgid, c_userid AS userid,
|
||||||
c_key AS configkey, c_config AS configvalue FROM dmz_user_config`+w)
|
c_key AS configkey, c_config AS configvalue FROM dmz_user_config`+w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -475,6 +463,8 @@ func (b backerHandler) dmzSpace(files *[]backupItem) (err error) {
|
||||||
err = b.Runtime.Db.Select(&sp, `SELECT id, c_refid AS refid,
|
err = b.Runtime.Db.Select(&sp, `SELECT id, c_refid AS refid,
|
||||||
c_name AS name, c_orgid AS orgid, c_userid AS userid,
|
c_name AS name, c_orgid AS orgid, c_userid AS userid,
|
||||||
c_type AS type, c_lifecycle AS lifecycle, c_likes AS likes,
|
c_type AS type, c_lifecycle AS lifecycle, c_likes AS likes,
|
||||||
|
c_icon AS icon, c_labelid AS labelid, c_desc AS description,
|
||||||
|
c_count_category As countcategory, c_count_content AS countcontent,
|
||||||
c_created AS created, c_revised AS revised
|
c_created AS created, c_revised AS revised
|
||||||
FROM dmz_space`+w)
|
FROM dmz_space`+w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -671,16 +661,6 @@ func (b backerHandler) dmzDocument(files *[]backupItem) (err error) {
|
||||||
*files = append(*files, backupItem{Filename: "dmz_doc.json", Content: content})
|
*files = append(*files, backupItem{Filename: "dmz_doc.json", Content: content})
|
||||||
|
|
||||||
// Vote
|
// Vote
|
||||||
type vote struct {
|
|
||||||
RefID string `json:"refId"`
|
|
||||||
OrgID string `json:"orgId"`
|
|
||||||
DocumentID string `json:"documentId"`
|
|
||||||
VoterID string `json:"voterId"`
|
|
||||||
Vote int `json:"vote"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
Revised time.Time `json:"revised"`
|
|
||||||
}
|
|
||||||
|
|
||||||
vt := []vote{}
|
vt := []vote{}
|
||||||
err = b.Runtime.Db.Select(&vt, `
|
err = b.Runtime.Db.Select(&vt, `
|
||||||
SELECT c_refid AS refid, c_orgid AS orgid,
|
SELECT c_refid AS refid, c_orgid AS orgid,
|
||||||
|
@ -716,16 +696,6 @@ func (b backerHandler) dmzDocument(files *[]backupItem) (err error) {
|
||||||
*files = append(*files, backupItem{Filename: "dmz_doc_link.json", Content: content})
|
*files = append(*files, backupItem{Filename: "dmz_doc_link.json", Content: content})
|
||||||
|
|
||||||
// Comment
|
// Comment
|
||||||
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 = 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,
|
||||||
|
@ -743,20 +713,6 @@ func (b backerHandler) dmzDocument(files *[]backupItem) (err error) {
|
||||||
*files = append(*files, backupItem{Filename: "dmz_doc_comment.json", Content: content})
|
*files = append(*files, backupItem{Filename: "dmz_doc_comment.json", Content: content})
|
||||||
|
|
||||||
// Share
|
// Share
|
||||||
type share struct {
|
|
||||||
ID uint64 `json:"id"`
|
|
||||||
OrgID string `json:"orgId"`
|
|
||||||
UserID string `json:"userId"`
|
|
||||||
DocumentID string `json:"documentId"`
|
|
||||||
Email string `json:"email"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
Viewed string `json:"viewed"` // recording each view as |date-viewed|date-viewed|
|
|
||||||
Secret string `json:"secret"` // secure token used to access document
|
|
||||||
Expires string `json:"expires"` // number of days from creation, value of 0 means never
|
|
||||||
Active bool `json:"active"`
|
|
||||||
Created time.Time `json:"created"`
|
|
||||||
}
|
|
||||||
|
|
||||||
sh := []share{}
|
sh := []share{}
|
||||||
err = b.Runtime.Db.Select(&sh, `
|
err = b.Runtime.Db.Select(&sh, `
|
||||||
SELECT id AS id, c_orgid AS orgid, c_docid AS documentid,
|
SELECT id AS id, c_orgid AS orgid, c_docid AS documentid,
|
||||||
|
|
78
domain/backup/models.go
Normal file
78
domain/backup/models.go
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// Package backup handle data backup/restore to/from ZIP format.
|
||||||
|
package backup
|
||||||
|
|
||||||
|
// Existing data models do not necessarily have fields to hold
|
||||||
|
// all data when loaded from the database.
|
||||||
|
// So we extend the existing models to hold additional fields
|
||||||
|
// for a complete backup and restore process.
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/documize/community/model/org"
|
||||||
|
)
|
||||||
|
|
||||||
|
type orgExtended struct {
|
||||||
|
org.Organization
|
||||||
|
Logo []byte `json:"logo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
ConfigKey string `json:"key"`
|
||||||
|
ConfigValue string `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type userConfig struct {
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
ConfigKey string `json:"key"`
|
||||||
|
ConfigValue string `json:"config"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vote
|
||||||
|
type vote struct {
|
||||||
|
RefID string `json:"refId"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
DocumentID string `json:"documentId"`
|
||||||
|
VoterID string `json:"voterId"`
|
||||||
|
Vote int `json:"vote"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
Revised time.Time `json:"revised"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Share
|
||||||
|
type share struct {
|
||||||
|
ID uint64 `json:"id"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
DocumentID string `json:"documentId"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Viewed string `json:"viewed"` // recording each view as |date-viewed|date-viewed|
|
||||||
|
Secret string `json:"secret"` // secure token used to access document
|
||||||
|
Expires string `json:"expires"` // number of days from creation, value of 0 means never
|
||||||
|
Active bool `json:"active"`
|
||||||
|
Created time.Time `json:"created"`
|
||||||
|
}
|
|
@ -39,7 +39,6 @@ import (
|
||||||
"github.com/documize/community/model/doc"
|
"github.com/documize/community/model/doc"
|
||||||
"github.com/documize/community/model/group"
|
"github.com/documize/community/model/group"
|
||||||
"github.com/documize/community/model/link"
|
"github.com/documize/community/model/link"
|
||||||
"github.com/documize/community/model/org"
|
|
||||||
"github.com/documize/community/model/page"
|
"github.com/documize/community/model/page"
|
||||||
"github.com/documize/community/model/permission"
|
"github.com/documize/community/model/permission"
|
||||||
"github.com/documize/community/model/pin"
|
"github.com/documize/community/model/pin"
|
||||||
|
@ -332,7 +331,7 @@ func (r *restoreHandler) readZip(filename string) (found bool, b []byte, err err
|
||||||
func (r *restoreHandler) dmzOrg() (err error) {
|
func (r *restoreHandler) dmzOrg() (err error) {
|
||||||
filename := "dmz_org.json"
|
filename := "dmz_org.json"
|
||||||
|
|
||||||
org := []org.Organization{}
|
org := []orgExtended{}
|
||||||
err = r.fileJSON(filename, &org)
|
err = r.fileJSON(filename, &org)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, fmt.Sprintf("failed to load %s", filename))
|
err = errors.Wrap(err, fmt.Sprintf("failed to load %s", filename))
|
||||||
|
@ -363,12 +362,14 @@ func (r *restoreHandler) dmzOrg() (err error) {
|
||||||
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
|
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
|
||||||
INSERT INTO dmz_org (c_refid, c_company, c_title, c_message,
|
INSERT INTO dmz_org (c_refid, c_company, c_title, c_message,
|
||||||
c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig,
|
c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig,
|
||||||
c_maxtags, c_verified, c_serial, c_sub, c_active, c_created, c_revised)
|
c_maxtags, c_verified, c_serial, c_sub, c_active,
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
c_theme, c_logo, c_created, c_revised)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
||||||
org[i].RefID, org[i].Company, org[i].Title, org[i].Message,
|
org[i].RefID, org[i].Company, org[i].Title, org[i].Message,
|
||||||
strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email),
|
strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email),
|
||||||
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
|
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
|
||||||
org[i].MaxTags, true, org[i].Serial, org[i].Subscription,
|
org[i].MaxTags, true, org[i].Serial, org[i].Subscription,
|
||||||
|
org[i].Theme, org[i].Logo,
|
||||||
org[i].Active, org[i].Created, org[i].Revised)
|
org[i].Active, org[i].Created, org[i].Revised)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Context.Transaction.Rollback()
|
r.Context.Transaction.Rollback()
|
||||||
|
@ -402,6 +403,7 @@ func (r *restoreHandler) dmzOrg() (err error) {
|
||||||
org[0].Serial = r.Spec.Org.Serial
|
org[0].Serial = r.Spec.Org.Serial
|
||||||
org[0].Title = r.Spec.Org.Title
|
org[0].Title = r.Spec.Org.Title
|
||||||
org[0].Subscription = r.Spec.Org.Subscription
|
org[0].Subscription = r.Spec.Org.Subscription
|
||||||
|
org[0].Theme = r.Spec.Org.Theme
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
|
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
|
||||||
|
@ -612,8 +614,16 @@ func (r *restoreHandler) dmzSpace() (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range sp {
|
for i := range sp {
|
||||||
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_space (c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle, c_likes, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
|
||||||
sp[i].RefID, sp[i].Name, r.remapOrg(sp[i].OrgID), r.remapUser(sp[i].UserID), sp[i].Type, sp[i].Lifecycle, sp[i].Likes, sp[i].Created, sp[i].Revised)
|
INSERT INTO dmz_space
|
||||||
|
(c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle,
|
||||||
|
c_likes, c_icon, c_desc, c_count_category, c_count_content,
|
||||||
|
c_labelid, c_created, c_revised)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
||||||
|
sp[i].RefID, sp[i].Name, r.remapOrg(sp[i].OrgID),
|
||||||
|
r.remapUser(sp[i].UserID), sp[i].Type, sp[i].Lifecycle,
|
||||||
|
sp[i].Likes, sp[i].Icon, sp[i].Description, sp[i].CountCategory,
|
||||||
|
sp[i].CountContent, sp[i].LabelID, sp[i].Created, sp[i].Revised)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.Context.Transaction.Rollback()
|
r.Context.Transaction.Rollback()
|
||||||
|
|
|
@ -13,6 +13,7 @@ package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -301,3 +302,41 @@ func (h *Handler) Themes(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
response.WriteJSON(w, th)
|
response.WriteJSON(w, th)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logo returns site logo based upon request domain (e.g. acme.documize.com).
|
||||||
|
// The default Documize logo is returned if organization has not uploaded
|
||||||
|
// their own logo.
|
||||||
|
func (h *Handler) Logo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
d := organization.GetSubdomainFromHost(r)
|
||||||
|
|
||||||
|
logo, err := h.Store.Organization.Logo(ctx, d)
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Infof("unable to fetch logo for domain %s", d)
|
||||||
|
response.WriteNotFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(string(logo)) == 0 {
|
||||||
|
logo, err = base64.StdEncoding.DecodeString(defaultLogo)
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Error("unable to decode default logo", err)
|
||||||
|
response.WriteEmpty(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/octet-stream")
|
||||||
|
w.Header().Set("Content-Length", fmt.Sprintf("%d", len(logo)))
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
_, err = w.Write(logo)
|
||||||
|
if err != nil {
|
||||||
|
h.Runtime.Log.Error("failed to write org logo", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultLogo = `
|
||||||
|
iVBORw0KGgoAAAANSUhEUgAAAEIAAAA2CAYAAABz508/AAAAAXNSR0IArs4c6QAABuRJREFUaAXtW1tsFFUY/s/s7tBCkcpFH0gRfeBBDPCAKAqJJUYSY+DBQIwPRo2JCIZ2W1qiKzIVtkBb2lIlER/UB6KJGE2JCcaQABpNRBQ0QYWYaLxxlRZobbszc36/s9spu9uZ7drZC4WeZDtnzu3//m/+c/nPORWUFgzjnZJu6+L9zDSPiKelZRf8lVlYpPEVQaKHNflbiUY/NRkb/841EJHcYDjSusgmq5OZS4WgCcRUkpxfjDhwSHwUkwR+ihQCLmJbCHFYMHWSHvqw3Qh3+8U2RERVpGUts73bb4OFrS+ukuAQZB4kLWB0bNnw7Wjlx4mo2bRzvi2to2BeH21DxawH65BM1K8xf6LrFB5N1xGGwdqlWPNJIXgOiNCKqZBv2YJiaMPSBO0sD5Y1Gca6nmzb1LpjzQsE8cwxT4LSWFk000SWouaS2fNDbaTljqyJgA0sZkGBbCuMhXJMPAk4K0yWx8ORHQuzwRyEJUwDi6UehWFa8ZHaIzv/ybBWTBYUxB9g5Ow/GKMO8a0205FwpPnJtmhdZya0IAITlBLlHso0TVS6ZxUmFeuHIEueJUnMxKB4J0u5HM8pGByVophKRwwTbZYfY1Z8aFd0w+depdGYdwCIgfatdYe9SxQnJ7ypda6U1mp8xOdAxhSgUF0hUxBYGhxBvXvattScdCs4JmcJpcyuaP3mJQtmzyLSolCsFwuuPjcFnTQ1xdqW+dnG7XsUccPCmCTC0WL16tV2R2PdNl3X7hIsPsV41uvkpT+xWtZA1tT+q5c/QnzYUDCmiXCUbTHqzrdH6x7HuLGXsdR00l2eJchcWP1Ky7r0vBuCCKUUTJ9fb6xfg3GtAW8D6YoOvTPfgnGlwTA+SFlF3zBEOIqiqzRgbfQm3r27CbF+2fr9BaeOet5wRCillsybXYvx4HshNHfLYCqT0oZV7JmoyquQcfpMFMnub7XRVi4tc0Z2pXNTSguGLri54GoQNYzdy7tiPX9CkvtaA6vpLvNyNfIbFRrfRNREdlbYZL8rYyYWXsNHYyUkXwEyuSrSdChAgadbo7V/JMtRDld1pGkrMG3G6rksOU/FVRqWkk8hGifCV9dQnqvN9j5MR8sKTUJCMcZCiZcpDApLIu3a3/LQjDcwcCqP1DWg7uyqaPvtKnNYZdcaHomXqOVueAL3eWQXLFlhUFjSBRrGM/1YPmzETOI+cKpdrz7zMVXPFxFBKQs6JqQrmvyuWTQ9+d2J6zrvT/glTkrSE90DXeQJleKLCKnpxzE6/5vUdHGiCkMweMJNuFpsEclf3fLiaSyXGsahoC8iEiO2CKsNVk9Bec5IyBZht9nDEa1R4D2vRRbqmz3WsQrfs0ZHtP4t7H6fsDVzBSaN+MDjAMj7U/A5jUP726I1RzPJEhp/jW2NPvyGTaVYklumFHN8E6EADALJCCYT0LznieAZkjFY/zBfC6I5CIOu8NU18q5AjgSUlAbPYsBM8S2cpuG1huColN0URGx7ef0FgYMPR/nkJ6beEH43BxEJxdlrQFfGELopLCLZArzi40QMMjNOxCAROZk+w5ualkqbVqLNwq4jiM5hCOxs21L/hZfJZ5vum4iqSPOLts0dxfE+iWxb1ADD+l3ROniaow++ukbYaJ3KJJuLRUJCbbjiwKCwjJ4Gn06XkOZ8LFuLfplEYYhj8cGEL4tgDsGzuz6CXyy+iGh9LfwjNj2+LDYVCoPC4geHLyLUWYKg0Cq4sgfg0Nh+gIyursBdKjqQwJDxYGfE5n3PGu2N4TOQ8qjaGr9i9hT0Ft4tobJ/DOP5nGwM+SbCoXoQUE5AOW0W8umraxQSaL5ljRMxyPA4EeNEpHa2cYsY5COHs8busm6KuR6ypHKfu7dy0i/+n0ulmST7JgJb+TNxkf3tLrPnYZwaFdTCukRMro80HQxQ8FnspP+VSdGR8nwBVwevkq19OFp+pNAkKMXiMiFbYcCBrtte/Uj6D+X7ImLwEHjxUGtFimAXenFVQ8tcP+Jxf1v2w2lxvVkCAZ5H6kro9XQIPCIW4a4jzjQGcObRi4u12s+4iOZiVkgUdCqTyemlk0+AxD4/XyIXdRUGhcWrLaUDrjKfhmMInVMDUvoCJE5p6o4yypnDvUfs/GiBNcrDTK167W37S2u70PYGNwHXSuU7hg+mUW0ci4eouJcc0lel76Th68eg5WnFQdwSOjo6JvxydmAvLqeuwACk/l1Iw+3MB9sb67/zaDslGdeHHpBkrwRjt6Vk5PkF4M/jpLsT14a+ykZUzas7Km2L3gfOyTARHboem6ovwrWASiulS6h7Ar30zfRJNKNb3TbJpvGxVkb9814vXSifRPdiDVKpPno8/AeFjniebez5hgAAAABJRU5ErkJggg==
|
||||||
|
`
|
||||||
|
|
|
@ -12,8 +12,11 @@
|
||||||
package organization
|
package organization
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"github.com/documize/community/model/audit"
|
||||||
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
@ -99,3 +102,50 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
response.WriteJSON(w, org)
|
response.WriteJSON(w, org)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UploadLogo stores log for organization.
|
||||||
|
func (h *Handler) UploadLogo(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "organization.UploadLogo"
|
||||||
|
ctx := domain.GetRequestContext(r)
|
||||||
|
|
||||||
|
if !ctx.Administrator {
|
||||||
|
response.WriteForbiddenError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use default logo if body is empty.
|
||||||
|
logo := []byte{}
|
||||||
|
|
||||||
|
filedata, _, err := r.FormFile("attachment")
|
||||||
|
if err == nil {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
_, err = io.Copy(b, filedata)
|
||||||
|
if err != nil {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
} else {
|
||||||
|
logo = b.Bytes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
||||||
|
if err != nil {
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = h.Store.Organization.UploadLogo(ctx, logo)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Transaction.Rollback()
|
||||||
|
response.WriteServerError(w, method, err)
|
||||||
|
h.Runtime.Log.Error(method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Transaction.Commit()
|
||||||
|
|
||||||
|
h.Store.Audit.Record(ctx, audit.EventTypeOrganizationLogo)
|
||||||
|
|
||||||
|
response.WriteEmpty(w)
|
||||||
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Or
|
||||||
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
|
||||||
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
|
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
|
||||||
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
|
||||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme
|
c_maxtags AS maxtags, c_theme AS theme, c_created AS created, c_revised AS revised
|
||||||
FROM dmz_org
|
FROM dmz_org
|
||||||
WHERE c_refid=?`),
|
WHERE c_refid=?`),
|
||||||
id)
|
id)
|
||||||
|
@ -176,3 +176,31 @@ func (s Store) CheckDomain(ctx domain.RequestContext, domain string) string {
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logo fetchs stored image from store or NULL.
|
||||||
|
func (s Store) Logo(ctx domain.RequestContext, domain string) (l []byte, err error) {
|
||||||
|
row := s.Runtime.Db.QueryRow(s.Bind("SELECT c_logo FROM dmz_org WHERE c_domain=? AND c_active=true"), domain)
|
||||||
|
|
||||||
|
err = row.Scan(&l)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
err = nil
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return l, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadLogo saves custom logo to the organization record.
|
||||||
|
func (s Store) UploadLogo(ctx domain.RequestContext, logo []byte) (err error) {
|
||||||
|
_, err = ctx.Transaction.Exec(s.Bind("UPDATE dmz_org SET c_logo=?, c_revised=? WHERE c_refid=?"),
|
||||||
|
logo, time.Now().UTC(), ctx.OrgID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
err = errors.Wrap(err, fmt.Sprintf("unable to save custom logo for org %s", ctx.OrgID))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -99,6 +99,9 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
var sp space.Space
|
var sp space.Space
|
||||||
sp.Name = model.Name
|
sp.Name = model.Name
|
||||||
|
sp.Description = model.Description
|
||||||
|
sp.Icon = model.Icon
|
||||||
|
sp.LabelID = model.LabelID
|
||||||
sp.RefID = uniqueid.Generate()
|
sp.RefID = uniqueid.Generate()
|
||||||
sp.OrgID = ctx.OrgID
|
sp.OrgID = ctx.OrgID
|
||||||
sp.UserID = ctx.UserID
|
sp.UserID = ctx.UserID
|
||||||
|
|
|
@ -30,7 +30,12 @@ type Store struct {
|
||||||
|
|
||||||
// Add adds new folder into the store.
|
// Add adds new folder into the store.
|
||||||
func (s Store) Add(ctx domain.RequestContext, sp space.Space) (err error) {
|
func (s Store) Add(ctx domain.RequestContext, sp space.Space) (err error) {
|
||||||
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_space (c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle, c_likes, c_icon, c_desc, c_count_category, c_count_content, c_labelid, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
_, err = ctx.Transaction.Exec(s.Bind(`
|
||||||
|
INSERT INTO dmz_space
|
||||||
|
(c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle,
|
||||||
|
c_likes, c_icon, c_desc, c_count_category, c_count_content,
|
||||||
|
c_labelid, c_created, c_revised)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
||||||
sp.RefID, sp.Name, sp.OrgID, sp.UserID, sp.Type, sp.Lifecycle, sp.Likes,
|
sp.RefID, sp.Name, sp.OrgID, sp.UserID, sp.Type, sp.Lifecycle, sp.Likes,
|
||||||
sp.Icon, sp.Description, sp.CountCategory, sp.CountContent, sp.LabelID,
|
sp.Icon, sp.Description, sp.CountCategory, sp.CountContent, sp.LabelID,
|
||||||
sp.Created, sp.Revised)
|
sp.Created, sp.Revised)
|
||||||
|
|
|
@ -154,6 +154,8 @@ type OrganizationStorer interface {
|
||||||
RemoveOrganization(ctx domain.RequestContext, orgID string) (err error)
|
RemoveOrganization(ctx domain.RequestContext, orgID string) (err error)
|
||||||
UpdateAuthConfig(ctx domain.RequestContext, org org.Organization) (err error)
|
UpdateAuthConfig(ctx domain.RequestContext, org org.Organization) (err error)
|
||||||
CheckDomain(ctx domain.RequestContext, domain string) string
|
CheckDomain(ctx domain.RequestContext, domain string) string
|
||||||
|
Logo(ctx domain.RequestContext, domain string) (l []byte, err error)
|
||||||
|
UploadLogo(ctx domain.RequestContext, l []byte) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PinStorer defines required methods for pin management
|
// PinStorer defines required methods for pin management
|
||||||
|
|
|
@ -32,6 +32,51 @@ export default Component.extend(Notifier, {
|
||||||
this.set('maxTags', this.get('model.general.maxTags'));
|
this.set('maxTags', this.get('model.general.maxTags'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
didInsertElement() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let url = this.get('appMeta.endpoint');
|
||||||
|
let orgId = this.get('appMeta.orgId');
|
||||||
|
let uploadUrl = `${url}/organization/${orgId}/logo`;
|
||||||
|
|
||||||
|
let dzone = new Dropzone("#upload-logo > div", {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + self.get('session.authToken')
|
||||||
|
},
|
||||||
|
url: uploadUrl,
|
||||||
|
method: "post",
|
||||||
|
paramName: 'attachment',
|
||||||
|
clickable: true,
|
||||||
|
maxFilesize: 50,
|
||||||
|
parallelUploads: 1,
|
||||||
|
uploadMultiple: false,
|
||||||
|
addRemoveLinks: false,
|
||||||
|
autoProcessQueue: true,
|
||||||
|
createImageThumbnails: false,
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
this.on("success", function (/*file, response*/ ) {
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on("queuecomplete", function () {
|
||||||
|
self.notifySuccess('Logo uploaded');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on("error", function (error, msg) {
|
||||||
|
self.notifyError(msg);
|
||||||
|
self.notifyError(error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
dzone.on("complete", function (file) {
|
||||||
|
dzone.removeFile(file);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.set('drop', dzone);
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
change() {
|
change() {
|
||||||
const selectEl = this.$('#maxTags')[0];
|
const selectEl = this.$('#maxTags')[0];
|
||||||
|
@ -63,7 +108,7 @@ export default Component.extend(Notifier, {
|
||||||
|
|
||||||
this.set('model.general.maxTags', this.get('maxTags'));
|
this.set('model.general.maxTags', this.get('maxTags'));
|
||||||
|
|
||||||
this.get('save')().then(() => {
|
this.get('onUpdate')().then(() => {
|
||||||
this.notifySuccess('Saved');
|
this.notifySuccess('Saved');
|
||||||
set(this, 'titleError', false);
|
set(this, 'titleError', false);
|
||||||
set(this, 'messageError', false);
|
set(this, 'messageError', false);
|
||||||
|
@ -74,6 +119,11 @@ export default Component.extend(Notifier, {
|
||||||
onThemeChange(theme) {
|
onThemeChange(theme) {
|
||||||
this.get('appMeta').setTheme(theme);
|
this.get('appMeta').setTheme(theme);
|
||||||
this.set('model.general.theme', theme);
|
this.set('model.general.theme', theme);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDefaultLogo() {
|
||||||
|
this.get('onDefaultLogo')(this.get('appMeta.orgId'));
|
||||||
|
this.notifySuccess('Using default logo');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,7 @@ import Component from '@ember/component';
|
||||||
export default Component.extend(AuthMixin, Notifier, {
|
export default Component.extend(AuthMixin, Notifier, {
|
||||||
router: service(),
|
router: service(),
|
||||||
spaceSvc: service('folder'),
|
spaceSvc: service('folder'),
|
||||||
|
iconSvc: service('icon'),
|
||||||
localStorage: service('localStorage'),
|
localStorage: service('localStorage'),
|
||||||
isSpaceAdmin: computed('permissions', function() {
|
isSpaceAdmin: computed('permissions', function() {
|
||||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||||
|
@ -40,7 +41,7 @@ export default Component.extend(AuthMixin, Notifier, {
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
this.populateIconList();
|
this.set('iconList', this.get('iconSvc').getSpaceIconList());
|
||||||
},
|
},
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
|
@ -76,62 +77,6 @@ export default Component.extend(AuthMixin, Notifier, {
|
||||||
this.set('spaceIcon', icon);
|
this.set('spaceIcon', icon);
|
||||||
},
|
},
|
||||||
|
|
||||||
populateIconList() {
|
|
||||||
let list = this.get('iconList');
|
|
||||||
let constants = this.get('constants');
|
|
||||||
|
|
||||||
list = A([]);
|
|
||||||
|
|
||||||
list.pushObject(constants.IconMeta.Star);
|
|
||||||
list.pushObject(constants.IconMeta.Support);
|
|
||||||
list.pushObject(constants.IconMeta.Message);
|
|
||||||
list.pushObject(constants.IconMeta.Apps);
|
|
||||||
list.pushObject(constants.IconMeta.Box);
|
|
||||||
list.pushObject(constants.IconMeta.Gift);
|
|
||||||
list.pushObject(constants.IconMeta.Design);
|
|
||||||
list.pushObject(constants.IconMeta.Bulb);
|
|
||||||
list.pushObject(constants.IconMeta.Metrics);
|
|
||||||
list.pushObject(constants.IconMeta.PieChart);
|
|
||||||
list.pushObject(constants.IconMeta.BarChart);
|
|
||||||
list.pushObject(constants.IconMeta.Finance);
|
|
||||||
list.pushObject(constants.IconMeta.Lab);
|
|
||||||
list.pushObject(constants.IconMeta.Code);
|
|
||||||
list.pushObject(constants.IconMeta.Help);
|
|
||||||
list.pushObject(constants.IconMeta.Manuals);
|
|
||||||
list.pushObject(constants.IconMeta.Flow);
|
|
||||||
list.pushObject(constants.IconMeta.Out);
|
|
||||||
list.pushObject(constants.IconMeta.In);
|
|
||||||
list.pushObject(constants.IconMeta.Partner);
|
|
||||||
list.pushObject(constants.IconMeta.Org);
|
|
||||||
list.pushObject(constants.IconMeta.Home);
|
|
||||||
list.pushObject(constants.IconMeta.Infinite);
|
|
||||||
list.pushObject(constants.IconMeta.Todo);
|
|
||||||
list.pushObject(constants.IconMeta.Procedure);
|
|
||||||
list.pushObject(constants.IconMeta.Outgoing);
|
|
||||||
list.pushObject(constants.IconMeta.Incoming);
|
|
||||||
list.pushObject(constants.IconMeta.Travel);
|
|
||||||
list.pushObject(constants.IconMeta.Winner);
|
|
||||||
list.pushObject(constants.IconMeta.Roadmap);
|
|
||||||
list.pushObject(constants.IconMeta.Money);
|
|
||||||
list.pushObject(constants.IconMeta.Security);
|
|
||||||
list.pushObject(constants.IconMeta.Tune);
|
|
||||||
list.pushObject(constants.IconMeta.Guide);
|
|
||||||
list.pushObject(constants.IconMeta.Smile);
|
|
||||||
list.pushObject(constants.IconMeta.Rocket);
|
|
||||||
list.pushObject(constants.IconMeta.Time);
|
|
||||||
list.pushObject(constants.IconMeta.Cup);
|
|
||||||
list.pushObject(constants.IconMeta.Marketing);
|
|
||||||
list.pushObject(constants.IconMeta.Announce);
|
|
||||||
list.pushObject(constants.IconMeta.Devops);
|
|
||||||
list.pushObject(constants.IconMeta.World);
|
|
||||||
list.pushObject(constants.IconMeta.Plan);
|
|
||||||
list.pushObject(constants.IconMeta.Components);
|
|
||||||
list.pushObject(constants.IconMeta.People);
|
|
||||||
list.pushObject(constants.IconMeta.Checklist);
|
|
||||||
|
|
||||||
this.set('iconList', list);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onSetSpaceType(t) {
|
onSetSpaceType(t) {
|
||||||
this.set('spaceType', t);
|
this.set('spaceType', t);
|
||||||
|
|
|
@ -9,9 +9,20 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
appMeta: service(),
|
||||||
icon: null,
|
icon: null,
|
||||||
meta: null
|
meta: null,
|
||||||
|
logo: false,
|
||||||
|
|
||||||
|
didReceiveAttrs() {
|
||||||
|
this._super(...arguments);
|
||||||
|
if (this.get('logo')) {
|
||||||
|
let cb = + new Date();
|
||||||
|
this.set('cacheBuster', cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
19
gui/app/helpers/random-id.js
Normal file
19
gui/app/helpers/random-id.js
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// 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 stringUtil from '../utils/string';
|
||||||
|
import { helper } from '@ember/component/helper';
|
||||||
|
|
||||||
|
// Usage: {{random-id}}
|
||||||
|
export default helper(function() {
|
||||||
|
return stringUtil.makeId(10);
|
||||||
|
});
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<div class="auth-center">
|
<div class="auth-center">
|
||||||
<div class="auth-box">
|
<div class="auth-box">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="/assets/img/logo-purple.png" title="Documize" alt="Documize" class="img-fluid">
|
<img src="{{appMeta.endpoint}}/public/logo?cb={{random-id}}"
|
||||||
<div class="url">{{appMeta.appHost}}</div>
|
title="Documize" alt="Documize" class="img-fluid">
|
||||||
|
<div class="url">{{appMeta.title}} ({{appMeta.appHost}})</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="login-form">
|
<div class="login-form">
|
||||||
{{user/forgot-password forgot=(action "forgot")}}
|
{{user/forgot-password forgot=(action "forgot")}}
|
||||||
|
|
|
@ -2,8 +2,9 @@
|
||||||
{{#if model.showLogin}}
|
{{#if model.showLogin}}
|
||||||
<div class="auth-box">
|
<div class="auth-box">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="/assets/img/logo-purple.png" title="Documize" alt="Documize" class="img-fluid">
|
<img src="{{appMeta.endpoint}}/public/logo?cb={{random-id}}"
|
||||||
<div class="url">{{appMeta.appHost}}</div>
|
title="Documize" alt="Documize" class="img-fluid">
|
||||||
|
<div class="url">{{appMeta.title}} ({{appMeta.appHost}})</div>
|
||||||
</div>
|
</div>
|
||||||
<form {{action "login" on="submit"}}>
|
<form {{action "login" on="submit"}}>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
<div class="auth-center">
|
<div class="auth-center">
|
||||||
<div class="auth-box">
|
<div class="auth-box">
|
||||||
<div class="logo">
|
<div class="logo">
|
||||||
<img src="/assets/img/logo-purple.png" title="Documize" alt="Documize" class="img-fluid">
|
<img src="{{appMeta.endpoint}}/public/logo?cb={{random-id}}"
|
||||||
<div class="url">{{appMeta.appHost}}</div>
|
title="Documize" alt="Documize" class="img-fluid">
|
||||||
|
<div class="url">{{appMeta.title}} ({{appMeta.appHost}})</div>
|
||||||
</div>
|
</div>
|
||||||
{{user/password-reset reset=(action "reset")}}
|
{{user/password-reset reset=(action "reset")}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -16,9 +16,13 @@ export default Controller.extend({
|
||||||
orgService: service('organization'),
|
orgService: service('organization'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
onUpdate() {
|
||||||
return this.get('orgService').save(this.model.general).then(() => {
|
return this.get('orgService').save(this.model.general).then(() => {
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onDefaultLogo(orgId) {
|
||||||
|
return this.get('orgService').useDefaultLogo(orgId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,4 +3,6 @@
|
||||||
desc="Options to help you customize Documize"
|
desc="Options to help you customize Documize"
|
||||||
icon=constants.Icon.Settings}}
|
icon=constants.Icon.Settings}}
|
||||||
|
|
||||||
{{customize/general-settings model=model save=(action "save")}}
|
{{customize/general-settings model=model
|
||||||
|
onUpdate=(action "onUpdate")
|
||||||
|
onDefaultLogo=(action "onDefaultLogo")}}
|
||||||
|
|
|
@ -19,19 +19,20 @@ import Controller from '@ember/controller';
|
||||||
export default Controller.extend(AuthMixin, Modals, {
|
export default Controller.extend(AuthMixin, Modals, {
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
folderService: service('folder'),
|
folderService: service('folder'),
|
||||||
|
|
||||||
spaceName: '',
|
|
||||||
copyTemplate: true,
|
copyTemplate: true,
|
||||||
copyPermission: true,
|
copyPermission: true,
|
||||||
copyDocument: false,
|
copyDocument: false,
|
||||||
hasClone: notEmpty('clonedSpace.id'),
|
hasClone: notEmpty('clonedSpace.id'),
|
||||||
clonedSpace: null,
|
clonedSpace: null,
|
||||||
|
|
||||||
selectedView: 'all',
|
selectedView: 'all',
|
||||||
selectedSpaces: null,
|
selectedSpaces: null,
|
||||||
publicSpaces: null,
|
publicSpaces: null,
|
||||||
protectedSpaces: null,
|
protectedSpaces: null,
|
||||||
personalSpaces: null,
|
personalSpaces: null,
|
||||||
|
spaceIcon: '',
|
||||||
|
spaceLabel: '',
|
||||||
|
spaceDesc: '',
|
||||||
|
spaceName: '',
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onShowModal() {
|
onShowModal() {
|
||||||
|
@ -42,10 +43,22 @@ export default Controller.extend(AuthMixin, Modals, {
|
||||||
this.set('clonedSpace', sp)
|
this.set('clonedSpace', sp)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
onSetIcon(icon) {
|
||||||
|
this.set('spaceIcon', icon);
|
||||||
|
},
|
||||||
|
|
||||||
|
onSetLabel(id) {
|
||||||
|
this.set('spaceLabel', id);
|
||||||
|
},
|
||||||
|
|
||||||
onAddSpace(e) {
|
onAddSpace(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
let spaceName = this.get('spaceName');
|
let spaceName = this.get('spaceName');
|
||||||
|
let spaceDesc = this.get('spaceDesc');
|
||||||
|
let spaceIcon = this.get('spaceIcon');
|
||||||
|
let spaceLabel = this.get('spaceLabel');
|
||||||
let clonedId = this.get('clonedSpace.id');
|
let clonedId = this.get('clonedSpace.id');
|
||||||
|
|
||||||
if (is.empty(spaceName)) {
|
if (is.empty(spaceName)) {
|
||||||
|
@ -55,6 +68,9 @@ export default Controller.extend(AuthMixin, Modals, {
|
||||||
|
|
||||||
let payload = {
|
let payload = {
|
||||||
name: spaceName,
|
name: spaceName,
|
||||||
|
desc: spaceDesc,
|
||||||
|
icon: spaceIcon,
|
||||||
|
labelId: spaceLabel,
|
||||||
cloneId: clonedId,
|
cloneId: clonedId,
|
||||||
copyTemplate: this.get('copyTemplate'),
|
copyTemplate: this.get('copyTemplate'),
|
||||||
copyPermission: this.get('copyPermission'),
|
copyPermission: this.get('copyPermission'),
|
||||||
|
@ -62,6 +78,9 @@ export default Controller.extend(AuthMixin, Modals, {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set('spaceName', '');
|
this.set('spaceName', '');
|
||||||
|
this.set('spaceDesc', '');
|
||||||
|
this.set('spaceIcon', '');
|
||||||
|
this.set('spaceLabel', '');
|
||||||
this.set('clonedSpace', null);
|
this.set('clonedSpace', null);
|
||||||
$("#new-space-name").removeClass("is-invalid");
|
$("#new-space-name").removeClass("is-invalid");
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-rout
|
||||||
export default Route.extend(AuthenticatedRouteMixin, {
|
export default Route.extend(AuthenticatedRouteMixin, {
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
folderService: service('folder'),
|
folderService: service('folder'),
|
||||||
|
iconSvc: service('icon'),
|
||||||
localStorage: service(),
|
localStorage: service(),
|
||||||
labelSvc: service('label'),
|
labelSvc: service('label'),
|
||||||
|
|
||||||
|
@ -66,6 +67,7 @@ export default Route.extend(AuthenticatedRouteMixin, {
|
||||||
controller.set('publicSpaces', publicSpaces);
|
controller.set('publicSpaces', publicSpaces);
|
||||||
controller.set('protectedSpaces', protectedSpaces);
|
controller.set('protectedSpaces', protectedSpaces);
|
||||||
controller.set('personalSpaces', personalSpaces);
|
controller.set('personalSpaces', personalSpaces);
|
||||||
|
controller.set('iconList', this.get('iconSvc').getSpaceIconList());
|
||||||
},
|
},
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
|
|
|
@ -32,11 +32,13 @@
|
||||||
{{#if labels}}
|
{{#if labels}}
|
||||||
<div class="list">
|
<div class="list">
|
||||||
{{#each labels as |label|}}
|
{{#each labels as |label|}}
|
||||||
|
{{#if (gt label.count 0)}}
|
||||||
<div class="item {{if (eq selectedView label.id) "selected"}}" {{action "onSelect" label.id}}>
|
<div class="item {{if (eq selectedView label.id) "selected"}}" {{action "onSelect" label.id}}>
|
||||||
<i class={{concat "dicon " constants.Icon.Checkbox}}
|
<i class={{concat "dicon " constants.Icon.Checkbox}}
|
||||||
style={{label.bgfgColor}}/>
|
style={{label.bgfgColor}}/>
|
||||||
<div class="name">{{label.name}} ({{label.count}})</div>
|
<div class="name">{{label.name}} ({{label.count}})</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{/if}}
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
@ -48,8 +50,10 @@
|
||||||
{{#layout/master-content}}
|
{{#layout/master-content}}
|
||||||
<div class="grid-container-8-2">
|
<div class="grid-container-8-2">
|
||||||
<div class="grid-cell-1">
|
<div class="grid-cell-1">
|
||||||
{{layout/page-heading title=appMeta.title}}
|
{{layout/logo-heading
|
||||||
{{layout/page-desc desc=appMeta.message}}
|
title=appMeta.title
|
||||||
|
desc=appMeta.message
|
||||||
|
logo=true}}
|
||||||
</div>
|
</div>
|
||||||
<div class="grid-cell-2 grid-cell-right">
|
<div class="grid-cell-2 grid-cell-right">
|
||||||
{{#if (or session.isEditor session.isAdmin)}}
|
{{#if (or session.isEditor session.isAdmin)}}
|
||||||
|
@ -68,16 +72,49 @@
|
||||||
{{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" role="document">
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">Add 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">Space 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">
|
||||||
|
<label>Description</label>
|
||||||
|
{{focus-input id="space-desc" type="text" value=spaceDesc class="form-control" placeholder="Space description" autocomplete="off"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Icon</label>
|
||||||
|
<div class="ui-icon-picker">
|
||||||
|
<ul class="list">
|
||||||
|
{{#each iconList as |icon|}}
|
||||||
|
<li class="item {{if (eq spaceIcon icon) "selected"}}" {{action "onSetIcon" 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>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="clone-space-dropdown">Clone Space</label>
|
<label for="clone-space-dropdown">Clone Space</label>
|
||||||
{{ui/ui-select id="clone-space-dropdown" content=model prompt="select space" action=(action "onCloneSpaceSelect") optionValuePath="id" optionLabelPath="name" selection=clonedSpace}}
|
{{ui/ui-select id="clone-space-dropdown" content=model prompt="select space" action=(action "onCloneSpaceSelect") optionValuePath="id" optionLabelPath="name" selection=clonedSpace}}
|
||||||
|
|
69
gui/app/services/icon.js
Normal file
69
gui/app/services/icon.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
// 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 { A } from '@ember/array';
|
||||||
|
import Service from '@ember/service';
|
||||||
|
|
||||||
|
export default Service.extend({
|
||||||
|
getSpaceIconList() {
|
||||||
|
let cs = this.get('constants');
|
||||||
|
|
||||||
|
let list = A([]);
|
||||||
|
list.pushObject(cs.IconMeta.Star);
|
||||||
|
list.pushObject(cs.IconMeta.Support);
|
||||||
|
list.pushObject(cs.IconMeta.Message);
|
||||||
|
list.pushObject(cs.IconMeta.Apps);
|
||||||
|
list.pushObject(cs.IconMeta.Box);
|
||||||
|
list.pushObject(cs.IconMeta.Gift);
|
||||||
|
list.pushObject(cs.IconMeta.Design);
|
||||||
|
list.pushObject(cs.IconMeta.Bulb);
|
||||||
|
list.pushObject(cs.IconMeta.Metrics);
|
||||||
|
list.pushObject(cs.IconMeta.PieChart);
|
||||||
|
list.pushObject(cs.IconMeta.BarChart);
|
||||||
|
list.pushObject(cs.IconMeta.Finance);
|
||||||
|
list.pushObject(cs.IconMeta.Lab);
|
||||||
|
list.pushObject(cs.IconMeta.Code);
|
||||||
|
list.pushObject(cs.IconMeta.Help);
|
||||||
|
list.pushObject(cs.IconMeta.Manuals);
|
||||||
|
list.pushObject(cs.IconMeta.Flow);
|
||||||
|
list.pushObject(cs.IconMeta.Out);
|
||||||
|
list.pushObject(cs.IconMeta.In);
|
||||||
|
list.pushObject(cs.IconMeta.Partner);
|
||||||
|
list.pushObject(cs.IconMeta.Org);
|
||||||
|
list.pushObject(cs.IconMeta.Home);
|
||||||
|
list.pushObject(cs.IconMeta.Infinite);
|
||||||
|
list.pushObject(cs.IconMeta.Todo);
|
||||||
|
list.pushObject(cs.IconMeta.Procedure);
|
||||||
|
list.pushObject(cs.IconMeta.Outgoing);
|
||||||
|
list.pushObject(cs.IconMeta.Incoming);
|
||||||
|
list.pushObject(cs.IconMeta.Travel);
|
||||||
|
list.pushObject(cs.IconMeta.Winner);
|
||||||
|
list.pushObject(cs.IconMeta.Roadmap);
|
||||||
|
list.pushObject(cs.IconMeta.Money);
|
||||||
|
list.pushObject(cs.IconMeta.Security);
|
||||||
|
list.pushObject(cs.IconMeta.Tune);
|
||||||
|
list.pushObject(cs.IconMeta.Guide);
|
||||||
|
list.pushObject(cs.IconMeta.Smile);
|
||||||
|
list.pushObject(cs.IconMeta.Rocket);
|
||||||
|
list.pushObject(cs.IconMeta.Time);
|
||||||
|
list.pushObject(cs.IconMeta.Cup);
|
||||||
|
list.pushObject(cs.IconMeta.Marketing);
|
||||||
|
list.pushObject(cs.IconMeta.Announce);
|
||||||
|
list.pushObject(cs.IconMeta.Devops);
|
||||||
|
list.pushObject(cs.IconMeta.World);
|
||||||
|
list.pushObject(cs.IconMeta.Plan);
|
||||||
|
list.pushObject(cs.IconMeta.Components);
|
||||||
|
list.pushObject(cs.IconMeta.People);
|
||||||
|
list.pushObject(cs.IconMeta.Checklist);
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
});
|
|
@ -72,5 +72,12 @@ export default Service.extend({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: JSON.stringify(config)
|
data: JSON.stringify(config)
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
useDefaultLogo(orgId) {
|
||||||
|
return this.get('ajax').request(`organization/${orgId}/logo`, {
|
||||||
|
method: 'POST',
|
||||||
|
data: '',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
|
||||||
> .icon, > .meta-icon {
|
> .icon, > .meta-icon, > .meta-logo {
|
||||||
align-self: center;
|
align-self: center;
|
||||||
margin-right: 25px;
|
margin-right: 25px;
|
||||||
|
|
||||||
|
|
|
@ -339,4 +339,10 @@
|
||||||
color: $color-white;
|
color: $color-white;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> #upload-logo {
|
||||||
|
.dz-preview, .dz-processing, .dz-file-preview {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,7 @@
|
||||||
@extend .text-truncate;
|
@extend .text-truncate;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 200px;
|
width: 200px;
|
||||||
margin: 0 20px 20px 0;
|
margin: 0 10px 10px 0;
|
||||||
padding: 0.5rem 0.75rem;
|
padding: 0.5rem 0.75rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
|
@ -22,13 +22,13 @@
|
||||||
justify-self: self-start;
|
justify-self: self-start;
|
||||||
|
|
||||||
> .name {
|
> .name {
|
||||||
font-size: 1.3rem;
|
font-size: 1.4rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: map-get($gray-shades, 800);
|
color: map-get($gray-shades, 800);
|
||||||
|
|
||||||
> .icon {
|
> .icon {
|
||||||
color: map-get($gray-shades, 700);
|
color: map-get($gray-shades, 700);
|
||||||
font-size: 20px;
|
font-size: 26px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<div class="view-customize">
|
<div class="view-customize">
|
||||||
<form>
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="siteTitle">Site Name</label>
|
<label for="siteTitle">Site Name</label>
|
||||||
{{focus-input id="siteTitle" type="text" value=model.general.title class=(if hasTitleInputError "form-control is-invalid" "form-control")}}
|
{{focus-input id="siteTitle" type="text" value=model.general.title class=(if hasTitleInputError "form-control is-invalid" "form-control")}}
|
||||||
|
@ -13,18 +12,28 @@
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Site Theme</label>
|
<label>Site Theme</label>
|
||||||
{{ui/theme-picker onChange=(action "onThemeChange")}}
|
{{ui/theme-picker onChange=(action "onThemeChange")}}
|
||||||
<small class="form-text text-muted">Users can set their own theme under Profile</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Site Logo</label>
|
||||||
|
<div>
|
||||||
|
{{ui/ui-button light=true color=constants.Color.Gray label="Use Default" onClick=(action "onDefaultLogo")}}
|
||||||
|
{{ui/ui-button-gap}}
|
||||||
|
{{ui/ui-button light=true color=constants.Color.Yellow label="Upload Custom" id="upload-logo"}}
|
||||||
|
</div>
|
||||||
|
<small class="form-text text-muted">You can choose to upload a small logo (e.g. 64px x 64px)</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Public Spaces Viewable By Anonymous Users</label>
|
<label>Public Spaces Viewable By Anonymous Users</label>
|
||||||
{{x-toggle value=model.general.allowAnonymousAccess size="medium" theme="light" onToggle=(action (mut model.general.allowAnonymousAccess))}}
|
{{x-toggle value=model.general.allowAnonymousAccess size="medium" theme="light" onToggle=(action (mut model.general.allowAnonymousAccess))}}
|
||||||
|
<small class="form-text text-muted">Share content with unauthenticated site visitors</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="conversionEndpoint">Conversion Service URL</label>
|
<label for="conversionEndpoint">Conversion Service URL</label>
|
||||||
{{input id="conversionEndpoint" type="text" value=model.general.conversionEndpoint class=(if hasConversionEndpointInputError "form-control is-invalid" "form-control")}}
|
{{input id="conversionEndpoint" type="text" value=model.general.conversionEndpoint class=(if hasConversionEndpointInputError "form-control is-invalid" "form-control")}}
|
||||||
<small class="form-text text-muted">
|
<small class="form-text text-muted">
|
||||||
Endpoint for handling import/export (e.g. https://api.documize.com,
|
Endpoint for handling import/export (e.g. https://api.documize.com,
|
||||||
<a href="https://docs.documize.com/s/WNEpptWJ9AABRnha/administration-guides/d/WO0pt_MXigAB6sJ7/general-options">view documentation</a>)
|
<a href="https://docs.documize.com/s/WNEpptWJ9AABRnha/administration-guides/d/WO0pt_MXigAB6sJ7/general-options">read the documentation</a>)
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -43,5 +52,4 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ui/ui-button color=constants.Color.Green light=true icon=constants.Icon.Settings label=constants.Label.Save onClick=(action "save")}}
|
{{ui/ui-button color=constants.Color.Green light=true icon=constants.Icon.Settings label=constants.Label.Save onClick=(action "save")}}
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
<ul class="space-labels">
|
<ul class="space-labels">
|
||||||
{{#each labels as |label|}}
|
{{#each labels as |label|}}
|
||||||
<li class="label" style={{concat "background-color:" label.color ";"}}>
|
<li class="label" style={{label.bgColor}}>
|
||||||
<div class="grid-container-6-4">
|
<div class="grid-container-6-4">
|
||||||
<div class="grid-cell-1 grid-cell-middle">
|
<div class="grid-cell-1 grid-cell-middle">
|
||||||
{{label.name}}
|
{{label.name}}
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
<li class="label none {{if (eq spaceLabel "") "selected"}}" {{action "onSetLabel" ""}}>None</li>
|
<li class="label none {{if (eq spaceLabel "") "selected"}}" {{action "onSetLabel" ""}}>None</li>
|
||||||
{{#each labels as |label|}}
|
{{#each labels as |label|}}
|
||||||
<li class="label {{if (eq spaceLabel label.id) "selected"}}"
|
<li class="label {{if (eq spaceLabel label.id) "selected"}}"
|
||||||
style={{concat "background-color:" label.color ";"}}
|
style={{label.bgColor}}
|
||||||
{{action "onSetLabel" label.id}} title={{label.name}}>
|
{{action "onSetLabel" label.id}} title={{label.name}}>
|
||||||
{{label.name}}
|
{{label.name}}
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
<div class="meta-icon">
|
<div class="meta-icon">
|
||||||
{{ui/ui-icon-meta icon=meta}}
|
{{ui/ui-icon-meta icon=meta}}
|
||||||
</div>
|
</div>
|
||||||
|
{{else if logo}}
|
||||||
|
<div class="meta-logo">
|
||||||
|
<img src="{{appMeta.endpoint}}/public/logo?cb={{cacheBuster}}"
|
||||||
|
style="max-width: 200px; max-height: 100px;">
|
||||||
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<div class="text">
|
<div class="text">
|
||||||
{{layout/page-heading title=title}}
|
{{layout/page-heading title=title}}
|
||||||
|
|
|
@ -99,6 +99,7 @@ const (
|
||||||
EventTypeLabelAdd EventType = "added-label"
|
EventTypeLabelAdd EventType = "added-label"
|
||||||
EventTypeLabelUpdate EventType = "updated-label"
|
EventTypeLabelUpdate EventType = "updated-label"
|
||||||
EventTypeLabelDelete EventType = "removed-label"
|
EventTypeLabelDelete EventType = "removed-label"
|
||||||
|
EventTypeOrganizationLogo EventType = "uploaded-logo"
|
||||||
|
|
||||||
// EventTypeVersionAdd records addition of version
|
// EventTypeVersionAdd records addition of version
|
||||||
EventTypeVersionAdd EventType = "added-version"
|
EventTypeVersionAdd EventType = "added-version"
|
||||||
|
|
|
@ -44,5 +44,4 @@ type SiteMeta struct {
|
||||||
Storage env.StoreType `json:"storageProvider"`
|
Storage env.StoreType `json:"storageProvider"`
|
||||||
Location string `json:"location"` // reserved for internal use
|
Location string `json:"location"` // reserved for internal use
|
||||||
Theme string `json:"theme"` // default side-wide theme, user overrideble
|
Theme string `json:"theme"` // default side-wide theme, user overrideble
|
||||||
Logo []byte `json:"logo"`
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,6 +94,9 @@ type InvitationModel struct {
|
||||||
// NewSpaceRequest details the new space to create.
|
// NewSpaceRequest details the new space to create.
|
||||||
type NewSpaceRequest struct {
|
type NewSpaceRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Description string `json:"desc"`
|
||||||
|
LabelID string `json:"labelId"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
CloneID string `json:"cloneId"` // existing space to clone, empty = no cloning
|
CloneID string `json:"cloneId"` // existing space to clone, empty = no cloning
|
||||||
CopyTemplate bool `json:"copyTemplate"` // copy templates and reusable content blocks
|
CopyTemplate bool `json:"copyTemplate"` // copy templates and reusable content blocks
|
||||||
CopyPermission bool `json:"copyPermission"` // copy uer permissions
|
CopyPermission bool `json:"copyPermission"` // copy uer permissions
|
||||||
|
|
|
@ -95,6 +95,7 @@ func RegisterEndpoints(rt *env.Runtime, s *store.Store) {
|
||||||
AddPublic(rt, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword)
|
AddPublic(rt, "reset/{token}", []string{"POST", "OPTIONS"}, nil, user.ResetPassword)
|
||||||
AddPublic(rt, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation)
|
AddPublic(rt, "share/{spaceID}", []string{"POST", "OPTIONS"}, nil, space.AcceptInvitation)
|
||||||
AddPublic(rt, "attachment/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, attachment.Download)
|
AddPublic(rt, "attachment/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, attachment.Download)
|
||||||
|
AddPublic(rt, "logo", []string{"GET", "OPTIONS"}, nil, meta.Logo)
|
||||||
|
|
||||||
//**************************************************
|
//**************************************************
|
||||||
// Secured private routes (require authentication)
|
// Secured private routes (require authentication)
|
||||||
|
@ -131,6 +132,7 @@ func RegisterEndpoints(rt *env.Runtime, s *store.Store) {
|
||||||
AddPrivate(rt, "organization/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
|
AddPrivate(rt, "organization/{orgID}", []string{"PUT", "OPTIONS"}, nil, organization.Update)
|
||||||
AddPrivate(rt, "organization/{orgID}/setting", []string{"GET", "OPTIONS"}, nil, setting.GetInstanceSetting)
|
AddPrivate(rt, "organization/{orgID}/setting", []string{"GET", "OPTIONS"}, nil, setting.GetInstanceSetting)
|
||||||
AddPrivate(rt, "organization/{orgID}/setting", []string{"POST", "OPTIONS"}, nil, setting.SaveInstanceSetting)
|
AddPrivate(rt, "organization/{orgID}/setting", []string{"POST", "OPTIONS"}, nil, setting.SaveInstanceSetting)
|
||||||
|
AddPrivate(rt, "organization/{orgID}/logo", []string{"POST", "OPTIONS"}, nil, organization.UploadLogo)
|
||||||
|
|
||||||
AddPrivate(rt, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
AddPrivate(rt, "space/{spaceID}", []string{"DELETE", "OPTIONS"}, nil, space.Delete)
|
||||||
AddPrivate(rt, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
AddPrivate(rt, "space/{spaceID}/move/{moveToId}", []string{"DELETE", "OPTIONS"}, nil, space.Remove)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue