mirror of
https://github.com/documize/community.git
synced 2025-07-24 15:49:44 +02:00
commit
7cc0b9c9b8
847 changed files with 47330 additions and 43756 deletions
14
README.md
14
README.md
|
@ -48,19 +48,13 @@ Integrations for embedding SaaS data within documents, zero add-on/marketplace f
|
|||
|
||||
## What does it look like?
|
||||
|
||||
All spaces.
|
||||
|
||||

|
||||
|
||||
Space view.
|
||||
|
||||

|
||||
|
||||
## Latest Release
|
||||
|
||||
[Community Edition: v1.76.2](https://github.com/documize/community/releases)
|
||||
[Community Edition: v2.0.0](https://github.com/documize/community/releases)
|
||||
|
||||
[Enterprise Edition: v1.76.2](https://documize.com/downloads)
|
||||
[Enterprise Edition: v2.0.0](https://documize.com/downloads)
|
||||
|
||||
## OS support
|
||||
|
||||
|
@ -99,8 +93,8 @@ Documize supports the following (evergreen) browsers:
|
|||
|
||||
Documize is built with the following technologies:
|
||||
|
||||
- EmberJS (v3.1.2)
|
||||
- Go (v1.11.2)
|
||||
- EmberJS (v3.5.1)
|
||||
- Go (v1.11.4)
|
||||
|
||||
## Authentication Options
|
||||
|
||||
|
|
|
@ -10,12 +10,15 @@ call ember b -o dist-prod/ --environment=production
|
|||
|
||||
echo "Copying Ember assets..."
|
||||
cd ..
|
||||
|
||||
rd /s /q embed\bindata\public
|
||||
mkdir embed\bindata\public
|
||||
echo "Copying Ember assets folder"
|
||||
robocopy /e /NFL /NDL /NJH gui\dist-prod\assets embed\bindata\public\assets
|
||||
echo "Copying Ember codemirror folder"
|
||||
robocopy /e /NFL /NDL /NJH gui\dist-prod\codemirror embed\bindata\public\codemirror
|
||||
echo "Copying Ember prism folder"
|
||||
robocopy /e /NFL /NDL /NJH gui\dist-prod\prism embed\bindata\public\prism
|
||||
echo "Copying Ember tinymce folder"
|
||||
robocopy /e /NFL /NDL /NJH gui\dist-prod\tinymce embed\bindata\public\tinymce
|
||||
echo "Copying Ember sections folder"
|
||||
|
|
9
build.sh
9
build.sh
|
@ -8,23 +8,26 @@ echo "Build process started $NOW"
|
|||
|
||||
echo "Building Ember assets..."
|
||||
cd gui
|
||||
ember b -o dist-prod/ --environment=production
|
||||
ember build ---environment=production --output-path dist-prod --suppress-sizes true
|
||||
cd ..
|
||||
|
||||
echo "Copying Ember assets..."
|
||||
cd ..
|
||||
rm -rf embed/bindata/public
|
||||
mkdir -p embed/bindata/public
|
||||
cp -r gui/dist-prod/assets embed/bindata/public
|
||||
cp -r gui/dist-prod/codemirror embed/bindata/public/codemirror
|
||||
cp -r gui/dist-prod/tinymce embed/bindata/public/tinymce
|
||||
cp -r gui/dist-prod/prism embed/bindata/public/prism
|
||||
cp -r gui/dist-prod/sections embed/bindata/public/sections
|
||||
cp -r gui/dist-prod/tinymce embed/bindata/public/tinymce
|
||||
cp gui/dist-prod/*.* embed/bindata
|
||||
cp gui/dist-prod/favicon.ico embed/bindata/public
|
||||
cp gui/dist-prod/manifest.json embed/bindata/public
|
||||
|
||||
rm -rf embed/bindata/mail
|
||||
mkdir -p embed/bindata/mail
|
||||
cp domain/mail/*.html embed/bindata/mail
|
||||
cp core/database/templates/*.html embed/bindata
|
||||
|
||||
rm -rf embed/bindata/scripts
|
||||
mkdir -p embed/bindata/scripts
|
||||
mkdir -p embed/bindata/scripts/mysql
|
||||
|
|
37
core/database/scripts/mysql/db_00027.sql
Normal file
37
core/database/scripts/mysql/db_00027.sql
Normal file
|
@ -0,0 +1,37 @@
|
|||
/* Community Edition */
|
||||
|
||||
-- Space labels provide name/color grouping
|
||||
DROP TABLE IF EXISTS `dmz_space_label`;
|
||||
CREATE TABLE IF NOT EXISTS `dmz_space_label` (
|
||||
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`c_refid` VARCHAR(20) NOT NULL COLLATE utf8_bin,
|
||||
`c_orgid` VARCHAR(20) NOT NULL COLLATE utf8_bin,
|
||||
`c_name` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`c_color` VARCHAR(10) NOT NULL DEFAULT '',
|
||||
`c_created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
`c_revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE INDEX `idx_space_label_1` (`id` ASC),
|
||||
INDEX `idx_space_label_2` (`c_refid` ASC),
|
||||
INDEX `idx_space_label_3` (`c_orgid` ASC))
|
||||
DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
|
||||
ENGINE = InnoDB;
|
||||
|
||||
-- Space table upgrade to support labelling, icon and summary stats
|
||||
ALTER TABLE dmz_space ADD COLUMN `c_desc` VARCHAR(200) NOT NULL DEFAULT '' AFTER `c_name`;
|
||||
ALTER TABLE dmz_space ADD COLUMN `c_labelid` VARCHAR(20) NOT NULL DEFAULT '' COLLATE utf8_bin AFTER `c_likes`;
|
||||
ALTER TABLE dmz_space ADD COLUMN `c_icon` VARCHAR(20) NOT NULL DEFAULT '' AFTER `c_labelid`;
|
||||
ALTER TABLE dmz_space ADD COLUMN `c_count_category` INT NOT NULL DEFAULT 0 AFTER `c_icon`;
|
||||
ALTER TABLE dmz_space ADD COLUMN `c_count_content` INT NOT NULL DEFAULT 0 AFTER `c_count_category`;
|
||||
|
||||
-- Org/tenant upgrade to support theming and custom logo
|
||||
ALTER TABLE dmz_org ADD COLUMN `c_theme` VARCHAR(20) NOT NULL DEFAULT '' AFTER `c_maxtags`;
|
||||
ALTER TABLE dmz_org ADD COLUMN `c_logo` LONGBLOB AFTER `c_theme`;
|
||||
|
||||
-- Populate default values for new fields
|
||||
UPDATE dmz_space s SET c_count_category=(SELECT COUNT(*) FROM dmz_category WHERE c_spaceid=s.c_refid);
|
||||
UPDATE dmz_space s SET c_count_content=(SELECT COUNT(*) FROM dmz_doc WHERE c_spaceid=s.c_refid);
|
||||
|
||||
-- BUGFIX: Remove zombie group membership records
|
||||
DELETE FROM dmz_group_member WHERE c_userid NOT IN (SELECT c_userid FROM dmz_user_account);
|
||||
|
||||
-- Deprecations
|
36
core/database/scripts/postgresql/db_00003.sql
Normal file
36
core/database/scripts/postgresql/db_00003.sql
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* Community Edition */
|
||||
|
||||
-- Space labels provide name/color grouping
|
||||
DROP TABLE IF EXISTS dmz_space_label;
|
||||
CREATE TABLE dmz_space_label (
|
||||
id bigserial NOT NULL,
|
||||
c_refid VARCHAR(20) NOT NULL COLLATE ucs_basic,
|
||||
c_orgid VARCHAR(20) NOT NULL COLLATE ucs_basic,
|
||||
c_name VARCHAR(50) NOT NULL DEFAULT '',
|
||||
c_color VARCHAR(10) NOT NULL DEFAULT '',
|
||||
c_created TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
c_revised TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (c_refid)
|
||||
);
|
||||
CREATE INDEX idx_space_label_1 ON dmz_space_label (id);
|
||||
CREATE INDEX idx_space_label_2 ON dmz_space_label (c_orgid);
|
||||
|
||||
-- Space table upgrade to support labelling, icon and summary stats
|
||||
ALTER TABLE dmz_space ADD COLUMN c_desc VARCHAR(200) NOT NULL DEFAULT '';
|
||||
ALTER TABLE dmz_space ADD COLUMN c_labelid VARCHAR(20) NOT NULL DEFAULT '' COLLATE ucs_basic;
|
||||
ALTER TABLE dmz_space ADD COLUMN c_icon VARCHAR(20) NOT NULL DEFAULT '';
|
||||
ALTER TABLE dmz_space ADD COLUMN c_count_category INT NOT NULL DEFAULT 0;
|
||||
ALTER TABLE dmz_space ADD COLUMN c_count_content INT NOT NULL DEFAULT 0;
|
||||
|
||||
-- Org/tenant upgrade to support theming and custom logo
|
||||
ALTER TABLE dmz_org ADD COLUMN c_theme VARCHAR(20) NOT NULL DEFAULT '';
|
||||
ALTER TABLE dmz_org ADD COLUMN c_logo BYTEA;
|
||||
|
||||
-- Populate default values for new fields
|
||||
UPDATE dmz_space s SET c_count_category=(SELECT COUNT(*) FROM dmz_category WHERE c_spaceid=s.c_refid);
|
||||
UPDATE dmz_space s SET c_count_content=(SELECT COUNT(*) FROM dmz_doc WHERE c_spaceid=s.c_refid);
|
||||
|
||||
-- BUGFIX: Remove zombie group membership records
|
||||
DELETE FROM dmz_group_member WHERE c_userid NOT IN (SELECT c_userid FROM dmz_user_account);
|
||||
|
||||
-- Deprecations
|
|
@ -14,10 +14,9 @@ package osutil
|
|||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/documize/community/core/log"
|
||||
)
|
||||
|
||||
var errTimeout = errors.New("conversion timelimit exceeded")
|
||||
|
@ -39,7 +38,7 @@ func CommandWithTimeout(command *exec.Cmd, timeout time.Duration) ([]byte, error
|
|||
select {
|
||||
case <-time.After(timeout):
|
||||
if err := command.Process.Kill(); err != nil {
|
||||
log.Error("failed to kill: ", err)
|
||||
fmt.Errorf("failed to kill: ", err)
|
||||
}
|
||||
<-done // prevent memory leak
|
||||
//fmt.Println("DEBUG timeout")
|
||||
|
|
|
@ -15,9 +15,12 @@ import (
|
|||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/documize/community/domain/auth"
|
||||
"github.com/documize/community/model/space"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/core/request"
|
||||
|
@ -47,9 +50,22 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
|
|||
method := "attachment.Download"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
ctx.Subdomain = organization.GetSubdomainFromHost(r)
|
||||
ctx.OrgID = request.Param(r, "orgID")
|
||||
|
||||
a, err := h.Store.Attachment.GetAttachment(ctx, request.Param(r, "orgID"), request.Param(r, "attachmentID"))
|
||||
// Is caller permitted to download this attachment?
|
||||
canDownload := false
|
||||
|
||||
// Do e have user authentication token?
|
||||
authToken := strings.TrimSpace(request.Query(r, "token"))
|
||||
|
||||
// Do we have secure sharing token (for external users)?
|
||||
secureToken := strings.TrimSpace(request.Query(r, "secure"))
|
||||
|
||||
// We now fetch attachment, the document and space it lives inside.
|
||||
// Any data loading issue spells the end of this request.
|
||||
|
||||
// Get attachment being requested.
|
||||
a, err := h.Store.Attachment.GetAttachment(ctx, ctx.OrgID, request.Param(r, "attachmentID"))
|
||||
if err == sql.ErrNoRows {
|
||||
response.WriteNotFoundError(w, method, request.Param(r, "fileID"))
|
||||
return
|
||||
|
@ -60,6 +76,99 @@ func (h *Handler) Download(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Get the document for this attachment
|
||||
doc, err := h.Store.Document.Get(ctx, a.DocumentID)
|
||||
if err == sql.ErrNoRows {
|
||||
response.WriteNotFoundError(w, method, a.DocumentID)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get attachment document", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the space for this attachment
|
||||
sp, err := h.Store.Space.Get(ctx, doc.SpaceID)
|
||||
if err == sql.ErrNoRows {
|
||||
response.WriteNotFoundError(w, method, a.DocumentID)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get attachment document", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the organization for this request
|
||||
// Get the space for this attachment
|
||||
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
|
||||
if err == sql.ErrNoRows {
|
||||
response.WriteNotFoundError(w, method, a.DocumentID)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get attachment org", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, all data associated data is loaded.
|
||||
// We now begin security checks based upon the request.
|
||||
|
||||
// If attachment is in public space then anyone can download
|
||||
if org.AllowAnonymousAccess && sp.Type == space.ScopePublic {
|
||||
canDownload = true
|
||||
}
|
||||
|
||||
// External users can be sent secure document viewing links.
|
||||
// Those documents may contain attachments that external viewers
|
||||
// can download as required.
|
||||
// Such secure document viewing links can have expiry dates.
|
||||
if !canDownload && len(secureToken) > 0 {
|
||||
canDownload = true
|
||||
}
|
||||
|
||||
// If an user authentication token was provided we check to see
|
||||
// if user can view document.
|
||||
// This check only applies to attachments NOT in public spaces.
|
||||
if !canDownload && len(authToken) > 0 {
|
||||
// Decode and check incoming token.
|
||||
creds, _, err := auth.DecodeJWT(h.Runtime, authToken)
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("get attachment decode auth token", err)
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
// Check for tampering.
|
||||
if ctx.OrgID != creds.OrgID {
|
||||
h.Runtime.Log.Error("get attachment org ID mismatch", err)
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// Use token-based user ID for subsequent processing.
|
||||
ctx.UserID = creds.UserID
|
||||
|
||||
// Check to see if user can view BOTH space and document.
|
||||
if !permission.CanViewSpace(ctx, *h.Store, sp.RefID) || !permission.CanViewDocument(ctx, *h.Store, a.DocumentID) {
|
||||
h.Runtime.Log.Error("get attachment cannot view document", err)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Authenticated user can view attachment.
|
||||
canDownload = true
|
||||
}
|
||||
|
||||
// Send back error if caller unable view attachment
|
||||
if !canDownload {
|
||||
h.Runtime.Log.Error("get attachment refused", err)
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
// At this point, user can view attachment so we send it back!
|
||||
typ := mime.TypeByExtension("." + a.Extension)
|
||||
if typ == "" {
|
||||
typ = "application/octet-stream"
|
||||
|
|
|
@ -46,7 +46,6 @@ import (
|
|||
"github.com/documize/community/model/doc"
|
||||
"github.com/documize/community/model/group"
|
||||
"github.com/documize/community/model/link"
|
||||
"github.com/documize/community/model/org"
|
||||
"github.com/documize/community/model/page"
|
||||
"github.com/documize/community/model/permission"
|
||||
"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)
|
||||
}
|
||||
|
||||
o := []org.Organization{}
|
||||
o := []orgExtended{}
|
||||
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_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_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)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -255,10 +254,6 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
|
|||
|
||||
// Config, User Config.
|
||||
func (b backerHandler) dmzConfig(files *[]backupItem) (err error) {
|
||||
type config struct {
|
||||
ConfigKey string `json:"key"`
|
||||
ConfigValue string `json:"config"`
|
||||
}
|
||||
c := []config{}
|
||||
err = b.Runtime.Db.Select(&c, `SELECT c_key AS configkey, c_config AS configvalue FROM dmz_config`)
|
||||
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)
|
||||
}
|
||||
|
||||
type userConfig struct {
|
||||
OrgID string `json:"orgId"`
|
||||
UserID string `json:"userId"`
|
||||
ConfigKey string `json:"key"`
|
||||
ConfigValue string `json:"config"`
|
||||
}
|
||||
uc := []userConfig{}
|
||||
|
||||
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)
|
||||
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,
|
||||
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_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
|
||||
FROM dmz_space`+w)
|
||||
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})
|
||||
|
||||
// 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{}
|
||||
err = b.Runtime.Db.Select(&vt, `
|
||||
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})
|
||||
|
||||
// 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{}
|
||||
err = b.Runtime.Db.Select(&cm, `
|
||||
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})
|
||||
|
||||
// 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{}
|
||||
err = b.Runtime.Db.Select(&sh, `
|
||||
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/group"
|
||||
"github.com/documize/community/model/link"
|
||||
"github.com/documize/community/model/org"
|
||||
"github.com/documize/community/model/page"
|
||||
"github.com/documize/community/model/permission"
|
||||
"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) {
|
||||
filename := "dmz_org.json"
|
||||
|
||||
org := []org.Organization{}
|
||||
org := []orgExtended{}
|
||||
err = r.fileJSON(filename, &org)
|
||||
if err != nil {
|
||||
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(`
|
||||
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_maxtags, c_verified, c_serial, c_sub, c_active, c_created, c_revised)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
||||
c_maxtags, c_verified, c_serial, c_sub, c_active,
|
||||
c_theme, c_logo, c_created, c_revised)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
|
||||
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),
|
||||
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
|
||||
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)
|
||||
if err != nil {
|
||||
r.Context.Transaction.Rollback()
|
||||
|
@ -402,6 +403,7 @@ func (r *restoreHandler) dmzOrg() (err error) {
|
|||
org[0].Serial = r.Spec.Org.Serial
|
||||
org[0].Title = r.Spec.Org.Title
|
||||
org[0].Subscription = r.Spec.Org.Subscription
|
||||
org[0].Theme = r.Spec.Org.Theme
|
||||
}
|
||||
|
||||
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
|
||||
|
@ -612,8 +614,16 @@ func (r *restoreHandler) dmzSpace() (err error) {
|
|||
}
|
||||
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||
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)
|
||||
_, 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_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 {
|
||||
r.Context.Transaction.Rollback()
|
||||
|
|
|
@ -105,6 +105,14 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = h.Store.Space.IncrementCategoryCount(ctx, cat.SpaceID)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
cat, err = h.Store.Category.Get(ctx, cat.RefID)
|
||||
|
@ -295,6 +303,14 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
err = h.Store.Space.DecrementCategoryCount(ctx, cat.SpaceID)
|
||||
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.EventTypeCategoryDelete)
|
||||
|
|
|
@ -50,14 +50,17 @@ func (s Store) GetBySpace(ctx domain.RequestContext, spaceID string) (c []catego
|
|||
SELECT id, c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_name AS name, c_created AS created, c_revised AS revised
|
||||
FROM dmz_category
|
||||
WHERE c_orgid=? AND c_spaceid=? AND c_refid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='category' AND c_refid IN
|
||||
(SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='category'
|
||||
(
|
||||
SELECT c_refid
|
||||
FROM dmz_permission
|
||||
WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='category'
|
||||
UNION ALL
|
||||
SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='category' AND (r.c_userid=? OR r.c_userid='0')
|
||||
))
|
||||
SELECT p.c_refid
|
||||
FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='category' AND (r.c_userid=? OR r.c_userid='0')
|
||||
)
|
||||
ORDER BY name`),
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
|
@ -77,14 +80,17 @@ func (s Store) GetAllBySpace(ctx domain.RequestContext, spaceID string) (c []cat
|
|||
SELECT id, c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_name AS name, c_created AS created, c_revised AS revised
|
||||
FROM dmz_category
|
||||
WHERE c_orgid=? AND c_spaceid=? AND c_spaceid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
(
|
||||
SELECT c_refid
|
||||
FROM dmz_permission
|
||||
WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
UNION ALL
|
||||
SELECT p.c_refid FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
SELECT p.c_refid
|
||||
FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
))
|
||||
)
|
||||
ORDER BY c_name`),
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
|
@ -280,15 +286,19 @@ func (s Store) GetSpaceCategoryMembership(ctx domain.RequestContext, spaceID str
|
|||
SELECT id, c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_categoryid AS categoryid, c_docid AS documentid, c_created AS created, c_revised AS revised
|
||||
FROM dmz_category_member
|
||||
WHERE c_orgid=? AND c_spaceid=? AND c_spaceid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
(
|
||||
SELECT c_refid
|
||||
FROM dmz_permission
|
||||
WHERE c_orgid=? AND c_who='user'
|
||||
AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
UNION ALL
|
||||
SELECT p.c_refid FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
SELECT p.c_refid
|
||||
FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='space'
|
||||
AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
))
|
||||
AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
)
|
||||
ORDER BY documentid`),
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, ctx.UserID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
|
@ -306,14 +316,17 @@ func (s Store) GetOrgCategoryMembership(ctx domain.RequestContext, userID string
|
|||
SELECT id, c_refid AS refid, c_orgid AS orgid, c_spaceid AS spaceid, c_categoryid AS categoryid, c_docid AS documentid, c_created AS created, c_revised AS revised
|
||||
FROM dmz_category_member
|
||||
WHERE c_orgid=? AND c_spaceid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid IN
|
||||
(SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
(
|
||||
SELECT c_refid
|
||||
FROM dmz_permission
|
||||
WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
UNION ALL
|
||||
SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='space'
|
||||
AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
))
|
||||
SELECT p.c_refid
|
||||
FROM dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid
|
||||
WHERE p.c_orgid=? AND p.c_who='role' AND p.c_location='space' AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
)
|
||||
ORDER BY documentid`),
|
||||
ctx.OrgID, ctx.OrgID, ctx.OrgID, userID, ctx.OrgID, userID)
|
||||
ctx.OrgID, ctx.OrgID, userID, ctx.OrgID, userID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
|
|
|
@ -248,6 +248,12 @@ func processDocument(ctx domain.RequestContext, r *env.Runtime, store *store.Sto
|
|||
SourceType: activity.SourceTypeDocument,
|
||||
ActivityType: activity.TypeCreated})
|
||||
|
||||
err = store.Space.IncrementContentCount(ctx, newDocument.SpaceID)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "cannot increment space content count")
|
||||
return
|
||||
}
|
||||
|
||||
err = ctx.Transaction.Commit()
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "cannot commit new document import")
|
||||
|
|
|
@ -166,9 +166,6 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Sort by title.
|
||||
sort.Sort(doc.ByName(documents))
|
||||
|
||||
// Remove documents that cannot be seen due to lack of
|
||||
// category view/access permission.
|
||||
cats, err := h.Store.Category.GetBySpace(ctx, spaceID)
|
||||
|
@ -178,6 +175,30 @@ func (h *Handler) BySpace(w http.ResponseWriter, r *http.Request) {
|
|||
// Keep the latest version when faced with multiple versions.
|
||||
filtered = FilterLastVersion(filtered)
|
||||
|
||||
// Sort document list by ID.
|
||||
sort.Sort(doc.ByID(filtered))
|
||||
|
||||
// Attach category membership to each document.
|
||||
// Put category names into map for easier retrieval.
|
||||
catNames := make(map[string]string)
|
||||
for i := range cats {
|
||||
catNames[cats[i].RefID] = cats[i].Name
|
||||
}
|
||||
// Loop the smaller list which is categories assigned to documents.
|
||||
for i := range members {
|
||||
// Get name of category
|
||||
cn := catNames[members[i].CategoryID]
|
||||
// Find document that is assigned this category.
|
||||
j := sort.Search(len(filtered), func(k int) bool { return filtered[k].RefID <= members[i].DocumentID })
|
||||
// Attach category name to document
|
||||
if j < len(filtered) && filtered[j].RefID == members[i].DocumentID {
|
||||
filtered[j].Category = append(filtered[j].Category, cn)
|
||||
}
|
||||
}
|
||||
|
||||
// Sort document list by title.
|
||||
sort.Sort(doc.ByName(filtered))
|
||||
|
||||
response.WriteJSON(w, filtered)
|
||||
}
|
||||
|
||||
|
@ -396,6 +417,14 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
ActivityType: activity.TypeDeleted})
|
||||
}
|
||||
|
||||
err = h.Store.Space.DecrementContentCount(ctx, doc.SpaceID)
|
||||
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.EventTypeDocumentDelete)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -81,17 +81,15 @@ func (s Store) GetBySpace(ctx domain.RequestContext, spaceID string) (documents
|
|||
c_versionorder AS versionorder, c_groupid AS groupid, c_created AS created, c_revised AS revised
|
||||
FROM dmz_doc
|
||||
WHERE c_orgid=? AND c_template=false AND c_spaceid IN
|
||||
(SELECT c_refid FROM dmz_space WHERE c_orgid=? AND c_refid IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid=? AND c_refid IN
|
||||
(SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
UNION ALL
|
||||
SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=?
|
||||
AND p.c_who='role' AND p.c_location='space' AND p.c_refid=? AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
)
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_location='space' AND c_refid=? AND c_refid IN
|
||||
(SELECT c_refid from dmz_permission WHERE c_orgid=? AND c_who='user' AND (c_whoid=? OR c_whoid='0') AND c_location='space' AND c_action='view'
|
||||
UNION ALL
|
||||
SELECT p.c_refid from dmz_permission p LEFT JOIN dmz_group_member r ON p.c_whoid=r.c_groupid WHERE p.c_orgid=?
|
||||
AND p.c_who='role' AND p.c_location='space' AND p.c_refid=? AND p.c_action='view' AND (r.c_userid=? OR r.c_userid='0')
|
||||
)
|
||||
)
|
||||
ORDER BY c_name, c_versionorder`),
|
||||
ctx.OrgID, ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, spaceID, ctx.UserID)
|
||||
ctx.OrgID, ctx.OrgID, spaceID, ctx.OrgID, ctx.UserID, ctx.OrgID, spaceID, ctx.UserID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
|
@ -100,6 +98,9 @@ func (s Store) GetBySpace(ctx domain.RequestContext, spaceID string) (documents
|
|||
err = errors.Wrap(err, "select documents by space")
|
||||
}
|
||||
|
||||
// (SELECT c_refid FROM dmz_space WHERE c_orgid=? AND c_refid IN
|
||||
// )
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -179,3 +179,19 @@ func (s Store) GetMembers(ctx domain.RequestContext) (r []group.Record, err erro
|
|||
|
||||
return
|
||||
}
|
||||
|
||||
// RemoveUserGroups remove user from all group.
|
||||
func (s Store) RemoveUserGroups(ctx domain.RequestContext, userID string) (err error) {
|
||||
_, err = s.DeleteWhere(ctx.Transaction,
|
||||
fmt.Sprintf("DELETE FROM dmz_group_member WHERE c_orgid='%s' AND c_userid='%s'",
|
||||
ctx.OrgID, userID))
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "RemoveUserGroups")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
201
domain/label/endpoint.go
Normal file
201
domain/label/endpoint.go
Normal file
|
@ -0,0 +1,201 @@
|
|||
// 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 label
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"github.com/documize/community/core/env"
|
||||
"github.com/documize/community/core/request"
|
||||
"github.com/documize/community/core/response"
|
||||
"github.com/documize/community/core/streamutil"
|
||||
"github.com/documize/community/core/uniqueid"
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/domain/store"
|
||||
"github.com/documize/community/model/audit"
|
||||
"github.com/documize/community/model/label"
|
||||
)
|
||||
|
||||
// Handler contains the runtime information such as logging and database.
|
||||
type Handler struct {
|
||||
Runtime *env.Runtime
|
||||
Store *store.Store
|
||||
}
|
||||
|
||||
// Add space label to the store.
|
||||
func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||
method := "label.Add"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !h.Runtime.Product.IsValid(ctx) {
|
||||
response.WriteBadLicense(w)
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Administrator {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
defer streamutil.Close(r.Body)
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
response.WriteBadRequestError(w, method, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
l := label.Label{}
|
||||
err = json.Unmarshal(body, &l)
|
||||
if err != nil {
|
||||
response.WriteBadRequestError(w, method, err.Error())
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
l.RefID = uniqueid.Generate()
|
||||
|
||||
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.Label.Add(ctx, l)
|
||||
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.EventTypeLabelAdd)
|
||||
|
||||
response.WriteJSON(w, l)
|
||||
}
|
||||
|
||||
// Get returns all space labels.
|
||||
func (h *Handler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
method := "label.Get"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
l, err := h.Store.Label.Get(ctx)
|
||||
if err != nil {
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
if len(l) == 0 {
|
||||
l = []label.Label{}
|
||||
}
|
||||
|
||||
response.WriteJSON(w, l)
|
||||
}
|
||||
|
||||
// Update persists label name/color changes.
|
||||
func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
||||
method := "label.Update"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
if !ctx.Administrator {
|
||||
response.WriteForbiddenError(w)
|
||||
return
|
||||
}
|
||||
|
||||
labelID := request.Param(r, "labelID")
|
||||
if len(labelID) == 0 {
|
||||
response.WriteMissingDataError(w, method, "labelID")
|
||||
return
|
||||
}
|
||||
|
||||
defer streamutil.Close(r.Body)
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
response.WriteBadRequestError(w, method, "Bad payload")
|
||||
return
|
||||
}
|
||||
|
||||
l := label.Label{}
|
||||
err = json.Unmarshal(body, &l)
|
||||
if err != nil {
|
||||
response.WriteBadRequestError(w, method, "Bad payload")
|
||||
return
|
||||
}
|
||||
|
||||
l.RefID = labelID
|
||||
|
||||
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.Label.Update(ctx, l)
|
||||
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.EventTypeLabelUpdate)
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
||||
|
||||
// Delete removes space label from store and
|
||||
// removes label association from spaces.
|
||||
func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
||||
method := "label.Delete"
|
||||
ctx := domain.GetRequestContext(r)
|
||||
|
||||
labelID := request.Param(r, "labelID")
|
||||
if len(labelID) == 0 {
|
||||
response.WriteMissingDataError(w, method, "labelID")
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
ctx.Transaction, err = h.Runtime.Db.Beginx()
|
||||
if err != nil {
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Label.Delete(ctx, labelID)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.Store.Label.RemoveReference(ctx, labelID)
|
||||
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.EventTypeLabelDelete)
|
||||
|
||||
response.WriteEmpty(w)
|
||||
}
|
106
domain/label/store.go
Normal file
106
domain/label/store.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
// 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 label
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/documize/community/domain"
|
||||
"github.com/documize/community/domain/store"
|
||||
"github.com/documize/community/model/label"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Store provides data access to section template information.
|
||||
type Store struct {
|
||||
store.Context
|
||||
store.LabelStorer
|
||||
}
|
||||
|
||||
// Add saves space label to store.
|
||||
func (s Store) Add(ctx domain.RequestContext, l label.Label) (err error) {
|
||||
l.OrgID = ctx.OrgID
|
||||
l.Created = time.Now().UTC()
|
||||
l.Revised = time.Now().UTC()
|
||||
|
||||
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_space_label (c_refid, c_orgid, c_name, c_color, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?)"),
|
||||
l.RefID, l.OrgID, l.Name, l.Color, l.Created, l.Revised)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute insert label")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns all space labels from store.
|
||||
func (s Store) Get(ctx domain.RequestContext) (l []label.Label, err error) {
|
||||
err = s.Runtime.Db.Select(&l, s.Bind(`
|
||||
SELECT id, c_refid as refid,
|
||||
c_orgid as orgid,
|
||||
c_name AS name, c_color AS color,
|
||||
c_created AS created, c_revised AS revised
|
||||
FROM dmz_space_label
|
||||
WHERE c_orgid=?`),
|
||||
ctx.OrgID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute select label")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Update persists space label changes to the store.
|
||||
func (s Store) Update(ctx domain.RequestContext, l label.Label) (err error) {
|
||||
l.Revised = time.Now().UTC()
|
||||
|
||||
_, err = ctx.Transaction.NamedExec(s.Bind(`UPDATE dmz_space_label SET
|
||||
c_name=:name, c_color=:color, c_revised=:revised
|
||||
WHERE c_orgid=:orgid AND c_refid=:refid`),
|
||||
l)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute update label")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Delete removes space label from the store.
|
||||
func (s Store) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
|
||||
return s.DeleteConstrained(ctx.Transaction, "dmz_space_label", ctx.OrgID, id)
|
||||
}
|
||||
|
||||
// RemoveReference clears space.labelID for given label.
|
||||
func (s Store) RemoveReference(ctx domain.RequestContext, spaceID string) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_space SET
|
||||
c_labelid='', c_revised=?
|
||||
WHERE c_orgid=? AND c_refid=?`),
|
||||
time.Now().UTC(), ctx.OrgID, spaceID)
|
||||
|
||||
if err == sql.ErrNoRows {
|
||||
err = nil
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute remove space label reference")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
|
@ -97,7 +97,6 @@ func (h *Handler) GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
|||
if len(files) == 0 {
|
||||
files = []attachment.Attachment{}
|
||||
}
|
||||
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
|
|
|
@ -76,7 +76,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">View document</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">View document</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -87,7 +87,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.Url}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
<a href="{{.Url}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -72,7 +72,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -82,7 +82,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to access Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -79,7 +79,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to reset your password</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Click here to reset your password</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -75,7 +75,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Login to Documize</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Login to Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -81,7 +81,7 @@ background-color: #f6f6f6;
|
|||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
|
||||
<a href="{{.URL}}" class="btn-primary" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Go to Documize</a>
|
||||
<a href="{{.URL}}" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; color: #FFF; text-decoration: none; line-height: 2; font-weight: bold; text-align: center; cursor: pointer; display: inline-block; border-radius: 5px; background: #4ccb6a; margin: 0; padding: 0; border-color: #4ccb6a; border-style: solid; border-width: 10px 20px;">Go to Documize</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
|
||||
|
|
|
@ -13,6 +13,7 @@ package meta
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -57,6 +58,7 @@ func (h *Handler) Meta(w http.ResponseWriter, r *http.Request) {
|
|||
data.AuthProvider = strings.TrimSpace(org.AuthProvider)
|
||||
data.AuthConfig = org.AuthConfig
|
||||
data.MaxTags = org.MaxTags
|
||||
data.Theme = org.Theme
|
||||
data.Version = h.Runtime.Product.Version
|
||||
data.Revision = h.Runtime.Product.Revision
|
||||
data.Edition = h.Runtime.Product.Edition
|
||||
|
@ -111,6 +113,10 @@ Disallow: /auth/*
|
|||
Disallow: /auth/**
|
||||
Disallow: /share
|
||||
Disallow: /share/*
|
||||
Disallow: /attachments
|
||||
Disallow: /attachments/*
|
||||
Disallow: /attachment
|
||||
Disallow: /attachment/*
|
||||
Sitemap: %s`, sitemap)
|
||||
}
|
||||
|
||||
|
@ -190,7 +196,7 @@ func (h *Handler) Reindex(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if !ctx.GlobalAdmin {
|
||||
response.WriteForbiddenError(w)
|
||||
h.Runtime.Log.Info(fmt.Sprintf("%s attempted search reindex"))
|
||||
h.Runtime.Log.Info(fmt.Sprintf("%s attempted search reindex", ctx.UserID))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -253,7 +259,7 @@ func (h *Handler) SearchStatus(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
if !ctx.GlobalAdmin {
|
||||
response.WriteForbiddenError(w)
|
||||
h.Runtime.Log.Info(fmt.Sprintf("%s attempted get of search status"))
|
||||
h.Runtime.Log.Info(fmt.Sprintf("%s attempted get of search status", ctx.UserID))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -277,3 +283,72 @@ type sitemapItem struct {
|
|||
type searchStatus struct {
|
||||
Entries int `json:"entries"`
|
||||
}
|
||||
|
||||
// Themes returns list of installed UI themes.
|
||||
func (h *Handler) Themes(w http.ResponseWriter, r *http.Request) {
|
||||
type theme struct {
|
||||
Name string `json:"name"`
|
||||
Primary string `json:"primary"`
|
||||
}
|
||||
|
||||
th := []theme{}
|
||||
th = append(th, theme{Name: "", Primary: "#280A42"})
|
||||
th = append(th, theme{Name: "Brave", Primary: "#BF360C"})
|
||||
th = append(th, theme{Name: "Conference", Primary: "#176091"})
|
||||
th = append(th, theme{Name: "Forest", Primary: "#00695C"})
|
||||
th = append(th, theme{Name: "Harvest", Primary: "#A65F20"})
|
||||
th = append(th, theme{Name: "Silver", Primary: "#AEBECC"})
|
||||
th = append(th, theme{Name: "Sunflower", Primary: "#D7B92F"})
|
||||
|
||||
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)
|
||||
|
||||
// If organization has logo, send it back.
|
||||
logo, err := h.Store.Organization.Logo(ctx, d)
|
||||
if err == nil && len(logo) > 0 {
|
||||
h.writeLogo(w, r, logo)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
h.Runtime.Log.Infof("unable to fetch logo for domain %s", d)
|
||||
}
|
||||
|
||||
// Otherwise, we send back default logo.
|
||||
h.DefaultLogo(w, r)
|
||||
}
|
||||
|
||||
// DefaultLogo write the default Documize logo to the HTTP response.
|
||||
func (h *Handler) DefaultLogo(w http.ResponseWriter, r *http.Request) {
|
||||
logo, err := base64.StdEncoding.DecodeString(defaultLogo)
|
||||
if err != nil {
|
||||
h.Runtime.Log.Error("unable to decode default logo", err)
|
||||
response.WriteEmpty(w)
|
||||
return
|
||||
}
|
||||
|
||||
h.writeLogo(w, r, logo)
|
||||
}
|
||||
|
||||
// writeLogo writes byte array as logo to HTTP response stream.
|
||||
func (h *Handler) writeLogo(w http.ResponseWriter, r *http.Request, logo []byte) {
|
||||
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 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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"github.com/documize/community/model/audit"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
|
@ -99,3 +102,50 @@ func (h *Handler) Update(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -50,9 +50,9 @@ func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Or
|
|||
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_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,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
c_maxtags AS maxtags, c_theme AS theme, c_created AS created, c_revised AS revised
|
||||
FROM dmz_org
|
||||
WHERE c_refid=?`),
|
||||
id)
|
||||
|
@ -82,9 +82,9 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
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_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,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme
|
||||
FROM dmz_org
|
||||
WHERE c_domain=? AND c_active=true`),
|
||||
subdomain)
|
||||
|
@ -97,9 +97,9 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
|
|||
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_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,
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised
|
||||
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme
|
||||
FROM dmz_org
|
||||
WHERE c_domain='' AND c_active=true`))
|
||||
|
||||
|
@ -116,7 +116,7 @@ func (s Store) UpdateOrganization(ctx domain.RequestContext, org org.Organizatio
|
|||
|
||||
_, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET
|
||||
c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email,
|
||||
c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_revised=:revised
|
||||
c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_theme=:theme, c_revised=:revised
|
||||
WHERE c_refid=:refid`,
|
||||
&org)
|
||||
|
||||
|
@ -176,3 +176,31 @@ func (s Store) CheckDomain(ctx domain.RequestContext, domain string) string {
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -157,7 +157,6 @@ func (p *Provider) Refresh(ctx *provider.Context, config, data string) (newData
|
|||
func auth(ctx *provider.Context, store *store.Store, w http.ResponseWriter, r *http.Request) {
|
||||
defer r.Body.Close()
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
|
||||
if err != nil {
|
||||
provider.WriteMessage(w, "gemini", "Bad payload")
|
||||
return
|
||||
|
@ -165,7 +164,6 @@ func auth(ctx *provider.Context, store *store.Store, w http.ResponseWriter, r *h
|
|||
|
||||
var config = geminiConfig{}
|
||||
err = json.Unmarshal(body, &config)
|
||||
|
||||
if err != nil {
|
||||
provider.WriteMessage(w, "gemini", "Bad payload")
|
||||
return
|
||||
|
@ -177,30 +175,26 @@ func auth(ctx *provider.Context, store *store.Store, w http.ResponseWriter, r *h
|
|||
provider.WriteMessage(w, "gemini", "Missing URL value")
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.Username) == 0 {
|
||||
provider.WriteMessage(w, "gemini", "Missing Username value")
|
||||
return
|
||||
}
|
||||
|
||||
if len(config.APIKey) == 0 {
|
||||
provider.WriteMessage(w, "gemini", "Missing APIKey value")
|
||||
return
|
||||
}
|
||||
|
||||
creds := []byte(fmt.Sprintf("%s:%s", config.Username, config.APIKey))
|
||||
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/users/username/%s", config.URL, config.Username), nil)
|
||||
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString(creds))
|
||||
|
||||
client := &http.Client{}
|
||||
res, err := client.Do(req)
|
||||
|
||||
if err != nil {
|
||||
provider.WriteError(w, "gemini", err)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
provider.WriteForbidden(w)
|
||||
return
|
||||
|
@ -213,9 +207,9 @@ func auth(ctx *provider.Context, store *store.Store, w http.ResponseWriter, r *h
|
|||
|
||||
dec := json.NewDecoder(res.Body)
|
||||
err = dec.Decode(&g)
|
||||
|
||||
if err != nil {
|
||||
provider.WriteError(w, "gemini", err)
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ const renderTemplate = `
|
|||
<tr>
|
||||
<td class="bordered no-width"><a href="{{ $app }}/browse/{{ $item.Key }}">{{ $item.Key }} </a></td>
|
||||
<td class="bordered no-width"><img class="section-jira-icon" src='{{ $item.Fields.Type.IconURL }}' /></td>
|
||||
<td class="bordered no-width"><span class="badge badge-warning">{{ $item.Fields.Status.Name }}</span> </td>
|
||||
<td class="bordered no-width"><span class="seciton-jira-status">{{ $item.Fields.Status.Name }}</span> </td>
|
||||
<td class="bordered no-width"><img class="section-jira-icon" src='{{ $item.Fields.Priority.IconURL }}' /></td>
|
||||
<td class="bordered no-width">
|
||||
{{range $comp := $item.Fields.Components}}
|
||||
|
|
|
@ -28,7 +28,7 @@ const renderTemplate = `
|
|||
<tbody>
|
||||
{{range $item := .Events}}
|
||||
<tr>
|
||||
<td class="bordered no-width color-gray">{{ $item.Dated }}</td>
|
||||
<td class="bordered no-width color-gray-600">{{ $item.Dated }}</td>
|
||||
<td class="bordered no-width">{{ $item.Severity }}</td>
|
||||
<td class="bordered width-90">{{ $item.Message }}</td>
|
||||
</tr>
|
||||
|
|
|
@ -99,6 +99,9 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
var sp space.Space
|
||||
sp.Name = model.Name
|
||||
sp.Description = model.Description
|
||||
sp.Icon = model.Icon
|
||||
sp.LabelID = model.LabelID
|
||||
sp.RefID = uniqueid.Generate()
|
||||
sp.OrgID = ctx.OrgID
|
||||
sp.UserID = ctx.UserID
|
||||
|
@ -673,77 +676,84 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Delete the space first.
|
||||
ok := true
|
||||
ctx.Transaction, ok = h.Runtime.StartTx()
|
||||
if !ok {
|
||||
if !ok {
|
||||
response.WriteError(w, method)
|
||||
return
|
||||
}
|
||||
|
||||
_, err := h.Store.Document.DeleteBySpace(ctx, id)
|
||||
_, err := h.Store.Space.Delete(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.Runtime.Commit(ctx.Transaction)
|
||||
|
||||
// Delete data associated with this space.
|
||||
ctx.Transaction, ok = h.Runtime.StartTx()
|
||||
if !ok {
|
||||
response.WriteError(w, method)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Document.DeleteBySpace(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Permission.DeleteSpacePermissions(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// remove category permissions
|
||||
_, err = h.Store.Permission.DeleteSpaceCategoryPermissions(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Category.DeleteBySpace(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Pin.DeletePinnedSpace(ctx, id)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// remove category and members for space
|
||||
_, err = h.Store.Category.DeleteBySpace(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = h.Store.Space.Delete(ctx, id)
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Close out the delete process
|
||||
h.Runtime.Commit(ctx.Transaction)
|
||||
|
||||
// Record this action.
|
||||
ctx.Transaction, ok = h.Runtime.StartTx()
|
||||
if !ok {
|
||||
response.WriteError(w, method)
|
||||
return
|
||||
}
|
||||
ctx.Transaction, ok = h.Runtime.StartTx()
|
||||
if !ok {
|
||||
response.WriteError(w, method)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.Store.Activity.RecordUserActivity(ctx, activity.UserActivity{
|
||||
SpaceID: id,
|
||||
SourceType: activity.SourceTypeSpace,
|
||||
ActivityType: activity.TypeDeleted})
|
||||
if err != nil {
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Rollback(ctx.Transaction)
|
||||
response.WriteServerError(w, method, err)
|
||||
}
|
||||
|
||||
h.Runtime.Commit(ctx.Transaction)
|
||||
h.Runtime.Commit(ctx.Transaction)
|
||||
|
||||
h.Store.Audit.Record(ctx, audit.EventTypeSpaceDelete)
|
||||
|
||||
|
|
|
@ -30,8 +30,15 @@ type Store struct {
|
|||
|
||||
// Add adds new folder into the store.
|
||||
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_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"),
|
||||
sp.RefID, sp.Name, sp.OrgID, sp.UserID, sp.Type, sp.Lifecycle, sp.Likes, sp.Created, sp.Revised)
|
||||
_, 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.Icon, sp.Description, sp.CountCategory, sp.CountContent, sp.LabelID,
|
||||
sp.Created, sp.Revised)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "unable to execute insert for space")
|
||||
|
@ -45,6 +52,8 @@ func (s Store) Get(ctx domain.RequestContext, id string) (sp space.Space, err er
|
|||
err = s.Runtime.Db.Get(&sp, s.Bind(`SELECT id, c_refid AS refid,
|
||||
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_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
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? and c_refid=?`),
|
||||
|
@ -62,6 +71,8 @@ func (s Store) PublicSpaces(ctx domain.RequestContext, orgID string) (sp []space
|
|||
qry := s.Bind(`SELECT id, c_refid AS refid,
|
||||
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_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
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND c_type=1`)
|
||||
|
@ -85,6 +96,8 @@ func (s Store) GetViewable(ctx domain.RequestContext) (sp []space.Space, err err
|
|||
q := s.Bind(`SELECT id, c_refid AS refid,
|
||||
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_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
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND c_refid IN
|
||||
|
@ -122,14 +135,18 @@ func (s Store) AdminList(ctx domain.RequestContext) (sp []space.Space, err error
|
|||
SELECT id, c_refid AS refid,
|
||||
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_created AS created, c_revised AS revised
|
||||
c_created AS created, c_revised AS revised,
|
||||
c_icon AS icon, c_labelid AS labelid, c_desc AS description,
|
||||
c_count_category AS countcategory, c_count_content AS countcontent
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND (c_type=? OR c_type=?)
|
||||
UNION ALL
|
||||
SELECT id, c_refid AS refid,
|
||||
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_created AS created, c_revised AS revised
|
||||
c_created AS created, c_revised AS revised,
|
||||
c_icon AS icon, c_labelid AS labelid, c_desc AS description,
|
||||
c_count_category AS countcategory, c_count_content AS countcontent
|
||||
FROM dmz_space
|
||||
WHERE c_orgid=? AND (c_type=? OR c_type=?) AND c_refid NOT IN
|
||||
(SELECT c_refid FROM dmz_permission WHERE c_orgid=? AND c_action='own')
|
||||
|
@ -154,7 +171,13 @@ func (s Store) AdminList(ctx domain.RequestContext) (sp []space.Space, err error
|
|||
func (s Store) Update(ctx domain.RequestContext, sp space.Space) (err error) {
|
||||
sp.Revised = time.Now().UTC()
|
||||
|
||||
_, err = ctx.Transaction.NamedExec("UPDATE dmz_space SET c_name=:name, c_type=:type, c_lifecycle=:lifecycle, c_userid=:userid, c_likes=:likes, c_revised=:revised WHERE c_orgid=:orgid AND c_refid=:refid", &sp)
|
||||
_, err = ctx.Transaction.NamedExec(`
|
||||
UPDATE dmz_space
|
||||
SET c_name=:name, c_type=:type, c_lifecycle=:lifecycle, c_userid=:userid,
|
||||
c_likes=:likes, c_desc=:description, c_labelid=:labelid, c_icon=:icon,
|
||||
c_count_category=:countcategory, c_count_content=:countcontent,
|
||||
c_revised=:revised
|
||||
WHERE c_orgid=:orgid AND c_refid=:refid`, &sp)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, fmt.Sprintf("unable to execute update for space %s", sp.RefID))
|
||||
}
|
||||
|
@ -166,3 +189,55 @@ func (s Store) Update(ctx domain.RequestContext, sp space.Space) (err error) {
|
|||
func (s Store) Delete(ctx domain.RequestContext, id string) (rows int64, err error) {
|
||||
return s.DeleteConstrained(ctx.Transaction, "dmz_space", ctx.OrgID, id)
|
||||
}
|
||||
|
||||
// IncrementCategoryCount increments usage counter for space category.
|
||||
func (s Store) IncrementCategoryCount(ctx domain.RequestContext, spaceID string) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_space SET
|
||||
c_count_category=c_count_category+1, c_revised=? WHERE c_orgid=? AND c_refid=?`),
|
||||
time.Now().UTC(), ctx.OrgID, spaceID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute increment category count")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecrementCategoryCount decrements usage counter for space category.
|
||||
func (s Store) DecrementCategoryCount(ctx domain.RequestContext, spaceID string) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_space SET
|
||||
c_count_category=c_count_category-1, c_revised=? WHERE c_orgid=? AND c_refid=?`),
|
||||
time.Now().UTC(), ctx.OrgID, spaceID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute decrement category count")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// IncrementContentCount increments usage counter for space category.
|
||||
func (s Store) IncrementContentCount(ctx domain.RequestContext, spaceID string) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_space SET
|
||||
c_count_content=c_count_content+1, c_revised=? WHERE c_orgid=? AND c_refid=?`),
|
||||
time.Now().UTC(), ctx.OrgID, spaceID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute increment content count")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// DecrementContentCount decrements usage counter for space category.
|
||||
func (s Store) DecrementContentCount(ctx domain.RequestContext, spaceID string) (err error) {
|
||||
_, err = ctx.Transaction.Exec(s.Bind(`UPDATE dmz_space SET
|
||||
c_count_content=c_count_content-1, c_revised=? WHERE c_orgid=? AND c_refid=?`),
|
||||
time.Now().UTC(), ctx.OrgID, spaceID)
|
||||
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "execute decrement category count")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/documize/community/model/category"
|
||||
"github.com/documize/community/model/doc"
|
||||
"github.com/documize/community/model/group"
|
||||
"github.com/documize/community/model/label"
|
||||
"github.com/documize/community/model/link"
|
||||
"github.com/documize/community/model/org"
|
||||
"github.com/documize/community/model/page"
|
||||
|
@ -42,6 +43,7 @@ type Store struct {
|
|||
Document DocumentStorer
|
||||
Group GroupStorer
|
||||
Link LinkStorer
|
||||
Label LabelStorer
|
||||
Meta MetaStorer
|
||||
Organization OrganizationStorer
|
||||
Page PageStorer
|
||||
|
@ -62,6 +64,10 @@ type SpaceStorer interface {
|
|||
Update(ctx domain.RequestContext, sp space.Space) (err error)
|
||||
Delete(ctx domain.RequestContext, id string) (rows int64, err error)
|
||||
AdminList(ctx domain.RequestContext) (sp []space.Space, err error)
|
||||
IncrementCategoryCount(ctx domain.RequestContext, spaceID string) (err error)
|
||||
DecrementCategoryCount(ctx domain.RequestContext, spaceID string) (err error)
|
||||
IncrementContentCount(ctx domain.RequestContext, spaceID string) (err error)
|
||||
DecrementContentCount(ctx domain.RequestContext, spaceID string) (err error)
|
||||
}
|
||||
|
||||
// CategoryStorer defines required methods for category and category membership management
|
||||
|
@ -148,6 +154,8 @@ type OrganizationStorer interface {
|
|||
RemoveOrganization(ctx domain.RequestContext, orgID string) (err error)
|
||||
UpdateAuthConfig(ctx domain.RequestContext, org org.Organization) (err error)
|
||||
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
|
||||
|
@ -285,6 +293,7 @@ type GroupStorer interface {
|
|||
GetMembers(ctx domain.RequestContext) (r []group.Record, err error)
|
||||
JoinGroup(ctx domain.RequestContext, groupID, userID string) (err error)
|
||||
LeaveGroup(ctx domain.RequestContext, groupID, userID string) (err error)
|
||||
RemoveUserGroups(ctx domain.RequestContext, userID string) (err error)
|
||||
}
|
||||
|
||||
// MetaStorer provide specialist methods for global administrators.
|
||||
|
@ -295,3 +304,12 @@ type MetaStorer interface {
|
|||
Attachments(ctx domain.RequestContext, docID string) (a []attachment.Attachment, err error)
|
||||
SearchIndexCount(ctx domain.RequestContext) (c int, err error)
|
||||
}
|
||||
|
||||
// LabelStorer defines required methods for space label management
|
||||
type LabelStorer interface {
|
||||
Add(ctx domain.RequestContext, l label.Label) (err error)
|
||||
Get(ctx domain.RequestContext) (l []label.Label, err error)
|
||||
Update(ctx domain.RequestContext, l label.Label) (err error)
|
||||
Delete(ctx domain.RequestContext, id string) (rows int64, err error)
|
||||
RemoveReference(ctx domain.RequestContext, spaceID string) (err error)
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import (
|
|||
"github.com/documize/community/model/doc"
|
||||
"github.com/documize/community/model/page"
|
||||
pm "github.com/documize/community/model/permission"
|
||||
"github.com/documize/community/model/template"
|
||||
// "github.com/documize/community/model/template"
|
||||
"github.com/documize/community/model/workflow"
|
||||
uuid "github.com/nu7hatch/gouuid"
|
||||
)
|
||||
|
@ -66,23 +66,23 @@ func (h *Handler) SavedList(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
templates := []template.Template{}
|
||||
// templates := []template.Template{}
|
||||
|
||||
for _, d := range documents {
|
||||
var t = template.Template{}
|
||||
t.ID = d.RefID
|
||||
t.Title = d.Name
|
||||
t.Description = d.Excerpt
|
||||
t.Author = ""
|
||||
t.Dated = d.Created
|
||||
t.Type = template.TypePrivate
|
||||
// for _, d := range documents {
|
||||
// var t = template.Template{}
|
||||
// t.ID = d.RefID
|
||||
// t.Title = d.Name
|
||||
// t.Description = d.Excerpt
|
||||
// t.Author = ""
|
||||
// t.Dated = d.Created
|
||||
// t.Type = template.TypePrivate
|
||||
|
||||
if d.SpaceID == spaceID {
|
||||
templates = append(templates, t)
|
||||
}
|
||||
}
|
||||
// if d.SpaceID == spaceID {
|
||||
// templates = append(templates, t)
|
||||
// }
|
||||
// }
|
||||
|
||||
response.WriteJSON(w, templates)
|
||||
response.WriteJSON(w, documents)
|
||||
}
|
||||
|
||||
// SaveAs saves existing document as a template.
|
||||
|
@ -245,6 +245,14 @@ func (h *Handler) SaveAs(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
err = h.Store.Space.IncrementContentCount(ctx, doc.SpaceID)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
response.WriteServerError(w, method, err)
|
||||
h.Runtime.Log.Error(method, err)
|
||||
return
|
||||
}
|
||||
|
||||
// Commit and return new document template
|
||||
ctx.Transaction.Commit()
|
||||
|
||||
|
@ -430,6 +438,14 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
err = h.Store.Space.IncrementContentCount(ctx, d.SpaceID)
|
||||
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.EventTypeTemplateUse)
|
||||
|
|
|
@ -405,7 +405,7 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// remove all associated roles for this user
|
||||
// Remove user's permissions
|
||||
_, err = h.Store.Permission.DeleteUserPermissions(ctx, userID)
|
||||
if err != nil {
|
||||
ctx.Transaction.Rollback()
|
||||
|
@ -414,6 +414,15 @@ func (h *Handler) Delete(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
// Remove all user groups memberships
|
||||
err = h.Store.Group.RemoveUserGroups(ctx, userID)
|
||||
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.EventTypeUserDelete)
|
||||
|
|
|
@ -42,6 +42,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s store.Store, orgID string,
|
|||
u.Active = false
|
||||
u.ViewUsers = false
|
||||
u.Analytics = false
|
||||
u.Theme = ""
|
||||
|
||||
for _, account := range u.Accounts {
|
||||
if account.OrgID == orgID {
|
||||
|
@ -50,6 +51,7 @@ func AttachUserAccounts(ctx domain.RequestContext, s store.Store, orgID string,
|
|||
u.Active = account.Active
|
||||
u.ViewUsers = account.Users
|
||||
u.Analytics = account.Analytics
|
||||
u.Theme = account.Theme
|
||||
break
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
package boot
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/documize/community/core/database"
|
||||
|
@ -57,28 +58,37 @@ func InitRuntime(r *env.Runtime, s *store.Store) bool {
|
|||
storage.SetMySQLProvider(r, s)
|
||||
case "postgresql":
|
||||
storage.SetPostgreSQLProvider(r, s)
|
||||
case "mssql":
|
||||
// storage.SetSQLServerProvider(r, s)
|
||||
// case "mssql":
|
||||
// storage.SetSQLServerProvider(r, s)
|
||||
default:
|
||||
r.Log.Infof("Unsupported database type: %s", r.Flags.DBType)
|
||||
r.Log.Info("Documize supports the following database types: mysql | mariadb | percona | postgresql")
|
||||
os.Exit(1)
|
||||
return false
|
||||
}
|
||||
|
||||
// Open connection to database
|
||||
db, err := sqlx.Open(r.StoreProvider.DriverName(), r.StoreProvider.MakeConnectionString()) //r.Flags.DBConn
|
||||
if err != nil {
|
||||
r.Log.Error("unable to open database", err)
|
||||
r.Log.Error("Unable to open database", err)
|
||||
os.Exit(1)
|
||||
return false
|
||||
}
|
||||
|
||||
// Database handle
|
||||
// Set the database handle
|
||||
r.Db = db
|
||||
|
||||
// Database connection defaults
|
||||
// Set connection defaults
|
||||
r.Db.SetMaxIdleConns(30)
|
||||
r.Db.SetMaxOpenConns(100)
|
||||
r.Db.SetConnMaxLifetime(time.Second * 14400)
|
||||
|
||||
// Database good?
|
||||
// Ping verifies a connection to the database is still alive, establishing a connection if necessary.
|
||||
err = r.Db.Ping()
|
||||
if err != nil {
|
||||
r.Log.Error("unable to connect to database - "+r.StoreProvider.Example(), err)
|
||||
r.Log.Error("Unable to connect to database", err)
|
||||
r.Log.Info(r.StoreProvider.Example())
|
||||
os.Exit(1)
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -39,10 +39,10 @@ func main() {
|
|||
|
||||
// product details
|
||||
rt.Product = domain.Product{}
|
||||
rt.Product.Major = "1"
|
||||
rt.Product.Minor = "76"
|
||||
rt.Product.Patch = "2"
|
||||
rt.Product.Revision = 181121115333
|
||||
rt.Product.Major = "2"
|
||||
rt.Product.Minor = "0"
|
||||
rt.Product.Patch = "0"
|
||||
rt.Product.Revision = 190115203818
|
||||
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
|
||||
rt.Product.Edition = domain.CommunityEdition
|
||||
rt.Product.Title = fmt.Sprintf("%s Edition", rt.Product.Edition)
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
category "github.com/documize/community/domain/category"
|
||||
document "github.com/documize/community/domain/document"
|
||||
group "github.com/documize/community/domain/group"
|
||||
label "github.com/documize/community/domain/label"
|
||||
link "github.com/documize/community/domain/link"
|
||||
meta "github.com/documize/community/domain/meta"
|
||||
org "github.com/documize/community/domain/organization"
|
||||
|
@ -149,6 +150,11 @@ func SetMySQLProvider(r *env.Runtime, s *store.Store) {
|
|||
userStore := user.Store{}
|
||||
userStore.Runtime = r
|
||||
s.User = userStore
|
||||
|
||||
// Space Label
|
||||
labelStore := label.Store{}
|
||||
labelStore.Runtime = r
|
||||
s.Label = labelStore
|
||||
}
|
||||
|
||||
// MySQLProvider supports MySQL 5.7.x and 8.0.x versions.
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
category "github.com/documize/community/domain/category"
|
||||
document "github.com/documize/community/domain/document"
|
||||
group "github.com/documize/community/domain/group"
|
||||
label "github.com/documize/community/domain/label"
|
||||
link "github.com/documize/community/domain/link"
|
||||
meta "github.com/documize/community/domain/meta"
|
||||
org "github.com/documize/community/domain/organization"
|
||||
|
@ -147,6 +148,11 @@ func SetPostgreSQLProvider(r *env.Runtime, s *store.Store) {
|
|||
userStore := user.Store{}
|
||||
userStore.Runtime = r
|
||||
s.User = userStore
|
||||
|
||||
// Space Label
|
||||
labelStore := label.Store{}
|
||||
labelStore.Runtime = r
|
||||
s.Label = labelStore
|
||||
}
|
||||
|
||||
// Type returns name of provider
|
||||
|
|
2923
embed/bindata.go
2923
embed/bindata.go
File diff suppressed because one or more lines are too long
|
@ -4,3 +4,25 @@ public/tinymce
|
|||
public/codemirror/**
|
||||
public/codemirror/
|
||||
public/codemirror
|
||||
|
||||
# unconventional js
|
||||
/blueprints/*/files/
|
||||
/vendor/
|
||||
|
||||
# compiled output
|
||||
/dist/
|
||||
/dist-prod/
|
||||
/tmp/
|
||||
|
||||
# dependencies
|
||||
/bower_components/
|
||||
/node_modules/
|
||||
|
||||
# misc
|
||||
/coverage/
|
||||
!.*
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/bower.json.ember-try
|
||||
/package.json.ember-try
|
||||
|
|
|
@ -20,8 +20,10 @@ module.exports = {
|
|||
// node files
|
||||
{
|
||||
files: [
|
||||
'testem.js',
|
||||
'.eslintrc.js',
|
||||
'.template-lintrc.js',
|
||||
'ember-cli-build.js',
|
||||
'testem.js',
|
||||
'config/**/*.js'
|
||||
],
|
||||
parserOptions: {
|
||||
|
@ -60,7 +62,6 @@ module.exports = {
|
|||
"userLogin": true,
|
||||
"Keycloak": true,
|
||||
"slug": true,
|
||||
"interact": true,
|
||||
"velocity": true
|
||||
"iziToast": true
|
||||
}
|
||||
};
|
||||
|
|
22
gui/.gitignore
vendored
22
gui/.gitignore
vendored
|
@ -1,18 +1,24 @@
|
|||
# See https://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/dist-prod
|
||||
/tmp
|
||||
/dist/
|
||||
/tmp/
|
||||
/dist-prod/
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/bower_components
|
||||
/bower_components/
|
||||
/node_modules/
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage/*
|
||||
/coverage/
|
||||
/libpeerconnection.log
|
||||
npm-debug.log*
|
||||
testem.log
|
||||
/npm-debug.log*
|
||||
/testem.log
|
||||
/yarn-error.log
|
||||
|
||||
# ember-try
|
||||
/.node_modules.ember-try/
|
||||
/bower.json.ember-try
|
||||
/package.json.ember-try
|
||||
|
|
19
gui/.template-lintrc.js
Normal file
19
gui/.template-lintrc.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
extends: 'recommended',
|
||||
rules: {
|
||||
'attribute-indentation': false,
|
||||
'block-indentation': false,
|
||||
'img-alt-attributes': false,
|
||||
'link-rel-noopener': false,
|
||||
'no-inline-styles': false,
|
||||
'no-invalid-interactive': false,
|
||||
'no-nested-interactive': false,
|
||||
'no-triple-curlies': false,
|
||||
'style-concatenation': false,
|
||||
'simple-unless': false,
|
||||
}
|
||||
};
|
||||
|
||||
// https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rules.md
|
|
@ -25,5 +25,6 @@ install:
|
|||
- yarn install --non-interactive
|
||||
|
||||
script:
|
||||
- yarn lint:js
|
||||
- yarn test
|
||||
- npm run lint:hbs
|
||||
- npm run lint:js
|
||||
- npm test
|
||||
|
|
|
@ -34,6 +34,12 @@ Make use of the many generators for code, try `ember help generate` for more det
|
|||
* `ember test`
|
||||
* `ember test --server`
|
||||
|
||||
### Linting
|
||||
|
||||
* `npm run lint:hbs`
|
||||
* `npm run lint:js`
|
||||
* `npm run lint:js -- --fix`
|
||||
|
||||
### Building
|
||||
|
||||
* `ember build` (development)
|
||||
|
|
|
@ -141,15 +141,13 @@ export default Component.extend(ModalMixin, Notifier, {
|
|||
},
|
||||
|
||||
onLDAPPreview() {
|
||||
this.showWait();
|
||||
|
||||
let config = this.get('ldapConfig');
|
||||
config.serverPort = parseInt(this.get('ldapConfig.serverPort'));
|
||||
|
||||
this.get('globalSvc').previewLDAP(config).then((preview) => {
|
||||
this.set('ldapPreview', preview);
|
||||
this.modalOpen("#ldap-preview-modal", {"show": true});
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -231,8 +229,6 @@ export default Component.extend(ModalMixin, Notifier, {
|
|||
break;
|
||||
}
|
||||
|
||||
this.showWait();
|
||||
|
||||
let data = { authProvider: provider, authConfig: JSON.stringify(config) };
|
||||
|
||||
this.get('onSave')(data).then(() => {
|
||||
|
@ -274,7 +270,7 @@ export default Component.extend(ModalMixin, Notifier, {
|
|||
});
|
||||
}
|
||||
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,7 +60,6 @@ export default Component.extend(Notifier, Modal, {
|
|||
},
|
||||
|
||||
doBackup() {
|
||||
this.showWait();
|
||||
this.set('backupFilename', '');
|
||||
this.set('backupSuccess', false);
|
||||
this.set('backupFailed', false);
|
||||
|
@ -69,13 +68,13 @@ export default Component.extend(Notifier, Modal, {
|
|||
let spec = this.get('backupSpec');
|
||||
|
||||
this.get('onBackup')(spec).then((filename) => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Completed');
|
||||
this.set('backupLabel', 'Start Backup');
|
||||
this.set('backupSuccess', true);
|
||||
this.set('backupFilename', filename);
|
||||
this.set('backupRunning', false);
|
||||
}, ()=> {
|
||||
this.showDone();
|
||||
this.notifyError('Failed');
|
||||
this.set('backupLabel', 'Run Backup');
|
||||
this.set('backupFailed', true);
|
||||
this.set('backupRunning', false);
|
||||
|
@ -134,7 +133,6 @@ export default Component.extend(Notifier, Modal, {
|
|||
}
|
||||
|
||||
// start restore process
|
||||
this.showWait();
|
||||
this.set('restoreButtonLabel', 'Please wait, restore running...');
|
||||
this.set('restoreSuccess', false);
|
||||
this.set('restoreFailed', false);
|
||||
|
@ -147,12 +145,12 @@ export default Component.extend(Notifier, Modal, {
|
|||
}
|
||||
|
||||
this.get('onRestore')(spec, filedata).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Completed');
|
||||
this.set('backupLabel', 'Restore');
|
||||
this.set('restoreSuccess', true);
|
||||
this.get('router').transitionTo('auth.logout');
|
||||
}, ()=> {
|
||||
this.showDone();
|
||||
this.notifyError('Failed');
|
||||
this.set('restorbackupLabel', 'Restore');
|
||||
this.set('restoreFailed', true);
|
||||
});
|
||||
|
|
|
@ -13,10 +13,12 @@ import $ from 'jquery';
|
|||
import { empty, and } from '@ember/object/computed';
|
||||
import { isEmpty } from '@ember/utils';
|
||||
import { set } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Notifier, {
|
||||
appMeta: service(),
|
||||
maxTags: 3,
|
||||
titleEmpty: empty('model.general.title'),
|
||||
messageEmpty: empty('model.general.message'),
|
||||
|
@ -30,6 +32,51 @@ export default Component.extend(Notifier, {
|
|||
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: {
|
||||
change() {
|
||||
const selectEl = this.$('#maxTags')[0];
|
||||
|
@ -60,16 +107,23 @@ export default Component.extend(Notifier, {
|
|||
}
|
||||
|
||||
this.set('model.general.maxTags', this.get('maxTags'));
|
||||
this.model.general.set('allowAnonymousAccess', $("#allowAnonymousAccess").prop('checked'));
|
||||
|
||||
this.showWait();
|
||||
|
||||
this.get('save')().then(() => {
|
||||
this.showDone();
|
||||
this.get('onUpdate')().then(() => {
|
||||
this.notifySuccess('Saved');
|
||||
set(this, 'titleError', false);
|
||||
set(this, 'messageError', false);
|
||||
set(this, 'conversionEndpointError', false);
|
||||
});
|
||||
},
|
||||
|
||||
onThemeChange(theme) {
|
||||
this.get('appMeta').setTheme(theme);
|
||||
this.set('model.general.theme', theme);
|
||||
},
|
||||
|
||||
onDefaultLogo() {
|
||||
this.get('onDefaultLogo')(this.get('appMeta.orgId'));
|
||||
this.notifySuccess('Using default logo');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -56,13 +56,12 @@ export default Component.extend(Notifier, {
|
|||
this.set('jiraCreds.url', url.substring(0, url.length-1));
|
||||
}
|
||||
|
||||
this.showWait();
|
||||
this.get('orgSvc').saveOrgSetting(orgId, 'jira', this.get('jiraCreds')).then(() => {
|
||||
if (this.get('session.isGlobalAdmin')) {
|
||||
this.get('orgSvc').saveGlobalSetting('SECTION-TRELLO', this.get('trelloCreds'));
|
||||
}
|
||||
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,10 +40,8 @@ export default Component.extend(Notifier, Modals, {
|
|||
|
||||
actions: {
|
||||
saveLicense() {
|
||||
this.showWait();
|
||||
|
||||
this.get('global').setLicense(this.get('license')).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
window.location.reload();
|
||||
});
|
||||
},
|
||||
|
@ -57,7 +55,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
let comment = this.get('comment');
|
||||
|
||||
this.get('global').deactivate(comment).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
this.modalOpen("#deactivation-confirmation-modal", {"show": true});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,16 +10,20 @@
|
|||
// https://documize.com
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
export default Component.extend(Notifier, {
|
||||
appMeta: service(),
|
||||
buttonLabel: 'Rebuild Search Index',
|
||||
buttonLabel: 'Rebuild',
|
||||
|
||||
actions: {
|
||||
reindex() {
|
||||
this.set('buttonLabel', 'Rebuilding search index...')
|
||||
this.get('reindex')();
|
||||
this.set('buttonLabel', 'Running...');
|
||||
this.notifyInfo("Starting search re-index process");
|
||||
this.get('reindex')(() => {
|
||||
this.notifySuccess("Search re-indexing complete");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -48,13 +48,18 @@ export default Component.extend(Notifier, {
|
|||
},
|
||||
);
|
||||
|
||||
this.showWait();
|
||||
this.set('buttonText', 'Please wait...');
|
||||
this.notifyInfo('Sending test email to you');
|
||||
|
||||
this.get('saveSMTP')().then((result) => {
|
||||
this.showDone();
|
||||
this.set('buttonText', 'Save & Test');
|
||||
this.set('testSMTP', result);
|
||||
|
||||
if (result.success) {
|
||||
this.notifySuccess(result.message);
|
||||
} else {
|
||||
this.notifyError(result.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,11 @@ export default Component.extend(Notifier, Modals, {
|
|||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.loadData();
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.deleteSpace = {
|
||||
id: '',
|
||||
name: ''
|
||||
|
@ -55,6 +53,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
actions: {
|
||||
onShow(id) {
|
||||
this.set('deleteSpace.id', id);
|
||||
this.modalOpen("#space-delete-modal", {"show": true}, '#delete-space-name');
|
||||
},
|
||||
|
||||
onDelete() {
|
||||
|
@ -76,29 +75,28 @@ export default Component.extend(Notifier, Modals, {
|
|||
this.set('deleteSpace.id', '');
|
||||
this.set('deleteSpace.name', '');
|
||||
this.loadData();
|
||||
this.notifySuccess('Deleted');
|
||||
});
|
||||
},
|
||||
|
||||
onExport() {
|
||||
this.showWait();
|
||||
|
||||
let spec = {
|
||||
spaceId: '',
|
||||
data: _.pluck(this.get('folders'), 'id'),
|
||||
filterType: 'space',
|
||||
};
|
||||
|
||||
this.notifyInfo('Export running...');
|
||||
|
||||
this.get('documentSvc').export(spec).then((htmlExport) => {
|
||||
this.get('browserSvc').downloadFile(htmlExport, 'documize.html');
|
||||
this.showDone();
|
||||
this.notifySuccess('Export completed');
|
||||
});
|
||||
},
|
||||
|
||||
onOwner(spaceId) {
|
||||
this.showWait();
|
||||
|
||||
this.get('spaceSvc').grantOwnerPermission(spaceId).then(() => { /* jshint ignore:line */
|
||||
this.showDone();
|
||||
this.notifySuccess('Added as owner');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
94
gui/app/components/customize/space-labels.js
Normal file
94
gui/app/components/customize/space-labels.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
// 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 $ from 'jquery';
|
||||
import Modals from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Modals, {
|
||||
labelName: '',
|
||||
labelColor: '',
|
||||
editLabel: null,
|
||||
deleetLabel: null,
|
||||
showDeleteDialog: false,
|
||||
|
||||
actions: {
|
||||
onShowAddModal() {
|
||||
this.set('labelName', '');
|
||||
this.set('labelColor', '');
|
||||
this.modalOpen("#add-label-modal", {"show": true}, '#add-label-name');
|
||||
},
|
||||
|
||||
onShowDeleteModal(label) {
|
||||
this.set('deleteLabel', label);
|
||||
this.set('showDeleteDialog', !this.get('showDeleteDialog'));
|
||||
},
|
||||
|
||||
onShowUpdateModal(label) {
|
||||
this.set('editLabel', label);
|
||||
this.set('labelName', label.get('name'));
|
||||
this.set('labelColor', label.get('color'));
|
||||
this.modalOpen("#edit-label-modal", {"show": true}, '#edit-label-name');
|
||||
},
|
||||
|
||||
onSetColor(color) {
|
||||
this.set('labelColor', color);
|
||||
},
|
||||
|
||||
onAdd() {
|
||||
let label = {
|
||||
name: this.get('labelName').trim(),
|
||||
color: this.get('labelColor').trim(),
|
||||
}
|
||||
|
||||
if (is.empty(label.name)) {
|
||||
$('#add-label-name').addClass('is-invalid').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
$('#add-label-name').removeClass('is-invalid');
|
||||
this.modalClose('#add-label-modal');
|
||||
|
||||
this.get('onAdd')(label);
|
||||
},
|
||||
|
||||
onUpdate() {
|
||||
let name = this.get('labelName').trim();
|
||||
let color = this.get('labelColor').trim();
|
||||
let label = this.get('editLabel');
|
||||
|
||||
if (is.empty(name)) {
|
||||
$('#edit-label-name').addClass('is-invalid').focus();
|
||||
return;
|
||||
}
|
||||
|
||||
$('#edit-label-name').removeClass('is-invalid');
|
||||
this.modalClose('#edit-label-modal');
|
||||
|
||||
label.set('name', name);
|
||||
label.set('color', color);
|
||||
|
||||
this.get('onUpdate')(label);
|
||||
|
||||
this.set('editLabel', null);
|
||||
},
|
||||
|
||||
onDelete() {
|
||||
let label = this.get('deleteLabel');
|
||||
|
||||
this.set('showDeleteDialog', false);
|
||||
this.get('onDelete')(label.get('id'));
|
||||
this.set('deleteLabel', null);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
|
@ -12,9 +12,10 @@
|
|||
import $ from 'jquery';
|
||||
import AuthProvider from '../../mixins/auth';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(AuthProvider, ModalMixin, {
|
||||
export default Component.extend(AuthProvider, ModalMixin, Notifier, {
|
||||
bulkUsers: '',
|
||||
newUser: null,
|
||||
|
||||
|
@ -51,6 +52,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
|
|||
|
||||
this.get('onAddUser')(user).then(() => {
|
||||
this.set('newUser', { firstname: '', lastname: '', email: '', active: true });
|
||||
this.notifySuccess('Added user');
|
||||
});
|
||||
|
||||
this.modalClose("#add-user-modal");
|
||||
|
@ -65,6 +67,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
|
|||
|
||||
this.get('onAddUsers')(this.get('bulkUsers')).then(() => {
|
||||
this.set('bulkUsers', '');
|
||||
this.notifySuccess('Added users');
|
||||
});
|
||||
|
||||
this.modalClose("#add-user-modal");
|
||||
|
|
|
@ -24,7 +24,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
|
|||
searchText: '',
|
||||
users: null,
|
||||
members: null,
|
||||
userLimit: 100,
|
||||
userLimit: 25,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
|
|
@ -10,20 +10,22 @@
|
|||
// https://documize.com
|
||||
|
||||
import $ from 'jquery';
|
||||
import { observer } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { schedule, debounce } from '@ember/runloop';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import AuthProvider from '../../mixins/auth';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(AuthProvider, ModalMixin, TooltipMixin, {
|
||||
export default Component.extend(AuthProvider, ModalMixin, Notifier, {
|
||||
groupSvc: service('group'),
|
||||
editUser: null,
|
||||
deleteUser: null,
|
||||
filter: '',
|
||||
hasSelectedUsers: false,
|
||||
showDeleteDialog: false,
|
||||
showPermExplain: false,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
@ -46,24 +48,27 @@ export default Component.extend(AuthProvider, ModalMixin, TooltipMixin, {
|
|||
});
|
||||
|
||||
this.set('users', users);
|
||||
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
onKeywordChange: function () {
|
||||
onKeywordChange: observer('filter', function() {
|
||||
debounce(this, this.filterUsers, 350);
|
||||
}.observes('filter'),
|
||||
}),
|
||||
|
||||
filterUsers() {
|
||||
this.get('onFilter')(this.get('filter'));
|
||||
},
|
||||
|
||||
actions: {
|
||||
togglePerms() {
|
||||
this.set('showPermExplain', !this.get('showPermExplain'));
|
||||
|
||||
if (this.showPermExplain) {
|
||||
this.$(".perms").show();
|
||||
} else {
|
||||
this.$(".perms").hide();
|
||||
}
|
||||
},
|
||||
|
||||
toggleSelect(user) {
|
||||
user.set('selected', !user.get('selected'));
|
||||
|
||||
|
@ -178,9 +183,15 @@ export default Component.extend(AuthProvider, ModalMixin, TooltipMixin, {
|
|||
let cb = this.get('onDelete');
|
||||
cb(this.get('deleteUser.id'));
|
||||
|
||||
this.notifySuccess("Deleted user");
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
onShowDeleteBulk() {
|
||||
this.modalOpen("#admin-user-delete-modal", {"show": true});
|
||||
},
|
||||
|
||||
onBulkDelete() {
|
||||
let su = this.get('selectedUsers');
|
||||
|
||||
|
@ -192,6 +203,8 @@ export default Component.extend(AuthProvider, ModalMixin, TooltipMixin, {
|
|||
this.set('selectedUsers', []);
|
||||
this.set('hasSelectedUsers', false);
|
||||
|
||||
this.notifySuccess("Deleted selected users");
|
||||
|
||||
this.modalClose('#admin-user-delete-modal');
|
||||
},
|
||||
|
||||
|
|
|
@ -12,13 +12,12 @@
|
|||
import $ from 'jquery';
|
||||
import { empty } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import Tooltips from '../../mixins/tooltip';
|
||||
import { computed, observer } from '@ember/object';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Modals from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Tooltips, Notifier, Modals, {
|
||||
export default Component.extend(Notifier, Modals, {
|
||||
documentService: service('document'),
|
||||
sectionService: service('section'),
|
||||
store: service(),
|
||||
|
@ -34,7 +33,7 @@ export default Component.extend(Tooltips, Notifier, Modals, {
|
|||
return this.get('blocks.length') > 0;
|
||||
}),
|
||||
|
||||
onModalToggle: function () {
|
||||
onModalToggle: observer('show', function() {
|
||||
let modalId = this.get('modalId');
|
||||
|
||||
if (this.get('show')) {
|
||||
|
@ -49,7 +48,7 @@ export default Component.extend(Tooltips, Notifier, Modals, {
|
|||
$(modalId).modal('hide');
|
||||
$(modalId).modal('dispose');
|
||||
}
|
||||
}.observes('show'),
|
||||
}),
|
||||
|
||||
addSection(model) {
|
||||
this.modalClose(this.get('modalId'));
|
||||
|
|
|
@ -10,14 +10,13 @@
|
|||
// https://documize.com
|
||||
|
||||
import { debounce } from '@ember/runloop';
|
||||
import { computed, set } from '@ember/object';
|
||||
import { computed, set, observer } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import stringUtil from '../../utils/string';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, TooltipMixin, {
|
||||
export default Component.extend(ModalMixin, {
|
||||
link: service(),
|
||||
linkName: '',
|
||||
selection: null,
|
||||
|
@ -36,7 +35,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
}),
|
||||
modalId: computed('page', function() { return '#content-linker-modal-' + this.get('page.id'); }),
|
||||
showModal: false,
|
||||
onToggle: function() {
|
||||
onToggle: observer('showModal', function() {
|
||||
let modalId = this.get('modalId');
|
||||
|
||||
if (!this.get('showModal')) {
|
||||
|
@ -56,7 +55,7 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
});
|
||||
|
||||
this.modalOpen(modalId, {show: true});
|
||||
}.observes('showModal'),
|
||||
}),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
@ -71,18 +70,16 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
this._super(...arguments);
|
||||
|
||||
this.$('#content-linker-networklocation').removeClass('is-invalid');
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.removeTooltips();
|
||||
this.modalClose(this.get('modalId'));
|
||||
},
|
||||
|
||||
onKeywordChange: function() {
|
||||
onKeywordChange: observer('keywords', function() {
|
||||
debounce(this, this.fetch, 750);
|
||||
}.observes('keywords'),
|
||||
}),
|
||||
|
||||
fetch() {
|
||||
let keywords = this.get('keywords');
|
||||
|
|
|
@ -9,176 +9,30 @@
|
|||
//
|
||||
// https://documize.com
|
||||
|
||||
import $ from 'jquery';
|
||||
import { A } from '@ember/array';
|
||||
import { computed } from '@ember/object';
|
||||
import { notEmpty } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Modals from '../../mixins/modal';
|
||||
import Tooltips from '../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Modals, Tooltips, {
|
||||
export default Component.extend(Modals, {
|
||||
documentService: service('document'),
|
||||
sessionService: service('session'),
|
||||
categoryService: service('category'),
|
||||
router: service(),
|
||||
|
||||
contributorMsg: '',
|
||||
approverMsg: '',
|
||||
selectedCategories: A([]),
|
||||
tagz: A([]),
|
||||
userChanges: notEmpty('contributorMsg'),
|
||||
isApprover: computed('permissions', function() {
|
||||
return this.get('permissions.documentApprove');
|
||||
}),
|
||||
isSpaceAdmin: computed('permissions', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
}),
|
||||
changeControlMsg: computed('document.protection', function() {
|
||||
let p = this.get('document.protection');
|
||||
let constants = this.get('constants');
|
||||
let msg = '';
|
||||
|
||||
switch (p) {
|
||||
case constants.ProtectionType.None:
|
||||
msg = constants.ProtectionType.NoneLabel;
|
||||
break;
|
||||
case constants.ProtectionType.Lock:
|
||||
msg = constants.ProtectionType.LockLabel;
|
||||
break;
|
||||
case constants.ProtectionType.Review:
|
||||
msg = constants.ProtectionType.ReviewLabel;
|
||||
break;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}),
|
||||
approvalMsg: computed('document.{protection,approval}', function() {
|
||||
let p = this.get('document.protection');
|
||||
let a = this.get('document.approval');
|
||||
let constants = this.get('constants');
|
||||
let msg = '';
|
||||
|
||||
if (p === constants.ProtectionType.Review) {
|
||||
switch (a) {
|
||||
case constants.ApprovalType.Anybody:
|
||||
msg = constants.ApprovalType.AnybodyLabel;
|
||||
break;
|
||||
case constants.ApprovalType.Majority:
|
||||
msg = constants.ApprovalType.MajorityLabel;
|
||||
break;
|
||||
case constants.ApprovalType.Unanimous:
|
||||
msg = constants.ApprovalType.UnanimousLabel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return msg;
|
||||
unassigned: computed('selectedCategories', 'tagz', function() {
|
||||
return this.get('selectedCategories').length === 0 && this.get('tagz').length === 0;
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.workflowStatus();
|
||||
this.popovers();
|
||||
this.load();
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.popovers();
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
$('#document-lifecycle-popover').popover('dispose');
|
||||
$('#document-protection-popover').popover('dispose');
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
popovers() {
|
||||
let constants = this.get('constants');
|
||||
|
||||
if (this.get('permissions.documentLifecycle')) {
|
||||
$('#document-lifecycle-popover').addClass('cursor-pointer');
|
||||
} else {
|
||||
$('#document-lifecycle-popover').popover('dispose');
|
||||
$('#document-lifecycle-popover').removeClass('cursor-pointer');
|
||||
|
||||
$('#document-lifecycle-popover').popover({
|
||||
html: true,
|
||||
title: 'Lifecycle',
|
||||
content: "<p>Draft — restricted visiblity and not searchable</p><p>Live — document visible to all</p><p>Archived — not visible or searchable</p>",
|
||||
placement: 'top',
|
||||
trigger: 'hover click'
|
||||
});
|
||||
}
|
||||
|
||||
if (this.get('permissions.documentApprove')) {
|
||||
$('#document-protection-popover').addClass('cursor-pointer');
|
||||
} else {
|
||||
$('#document-protection-popover').popover('dispose');
|
||||
$('#document-protection-popover').removeClass('cursor-pointer');
|
||||
|
||||
let ccMsg = `<p>${this.changeControlMsg}</p>`;
|
||||
|
||||
if (this.get('document.protection') === constants.ProtectionType.Review) {
|
||||
ccMsg += '<ul>'
|
||||
ccMsg += `<li>${this.approvalMsg}</li>`;
|
||||
if (this.get('userChanges')) ccMsg += `<li>Your contributions: ${this.contributorMsg}</li>`;
|
||||
if (this.get('isApprover') && this.get('approverMsg.length') > 0) ccMsg += `<li>${this.approverMsg}</li>`;
|
||||
ccMsg += '</ul>'
|
||||
}
|
||||
|
||||
$('#document-protection-popover').popover({
|
||||
html: true,
|
||||
title: 'Change Control',
|
||||
content: ccMsg,
|
||||
placement: 'top',
|
||||
trigger: 'hover click'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
workflowStatus() {
|
||||
let pages = this.get('pages');
|
||||
let contributorMsg = '';
|
||||
let userPendingCount = 0;
|
||||
let userReviewCount = 0;
|
||||
let userRejectedCount = 0;
|
||||
let approverMsg = '';
|
||||
let approverPendingCount = 0;
|
||||
let approverReviewCount = 0;
|
||||
let approverRejectedCount = 0;
|
||||
|
||||
pages.forEach((item) => {
|
||||
if (item.get('userHasChangePending')) userPendingCount+=1;
|
||||
if (item.get('userHasChangeAwaitingReview')) userReviewCount+=1;
|
||||
if (item.get('userHasChangeRejected')) userRejectedCount+=1;
|
||||
if (item.get('changePending')) approverPendingCount+=1;
|
||||
if (item.get('changeAwaitingReview')) approverReviewCount+=1;
|
||||
if (item.get('changeRejected')) approverRejectedCount+=1;
|
||||
});
|
||||
|
||||
if (userPendingCount > 0 || userReviewCount > 0 || userRejectedCount > 0) {
|
||||
let label = userPendingCount === 1 ? 'change' : 'changes';
|
||||
contributorMsg = `${userPendingCount} ${label} progressing, ${userReviewCount} awaiting review, ${userRejectedCount} rejected`;
|
||||
}
|
||||
this.set('contributorMsg', contributorMsg);
|
||||
|
||||
if (approverPendingCount > 0 || approverReviewCount > 0 || approverRejectedCount > 0) {
|
||||
let label = approverPendingCount === 1 ? 'change' : 'changes';
|
||||
approverMsg = `${approverPendingCount} ${label} progressing, ${approverReviewCount} awaiting review, ${approverRejectedCount} rejected`;
|
||||
}
|
||||
|
||||
this.set('approverMsg', approverMsg);
|
||||
this.set('selectedVersion', this.get('versions').findBy('documentId', this.get('document.id')));
|
||||
|
||||
this.popovers();
|
||||
},
|
||||
|
||||
load() {
|
||||
this.get('categoryService').getDocumentCategories(this.get('document.id')).then((selected) => {
|
||||
this.set('selectedCategories', selected);
|
||||
|
@ -198,24 +52,10 @@ export default Component.extend(Modals, Tooltips, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
onSelectVersion(version) {
|
||||
let space = this.get('folder');
|
||||
|
||||
this.get('router').transitionTo('document',
|
||||
space.get('id'), space.get('slug'),
|
||||
version.documentId, this.get('document.slug'));
|
||||
},
|
||||
|
||||
onEditLifecycle() {
|
||||
},
|
||||
|
||||
onEditProtection() {
|
||||
},
|
||||
|
||||
onEditCategory() {
|
||||
if (!this.get('permissions.spaceManage')) return;
|
||||
if (!this.get('permissions.documentEdit')) return;
|
||||
|
||||
this.get('router').transitionTo('document.settings', {queryParams: {tab: 'meta'}});
|
||||
this.get('router').transitionTo('document.settings', {queryParams: {tab: 'category'}});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,9 +11,8 @@
|
|||
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
|
||||
export default Component.extend(TooltipMixin, {
|
||||
export default Component.extend({
|
||||
documentService: service('document'),
|
||||
sectionService: service('section'),
|
||||
editMode: false,
|
||||
|
|
|
@ -11,13 +11,13 @@
|
|||
|
||||
import $ from 'jquery';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import AuthMixin from '../../mixins/auth';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
||||
export default Component.extend(ModalMixin, AuthMixin, Notifier, {
|
||||
store: service(),
|
||||
spaceSvc: service('folder'),
|
||||
session: service(),
|
||||
|
@ -25,6 +25,29 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
pinned: service(),
|
||||
browserSvc: service('browser'),
|
||||
documentSvc: service('document'),
|
||||
showRevisions: computed('permissions', 'document.protection', function() {
|
||||
if (!this.get('session.authenticated')) return false;
|
||||
if (!this.get('session.viewUsers')) return false;
|
||||
if (this.get('document.protection') === this.get('constants').ProtectionType.None) return true;
|
||||
if (this.get('document.protection') === this.get('constants').ProtectionType.Review && this.get('permissions.documentApprove')) return true;
|
||||
|
||||
return false;
|
||||
}),
|
||||
showActivity: computed('permissions', function() {
|
||||
if (this.get('appMeta.edition') !== this.get('constants').Product.EnterpriseEdition) return false;
|
||||
if (!this.get('session.authenticated')) return false;
|
||||
if (!this.get('session.viewUsers')) return false;
|
||||
if (this.get('permissions.spaceView')) return true;
|
||||
|
||||
return false;
|
||||
}),
|
||||
hasToolbar: computed('permissions', 'showRevisions', 'showActivity', function() {
|
||||
if (this.get('showRevisions') || this.get('showActivity')) return true;
|
||||
if (this.get('permissions.documentAdd') || this.get('permissions.documentDelete')) return true;
|
||||
if (this.get('appMeta.edition') === this.get('constants').Product.EnterpriseEdition &&
|
||||
this.get('permissions.documentEdit')) return true;
|
||||
|
||||
}),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
@ -49,24 +72,25 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
this.set('pinState.pinId', pinId);
|
||||
this.set('pinState.isPinned', pinId !== '');
|
||||
this.set('pinState.newName', doc.get('name'));
|
||||
this.renderTooltips();
|
||||
});
|
||||
|
||||
this.set('saveTemplate.name', this.get('document.name'));
|
||||
this.set('saveTemplate.description', this.get('document.excerpt'));
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this.modalInputFocus('#document-template-modal', '#new-template-name');
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
actions: {
|
||||
onShowTemplateModal() {
|
||||
this.modalOpen("#document-template-modal", {show:true}, "#new-template-name");
|
||||
},
|
||||
|
||||
onShowDeleteModal() {
|
||||
this.modalOpen("#document-delete-modal", {show:true});
|
||||
},
|
||||
|
||||
onDocumentDelete() {
|
||||
this.modalClose('#document-delete-modal');
|
||||
|
||||
|
@ -80,11 +104,9 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
|
||||
onUnpin() {
|
||||
this.get('pinned').unpinItem(this.get('pinState.pinId')).then(() => {
|
||||
$('#document-pin-button').tooltip('dispose');
|
||||
this.set('pinState.isPinned', false);
|
||||
this.set('pinState.pinId', '');
|
||||
this.eventBus.publish('pinChange');
|
||||
this.renderTooltips();
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -96,11 +118,9 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
};
|
||||
|
||||
this.get('pinned').pinItem(pin).then((pin) => {
|
||||
$('#document-pin-button').tooltip('dispose');
|
||||
this.set('pinState.isPinned', true);
|
||||
this.set('pinState.pinId', pin.get('id'));
|
||||
this.eventBus.publish('pinChange');
|
||||
this.renderTooltips();
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -135,10 +155,8 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
},
|
||||
|
||||
onExport() {
|
||||
this.showWait();
|
||||
|
||||
let spec = {
|
||||
spaceId: this.get('document.folderId'),
|
||||
spaceId: this.get('document.spaceId'),
|
||||
data: [],
|
||||
filterType: 'document',
|
||||
};
|
||||
|
@ -147,7 +165,7 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
|
||||
this.get('documentSvc').export(spec).then((htmlExport) => {
|
||||
this.get('browserSvc').downloadFile(htmlExport, this.get('document.slug') + '.html');
|
||||
this.showDone();
|
||||
this.notifySuccess('Exported');
|
||||
});
|
||||
}
|
||||
}
|
|
@ -10,15 +10,14 @@
|
|||
// https://documize.com
|
||||
|
||||
import $ from 'jquery';
|
||||
import { computed } from '@ember/object';
|
||||
import { computed, observer } from '@ember/object';
|
||||
import { debounce } from '@ember/runloop';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Tooltips from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import tocUtil from '../../utils/toc';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, Tooltips, {
|
||||
export default Component.extend(ModalMixin, {
|
||||
documentService: service('document'),
|
||||
searchService: service('search'),
|
||||
router: service(),
|
||||
|
@ -29,9 +28,9 @@ export default Component.extend(ModalMixin, Tooltips, {
|
|||
canDelete: false,
|
||||
canMove: false,
|
||||
docSearchFilter: '',
|
||||
onKeywordChange: function () {
|
||||
onKeywordChange: observer('docSearchFilter', function() {
|
||||
debounce(this, this.searchDocs, 750);
|
||||
}.observes('docSearchFilter'),
|
||||
}),
|
||||
emptySearch: computed('docSearchResults', function() {
|
||||
return this.get('docSearchResults.length') === 0;
|
||||
}),
|
||||
|
@ -78,14 +77,6 @@ export default Component.extend(ModalMixin, Tooltips, {
|
|||
this.setState(this.get('page.id'));
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get('session.authenticated')) {
|
||||
this.renderTooltips();
|
||||
}
|
||||
},
|
||||
|
||||
searchDocs() {
|
||||
let payload = { keywords: this.get('docSearchFilter').trim(), doc: true };
|
||||
if (payload.keywords.length == 0) return;
|
||||
|
|
|
@ -125,8 +125,6 @@ export default Component.extend(Notifier, {
|
|||
|
||||
actions: {
|
||||
onSave() {
|
||||
this.showWait();
|
||||
|
||||
let docId = this.get('document.id');
|
||||
let folderId = this.get('space.id');
|
||||
let link = this.get('categories').filterBy('selected', true);
|
||||
|
@ -158,7 +156,6 @@ export default Component.extend(Notifier, {
|
|||
|
||||
this.get('categoryService').setCategoryMembership(toUnlink, 'unlink').then(() => {
|
||||
this.get('categoryService').setCategoryMembership(toLink, 'link').then(() => {
|
||||
this.showDone();
|
||||
});
|
||||
});
|
||||
|
|
@ -32,7 +32,7 @@ export default Component.extend(Notifier, {
|
|||
if (this.get('hasNameError')) return;
|
||||
if (!this.get('permissions.documentEdit')) return;
|
||||
|
||||
this.set('document.name', this.get('docName'));
|
||||
this.set('document.name', this.get('docName').trim());
|
||||
this.set('document.excerpt', this.get('docExcerpt').trim());
|
||||
|
||||
let cb = this.get('onSaveDocument');
|
||||
|
|
189
gui/app/components/document/settings-tag.js
Normal file
189
gui/app/components/document/settings-tag.js
Normal file
|
@ -0,0 +1,189 @@
|
|||
// 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 $ from 'jquery';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { A } from '@ember/array';
|
||||
import { computed } from '@ember/object';
|
||||
import { schedule } from '@ember/runloop';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Notifier, {
|
||||
appMeta: service(),
|
||||
documentSvc: service('document'),
|
||||
categoryService: service('category'),
|
||||
|
||||
tagz: A([]),
|
||||
categories: A([]),
|
||||
newCategory: '',
|
||||
showCategoryModal: false,
|
||||
hasCategories: computed('categories', function() {
|
||||
return this.get('categories').length > 0;
|
||||
}),
|
||||
canSelectCategory: computed('categories', function() {
|
||||
return (this.get('categories').length > 0 && this.get('permissions.documentEdit'));
|
||||
}),
|
||||
canAddCategory: computed('categories', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
}),
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
this.load();
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
schedule('afterRender', () => {
|
||||
$("#add-tag-field-1").focus();
|
||||
|
||||
$(".tag-input").off("keydown").on("keydown", function(e) {
|
||||
if (e.shiftKey && e.which === 9) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (e.shiftKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.which === 9 ||
|
||||
e.which === 13 ||
|
||||
e.which === 16 ||
|
||||
e.which === 37 ||
|
||||
e.which === 38 ||
|
||||
e.which === 39 ||
|
||||
e.which === 40 ||
|
||||
e.which === 45 ||
|
||||
e.which === 189 ||
|
||||
e.which === 8 ||
|
||||
e.which === 127 ||
|
||||
(e.which >= 65 && e.which <= 90) ||
|
||||
(e.which >= 97 && e.which <= 122) ||
|
||||
(e.which >= 48 && e.which <= 57)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
$(".tag-input").off("keydown");
|
||||
},
|
||||
|
||||
load() {
|
||||
this.get('categoryService').getUserVisible(this.get('space.id')).then((categories) => {
|
||||
let cats = A(categories);
|
||||
this.set('categories', cats);
|
||||
this.get('categoryService').getDocumentCategories(this.get('document.id')).then((selected) => {
|
||||
this.set('selectedCategories', selected);
|
||||
selected.forEach((s) => {
|
||||
let cat = cats.findBy('id', s.id);
|
||||
if (is.not.undefined(cat)) {
|
||||
cat.set('selected', true);
|
||||
this.set('categories', cats);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
let counter = 1;
|
||||
let tagz = A([]);
|
||||
let maxTags = this.get('appMeta.maxTags');
|
||||
|
||||
if (!_.isUndefined(this.get('document.tags')) && this.get('document.tags').length > 1) {
|
||||
let tags = this.get('document.tags').split('#');
|
||||
|
||||
_.each(tags, (tag) => {
|
||||
tag = tag.trim();
|
||||
if (tag.length > 0 && counter <= maxTags) {
|
||||
tagz.pushObject({number: counter, value: tag});
|
||||
counter++;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (let index = counter; index <= maxTags; index++) {
|
||||
tagz.pushObject({number: index, value: ''});
|
||||
}
|
||||
|
||||
this.set('tagz', tagz);
|
||||
},
|
||||
|
||||
actions: {
|
||||
onSave() {
|
||||
let docId = this.get('document.id');
|
||||
let folderId = this.get('space.id');
|
||||
let link = this.get('categories').filterBy('selected', true);
|
||||
let unlink = this.get('categories').filterBy('selected', false);
|
||||
let toLink = [];
|
||||
let toUnlink = [];
|
||||
|
||||
// prepare links associated with document
|
||||
link.forEach((l) => {
|
||||
let t = {
|
||||
spaceId: folderId,
|
||||
documentId: docId,
|
||||
categoryId: l.get('id')
|
||||
};
|
||||
|
||||
toLink.push(t);
|
||||
});
|
||||
|
||||
// prepare links no longer associated with document
|
||||
unlink.forEach((l) => {
|
||||
let t = {
|
||||
spaceId: folderId,
|
||||
documentId: docId,
|
||||
categoryId: l.get('id')
|
||||
};
|
||||
|
||||
toUnlink.pushObject(t);
|
||||
});
|
||||
|
||||
this.get('categoryService').setCategoryMembership(toUnlink, 'unlink').then(() => {
|
||||
this.get('categoryService').setCategoryMembership(toLink, 'link').then(() => {
|
||||
});
|
||||
});
|
||||
|
||||
let tagz = this.get('tagz');
|
||||
let tagzToSave = [];
|
||||
|
||||
_.each(tagz, (t) => {
|
||||
let tag = t.value.toLowerCase().trim();
|
||||
if (tag.length> 0) {
|
||||
if (!_.contains(tagzToSave, tag) && is.not.startWith(tag, '-')) {
|
||||
tagzToSave.push(tag);
|
||||
this.$('#add-tag-field-' + t.number).removeClass('is-invalid');
|
||||
} else {
|
||||
this.$('#add-tag-field-' + t.number).addClass('is-invalid');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let save = "#";
|
||||
_.each(tagzToSave, (t) => {
|
||||
save += t;
|
||||
save += '#';
|
||||
});
|
||||
|
||||
let doc = this.get('document');
|
||||
doc.set('tags', save);
|
||||
|
||||
this.get('onSaveDocument')(doc);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -17,18 +17,17 @@ import Notifier from '../../mixins/notifier';
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Modals, Notifier, {
|
||||
classNames: ["section"],
|
||||
documentService: service('document'),
|
||||
browserSvc: service('browser'),
|
||||
appMeta: service(),
|
||||
session: service(),
|
||||
hasAttachments: notEmpty('files'),
|
||||
canEdit: computed('permissions.documentEdit', 'document.protection', function() {
|
||||
return this.get('document.protection') !== this.get('constants').ProtectionType.Lock && this.get('permissions.documentEdit');
|
||||
}),
|
||||
showDialog: false,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.deleteAttachment = { id: '', name: '' };
|
||||
},
|
||||
downloadQuery: '',
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
@ -47,9 +46,9 @@ export default Component.extend(Modals, Notifier, {
|
|||
let url = this.get('appMeta.endpoint');
|
||||
let uploadUrl = `${url}/documents/${documentId}/attachments`;
|
||||
|
||||
let dzone = new Dropzone("#upload-document-files", {
|
||||
let dzone = new Dropzone("#upload-document-files > div", {
|
||||
headers: {
|
||||
'Authorization': 'Bearer ' + self.get('session.session.content.authenticated.token')
|
||||
'Authorization': 'Bearer ' + self.get('session.authToken')
|
||||
},
|
||||
url: uploadUrl,
|
||||
method: "post",
|
||||
|
@ -66,17 +65,16 @@ export default Component.extend(Modals, Notifier, {
|
|||
});
|
||||
|
||||
this.on("queuecomplete", function () {
|
||||
self.showDone();
|
||||
self.notifySuccess('Uploaded file');
|
||||
self.getAttachments();
|
||||
});
|
||||
|
||||
this.on("addedfile", function ( /*file*/ ) {
|
||||
self.showWait();
|
||||
});
|
||||
|
||||
this.on("error", function (error, msg) { // // eslint-disable-line no-unused-vars
|
||||
self.showNotification(msg);
|
||||
console.log(msg); // eslint-disable-line no-console
|
||||
this.on("error", function (error, msg) {
|
||||
self.notifyError(msg);
|
||||
self.notifyError(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -86,6 +84,15 @@ export default Component.extend(Modals, Notifier, {
|
|||
});
|
||||
|
||||
this.set('drop', dzone);
|
||||
|
||||
// For authenticated users we send server auth token.
|
||||
let qry = '';
|
||||
if (this.get('session.hasSecureToken')) {
|
||||
qry = '?secure=' + this.get('session.secureToken');
|
||||
} else if (this.get('session.authenticated')) {
|
||||
qry = '?token=' + this.get('session.authToken');
|
||||
}
|
||||
this.set('downloadQuery', qry);
|
||||
},
|
||||
|
||||
getAttachments() {
|
||||
|
@ -95,26 +102,20 @@ export default Component.extend(Modals, Notifier, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
onShowDialog(id, name) {
|
||||
this.set('deleteAttachment', { id: id, name: name });
|
||||
|
||||
this.set('showDialog', true);
|
||||
onDelete(attachment) {
|
||||
this.get('documentService').deleteAttachment(this.get('document.id'), attachment.id).then(() => {
|
||||
this.notifySuccess('File deleted');
|
||||
this.getAttachments();
|
||||
});
|
||||
},
|
||||
|
||||
onDelete() {
|
||||
this.set('showDialog', false);
|
||||
|
||||
let attachment = this.get('deleteAttachment');
|
||||
|
||||
this.get('documentService').deleteAttachment(this.get('document.id'), attachment.id).then(() => {
|
||||
this.getAttachments();
|
||||
this.set('deleteAttachment', {
|
||||
id: "",
|
||||
name: ""
|
||||
});
|
||||
onExport() {
|
||||
this.get('documentSvc').export({}).then((htmlExport) => {
|
||||
this.get('browserSvc').downloadFile(htmlExport, this.get('space.slug') + '.html');
|
||||
this.notifySuccess('Exported');
|
||||
});
|
||||
|
||||
return true;
|
||||
this.modalClose("#space-export-modal");
|
||||
}
|
||||
}
|
||||
});
|
41
gui/app/components/document/sidebar-meta.js
Normal file
41
gui/app/components/document/sidebar-meta.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
import { computed } from '@ember/object';
|
||||
import { notEmpty } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Modals from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(Modals, {
|
||||
appMeta: service(),
|
||||
documentService: service('document'),
|
||||
sessionService: service('session'),
|
||||
router: service(),
|
||||
userChanges: notEmpty('contributorMsg'),
|
||||
unassigned: computed('selectedCategories', 'tagz', function() {
|
||||
return this.get('selectedCategories').length === 0 && this.get('tagz').length === 0;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onEditStatus() {
|
||||
if (!this.get('permissions.documentEdit')) return;
|
||||
|
||||
this.get('router').transitionTo('document.settings', {queryParams: {tab: 'general'}});
|
||||
},
|
||||
|
||||
onSelectVersion(version) {
|
||||
let space = this.get('space');
|
||||
|
||||
this.get('router').transitionTo('document', space.get('id'), space.get('slug'), version.documentId, this.get('document.slug'));
|
||||
}
|
||||
}
|
||||
});
|
|
@ -13,10 +13,10 @@ import { computed } from '@ember/object';
|
|||
import { schedule } from '@ember/runloop';
|
||||
import { inject as service } from '@ember/service';
|
||||
import tocUtil from '../../utils/toc';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(TooltipMixin, {
|
||||
export default Component.extend({
|
||||
classNames: ["section"],
|
||||
documentService: service('document'),
|
||||
emptyState: computed('pages', function () {
|
||||
return this.get('pages.length') === 0;
|
||||
|
@ -54,13 +54,11 @@ export default Component.extend(TooltipMixin, {
|
|||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
this.eventBus.subscribe('documentPageAdded', this, 'onDocumentPageAdded');
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.eventBus.unsubscribe('documentPageAdded');
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
onDocumentPageAdded(pageId) {
|
|
@ -13,16 +13,15 @@ import $ from 'jquery';
|
|||
import { notEmpty } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(TooltipMixin, Notifier, {
|
||||
export default Component.extend(Notifier, {
|
||||
documentService: service('document'),
|
||||
sectionService: service('section'),
|
||||
store: service(),
|
||||
appMeta: service(),
|
||||
link: service(),
|
||||
linkSvc: service('link'),
|
||||
hasPages: notEmpty('pages'),
|
||||
showInsertSectionModal: false,
|
||||
newSectionLocation: '',
|
||||
|
@ -41,36 +40,21 @@ export default Component.extend(TooltipMixin, Notifier, {
|
|||
this.set('showLikes', this.get('folder.allowLikes') && this.get('document.isLive'));
|
||||
},
|
||||
|
||||
didRender() {
|
||||
this._super(...arguments);
|
||||
this.contentLinkHandler();
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get('session.authenticated')) {
|
||||
this.renderTooltips();
|
||||
}
|
||||
|
||||
this.jumpToSection(this.get('currentPageId'));
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
if (this.get('session.authenticated')) {
|
||||
this.removeTooltips();
|
||||
}
|
||||
this.contentLinkHandler();
|
||||
},
|
||||
|
||||
contentLinkHandler() {
|
||||
let links = this.get('link');
|
||||
let linkSvc = this.get('linkSvc');
|
||||
let doc = this.get('document');
|
||||
let self = this;
|
||||
|
||||
$("a[data-documize='true']").off('click').on('click', function (e) {
|
||||
let link = links.getLinkObject(self.get('links'), this);
|
||||
let link = linkSvc.getLinkObject(self.get('links'), this);
|
||||
|
||||
// local link? exists?
|
||||
if ((link.linkType === "section" || link.linkType === "tab") && link.documentId === doc.get('id')) {
|
||||
|
@ -92,7 +76,10 @@ export default Component.extend(TooltipMixin, Notifier, {
|
|||
return false;
|
||||
}
|
||||
|
||||
links.linkClick(doc, link);
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
linkSvc.linkClick(doc, link);
|
||||
return false;
|
||||
});
|
||||
},
|
||||
|
|
|
@ -9,19 +9,15 @@
|
|||
//
|
||||
// https://documize.com
|
||||
|
||||
import { computed, set } from '@ember/object';
|
||||
import { computed } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, {
|
||||
documentService: service('document'),
|
||||
revision: null,
|
||||
revisions: null,
|
||||
diff: '',
|
||||
hasRevisions: computed('revisions', function() {
|
||||
return this.get('revisions').length > 0;
|
||||
}),
|
||||
revision: null,
|
||||
hasDiff: computed('diff', function() {
|
||||
return this.get('diff').length > 0;
|
||||
}),
|
||||
|
@ -34,32 +30,17 @@ export default Component.extend(ModalMixin, {
|
|||
this.get('document.protection') === constants.ProtectionType.None;
|
||||
}),
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.revisions = [];
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
this.fetchRevisions();
|
||||
},
|
||||
|
||||
fetchRevisions() {
|
||||
this.get('documentService').getDocumentRevisions(this.get('document.id')).then((revisions) => {
|
||||
revisions.forEach((r) => {
|
||||
set(r, 'deleted', r.revisions === 0);
|
||||
let date = moment(r.created).format('Do MMMM YYYY HH:mm');
|
||||
let format = `${r.firstname} ${r.lastname} on ${date} changed ${r.title}`;
|
||||
set(r, 'label', format);
|
||||
});
|
||||
let revision = this.get('revision');
|
||||
|
||||
this.set('revisions', revisions);
|
||||
|
||||
if (revisions.length > 0 && is.null(this.get('revision'))) {
|
||||
this.send('onSelectRevision', revisions[0]);
|
||||
if (is.not.null(revision)) {
|
||||
if (!revision.deleted) {
|
||||
this.fetchDiff(revision.pageId, revision.id);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
fetchDiff(pageId, revisionId) {
|
||||
this.get('documentService').getPageRevisionDiff(this.get('document.id'), pageId, revisionId).then((revision) => {
|
||||
|
@ -68,12 +49,8 @@ export default Component.extend(ModalMixin, {
|
|||
},
|
||||
|
||||
actions: {
|
||||
onSelectRevision(revision) {
|
||||
this.set('revision', revision);
|
||||
|
||||
if (!revision.deleted) {
|
||||
this.fetchDiff(revision.pageId, revision.id);
|
||||
}
|
||||
onShowModal() {
|
||||
this.modalOpen('#document-rollback-modal', {show:true});
|
||||
},
|
||||
|
||||
onRollback() {
|
||||
|
|
16
gui/app/components/folder/document-categories.js
Normal file
16
gui/app/components/folder/document-categories.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['categories'],
|
||||
});
|
|
@ -12,6 +12,8 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['hashtags'],
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
let tagz = [];
|
||||
|
@ -20,7 +22,7 @@ export default Component.extend({
|
|||
let tags = this.get('documentTags').split('#');
|
||||
_.each(tags, function(tag) {
|
||||
if (tag.length > 0) {
|
||||
tagz.pushObject("#" + tag);
|
||||
tagz.pushObject(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -11,10 +11,9 @@
|
|||
|
||||
import { computed } from '@ember/object';
|
||||
import { A } from '@ember/array';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(TooltipMixin, {
|
||||
export default Component.extend({
|
||||
showDeleteDialog: false,
|
||||
showMoveDialog: false,
|
||||
selectedDocuments: A([]),
|
||||
|
@ -105,7 +104,6 @@ export default Component.extend(TooltipMixin, {
|
|||
|
||||
this.set('selectedCaption', list.length > 1 ? 'documents' : 'document');
|
||||
this.set('selectedDocuments', A(list));
|
||||
this.renderTooltips();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -52,11 +52,9 @@ export default Component.extend(AuthMixin, Notifier, {
|
|||
|
||||
let id = this.get('deleteBlockId');
|
||||
|
||||
this.showWait();
|
||||
|
||||
this.get('sectionSvc').deleteBlock(id).then(() => {
|
||||
this.set('deleteBlockId', '');
|
||||
this.showDone();
|
||||
this.notifySuccess('Deleted');
|
||||
|
||||
this.get('sectionSvc').getSpaceBlocks(this.get('space.id')).then((blocks) => {
|
||||
this.set('blocks', blocks);
|
||||
|
|
|
@ -12,19 +12,19 @@
|
|||
import $ from 'jquery';
|
||||
import { A } from '@ember/array';
|
||||
import { inject as service } from '@ember/service';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Notifer from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, TooltipMixin, Notifer, {
|
||||
export default Component.extend(ModalMixin, Notifer, {
|
||||
spaceSvc: service('folder'),
|
||||
groupSvc: service('group'),
|
||||
categorySvc: service('category'),
|
||||
appMeta: service(),
|
||||
store: service(),
|
||||
editId: '',
|
||||
editName: '',
|
||||
deleteId: '',
|
||||
dropdown: null,
|
||||
newCategory: '',
|
||||
|
||||
init() {
|
||||
|
@ -34,13 +34,11 @@ export default Component.extend(ModalMixin, TooltipMixin, Notifer, {
|
|||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
this.renderTooltips();
|
||||
this.load();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
load() {
|
||||
|
@ -120,13 +118,20 @@ export default Component.extend(ModalMixin, TooltipMixin, Notifer, {
|
|||
spaceId: this.get('space.id')
|
||||
};
|
||||
|
||||
this.showWait();
|
||||
this.get('categorySvc').add(c).then(() => {
|
||||
this.load();
|
||||
this.showDone();
|
||||
this.notifySuccess('Category added');
|
||||
});
|
||||
},
|
||||
|
||||
onShowEdit(id) {
|
||||
let cat = this.get('category').findBy('id', id);
|
||||
this.set('editId', cat.get('id'));
|
||||
this.set('editName', cat.get('category'));
|
||||
|
||||
this.modalOpen('#category-edit-modal', {show: true}, "#edit-category-id");
|
||||
},
|
||||
|
||||
onShowDelete(id) {
|
||||
let cat = this.get('category').findBy('id', id);
|
||||
this.set('deleteId', cat.get('id'));
|
||||
|
@ -142,32 +147,22 @@ export default Component.extend(ModalMixin, TooltipMixin, Notifer, {
|
|||
});
|
||||
},
|
||||
|
||||
onEdit(id) {
|
||||
this.setEdit(id, true);
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
onEditCancel(id) {
|
||||
this.setEdit(id, false);
|
||||
this.load();
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
onSave(id) {
|
||||
let cat = this.setEdit(id, true);
|
||||
if (cat.get('category') === '') {
|
||||
$('#edit-category-' + cat.get('id')).addClass('is-invalid').focus();
|
||||
onSave() {
|
||||
let name = this.get('editName');
|
||||
if (name === '') {
|
||||
$('#edit-category-name').addClass('is-invalid').focus();
|
||||
return false;
|
||||
}
|
||||
|
||||
cat = this.setEdit(id, false);
|
||||
$('#edit-category-' + cat.get('id')).removeClass('is-invalid');
|
||||
let cat = this.get('category').findBy('id', this.get('editId'));
|
||||
cat.set('category', name);
|
||||
|
||||
this.modalClose('#category-edit-modal');
|
||||
$('#edit-category-name').removeClass('is-invalid');
|
||||
|
||||
this.get('categorySvc').save(cat).then(() => {
|
||||
this.load();
|
||||
});
|
||||
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
onShowAccessPicker(catId) {
|
||||
|
|
|
@ -36,9 +36,10 @@ export default Component.extend(AuthMixin, Notifier, {
|
|||
$("#delete-space-name").removeClass("is-invalid");
|
||||
|
||||
this.get('spaceSvc').delete(this.get('space.id')).then(() => { /* jshint ignore:line */
|
||||
this.get('localStorage').clearSessionItem('folder');
|
||||
this.get('router').transitionTo('folders');
|
||||
});
|
||||
|
||||
this.get('localStorage').clearSessionItem('folder');
|
||||
this.get('router').transitionTo('folders');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
import { A } from '@ember/array';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { schedule } from '@ember/runloop';
|
||||
import { computed } from '@ember/object';
|
||||
import { empty } from '@ember/object/computed';
|
||||
import AuthMixin from '../../mixins/auth';
|
||||
|
@ -21,18 +20,29 @@ import Component from '@ember/component';
|
|||
export default Component.extend(AuthMixin, Notifier, {
|
||||
router: service(),
|
||||
spaceSvc: service('folder'),
|
||||
iconSvc: service('icon'),
|
||||
localStorage: service('localStorage'),
|
||||
|
||||
isSpaceAdmin: computed('permissions', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
}),
|
||||
|
||||
spaceName: '',
|
||||
hasNameError: empty('spaceName'),
|
||||
spaceTypeOptions: A([]),
|
||||
spaceType: 0,
|
||||
likes: '',
|
||||
allowLikes: false,
|
||||
spaceLifecycleOptions: A([]),
|
||||
spaceLifecycle: null,
|
||||
iconList: A([]),
|
||||
spaceIcon: '',
|
||||
spaceDesc: '',
|
||||
spaceLabel: '',
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.set('iconList', this.get('iconSvc').getSpaceIconList());
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
|
@ -56,6 +66,15 @@ export default Component.extend(AuthMixin, Notifier, {
|
|||
}
|
||||
|
||||
this.set('spaceName', this.get('space.name'));
|
||||
this.set('spaceDesc', this.get('space.desc'));
|
||||
this.set('spaceLabel', this.get('space.labelId'));
|
||||
|
||||
let icon = this.get('space.icon');
|
||||
if (is.empty(icon)) {
|
||||
icon = constants.IconMeta.Apps;
|
||||
}
|
||||
|
||||
this.set('spaceIcon', icon);
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -63,12 +82,16 @@ export default Component.extend(AuthMixin, Notifier, {
|
|||
this.set('spaceType', t);
|
||||
},
|
||||
|
||||
onSetLikes(l) {
|
||||
this.set('allowLikes', l);
|
||||
onSetSpaceLifecycle(l) {
|
||||
this.set('spaceLifecycle', l);
|
||||
},
|
||||
|
||||
schedule('afterRender', () => {
|
||||
if (l) this.$('#space-likes-prompt').focus();
|
||||
});
|
||||
onSetIcon(icon) {
|
||||
this.set('spaceIcon', icon);
|
||||
},
|
||||
|
||||
onSetLabel(id) {
|
||||
this.set('spaceLabel', id);
|
||||
},
|
||||
|
||||
onSave() {
|
||||
|
@ -84,10 +107,12 @@ export default Component.extend(AuthMixin, Notifier, {
|
|||
if (spaceName.length === 0) return;
|
||||
space.set('name', spaceName);
|
||||
|
||||
this.showWait();
|
||||
space.set('icon', this.get('spaceIcon'));
|
||||
space.set('desc', this.get('spaceDesc'));
|
||||
space.set('labelId', this.get('spaceLabel'));
|
||||
|
||||
this.get('spaceSvc').save(space).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@ export default Component.extend(Notifier, Modals, {
|
|||
searchText: '',
|
||||
inviteEmail: '',
|
||||
inviteMessage: '',
|
||||
showSpacePermExplain: false,
|
||||
showDocumentPermExplain: false,
|
||||
|
||||
isSpaceAdmin: computed('permissions', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
|
@ -132,8 +134,6 @@ export default Component.extend(Notifier, Modals, {
|
|||
let spacePermissions = this.get('spacePermissions');
|
||||
let filteredUsers = A([]);
|
||||
|
||||
this.showWait();
|
||||
|
||||
this.get('userSvc').matchUsers(s).then((users) => {
|
||||
users.forEach((user) => {
|
||||
let exists = spacePermissions.findBy('whoId', user.get('id'));
|
||||
|
@ -144,11 +144,30 @@ export default Component.extend(Notifier, Modals, {
|
|||
});
|
||||
|
||||
this.set('filteredUsers', filteredUsers);
|
||||
this.showDone();
|
||||
});
|
||||
},
|
||||
|
||||
actions: {
|
||||
toggleSpacePerms() {
|
||||
this.set('showSpacePermExplain', !this.get('showSpacePermExplain'));
|
||||
|
||||
if (this.showSpacePermExplain) {
|
||||
this.$(".space-perms").show();
|
||||
} else {
|
||||
this.$(".space-perms").hide();
|
||||
}
|
||||
},
|
||||
|
||||
toggleDocumentPerms() {
|
||||
this.set('showDocumentPermExplain', !this.get('showDocumentPermExplain'));
|
||||
|
||||
if (this.showDocumentPermExplain) {
|
||||
this.$(".document-perms").show();
|
||||
} else {
|
||||
this.$(".document-perms").hide();
|
||||
}
|
||||
},
|
||||
|
||||
onShowInviteModal() {
|
||||
this.modalOpen("#space-invite-user-modal", {"show": true}, '#space-invite-email');
|
||||
},
|
||||
|
@ -160,9 +179,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
onSave() {
|
||||
if (!this.get('isSpaceAdmin')) return;
|
||||
|
||||
this.showWait();
|
||||
|
||||
let message = this.getDefaultInvitationMessage();
|
||||
let message = this.getDefaultInvitationMessage();
|
||||
let permissions = this.get('spacePermissions');
|
||||
let folder = this.get('folder');
|
||||
let payload = { Message: message, Permissions: permissions };
|
||||
|
@ -197,7 +214,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
}
|
||||
|
||||
this.get('spaceSvc').savePermissions(folder.get('id'), payload).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Saved');
|
||||
this.get('onRefresh')();
|
||||
});
|
||||
},
|
||||
|
@ -219,17 +236,13 @@ export default Component.extend(Notifier, Modals, {
|
|||
let spacePermissions = this.get('spacePermissions');
|
||||
let constants = this.get('constants');
|
||||
|
||||
this.showWait();
|
||||
|
||||
let exists = spacePermissions.findBy('whoId', user.get('id'));
|
||||
let exists = spacePermissions.findBy('whoId', user.get('id'));
|
||||
|
||||
if (is.undefined(exists)) {
|
||||
spacePermissions.pushObject(this.permissionRecord(constants.WhoType.User, user.get('id'), user.get('fullname')));
|
||||
this.set('spacePermissions', spacePermissions);
|
||||
this.send('onSearch');
|
||||
}
|
||||
|
||||
this.showDone();
|
||||
},
|
||||
|
||||
onSpaceInvite(e) {
|
||||
|
@ -248,9 +261,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
return;
|
||||
}
|
||||
|
||||
this.showWait();
|
||||
|
||||
var result = {
|
||||
var result = {
|
||||
Message: message,
|
||||
Recipients: []
|
||||
};
|
||||
|
@ -271,7 +282,7 @@ export default Component.extend(Notifier, Modals, {
|
|||
this.set('inviteEmail', '');
|
||||
|
||||
this.get('spaceSvc').share(this.get('folder.id'), result).then(() => {
|
||||
this.showDone();
|
||||
this.notifySuccess('Invites sent');
|
||||
this.$('#space-invite-email').removeClass('is-invalid');
|
||||
this.modalClose("#space-invite-user-modal");
|
||||
this.load();
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import { computed } from '@ember/object';
|
||||
import stringUtil from '../../utils/string';
|
||||
import AuthMixin from '../../mixins/auth';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(AuthMixin, Notifier, {
|
||||
spaceSvc: service('folder'),
|
||||
|
||||
isSpaceAdmin: computed('permissions', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
}),
|
||||
|
||||
actions: {
|
||||
onOpenTemplate(id) {
|
||||
if (is.empty(id)) {
|
||||
return;
|
||||
}
|
||||
let template = this.get('templates').findBy('id', id)
|
||||
|
||||
let slug = stringUtil.makeSlug(template.get('title'));
|
||||
this.get('router').transitionTo('document', this.get('space.id'), this.get('space.slug'), id, slug);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -17,6 +17,7 @@ import AuthMixin from '../../mixins/auth';
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(AuthMixin, {
|
||||
classNames: ["section"],
|
||||
router: service(),
|
||||
documentService: service('document'),
|
||||
folderService: service('folder'),
|
||||
|
@ -26,6 +27,8 @@ export default Component.extend(AuthMixin, {
|
|||
spaceSettings: computed('permissions', function() {
|
||||
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
|
||||
}),
|
||||
selectedFilter: '',
|
||||
spaceLabel: null,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
@ -53,6 +56,7 @@ export default Component.extend(AuthMixin, {
|
|||
|
||||
this.set('categories', categories);
|
||||
this.set('categoryLinkName', categories.length > 0 ? 'Manage' : 'Add');
|
||||
this.set('spaceLabel', _.findWhere(this.get('labels'), {id: this.get('space.labelId')}));
|
||||
|
||||
schedule('afterRender', () => {
|
||||
if (this.get('categoryFilter') !== '') {
|
||||
|
@ -81,8 +85,6 @@ export default Component.extend(AuthMixin, {
|
|||
});
|
||||
|
||||
this.set('categoryFilter', id);
|
||||
this.set('spaceSelected', false);
|
||||
this.set('uncategorizedSelected', false);
|
||||
break;
|
||||
|
||||
case 'uncategorized':
|
||||
|
@ -94,19 +96,29 @@ export default Component.extend(AuthMixin, {
|
|||
});
|
||||
|
||||
this.set('categoryFilter', '');
|
||||
this.set('spaceSelected', false);
|
||||
this.set('uncategorizedSelected', true);
|
||||
break;
|
||||
|
||||
case 'space':
|
||||
allowed = _.pluck(categoryMembers, 'documentId');
|
||||
docs.forEach((d) => {
|
||||
filtered.pushObject(d);
|
||||
});
|
||||
|
||||
this.set('categoryFilter', '');
|
||||
this.set('spaceSelected', true);
|
||||
this.set('uncategorizedSelected', false);
|
||||
break;
|
||||
|
||||
case 'template':
|
||||
filtered.pushObjects(this.get('templates'));
|
||||
this.set('categoryFilter', '');
|
||||
break;
|
||||
|
||||
case 'draft':
|
||||
filtered = this.get('documentsDraft');
|
||||
this.set('categoryFilter', '');
|
||||
break;
|
||||
|
||||
case 'live':
|
||||
filtered = this.get('documentsLive');
|
||||
this.set('categoryFilter', '');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -114,6 +126,7 @@ export default Component.extend(AuthMixin, {
|
|||
cat.set('selected', cat.get('id') === id);
|
||||
});
|
||||
|
||||
this.set('selectedFilter', filter);
|
||||
this.set('categories', categories);
|
||||
this.get('onFiltered')(filtered);
|
||||
}
|
|
@ -13,13 +13,13 @@ import $ from 'jquery';
|
|||
import { computed } from '@ember/object';
|
||||
import { schedule } from '@ember/runloop';
|
||||
import { inject as service } from '@ember/service';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import AuthMixin from '../../mixins/auth';
|
||||
import Notifier from '../../mixins/notifier';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
||||
export default Component.extend(ModalMixin, AuthMixin, Notifier, {
|
||||
classNames: ["display-inline-block"],
|
||||
spaceService: service('folder'),
|
||||
localStorage: service(),
|
||||
templateService: service('template'),
|
||||
|
@ -75,7 +75,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
this.set('pinState.pinId', pinId);
|
||||
this.set('pinState.isPinned', pinId !== '');
|
||||
this.set('pinState.newName', folder.get('name'));
|
||||
this.renderTooltips();
|
||||
});
|
||||
|
||||
let cats = this.get('categories');
|
||||
|
@ -92,7 +91,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
this.removeTooltips();
|
||||
|
||||
if (is.not.null(this.get('dropzone'))) {
|
||||
this.get('dropzone').destroy();
|
||||
|
@ -152,11 +150,9 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
actions: {
|
||||
onUnpin() {
|
||||
this.get('pinned').unpinItem(this.get('pinState.pinId')).then(() => {
|
||||
$('#space-pin-button').tooltip('dispose');
|
||||
this.set('pinState.isPinned', false);
|
||||
this.set('pinState.pinId', '');
|
||||
this.eventBus.publish('pinChange');
|
||||
this.renderTooltips();
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -168,11 +164,9 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
};
|
||||
|
||||
this.get('pinned').pinItem(pin).then((pin) => {
|
||||
$('#space-pin-button').tooltip('dispose');
|
||||
this.set('pinState.isPinned', true);
|
||||
this.set('pinState.pinId', pin.get('id'));
|
||||
this.eventBus.publish('pinChange');
|
||||
this.renderTooltips();
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -288,8 +282,6 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
},
|
||||
|
||||
onExport() {
|
||||
this.showWait();
|
||||
|
||||
let spec = {
|
||||
spaceId: this.get('space.id'),
|
||||
data: [],
|
||||
|
@ -310,7 +302,7 @@ export default Component.extend(ModalMixin, TooltipMixin, AuthMixin, Notifier, {
|
|||
|
||||
this.get('documentSvc').export(spec).then((htmlExport) => {
|
||||
this.get('browserSvc').downloadFile(htmlExport, this.get('space.slug') + '.html');
|
||||
this.showDone();
|
||||
this.notifySuccess('Exported');
|
||||
});
|
||||
|
||||
this.modalClose("#space-export-modal");
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
import $ from 'jquery';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['layout-footer', 'non-printable'],
|
||||
tagName: 'footer',
|
||||
appMeta: service(),
|
||||
showWait: false,
|
||||
showDone: false,
|
||||
showMessage: false,
|
||||
message: '',
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.eventBus.subscribe('notifyUser', this, 'processNotification');
|
||||
},
|
||||
|
||||
processNotification(msg) {
|
||||
if (this.get('isDestroyed') || this.get('isDestroying')) return;
|
||||
|
||||
if (msg === 'wait') {
|
||||
this.set('showWait', true);
|
||||
this.set('showMessage', false);
|
||||
this.set('showDone', false);
|
||||
}
|
||||
|
||||
if (msg === 'done') {
|
||||
$('.progress-done').removeClass('zoomOut').addClass('zoomIn');
|
||||
this.set('showWait', false);
|
||||
this.set('showMessage', false);
|
||||
this.set('showDone', true);
|
||||
|
||||
setTimeout(function() {
|
||||
$('.progress-done').removeClass('zoomIn').addClass('zoomOut');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
if (msg !== 'done' && msg !== 'wait') {
|
||||
$('.progress-notification').removeClass('zoomOut').addClass('zoomIn');
|
||||
this.set('showWait', false);
|
||||
this.set('showDone', false);
|
||||
this.set('showMessage', true);
|
||||
this.set('message', msg);
|
||||
|
||||
setTimeout(function() {
|
||||
$('.progress-notification').removeClass('zoomIn').addClass('zoomOut');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
});
|
28
gui/app/components/layout/logo-heading.js
Normal file
28
gui/app/components/layout/logo-heading.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
||||
//
|
||||
// This software (Documize Community Edition) is licensed under
|
||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
||||
//
|
||||
// You can operate outside the AGPL restrictions by purchasing
|
||||
// Documize Enterprise Edition and obtaining a commercial license
|
||||
// by contacting <sales@documize.com>.
|
||||
//
|
||||
// https://documize.com
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
appMeta: service(),
|
||||
icon: null,
|
||||
meta: null,
|
||||
logo: false,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
if (this.get('logo')) {
|
||||
let cb = + new Date();
|
||||
this.set('cacheBuster', cb);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -12,21 +12,14 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'nav',
|
||||
classNames: ['layout-sidebar', 'non-printable'],
|
||||
classNameBindings: ['scrollable:sidebar-scroll'],
|
||||
scrollable: false,
|
||||
tagName: 'div',
|
||||
classNames: ['master-container'],
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
// let sb = this.$().overlayScrollbars({ scrollbars: { autoHide: 'leave' }});
|
||||
// this.set('scrollbars', sb);
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
// let sb = this.get('scrollbars');
|
||||
// sb.destroy();
|
||||
}
|
||||
});
|
|
@ -11,14 +11,14 @@
|
|||
|
||||
import $ from 'jquery';
|
||||
import { notEmpty } from '@ember/object/computed';
|
||||
import { inject as service } from '@ember/service'
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Modals from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(ModalMixin, TooltipMixin, {
|
||||
classNames: ['layout-header', 'non-printable'],
|
||||
tagName: 'header',
|
||||
export default Component.extend(Modals, {
|
||||
tagName: 'div',
|
||||
classNames: ['master-sidebar-container', 'non-printable'],
|
||||
selectedItem: '',
|
||||
folderService: service('folder'),
|
||||
appMeta: service(),
|
||||
session: service(),
|
||||
|
@ -30,9 +30,11 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
hasDocumentPins: notEmpty('documentPins'),
|
||||
hasWhatsNew: false,
|
||||
newsContent: '',
|
||||
hideNavigation: false,
|
||||
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
|
||||
let constants = this.get('constants');
|
||||
|
||||
this.pins = [];
|
||||
|
@ -71,7 +73,15 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
this.setupPins();
|
||||
}
|
||||
|
||||
this.renderTooltips();
|
||||
this.eventBus.subscribe('notifyUser', this, 'processNotification');
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.eventBus.unsubscribe('notifyUser');
|
||||
this.eventBus.unsubscribe('pinChange');
|
||||
iziToast.destroy();
|
||||
},
|
||||
|
||||
setupPins() {
|
||||
|
@ -87,11 +97,39 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
});
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
processNotification(msg, type) {
|
||||
if (this.get('isDestroyed') || this.get('isDestroying')) return;
|
||||
|
||||
this.removeTooltips();
|
||||
this.eventBus.unsubscribe('pinChange');
|
||||
if (is.not.undefined(type)) {
|
||||
switch (type) {
|
||||
case 'info':
|
||||
iziToast.info({
|
||||
title: '',
|
||||
message: msg,
|
||||
});
|
||||
break;
|
||||
case 'success':
|
||||
iziToast.success({
|
||||
title: '',
|
||||
message: msg,
|
||||
});
|
||||
break;
|
||||
case 'warn':
|
||||
iziToast.warning({
|
||||
title: '',
|
||||
message: msg,
|
||||
});
|
||||
break;
|
||||
case 'error':
|
||||
iziToast.error({
|
||||
title: '',
|
||||
message: msg,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
@ -110,12 +148,11 @@ export default Component.extend(ModalMixin, TooltipMixin, {
|
|||
}
|
||||
},
|
||||
|
||||
onShowWhatsNewModal() {
|
||||
this.modalOpen("#whats-new-modal", { "show": true });
|
||||
|
||||
onNew() {
|
||||
if (this.get('newsContent.length') > 0) {
|
||||
this.get('session').seenNewVersion();
|
||||
this.set('hasWhatsNew', false);
|
||||
this.get('router').transitionTo('updates');
|
||||
}
|
||||
},
|
||||
|
|
@ -13,6 +13,6 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['layout-body'],
|
||||
tagName: 'main'
|
||||
tagName: 'p',
|
||||
classNames: ['master-page-desc'],
|
||||
});
|
|
@ -13,6 +13,6 @@
|
|||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend({
|
||||
classNames: ['layout-content'],
|
||||
tagName: 'article'
|
||||
tagName: 'h1',
|
||||
classNames: ['master-page-heading'],
|
||||
});
|
|
@ -95,11 +95,14 @@ export default Component.extend({
|
|||
}
|
||||
|
||||
if ($("#stage-2-password-confirm").val() !== $("#stage-2-password").val()) {
|
||||
$(".mismatch").show();
|
||||
$("#stage-2-password").addClass("is-invalid");
|
||||
$("#stage-2-password-confirm").addClass("is-invalid");
|
||||
// $(".mismatch").show();
|
||||
// $(".password-status").attr("src", "/assets/img/onboard/lock-red.png");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#stage-2-password").removeClass("is-invalid");
|
||||
$("#stage-2-password-confirm").removeClass("is-invalid");
|
||||
|
||||
self.set('processing', false);
|
||||
|
@ -121,7 +124,7 @@ export default Component.extend({
|
|||
let creds = { password: password, email: user.email };
|
||||
|
||||
self.get('session').authenticate('authenticator:documize', creds).then(() => {
|
||||
window.location.href = 's/' + self.folderId + "/" + self.slug;
|
||||
window.location.href = '//' + window.location.host + '/s/' + self.folderId + "/" + self.slug;
|
||||
});
|
||||
|
||||
// var credentials = encodingUtil.Base64.encode(netUtil.getSubdomain() + ":" + user.email + ":" + password);
|
||||
|
|
|
@ -12,11 +12,10 @@
|
|||
import $ from 'jquery';
|
||||
import { empty } from '@ember/object/computed';
|
||||
import { computed } from '@ember/object';
|
||||
import TooltipMixin from '../../mixins/tooltip';
|
||||
import ModalMixin from '../../mixins/modal';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(TooltipMixin, ModalMixin, {
|
||||
export default Component.extend(ModalMixin, {
|
||||
busy: false,
|
||||
mousetrap: null,
|
||||
showLinkModal: false,
|
||||
|
@ -57,15 +56,11 @@ export default Component.extend(TooltipMixin, ModalMixin, {
|
|||
$('#' + this.get('pageId')).focus(function() {
|
||||
$(this).select();
|
||||
});
|
||||
|
||||
this.renderTooltips();
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this._super(...arguments);
|
||||
|
||||
this.removeTooltips();
|
||||
|
||||
let mousetrap = this.get('mousetrap');
|
||||
if (is.not.null(mousetrap)) {
|
||||
mousetrap.unbind('esc');
|
||||
|
|
|
@ -10,10 +10,9 @@
|
|||
// https://documize.com
|
||||
|
||||
import { computed } from '@ember/object';
|
||||
import TooltipMixin from '../../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(TooltipMixin, {
|
||||
export default Component.extend({
|
||||
isDirty: false,
|
||||
pageBody: "",
|
||||
codeSyntax: null,
|
||||
|
@ -100,9 +99,6 @@ export default Component.extend(TooltipMixin, {
|
|||
editor = null;
|
||||
this.set('codeEditor', null);
|
||||
}
|
||||
|
||||
this.removeTooltips();
|
||||
|
||||
},
|
||||
|
||||
// Wrap code in PRE tag with language identifier for subsequent rendering.
|
||||
|
|
|
@ -13,11 +13,10 @@ import $ from 'jquery';
|
|||
import { set } from '@ember/object';
|
||||
import { schedule } from '@ember/runloop';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import SectionMixin from '../../../mixins/section';
|
||||
import TooltipMixin from '../../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(SectionMixin, TooltipMixin, {
|
||||
export default Component.extend(SectionMixin, {
|
||||
sectionService: service('section'),
|
||||
isDirty: false,
|
||||
waiting: false,
|
||||
|
@ -27,7 +26,7 @@ export default Component.extend(SectionMixin, TooltipMixin, {
|
|||
this._super(...arguments);
|
||||
this.user = {};
|
||||
this.workspaces = [];
|
||||
this.config = {};
|
||||
this.config = {};
|
||||
},
|
||||
|
||||
didReceiveAttrs() {
|
||||
|
@ -92,7 +91,6 @@ export default Component.extend(SectionMixin, TooltipMixin, {
|
|||
|
||||
schedule('afterRender', () => {
|
||||
window.scrollTo(0, document.body.scrollHeight);
|
||||
self.renderTooltips();
|
||||
});
|
||||
self.set('waiting', false);
|
||||
}, function (reason) { // eslint-disable-line no-unused-vars
|
||||
|
|
|
@ -10,11 +10,10 @@
|
|||
// https://documize.com
|
||||
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import SectionMixin from '../../../mixins/section';
|
||||
import TooltipMixin from '../../../mixins/tooltip';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(SectionMixin, TooltipMixin, {
|
||||
export default Component.extend(SectionMixin, {
|
||||
sectionService: service('section'),
|
||||
isDirty: false,
|
||||
waiting: false,
|
||||
|
|
|
@ -33,7 +33,7 @@ export default Component.extend({
|
|||
init() {
|
||||
this._super(...arguments);
|
||||
let body = (is.not.undefined(this.get('meta'))) ? this.get('meta.rawBody').trim() : '';
|
||||
this.set('pageBody', body);
|
||||
this.set('pageBody', body);
|
||||
},
|
||||
|
||||
didInsertElement() {
|
||||
|
@ -67,7 +67,7 @@ export default Component.extend({
|
|||
dragDrop: false,
|
||||
extraKeys: {"Enter": "newlineAndIndentContinueMarkdownList"}
|
||||
});
|
||||
|
||||
|
||||
CodeMirror.commands.save = function(/*instance*/){
|
||||
Mousetrap.trigger('ctrl+s');
|
||||
};
|
||||
|
|
|
@ -14,12 +14,11 @@ import $ from 'jquery';
|
|||
import { htmlSafe } from '@ember/string';
|
||||
import { computed, set } from '@ember/object';
|
||||
import { inject as service } from '@ember/service';
|
||||
import Component from '@ember/component';
|
||||
import NotifierMixin from '../../../mixins/notifier';
|
||||
import TooltipMixin from '../../../mixins/tooltip';
|
||||
import SectionMixin from '../../../mixins/section';
|
||||
import Component from '@ember/component';
|
||||
|
||||
export default Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
|
||||
export default Component.extend(SectionMixin, NotifierMixin, {
|
||||
sectionService: service('section'),
|
||||
isDirty: false,
|
||||
busy: false,
|
||||
|
@ -93,10 +92,6 @@ export default Component.extend(SectionMixin, NotifierMixin, TooltipMixin, {
|
|||
});
|
||||
},
|
||||
|
||||
willDestroyElement() {
|
||||
this.removeTooltips();
|
||||
},
|
||||
|
||||
getBoardLists() {
|
||||
this.set('busy', true);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue