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

Enable in-app backup/restore for global/tenants

Re-mapping of ORG and USER ID values ensures conflict-free merging.

Version numbers bumped.

Co-Authored-By: Harvey Kandola <harvey@documize.com>
This commit is contained in:
sauls8t 2018-10-19 12:40:45 +01:00
parent ec1939c01d
commit db04057d9e
10 changed files with 913 additions and 788 deletions

View file

@ -58,9 +58,9 @@ Space view.
## Latest version ## Latest version
[Community edition: v1.71.0](https://github.com/documize/community/releases) [Community edition: v1.72.0](https://github.com/documize/community/releases)
[Enterprise edition: v1.73.0](https://documize.com/downloads) [Enterprise edition: v1.74.0](https://documize.com/downloads)
## OS support ## OS support

1
core/env/logger.go vendored
View file

@ -15,6 +15,7 @@ package env
// Logger provides the interface for Documize compatible loggers. // Logger provides the interface for Documize compatible loggers.
type Logger interface { type Logger interface {
Info(message string) Info(message string)
Infof(message string, a ...interface{})
Trace(message string) Trace(message string)
Error(message string, err error) Error(message string, err error)
// SetDB(l Logger, db *sqlx.DB) Logger // SetDB(l Logger, db *sqlx.DB) Logger

View file

@ -194,6 +194,7 @@ func (h *Handler) Restore(w http.ResponseWriter, r *http.Request) {
spec := m.ImportSpec{OverwriteOrg: overwriteOrg, CreateUsers: createUsers, Org: org} spec := m.ImportSpec{OverwriteOrg: overwriteOrg, CreateUsers: createUsers, Org: org}
rh := restoreHandler{Runtime: h.Runtime, Store: h.Store, Context: ctx, Spec: spec} rh := restoreHandler{Runtime: h.Runtime, Store: h.Store, Context: ctx, Spec: spec}
// Run the restore process.
err = rh.PerformRestore(b.Bytes(), r.ContentLength) err = rh.PerformRestore(b.Bytes(), r.ContentLength)
if err != nil { if err != nil {
response.WriteServerError(w, method, err) response.WriteServerError(w, method, err)
@ -201,6 +202,8 @@ func (h *Handler) Restore(w http.ResponseWriter, r *http.Request) {
return return
} }
h.Runtime.Log.Infof("Restore remapped %d OrgID values", len(rh.MapOrgID))
h.Runtime.Log.Infof("Restore remapped %d UserID values", len(rh.MapUserID))
h.Runtime.Log.Info("Restore completed") h.Runtime.Log.Info("Restore completed")
response.WriteEmpty(w) response.WriteEmpty(w)

View file

@ -18,6 +18,7 @@ package backup
import ( import (
"archive/zip" "archive/zip"
"bytes" "bytes"
"database/sql"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/documize/community/model/account" "github.com/documize/community/model/account"
@ -53,6 +54,27 @@ type restoreHandler struct {
Spec m.ImportSpec Spec m.ImportSpec
Context domain.RequestContext Context domain.RequestContext
Zip *zip.Reader Zip *zip.Reader
MapOrgID map[string]string
MapUserID map[string]string
}
// During the restore process, it may be necessary to change
// ID values found in backup file with a value that exists in the
// target database.
//
// NOTE: this only applies to tenant backups as we have to restore data
// into the active tenant.
func (r *restoreHandler) remapOrg(id string) string {
if n, ok := r.MapOrgID[id]; ok {
return n
}
return id
}
func (r *restoreHandler) remapUser(id string) string {
if n, ok := r.MapUserID[id]; ok {
return n
}
return id
} }
// PerformRestore will unzip backup file and verify contents // PerformRestore will unzip backup file and verify contents
@ -79,12 +101,40 @@ func (r *restoreHandler) PerformRestore(b []byte, l int64) (err error) {
r.Spec.GlobalBackup = false r.Spec.GlobalBackup = false
} }
// Process might require reassignment of ID's so prepare map.
r.MapOrgID = make(map[string]string)
r.MapUserID = make(map[string]string)
// Organization. // Organization.
err = r.dmzOrg() err = r.dmzOrg()
if err != nil { if err != nil {
return return
} }
// User.
err = r.dmzUser()
if err != nil {
return
}
// User Account.
err = r.dmzUserAccount()
if err != nil {
return
}
// User Activity.
err = r.dmzUserActivity()
if err != nil {
return
}
// User Config.
err = r.dmzUserConfig()
if err != nil {
return
}
// Config. // Config.
err = r.dmzConfig() err = r.dmzConfig()
if err != nil { if err != nil {
@ -205,30 +255,6 @@ func (r *restoreHandler) PerformRestore(b []byte, l int64) (err error) {
return return
} }
// User.
err = r.dmzUser()
if err != nil {
return
}
// User Account.
err = r.dmzUserAccount()
if err != nil {
return
}
// User Activity.
err = r.dmzUserActivity()
if err != nil {
return
}
// User Config.
err = r.dmzUserConfig()
if err != nil {
return
}
return nil return nil
} }
@ -319,18 +345,17 @@ func (r *restoreHandler) dmzOrg() (err error) {
return return
} }
// Nuke all existing data.
_, err = r.Context.Transaction.Exec("TRUNCATE TABLE dmz_org")
if err != nil {
r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename))
return
}
// For global backup we recreate everything. // For global backup we recreate everything.
// For tenant backup we just update the current OrgID to match // For tenant backup we just update the current OrgID to match
// the one in the backup file, ensuring correct data linkage. // the one in the backup file, ensuring correct data linkage.
if r.Spec.GlobalBackup { if r.Spec.GlobalBackup {
// Nuke all existing data.
_, err = r.Context.Transaction.Exec("TRUNCATE TABLE dmz_org")
if err != nil {
r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return
}
for i := range org { for i := range org {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
@ -356,7 +381,10 @@ func (r *restoreHandler) dmzOrg() (err error) {
return return
} }
org[1].RefID = r.Spec.Org.RefID // Existing orgID from database overrides all incoming orgID values
// by using remapOrg().
r.MapOrgID[org[0].RefID] = r.Spec.Org.RefID
org[0].RefID = r.remapOrg(org[0].RefID) // e.g. remap orgID
// Update org settings if allowed to do so. // Update org settings if allowed to do so.
if !r.Spec.OverwriteOrg { if !r.Spec.OverwriteOrg {
@ -460,13 +488,13 @@ func (r *restoreHandler) dmzAudit() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
for i := range log { for i := range log {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_audit_log (c_orgid, c_userid, c_eventtype, c_ip, c_created) VALUES (?, ?, ?, ?, ?)"), _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_audit_log (c_orgid, c_userid, c_eventtype, c_ip, c_created) VALUES (?, ?, ?, ?, ?)"),
log[i].OrgID, log[i].UserID, log[i].Type, log[i].IP, log[i].Created) r.remapOrg(log[i].OrgID), r.remapUser(log[i].UserID), log[i].Type, log[i].IP, log[i].Created)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to insert %s %d", filename, log[i].ID)) err = errors.Wrap(err, fmt.Sprintf("unable to insert %s %d", filename, log[i].ID))
@ -516,13 +544,13 @@ func (r *restoreHandler) dmzAction() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
for i := range ac { for i := range ac {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_action (c_refid, c_orgid, c_userid, c_docid, c_actiontype, c_note, c_requestorid, c_requested, c_due, c_reftype, c_reftypeid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_action (c_refid, c_orgid, c_userid, c_docid, c_actiontype, c_note, c_requestorid, c_requested, c_due, c_reftype, c_reftypeid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
ac[i].RefID, ac[i].OrgID, ac[i].UserID, ac[i].DocumentID, ac[i].ActionType, ac[i].Note, ac[i].RequestorID, ac[i].Requested, ac[i].Due, ac[i].RefType, ac[i].RefTypeID) ac[i].RefID, r.remapOrg(ac[i].OrgID), r.remapUser(ac[i].UserID), ac[i].DocumentID, ac[i].ActionType, ac[i].Note, ac[i].RequestorID, ac[i].Requested, ac[i].Due, ac[i].RefType, ac[i].RefTypeID)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to insert %s %s", filename, ac[i].RefID)) err = errors.Wrap(err, fmt.Sprintf("unable to insert %s %s", filename, ac[i].RefID))
@ -569,13 +597,13 @@ func (r *restoreHandler) dmzSpace() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
for i := range sp { for i := range sp {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("INSERT INTO dmz_space (c_refid, c_name, c_orgid, c_userid, c_type, c_lifecycle, c_likes, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"), _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind("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, sp[i].OrgID, sp[i].UserID, sp[i].Type, sp[i].Lifecycle, sp[i].Likes, sp[i].Created, sp[i].Revised) 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)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -623,7 +651,7 @@ func (r *restoreHandler) dmzCategory() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -631,7 +659,7 @@ func (r *restoreHandler) dmzCategory() (err error) {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_category (c_refid, c_orgid, c_spaceid, c_name, c_created, c_revised) INSERT INTO dmz_category (c_refid, c_orgid, c_spaceid, c_name, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?)`),
ct[i].RefID, ct[i].OrgID, ct[i].SpaceID, ct[i].Name, ct[i].Created, ct[i].Revised) ct[i].RefID, r.remapOrg(ct[i].OrgID), ct[i].SpaceID, ct[i].Name, ct[i].Created, ct[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -679,7 +707,7 @@ func (r *restoreHandler) dmzCategoryMember() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -688,7 +716,7 @@ func (r *restoreHandler) dmzCategoryMember() (err error) {
INSERT INTO dmz_category_member INSERT INTO dmz_category_member
(c_refid, c_orgid, c_categoryid, c_spaceid, c_docid, c_created, c_revised) (c_refid, c_orgid, c_categoryid, c_spaceid, c_docid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?)`),
cm[i].RefID, cm[i].OrgID, cm[i].CategoryID, cm[i].SpaceID, cm[i].DocumentID, cm[i].Created, cm[i].Revised) cm[i].RefID, r.remapOrg(cm[i].OrgID), cm[i].CategoryID, cm[i].SpaceID, cm[i].DocumentID, cm[i].Created, cm[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -736,7 +764,7 @@ func (r *restoreHandler) dmzGroup() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -745,7 +773,7 @@ func (r *restoreHandler) dmzGroup() (err error) {
INSERT INTO dmz_group INSERT INTO dmz_group
(c_refid, c_orgid, c_name, c_desc, c_created, c_revised) (c_refid, c_orgid, c_name, c_desc, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?)`),
gr[i].RefID, gr[i].OrgID, gr[i].Name, gr[i].Purpose, gr[i].Created, gr[i].Revised) gr[i].RefID, r.remapOrg(gr[i].OrgID), gr[i].Name, gr[i].Purpose, gr[i].Created, gr[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -793,7 +821,7 @@ func (r *restoreHandler) dmzGroupMember() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -802,7 +830,7 @@ func (r *restoreHandler) dmzGroupMember() (err error) {
INSERT INTO dmz_group_member INSERT INTO dmz_group_member
(c_orgid, c_groupid, c_userid) (c_orgid, c_groupid, c_userid)
VALUES (?, ?, ?)`), VALUES (?, ?, ?)`),
gm[i].OrgID, gm[i].GroupID, gm[i].UserID) r.remapOrg(gm[i].OrgID), gm[i].GroupID, r.remapUser(gm[i].UserID))
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -850,7 +878,7 @@ func (r *restoreHandler) dmzPermission() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -859,7 +887,9 @@ func (r *restoreHandler) dmzPermission() (err error) {
INSERT INTO dmz_permission INSERT INTO dmz_permission
(c_orgid, c_who, c_whoid, c_action, c_scope, c_location, c_refid, c_created) (c_orgid, c_who, c_whoid, c_action, c_scope, c_location, c_refid, c_created)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?)`),
pm[i].OrgID, string(pm[i].Who), pm[i].WhoID, string(pm[i].Action), string(pm[i].Scope), string(pm[i].Location), pm[i].RefID, pm[i].Created) r.remapOrg(pm[i].OrgID), string(pm[i].Who), r.remapUser(pm[i].WhoID),
string(pm[i].Action), string(pm[i].Scope),
string(pm[i].Location), pm[i].RefID, pm[i].Created)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -907,7 +937,7 @@ func (r *restoreHandler) dmzPin() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -916,7 +946,8 @@ func (r *restoreHandler) dmzPin() (err error) {
INSERT INTO dmz_pin INSERT INTO dmz_pin
(c_refid, c_orgid, c_userid, c_spaceid, c_docid, c_name, c_sequence, c_created, c_revised) (c_refid, c_orgid, c_userid, c_spaceid, c_docid, c_name, c_sequence, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
pin[i].RefID, pin[i].OrgID, pin[i].UserID, pin[i].SpaceID, pin[i].DocumentID, pin[i].Name, pin[i].Sequence, pin[i].Created, pin[i].Revised) pin[i].RefID, r.remapOrg(pin[i].OrgID), r.remapUser(pin[i].UserID), pin[i].SpaceID,
pin[i].DocumentID, pin[i].Name, pin[i].Sequence, pin[i].Created, pin[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -964,7 +995,7 @@ func (r *restoreHandler) dmzSection() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -974,7 +1005,7 @@ func (r *restoreHandler) dmzSection() (err error) {
(c_refid, c_orgid, c_docid, c_userid, c_contenttype, c_type, c_level, c_name, c_body, (c_refid, c_orgid, c_docid, c_userid, c_contenttype, c_type, c_level, c_name, c_body,
c_revisions, c_sequence, c_templateid, c_status, c_relativeid, c_created, c_revised) c_revisions, c_sequence, c_templateid, c_status, c_relativeid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
sc[i].RefID, sc[i].OrgID, sc[i].DocumentID, sc[i].UserID, sc[i].RefID, r.remapOrg(sc[i].OrgID), sc[i].DocumentID, r.remapUser(sc[i].UserID),
sc[i].ContentType, sc[i].Type, sc[i].Level, sc[i].Name, sc[i].ContentType, sc[i].Type, sc[i].Level, sc[i].Name,
sc[i].Body, sc[i].Revisions, sc[i].Sequence, sc[i].TemplateID, sc[i].Body, sc[i].Revisions, sc[i].Sequence, sc[i].TemplateID,
sc[i].Status, sc[i].RelativeID, sc[i].Created, sc[i].Revised) sc[i].Status, sc[i].RelativeID, sc[i].Created, sc[i].Revised)
@ -1026,7 +1057,7 @@ func (r *restoreHandler) dmzSectionMeta() (err error) {
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1036,7 +1067,7 @@ func (r *restoreHandler) dmzSectionMeta() (err error) {
(c_sectionid, c_orgid, c_userid, c_docid, c_rawbody, (c_sectionid, c_orgid, c_userid, c_docid, c_rawbody,
c_config, c_external, c_created, c_revised) c_config, c_external, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
sm[i].SectionID, sm[i].OrgID, sm[i].UserID, sm[i].DocumentID, sm[i].SectionID, r.remapOrg(sm[i].OrgID), r.remapUser(sm[i].UserID), sm[i].DocumentID,
sm[i].RawBody, sm[i].Config, sm[i].ExternalSource, sm[i].RawBody, sm[i].Config, sm[i].ExternalSource,
sm[i].Created, sm[i].Revised) sm[i].Created, sm[i].Revised)
@ -1086,7 +1117,7 @@ func (r *restoreHandler) dmzSectionRevision() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1096,8 +1127,8 @@ func (r *restoreHandler) dmzSectionRevision() (err error) {
(c_refid, c_orgid, c_docid, c_ownerid, c_sectionid, c_userid, c_contenttype, (c_refid, c_orgid, c_docid, c_ownerid, c_sectionid, c_userid, c_contenttype,
c_type, c_name, c_body, c_rawbody, c_config, c_created, c_revised) c_type, c_name, c_body, c_rawbody, c_config, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
sr[i].RefID, sr[i].OrgID, sr[i].DocumentID, sr[i].OwnerID, sr[i].RefID, r.remapOrg(sr[i].OrgID), sr[i].DocumentID, sr[i].OwnerID,
sr[i].SectionID, sr[i].UserID, sr[i].ContentType, sr[i].Type, sr[i].Name, sr[i].SectionID, r.remapUser(sr[i].UserID), sr[i].ContentType, sr[i].Type, sr[i].Name,
sr[i].Body, sr[i].RawBody, sr[i].Config, sr[i].Created, sr[i].Revised) sr[i].Body, sr[i].RawBody, sr[i].Config, sr[i].Created, sr[i].Revised)
if err != nil { if err != nil {
@ -1146,7 +1177,7 @@ func (r *restoreHandler) dmzSectionTemplate() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1157,7 +1188,8 @@ func (r *restoreHandler) dmzSectionTemplate() (err error) {
c_type, c_name, c_body, c_desc, c_rawbody, c_used, c_type, c_name, c_body, c_desc, c_rawbody, c_used,
c_config, c_external, c_created, c_revised) c_config, c_external, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
st[i].RefID, st[i].OrgID, st[i].SpaceID, st[i].UserID, st[i].ContentType, st[i].Type, st[i].RefID, r.remapOrg(st[i].OrgID), st[i].SpaceID, r.remapUser(st[i].UserID),
st[i].ContentType, st[i].Type,
st[i].Name, st[i].Body, st[i].Excerpt, st[i].RawBody, st[i].Used, st[i].Name, st[i].Body, st[i].Excerpt, st[i].RawBody, st[i].Used,
st[i].Config, st[i].ExternalSource, st[i].Created, st[i].Revised) st[i].Config, st[i].ExternalSource, st[i].Created, st[i].Revised)
@ -1207,7 +1239,7 @@ func (r *restoreHandler) dmzDoc() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1218,7 +1250,7 @@ func (r *restoreHandler) dmzDoc() (err error) {
c_name, c_desc, c_slug, c_tags, c_template, c_protection, c_approval, c_name, c_desc, c_slug, c_tags, c_template, c_protection, c_approval,
c_lifecycle, c_versioned, c_versionid, c_versionorder, c_groupid, c_created, c_revised) c_lifecycle, c_versioned, c_versionid, c_versionorder, c_groupid, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
doc[i].RefID, doc[i].OrgID, doc[i].SpaceID, doc[i].UserID, doc[i].Job, doc[i].RefID, r.remapOrg(doc[i].OrgID), doc[i].SpaceID, r.remapUser(doc[i].UserID), doc[i].Job,
doc[i].Location, doc[i].Name, doc[i].Excerpt, doc[i].Slug, doc[i].Tags, doc[i].Location, doc[i].Name, doc[i].Excerpt, doc[i].Slug, doc[i].Tags,
doc[i].Template, doc[i].Protection, doc[i].Approval, doc[i].Lifecycle, doc[i].Template, doc[i].Protection, doc[i].Approval, doc[i].Lifecycle,
doc[i].Versioned, doc[i].VersionID, doc[i].VersionOrder, doc[i].GroupID, doc[i].Versioned, doc[i].VersionID, doc[i].VersionOrder, doc[i].GroupID,
@ -1279,7 +1311,7 @@ func (r *restoreHandler) dmzDocVote() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1287,7 +1319,7 @@ func (r *restoreHandler) dmzDocVote() (err error) {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_doc_vote (c_refid, c_orgid, c_docid, c_voter, c_vote, c_created, c_revised) INSERT INTO dmz_doc_vote (c_refid, c_orgid, c_docid, c_voter, c_vote, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?)`),
v[i].RefID, v[i].OrgID, v[i].DocumentID, v[i].VoterID, v[i].Vote, v[i].Created, v[i].Revised) v[i].RefID, r.remapOrg(v[i].OrgID), v[i].DocumentID, v[i].VoterID, v[i].Vote, v[i].Created, v[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -1335,7 +1367,7 @@ func (r *restoreHandler) dmzDocLink() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1345,7 +1377,8 @@ func (r *restoreHandler) dmzDocLink() (err error) {
(c_refid, c_orgid, c_spaceid, c_userid, c_sourcedocid, c_sourcesectionid, (c_refid, c_orgid, c_spaceid, c_userid, c_sourcedocid, c_sourcesectionid,
c_targetdocid, c_targetid, c_externalid, c_type, c_orphan, c_created, c_revised) c_targetdocid, c_targetid, c_externalid, c_type, c_orphan, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
lk[i].RefID, lk[i].OrgID, lk[i].SpaceID, lk[i].UserID, lk[i].SourceDocumentID, lk[i].SourceSectionID, lk[i].RefID, r.remapOrg(lk[i].OrgID), lk[i].SpaceID, r.remapUser(lk[i].UserID),
lk[i].SourceDocumentID, lk[i].SourceSectionID,
lk[i].TargetDocumentID, lk[i].TargetID, lk[i].ExternalID, lk[i].LinkType, lk[i].Orphan, lk[i].TargetDocumentID, lk[i].TargetID, lk[i].ExternalID, lk[i].LinkType, lk[i].Orphan,
lk[i].Created, lk[i].Revised) lk[i].Created, lk[i].Revised)
@ -1395,7 +1428,7 @@ func (r *restoreHandler) dmzDocAttachment() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1405,7 +1438,8 @@ func (r *restoreHandler) dmzDocAttachment() (err error) {
(c_refid, c_orgid, c_docid, c_job, c_fileid, (c_refid, c_orgid, c_docid, c_job, c_fileid,
c_filename, c_data, c_extension, c_created, c_revised) c_filename, c_data, c_extension, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
at[i].RefID, at[i].OrgID, at[i].DocumentID, at[i].Job, at[i].FileID, at[i].Filename, at[i].RefID, r.remapOrg(at[i].OrgID), at[i].DocumentID, at[i].Job,
at[i].FileID, at[i].Filename,
at[i].Data, at[i].Extension, at[i].Created, at[i].Revised) at[i].Data, at[i].Extension, at[i].Created, at[i].Revised)
if err != nil { if err != nil {
@ -1440,7 +1474,6 @@ func (r *restoreHandler) dmzDocComment() (err error) {
Feedback string `json:"feedback"` Feedback string `json:"feedback"`
Created string `json:"created"` Created string `json:"created"`
} }
cm := []comment{} cm := []comment{}
err = r.fileJSON(filename, &cm) err = r.fileJSON(filename, &cm)
if err != nil { if err != nil {
@ -1464,7 +1497,7 @@ func (r *restoreHandler) dmzDocComment() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1473,7 +1506,7 @@ func (r *restoreHandler) dmzDocComment() (err error) {
INSERT INTO dmz_doc_comment INSERT INTO dmz_doc_comment
(c_refid, c_orgid, c_userid, c_docid, c_email, c_feedback, c_created) (c_refid, c_orgid, c_userid, c_docid, c_email, c_feedback, c_created)
VALUES (?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?)`),
cm[i].RefID, cm[i].OrgID, cm[i].UserID, cm[i].DocumentID, cm[i].RefID, r.remapOrg(cm[i].OrgID), r.remapUser(cm[i].UserID), cm[i].DocumentID,
cm[i].Email, cm[i].Feedback, cm[i].Created) cm[i].Email, cm[i].Feedback, cm[i].Created)
if err != nil { if err != nil {
@ -1512,7 +1545,6 @@ func (r *restoreHandler) dmzDocShare() (err error) {
Active bool `json:"active"` Active bool `json:"active"`
Created time.Time `json:"created"` Created time.Time `json:"created"`
} }
sh := []share{} sh := []share{}
err = r.fileJSON(filename, &sh) err = r.fileJSON(filename, &sh)
if err != nil { if err != nil {
@ -1536,7 +1568,7 @@ func (r *restoreHandler) dmzDocShare() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1546,7 +1578,7 @@ func (r *restoreHandler) dmzDocShare() (err error) {
(c_orgid, c_userid, c_docid, c_email, c_message, (c_orgid, c_userid, c_docid, c_email, c_message,
c_secret, c_expires, c_active, c_created) c_secret, c_expires, c_active, c_created)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
sh[i].OrgID, sh[i].UserID, sh[i].DocumentID, sh[i].Email, sh[i].Message, r.remapOrg(sh[i].OrgID), r.remapUser(sh[i].UserID), sh[i].DocumentID, sh[i].Email, sh[i].Message,
sh[i].Secret, sh[i].Expires, sh[i].Active, sh[i].Created) sh[i].Secret, sh[i].Expires, sh[i].Active, sh[i].Created)
if err != nil { if err != nil {
@ -1592,18 +1624,41 @@ func (r *restoreHandler) dmzUser() (err error) {
_, err = r.Context.Transaction.Exec("TRUNCATE TABLE dmz_user") _, err = r.Context.Transaction.Exec("TRUNCATE TABLE dmz_user")
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
} }
for i := range u { for i := range u {
// For tenant backups we first check to see if user exists.
insert := true
if !r.Spec.GlobalBackup {
row := r.Runtime.Db.QueryRow(r.Runtime.Db.Rebind("SELECT COALESCE(c_refid, '') AS userid FROM dmz_user WHERE c_email=?"), u[i].Email)
userID := ""
err = row.Scan(&userID)
if err == sql.ErrNoRows {
err = nil
insert = true
}
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("unable to check email %s", u[i].Email))
return
}
// Existing userID from database overrides all incoming userID values
// by using remapUser().
if len(userID) > 0 {
r.MapUserID[u[i].RefID] = userID
insert = false
}
}
if insert {
_, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(` _, err = r.Context.Transaction.Exec(r.Runtime.Db.Rebind(`
INSERT INTO dmz_user INSERT INTO dmz_user
(c_refid, c_firstname, c_lastname, c_email, c_initials, c_globaladmin, (c_refid, c_firstname, c_lastname, c_email, c_initials, c_globaladmin,
c_password, c_salt, c_reset, c_active, c_lastversion, c_created, c_revised) c_password, c_salt, c_reset, c_active, c_lastversion, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
u[i].RefID, u[i].Firstname, u[i].Lastname, strings.ToLower(u[i].Email), u[i].Initials, r.remapUser(u[i].RefID), u[i].Firstname, u[i].Lastname, strings.ToLower(u[i].Email), u[i].Initials,
u[i].GlobalAdmin, u[i].Password, u[i].Salt, u[i].Reset, u[i].Active, u[i].GlobalAdmin, u[i].Password, u[i].Salt, u[i].Reset, u[i].Active,
u[i].LastVersion, u[i].Created, u[i].Revised) u[i].LastVersion, u[i].Created, u[i].Revised)
@ -1613,6 +1668,7 @@ func (r *restoreHandler) dmzUser() (err error) {
return return
} }
} }
}
err = r.Context.Transaction.Commit() err = r.Context.Transaction.Commit()
if err != nil { if err != nil {
@ -1653,7 +1709,7 @@ func (r *restoreHandler) dmzUserAccount() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1663,7 +1719,7 @@ func (r *restoreHandler) dmzUserAccount() (err error) {
(c_refid, c_orgid, c_userid, c_admin, c_editor, c_users, (c_refid, c_orgid, c_userid, c_admin, c_editor, c_users,
c_analytics, c_active, c_created, c_revised) c_analytics, c_active, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
ac[i].RefID, ac[i].OrgID, ac[i].UserID, ac[i].Admin, ac[i].Editor, ac[i].RefID, r.remapOrg(ac[i].OrgID), r.remapUser(ac[i].UserID), ac[i].Admin, ac[i].Editor,
ac[i].Users, ac[i].Analytics, ac[i].Active, ac[i].Created, ac[i].Revised) ac[i].Users, ac[i].Analytics, ac[i].Active, ac[i].Created, ac[i].Revised)
if err != nil { if err != nil {
@ -1712,7 +1768,7 @@ func (r *restoreHandler) dmzUserActivity() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1722,7 +1778,7 @@ func (r *restoreHandler) dmzUserActivity() (err error) {
(c_orgid, c_userid, c_spaceid, c_docid, c_sectionid, c_sourcetype, (c_orgid, c_userid, c_spaceid, c_docid, c_sectionid, c_sourcetype,
c_activitytype, c_metadata, c_created) c_activitytype, c_metadata, c_created)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`),
ac[i].OrgID, ac[i].UserID, ac[i].SpaceID, ac[i].DocumentID, r.remapOrg(ac[i].OrgID), r.remapUser(ac[i].UserID), ac[i].SpaceID, ac[i].DocumentID,
ac[i].SectionID, ac[i].SourceType, ac[i].ActivityType, ac[i].SectionID, ac[i].SourceType, ac[i].ActivityType,
ac[i].Metadata, ac[i].Created) ac[i].Metadata, ac[i].Created)
@ -1778,7 +1834,7 @@ func (r *restoreHandler) dmzUserConfig() (err error) {
_, err = r.Context.Transaction.Exec(nuke) _, err = r.Context.Transaction.Exec(nuke)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
err = errors.Wrap(err, fmt.Sprintf("unable to truncate table", filename)) err = errors.Wrap(err, fmt.Sprintf("unable to truncate table %s", filename))
return return
} }
@ -1787,7 +1843,7 @@ func (r *restoreHandler) dmzUserConfig() (err error) {
INSERT INTO dmz_user_config INSERT INTO dmz_user_config
(c_orgid, c_userid, c_key, c_config) (c_orgid, c_userid, c_key, c_config)
VALUES (?, ?, ?, ?)`), VALUES (?, ?, ?, ?)`),
uc[i].OrgID, uc[i].UserID, uc[i].ConfigKey, uc[i].ConfigValue) r.remapOrg(uc[i].OrgID), r.remapUser(uc[i].UserID), uc[i].ConfigKey, uc[i].ConfigValue)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()

View file

@ -0,0 +1,59 @@
// 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
import (
"testing"
)
// go test github.com/documize/community/domain/backup -run TestRemapORg
func TestRemapOrg(t *testing.T) {
r := restoreHandler{MapOrgID: make(map[string]string)}
r.MapOrgID["abc"] = "def"
r.MapOrgID["xyz"] = "123"
n := r.remapOrg("abc")
if n != "def" {
t.Errorf("expected def got %s", n)
}
n = r.remapOrg("xyz")
if n != "123" {
t.Errorf("expected 123 got %s", n)
}
n = r.remapOrg("jkl")
if n != "jkl" {
t.Errorf("expected jkl got %s", n)
}
}
func TestRemapUser(t *testing.T) {
r := restoreHandler{MapUserID: make(map[string]string)}
r.MapUserID["abc"] = "def"
r.MapUserID["xyz"] = "123"
n := r.remapUser("abc")
if n != "def" {
t.Errorf("expected def got %s", n)
}
n = r.remapUser("xyz")
if n != "123" {
t.Errorf("expected 123 got %s", n)
}
n = r.remapUser("jkl")
if n != "jkl" {
t.Errorf("expected jkl got %s", n)
}
}

View file

@ -38,7 +38,7 @@ func main() {
// product details // product details
rt.Product = env.ProdInfo{} rt.Product = env.ProdInfo{}
rt.Product.Major = "1" rt.Product.Major = "1"
rt.Product.Minor = "71" rt.Product.Minor = "72"
rt.Product.Patch = "0" rt.Product.Patch = "0"
rt.Product.Revision = 181007125514 rt.Product.Revision = 181007125514
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)

View file

@ -35,6 +35,11 @@ func (l Logger) Info(message string) {
l.log.Println(message) l.log.Println(message)
} }
// Infof logs message via Sprintf.
func (l Logger) Infof(message string, a ...interface{}) {
l.log.Println(fmt.Sprintf(message, a))
}
// Trace logs message if tracing enabled. // Trace logs message if tracing enabled.
func (l Logger) Trace(message string) { func (l Logger) Trace(message string) {
if l.trace { if l.trace {

File diff suppressed because one or more lines are too long

View file

@ -24,6 +24,7 @@
As a Documize <b>Tenant Administrator</b> you can perform a tenant-level backup (e.g. marketing.mycompany.com). As a Documize <b>Tenant Administrator</b> you can perform a tenant-level backup (e.g. marketing.mycompany.com).
</p> </p>
{{/if}} {{/if}}
<p>Please use a Tenant Backup when migrating between self-host and Documize Cloud hosting.</p>
<p>It can take <b>several minutes</b> to complete the backup process &mdash; please be patient while the backup operation is in progress.</p> <p>It can take <b>several minutes</b> to complete the backup process &mdash; please be patient while the backup operation is in progress.</p>
<div class="margin-top-30 margin-bottom-20"> <div class="margin-top-30 margin-bottom-20">
@ -35,10 +36,10 @@
{{#if backupRunning}} {{#if backupRunning}}
<h3 class="text-success">Backup running, please wait...</h3> <h3 class="text-success">Backup running, please wait...</h3>
{{else}} {{else}}
<button class="btn btn-success mb-3" {{action 'onBackup'}}>BACKUP TENANT</button> <button class="btn btn-success mb-3" {{action 'onBackup'}}>TENANT BACKUP ({{appMeta.appHost}})</button>
{{#if session.isGlobalAdmin}} {{#if session.isGlobalAdmin}}
<div class="button-gap" /> <div class="button-gap" />
<button class="btn btn-success mb-3" {{action 'onSystemBackup'}}>BACKUP SYSTEM</button> <button class="btn btn-success mb-3" {{action 'onSystemBackup'}}>SYSTEM BACKUP</button>
{{/if}} {{/if}}
{{/if}} {{/if}}
{{#if backupFailed}} {{#if backupFailed}}

View file

@ -1,6 +1,6 @@
{ {
"name": "documize", "name": "documize",
"version": "1.71.0", "version": "1.72.0",
"description": "The Document IDE", "description": "The Document IDE",
"private": true, "private": true,
"repository": "", "repository": "",