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

Merge pull request #382 from documize/i18n

Localization support
This commit is contained in:
Harvey Kandola 2022-03-21 13:18:44 -04:00 committed by GitHub
commit e56263564c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
546 changed files with 2432 additions and 74338 deletions

View file

@ -22,6 +22,8 @@ echo "Copying Ember pdfjs folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs edition\static\public\pdfjs robocopy /e /NFL /NDL /NJH gui\dist-prod\pdfjs edition\static\public\pdfjs
echo "Copying Ember sections folder" echo "Copying Ember sections folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\sections edition\static\public\sections robocopy /e /NFL /NDL /NJH gui\dist-prod\sections edition\static\public\sections
echo "Copying i18n folder"
robocopy /e /NFL /NDL /NJH gui\dist-prod\i18n edition\static\public\i18n
copy gui\dist-prod\*.* edition\static copy gui\dist-prod\*.* edition\static
copy gui\dist-prod\favicon.ico edition\static\public copy gui\dist-prod\favicon.ico edition\static\public
@ -32,6 +34,10 @@ mkdir edition\static\mail
copy domain\mail\*.html edition\static\mail copy domain\mail\*.html edition\static\mail
copy core\database\templates\*.html edition\static copy core\database\templates\*.html edition\static
rd /s /q edition\static\i18n
mkdir edition\static\i18n
robocopy /e /NFL /NDL /NJH gui\dist-prod\i18n\*.json edition\static\i18n
rd /s /q edition\static\scripts rd /s /q edition\static\scripts
mkdir edition\static\scripts mkdir edition\static\scripts
mkdir edition\static\scripts\mysql mkdir edition\static\scripts\mysql

View file

@ -20,6 +20,7 @@ cp -r gui/dist-prod/prism edition/static/public/prism
cp -r gui/dist-prod/sections edition/static/public/sections cp -r gui/dist-prod/sections edition/static/public/sections
cp -r gui/dist-prod/tinymce edition/static/public/tinymce cp -r gui/dist-prod/tinymce edition/static/public/tinymce
cp -r gui/dist-prod/pdfjs edition/static/public/pdfjs cp -r gui/dist-prod/pdfjs edition/static/public/pdfjs
cp -r gui/dist-prod/i18n edition/static/public/i18n
cp gui/dist-prod/*.* edition/static cp gui/dist-prod/*.* edition/static
cp gui/dist-prod/favicon.ico edition/static/public cp gui/dist-prod/favicon.ico edition/static/public
cp gui/dist-prod/manifest.json edition/static/public cp gui/dist-prod/manifest.json edition/static/public
@ -29,6 +30,10 @@ mkdir -p edition/static/mail
cp domain/mail/*.html edition/static/mail cp domain/mail/*.html edition/static/mail
cp core/database/templates/*.html edition/static cp core/database/templates/*.html edition/static
rm -rf edition/static/i18n
mkdir -p edition/static/i18n
cp -r gui/dist-prod/i18n/*.json edition/static/i18n
rm -rf edition/static/scripts rm -rf edition/static/scripts
mkdir -p edition/static/scripts mkdir -p edition/static/scripts
mkdir -p edition/static/scripts/mysql mkdir -p edition/static/scripts/mysql

View file

@ -19,8 +19,8 @@ import (
"net/http" "net/http"
"path/filepath" "path/filepath"
"context"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context"
) )
// Msword type provides a peg to hang the Convert method on. // Msword type provides a peg to hang the Convert method on.

View file

@ -19,7 +19,7 @@ import (
"github.com/documize/community/core/api/plugins" "github.com/documize/community/core/api/plugins"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context" "context"
) )
// Convert provides the entry-point into the document conversion process. // Convert provides the entry-point into the document conversion process.

View file

@ -16,7 +16,7 @@ import (
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"golang.org/x/net/context" "context"
) )
// Convert provides the standard interface for conversion of a ".documizeapi" json document. // Convert provides the standard interface for conversion of a ".documizeapi" json document.

View file

@ -16,9 +16,9 @@ import (
"fmt" "fmt"
"strings" "strings"
"context"
api "github.com/documize/community/core/convapi" api "github.com/documize/community/core/convapi"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
"golang.org/x/net/context"
"golang.org/x/net/html" "golang.org/x/net/html"
"golang.org/x/net/html/atom" "golang.org/x/net/html/atom"
) )

View file

@ -16,7 +16,7 @@ import (
"github.com/documize/blackfriday" "github.com/documize/blackfriday"
"golang.org/x/net/context" "context"
) )
// Convert provides the standard interface for conversion of a Markdown document. // Convert provides the standard interface for conversion of a Markdown document.

View file

@ -1,61 +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
package plugins
import (
"os"
"testing"
)
func TestSetup(t *testing.T) {
err := LibSetup()
if err == nil {
//t.Error("should error on non-existent config file")
//t.Fail()
}
ssc, err := Lib.Actions("Convert")
if err != nil {
t.Error(err)
}
// TODO(Elliott) review for empty database
//if len(ssc) > 3 {
// t.Errorf("extra convert formats:%v", ssc)
//}
/* this code leaves plugins still running */
err = os.Chdir("../../..")
if err != nil {
t.Error(err)
}
err = LibSetup()
if err != nil {
t.Error(err)
}
ssc, err = Lib.Actions("Convert")
if err != nil {
t.Error(err)
}
if len(ssc) == 0 {
t.Error("no extra convert formats (defined)")
}
err = os.Chdir("documize/api/plugins")
if err != nil {
t.Error(err)
}
err = Lib.KillSubProcs()
if err != nil {
t.Error(err)
}
}

View file

@ -1,110 +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
package database
// import (
// "crypto/rand"
// "time"
// "github.com/documize/community/core/env"
// "github.com/jmoiron/sqlx"
// )
// // Lock will try to lock the database instance to the running process.
// // Uses a "random" delay as a por man's database cluster-aware process.
// // We skip delay if there are no scripts to process.
// func Lock(runtime *env.Runtime, scriptsToProcess int) (bool, error) {
// // Wait for random period of time.
// b := make([]byte, 2)
// _, err := rand.Read(b)
// if err != nil {
// return false, err
// }
// wait := ((time.Duration(b[0]) << 8) | time.Duration(b[1])) * time.Millisecond / 10 // up to 6.5 secs wait
// // Why delay if nothing to process?
// if scriptsToProcess > 0 {
// time.Sleep(wait)
// }
// // Start transaction fotr lock process.
// tx, err := runtime.Db.Beginx()
// if err != nil {
// runtime.Log.Error("Database: unable to start transaction", err)
// return false, err
// }
// // Lock the database.
// _, err = tx.Exec(runtime.StoreProvider.QueryStartLock())
// if err != nil {
// runtime.Log.Error("Database: unable to lock tables", err)
// return false, err
// }
// // Unlock the database at the end of this function.
// defer func() {
// _, err = tx.Exec(runtime.StoreProvider.QueryFinishLock())
// if err != nil {
// runtime.Log.Error("Database: unable to unlock tables", err)
// }
// tx.Commit()
// }()
// // Try to record this process as leader of database migration process.
// _, err = tx.Exec(runtime.StoreProvider.QueryInsertProcessID())
// if err != nil {
// runtime.Log.Info("Database: marked as slave process awaiting upgrade")
// return false, nil
// }
// // We are the leader!
// runtime.Log.Info("Database: marked as database upgrade process leader")
// return true, err
// }
// // Unlock completes process that was started with Lock().
// func Unlock(runtime *env.Runtime, tx *sqlx.Tx, err error, amLeader bool) error {
// if amLeader {
// defer func() {
// doUnlock(runtime)
// }()
// if tx != nil {
// if err == nil {
// tx.Commit()
// runtime.Log.Info("Database: is ready")
// return nil
// }
// tx.Rollback()
// }
// runtime.Log.Error("Database: install/upgrade failed", err)
// return err
// }
// return nil // not the leader, so ignore errors
// }
// // Helper method for defer function called from Unlock().
// func doUnlock(runtime *env.Runtime) error {
// tx, err := runtime.Db.Beginx()
// if err != nil {
// return err
// }
// _, err = tx.Exec(runtime.StoreProvider.QueryDeleteProcessID())
// if err != nil {
// return err
// }
// return tx.Commit()
// }

View file

@ -0,0 +1,5 @@
/* Community Edition */
-- Local aware.
ALTER TABLE dmz_org ADD COLUMN `c_locale` VARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD COLUMN `c_locale` VARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -0,0 +1,5 @@
/* Community Edition */
-- Local aware.
ALTER TABLE dmz_org ADD COLUMN c_locale VARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD COLUMN c_locale VARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -0,0 +1,5 @@
/* Community edition */
-- Local aware.
ALTER TABLE dmz_org ADD c_locale NVARCHAR(20) NOT NULL DEFAULT 'en-US';
ALTER TABLE dmz_user ADD c_locale NVARCHAR(20) NOT NULL DEFAULT 'en-US';

View file

@ -102,7 +102,7 @@
<body> <body>
<div class="container"> <div class="container">
<div class="logo"> <div class="logo">
<img src="/assets/img/setup/logo.png" alt="Documize"> <img src="/assets/img/setup/logo.png" alt="Documize Community">
</div> </div>
<div class="content clearfix"> <div class="content clearfix">
<div class="image"> <div class="image">
@ -110,7 +110,7 @@
</div> </div>
<div class="text"> <div class="text">
<h1>Database Error</h1> <h1>Database Error</h1>
<p>There seems to be a problem with the Documize database: <strong>{{.DBname}}</strong></p> <p>There seems to be a problem with the Documize Community database: <strong>{{.DBname}}</strong></p>
<p><em>{{.Issue}}</em></p> <p><em>{{.Issue}}</em></p>
</div> </div>
</div> </div>

File diff suppressed because one or more lines are too long

91
core/i18n/localize.go Normal file
View file

@ -0,0 +1,91 @@
package i18n
import (
"embed"
"encoding/json"
"fmt"
"strings"
"github.com/documize/community/core/asset"
"github.com/pkg/errors"
)
const (
DefaultLocale = "en-US"
)
var localeMap map[string]map[string]string
// type translation struct {
// Key string `json:"key"`
// Value string `json:"value"`
// }
// SupportedLocales returns array of locales.
func SupportedLocales() (locales []string) {
locales = append(locales, "en-US")
return
}
// Intialize will load language files
func Initialize(e embed.FS) (err error) {
localeMap = make(map[string]map[string]string)
locales := SupportedLocales()
for i := range locales {
content, _, err := asset.FetchStatic(e, "i18n/"+locales[i]+".json")
if err != nil {
err = errors.Wrap(err, fmt.Sprintf("missing locale %s", locales[i]))
return err
}
var payload interface{}
json.Unmarshal([]byte(content), &payload)
m := payload.(map[string]interface{})
// translations := []translation{}
translations := make(map[string]string)
for j := range m {
translations[j] = m[j].(string)
}
localeMap[locales[i]] = translations
}
return nil
}
// Localize will returns string value for given key using specified locale).
// e.g. locale = "en-US", key = "admin_billing"
//
// Replacements are for replacing string placeholders ({1} {2} {3}) with
// replacement text.
// e.g. "This is {1} example" where replacements[0] will replace {1}
func Localize(locale string, key string, replacements ...string) (s string) {
l, ok := localeMap[locale]
if !ok {
// fallback
l = localeMap[DefaultLocale]
}
s, ok = l[key]
if !ok {
// missing translation key is echo'ed back
s = fmt.Sprintf("!! %s !!", key)
}
// placeholders are one-based: {1} {2} {3}
// replacements array is zero-based hence the +1 below
if len(replacements) > 0 {
for i := range replacements {
s = strings.Replace(s, fmt.Sprintf("{%d}", i+1), replacements[i], 1)
}
}
return
}

View file

@ -38,7 +38,7 @@ func CommandWithTimeout(command *exec.Cmd, timeout time.Duration) ([]byte, error
select { select {
case <-time.After(timeout): case <-time.After(timeout):
if err := command.Process.Kill(); err != nil { if err := command.Process.Kill(); err != nil {
fmt.Errorf("failed to kill: ", err) fmt.Printf("failed to kill: %s", err.Error())
} }
<-done // prevent memory leak <-done // prevent memory leak
//fmt.Println("DEBUG timeout") //fmt.Println("DEBUG timeout")

View file

@ -1,6 +1,6 @@
# This Docker Compose file will start up Documize with PostgreSQL. # This Docker Compose file will start up Documize with PostgreSQL.
# #
# Use 'documize-enterprise-linux-amd64' for Enterprise Edition (default). # Use 'documize-community-plus-linux-amd64' for Community+ Edition (default).
# Use 'documize-community-linux-amd64' for Community Edition. # Use 'documize-community-linux-amd64' for Community Edition.
# #
# You can move between editions anytime without any data loss # You can move between editions anytime without any data loss

View file

@ -48,6 +48,7 @@ func AddExternalUser(ctx domain.RequestContext, rt *env.Runtime, store *store.St
if addUser { if addUser {
userID = uniqueid.Generate() userID = uniqueid.Generate()
u.RefID = userID u.RefID = userID
u.Locale = ctx.OrgLocale
err = store.User.Add(ctx, u) err = store.User.Add(ctx, u)
if err != nil { if err != nil {

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -57,7 +58,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_err_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -66,7 +67,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using Keycloak // Exit if not using Keycloak
if org.AuthProvider != ath.AuthProviderKeycloak { if org.AuthProvider != ath.AuthProviderKeycloak {
result.Message = "Error: skipping user sync with Keycloak as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -77,7 +78,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := ath.KeycloakConfig{} c := ath.KeycloakConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read Keycloak configuration data" result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -87,7 +88,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Keycloak // User list from Keycloak
kcUsers, err := Fetch(c) kcUsers, err := Fetch(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Keycloak users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_keycloak_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -97,7 +98,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// User list from Documize // User list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -135,8 +136,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
} }
result.Message = fmt.Sprintf("Keycloak sync found %d users, %d new users added, %d users with missing data ignored", result.Message = i18n.Localize(ctx.Locale, "server_keycloak_summary",
len(kcUsers), len(insert), missing) fmt.Sprintf("%d", len(kcUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -146,7 +147,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Org contains raw auth provider config // Org contains raw auth provider config
org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID) org, err := h.Store.Organization.GetOrganization(ctx, ctx.OrgID)
if err != nil { if err != nil {
result.Message = "Error: unable to get organization record" result.Message = i18n.Localize(ctx.Locale, "server_error_org")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -155,7 +156,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Exit if not using LDAP // Exit if not using LDAP
if org.AuthProvider != ath.AuthProviderLDAP { if org.AuthProvider != ath.AuthProviderLDAP {
result.Message = "Error: skipping user sync with LDAP as it is not the configured option" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error1")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)
@ -166,7 +167,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
c := lm.LDAPConfig{} c := lm.LDAPConfig{}
err = json.Unmarshal([]byte(org.AuthConfig), &c) err = json.Unmarshal([]byte(org.AuthConfig), &c)
if err != nil { if err != nil {
result.Message = "Error: unable read LDAP configuration data" result.Message = i18n.Localize(ctx.Locale, "server_ldap_error2")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -176,7 +177,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from LDAP. // Get user list from LDAP.
ldapUsers, err := fetchUsers(c) ldapUsers, err := fetchUsers(c)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch LDAP users: " + err.Error() result.Message = i18n.Localize(ctx.Locale, "server_ldap_error3", err.Error())
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -186,7 +187,7 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
// Get user list from Documize // Get user list from Documize
dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999) dmzUsers, err := h.Store.User.GetUsersForOrganization(ctx, "", 99999)
if err != nil { if err != nil {
result.Message = "Error: unable to fetch Documize users" result.Message = i18n.Localize(ctx.Locale, "server_error_user")
result.IsError = true result.IsError = true
response.WriteJSON(w, result) response.WriteJSON(w, result)
h.Runtime.Log.Error(result.Message, err) h.Runtime.Log.Error(result.Message, err)
@ -223,10 +224,8 @@ func (h *Handler) Sync(w http.ResponseWriter, r *http.Request) {
} }
result.IsError = false result.IsError = false
result.Message = "Sync complete with LDAP server" result.Message = i18n.Localize(ctx.Locale, "server_ldap_complete")
result.Message = fmt.Sprintf( result.Message = i18n.Localize(ctx.Locale, "server_ldap_summary", fmt.Sprintf("%d", len(ldapUsers)), fmt.Sprintf("%d", len(insert)), fmt.Sprintf("%d", missing))
"LDAP sync found %d users, %d new users added, %d users with missing data ignored",
len(ldapUsers), len(insert), missing)
h.Runtime.Log.Info(result.Message) h.Runtime.Log.Info(result.Message)

View file

@ -244,7 +244,7 @@ func (b backerHandler) dmzOrg(files *[]backupItem) (err error) {
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription, coalesce(c_sub,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS subscription,
coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags, coalesce(c_authconfig,`+b.Runtime.StoreProvider.JSONEmpty()+`) AS authconfig, c_maxtags AS maxtags,
c_theme AS theme, c_logo AS logo, c_created AS created, c_revised AS revised c_theme AS theme, c_logo AS logo, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_org`+w) FROM dmz_org`+w)
if err != nil { if err != nil {
return return
@ -308,7 +308,7 @@ func (b backerHandler) dmzUserAccount(files *[]backupItem) (err error) {
err = b.Runtime.Db.Select(&u, `SELECT u.id, u.c_refid AS refid, err = b.Runtime.Db.Select(&u, `SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u`+w) FROM dmz_user u`+w)
if err != nil { if err != nil {

View file

@ -370,14 +370,14 @@ func (r *restoreHandler) dmzOrg() (err error) {
INSERT INTO dmz_org (c_refid, c_company, c_title, c_message, INSERT INTO dmz_org (c_refid, c_company, c_title, c_message,
c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig, c_domain, c_service, c_email, c_anonaccess, c_authprovider, c_authconfig,
c_maxtags, c_verified, c_serial, c_sub, c_active, c_maxtags, c_verified, c_serial, c_sub, c_active,
c_theme, c_logo, c_created, c_revised) c_theme, c_logo, c_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
org[i].RefID, org[i].Company, org[i].Title, org[i].Message, org[i].RefID, org[i].Company, org[i].Title, org[i].Message,
strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email), strings.ToLower(org[i].Domain), org[i].ConversionEndpoint, strings.ToLower(org[i].Email),
org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig, org[i].AllowAnonymousAccess, org[i].AuthProvider, org[i].AuthConfig,
org[i].MaxTags, r.Runtime.StoreProvider.IsTrue(), org[i].Serial, org[i].MaxTags, r.Runtime.StoreProvider.IsTrue(), org[i].Serial,
org[i].Subscription, org[i].Active, org[i].Subscription, org[i].Active,
org[i].Theme, org[i].Logo, org[i].Theme, org[i].Logo, org[i].Locale,
org[i].Created, org[i].Revised) org[i].Created, org[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -412,6 +412,7 @@ func (r *restoreHandler) dmzOrg() (err error) {
org[0].Title = r.Spec.Org.Title org[0].Title = r.Spec.Org.Title
org[0].Subscription = r.Spec.Org.Subscription org[0].Subscription = r.Spec.Org.Subscription
org[0].Theme = r.Spec.Org.Theme org[0].Theme = r.Spec.Org.Theme
org[0].Locale = r.Spec.Org.Locale
} }
_, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET _, err = r.Context.Transaction.NamedExec(`UPDATE dmz_org SET
@ -425,7 +426,8 @@ func (r *restoreHandler) dmzOrg() (err error) {
c_message=:message, c_message=:message,
c_title=:title, c_title=:title,
c_serial=:serial, c_serial=:serial,
c_sub=:subscription c_sub=:subscription,
c_locale=:locale,
WHERE c_refid=:refid`, &org[0]) WHERE c_refid=:refid`, &org[0])
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()
@ -1735,11 +1737,11 @@ func (r *restoreHandler) dmzUser() (err error) {
_, 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_locale, c_created, c_revised)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`), VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`),
r.remapUser(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].Locale, u[i].Created, u[i].Revised)
if err != nil { if err != nil {
r.Context.Transaction.Rollback() r.Context.Transaction.Rollback()

View file

@ -44,6 +44,8 @@ type RequestContext struct {
GlobalAdmin bool GlobalAdmin bool
ViewUsers bool ViewUsers bool
Subscription Subscription Subscription Subscription
Locale string
OrgLocale string
} }
//GetAppURL returns full HTTP url for the app //GetAppURL returns full HTTP url for the app

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
Document Approval Role Granted {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -69,19 +69,14 @@ background-color: #f6f6f6;
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<p>You are requested to approve all changes to the following document:</p> <p>{{.ActionText}}</p>
<p style="font-weight: bold;">{{.Document}}</p> <p style="font-weight: bold;">{{.Document}}</p>
<p>{{.Inviter}}</p> <p>{{.Inviter}}</p>
</td> </td>1
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -16,6 +16,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -26,32 +27,32 @@ func (m *Mailer) DocumentApprover(recipient, inviterName, inviterEmail, url, doc
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has granted you document approval", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_approval", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
URL string URL string
Document string Document string
SenderEmail string SenderEmail string
ActionText string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
document, document,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_approval_explain"),
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/document-approver.html", parameters) html, err := m.ParseTemplate("mail/document-approver.html", parameters)

View file

@ -1,109 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<head>
<meta name="viewport" content="width=device-width" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Your Documize Community Invitation</title>
<style type="text/css">
img {
max-width: 100%;
}
body {
-webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6;
}
body {
background-color: #f6f6f6;
}
@media only screen and (max-width: 640px) {
h1 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h2 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h3 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h4 {
font-weight: 600 !important; margin: 20px 0 5px !important;
}
h1 {
font-size: 22px !important;
}
h2 {
font-size: 18px !important;
}
h3 {
font-size: 16px !important;
}
.container {
width: 100% !important;
}
.content {
padding: 10px !important;
}
.content-wrap {
padding: 10px !important;
}
.invoice {
width: 100% !important;
}
}
</style>
</head>
<body style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: none; width: 100% !important; height: 100%; line-height: 1.6; background: #f6f6f6; margin: 0; padding: 0;">
<table class="body-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; width: 100%; background: #f6f6f6; margin: 0; padding: 0;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
<td class="container" width="600" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; display: block !important; max-width: 600px !important; clear: both !important; margin: 0 auto; padding: 0;" valign="top">
<div class="content" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; max-width: 600px; display: block; margin: 0 auto; padding: 20px;">
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to use Documize Community
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides easy access to all your Word documents so everyone can find and edit documents - no more network drives.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">Your co-workers are using Documize right now.</strong>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Use your email address as your password ({{.Email}}).
</td>
</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}}" 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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</td>
<td style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 0;" valign="top"></td>
</tr>
</table>
</body>
</html>

View file

@ -59,25 +59,15 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to their Documize Community account {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -59,35 +59,20 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has invited you to Documize Community {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top"> <td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">Your co-workers are using Documize right now.</strong> {{.Password}}
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Your temporary password: {{.Password}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,30 +61,15 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
Your Documize Community password reset request {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top"> <td class="content-wrap" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; vertical-align: top; margin: 0; padding: 20px;" valign="top">
<table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <table width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Someone has requested to reset your Documize Community password. If this was you, then please click below to specify a new password.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
<td class="content-block" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
<strong style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">If you did not request a password reset, please change your password and contact us.</strong>
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has shared {{.Folder}} with you {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -75,12 +75,7 @@ background-color: #f6f6f6;
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:{{.SenderEmail}}" style="color: #7a8184;">Contact Us</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -61,7 +61,7 @@ background-color: #f6f6f6;
<table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;"> <table class="main" width="100%" cellpadding="0" cellspacing="0" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; border-radius: 3px; background: #fff; margin: 0; padding: 0; border: 1px solid #e9e9e9;">
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;">
<td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top"> <td class="alert alert-warning" style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; vertical-align: top; color: #fff; font-weight: 500; text-align: center; border-radius: 3px 3px 0 0; background: #1b75bb; margin: 0; padding: 20px;" align="center" valign="top">
{{.Inviter}} has shared {{.Folder}} with you on Documize Community {{.Subject}}
</td> </td>
</tr> </tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;"> <tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 16px; margin: 0; padding: 0;">
@ -74,19 +74,9 @@ background-color: #f6f6f6;
</strong> </strong>
</td> </td>
</tr> </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: 16px; vertical-align: top; margin: 0; padding: 0 0 20px;" valign="top">
Documize Community provides secure and easy access to all your documentation so everyone can find and edit the same thing.
</td>
</tr>
<tr style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; box-sizing: border-box; font-size: 14px; margin: 0; padding: 0;"> <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"> <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}}" 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;">{{.ClickHere}}</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;">
<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; color: #7a8184;" valign="top">
Have any questions? <a href="mailto:team@documize.com" style="color: #7a8184;">Contact Documize</a>
</td> </td>
</tr> </tr>
</table> </table>

View file

@ -14,6 +14,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -24,20 +25,16 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has shared %s with you", inviterName, folder) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_shared", inviterName, folder)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -45,6 +42,7 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
Folder string Folder string
Intro string Intro string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
@ -52,6 +50,7 @@ func (m *Mailer) ShareSpaceExistingUser(recipient, inviterName, inviterEmail, ur
folder, folder,
intro, intro,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/share-space-existing-user.html", parameters) html, err := m.ParseTemplate("mail/share-space-existing-user.html", parameters)
@ -77,20 +76,16 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has shared %s with you on Documize Community", inviterName, space) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_invited", inviterName, space)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -98,6 +93,7 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
Invitation string Invitation string
Folder string Folder string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
@ -105,6 +101,7 @@ func (m *Mailer) ShareSpaceNewUser(recipient, inviterName, inviterEmail, url, sp
invitationMessage, invitationMessage,
space, space,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/share-space-new-user.html", parameters) html, err := m.ParseTemplate("mail/share-space-new-user.html", parameters)

View file

@ -14,6 +14,7 @@ package mail
import ( import (
"fmt" "fmt"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain/smtp" "github.com/documize/community/domain/smtp"
) )
@ -24,20 +25,16 @@ func (m *Mailer) InviteNewUser(recipient, inviterName, inviterEmail, url, userna
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has invited you to Documize Community", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_user_invite", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
@ -45,13 +42,15 @@ func (m *Mailer) InviteNewUser(recipient, inviterName, inviterEmail, url, userna
Username string Username string
Password string Password string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
recipient, recipient,
password, i18n.Localize(m.Context.Locale, "mail_template_password") + " " + password,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/invite-new-user.html", parameters) html, err := m.ParseTemplate("mail/invite-new-user.html", parameters)
@ -77,30 +76,28 @@ func (m *Mailer) InviteExistingUser(recipient, inviterName, inviterEmail, url st
// check inviter name // check inviter name
if inviterName == "Hello You" || len(inviterName) == 0 { if inviterName == "Hello You" || len(inviterName) == 0 {
inviterName = "Your colleague" inviterName = i18n.Localize(m.Context.Locale, "mail_template_sender")
} }
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = fmt.Sprintf("%s has invited you to their Documize Community account", inviterName) em.Subject = i18n.Localize(m.Context.Locale, "mail_template_user_existing", inviterName)
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
em.ReplyTo = inviterEmail em.ReplyTo = inviterEmail
em.ReplyName = inviterName em.ReplyName = inviterName
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
Inviter string Inviter string
URL string URL string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
inviterName, inviterName,
url, url,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/invite-existing-user.html", parameters) html, err := m.ParseTemplate("mail/invite-existing-user.html", parameters)
@ -125,22 +122,20 @@ func (m *Mailer) PasswordReset(recipient, url string) {
m.Initialize() m.Initialize()
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = "Documize Community password reset request" em.Subject = i18n.Localize(m.Context.Locale, "mail_template_reset_password")
em.ToEmail = recipient em.ToEmail = recipient
em.ToName = recipient em.ToName = recipient
if IsBlockedEmailDomain(em.ToEmail) {
return
}
parameters := struct { parameters := struct {
Subject string Subject string
URL string URL string
SenderEmail string SenderEmail string
ClickHere string
}{ }{
em.Subject, em.Subject,
url, url,
m.Config.SenderEmail, m.Config.SenderEmail,
i18n.Localize(m.Context.Locale, "mail_template_click_here"),
} }
html, err := m.ParseTemplate("mail/password-reset.html", parameters) html, err := m.ParseTemplate("mail/password-reset.html", parameters)

File diff suppressed because one or more lines are too long

View file

@ -52,7 +52,7 @@ func (s Store) GetOrganization(ctx domain.RequestContext, id string) (org org.Or
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_theme AS theme, c_created AS created, c_revised AS revised c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_org FROM dmz_org
WHERE c_refid=?`), WHERE c_refid=?`),
id) id)
@ -84,7 +84,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised, c_theme AS theme
FROM dmz_org FROM dmz_org
WHERE c_domain=? AND c_active=`+s.IsTrue()), WHERE c_domain=? AND c_active=`+s.IsTrue()),
subdomain) subdomain)
@ -99,7 +99,7 @@ func (s Store) GetOrganizationByDomain(subdomain string) (o org.Organization, er
c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider, c_anonaccess AS allowanonymousaccess, c_authprovider AS authprovider,
coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig, coalesce(c_authconfig,`+s.EmptyJSON()+`) AS authconfig,
coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription, coalesce(c_sub,`+s.EmptyJSON()+`) AS subscription,
c_maxtags AS maxtags, c_created AS created, c_revised AS revised, c_theme AS theme c_maxtags AS maxtags, c_theme AS theme, c_locale as locale, c_created AS created, c_revised AS revised, c_theme AS theme
FROM dmz_org FROM dmz_org
WHERE c_domain='' AND c_active=`+s.IsTrue())) WHERE c_domain='' AND c_active=`+s.IsTrue()))
@ -116,7 +116,7 @@ func (s Store) UpdateOrganization(ctx domain.RequestContext, org org.Organizatio
_, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET _, err = ctx.Transaction.NamedExec(`UPDATE dmz_org SET
c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email, c_domain=:domain, c_title=:title, c_message=:message, c_service=:conversionendpoint, c_email=:email, c_domain=:domain,
c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_theme=:theme, c_revised=:revised c_anonaccess=:allowanonymousaccess, c_maxtags=:maxtags, c_theme=:theme, c_locale=:locale, c_revised=:revised
WHERE c_refid=:refid`, WHERE c_refid=:refid`,
&org) &org)

View file

@ -88,30 +88,32 @@ type Product struct {
// IsValid returns if subscription is valid using RequestContext. // IsValid returns if subscription is valid using RequestContext.
func (p *Product) IsValid(ctx RequestContext) bool { func (p *Product) IsValid(ctx RequestContext) bool {
// Community edition is always valid.
if p.Edition == CommunityEdition {
return true return true
}
// Community edition is always valid.
// if p.Edition == CommunityEdition {
// return true
// }
// Empty means we cannot be valid. // Empty means we cannot be valid.
if ctx.Subscription.IsEmpty() { // if ctx.Subscription.IsEmpty() {
return false // return false
} // }
// Enterprise edition is valid if system has loaded up user count by tenant. // Enterprise edition is valid if system has loaded up user count by tenant.
if uc, ok := p.UserCount[ctx.OrgID]; ok { // if uc, ok := p.UserCount[ctx.OrgID]; ok {
// Enterprise edition is valid if subcription date is greater than now and we have enough users/seats. // // Enterprise edition is valid if subcription date is greater than now and we have enough users/seats.
if time.Now().UTC().Before(ctx.Subscription.End) && uc <= int(ctx.Subscription.Seats) { // if time.Now().UTC().Before(ctx.Subscription.End) && uc <= int(ctx.Subscription.Seats) {
return true // return true
} // }
} else { // } else {
// First 10 is free for Enterprise edition. // // First 10 is free for Enterprise edition.
if Seats1 == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) { // if Seats1 == ctx.Subscription.Seats && time.Now().UTC().Before(ctx.Subscription.End) {
return true // return true
} // }
} // }
return false // return false
} }
// SubscriptionData holds encrypted data and is unpacked into Subscription. // SubscriptionData holds encrypted data and is unpacked into Subscription.

View file

@ -1,116 +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
package github
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
"golang.org/x/oauth2"
)
func clientID(ctx domain.RequestContext, s *domain.Store) string {
c, _ := s.Setting.Get(meta.ConfigHandle(), "clientID")
return c
}
func clientSecret(ctx domain.RequestContext, s *domain.Store) string {
c, _ := s.Setting.Get(meta.ConfigHandle(), "clientSecret")
return c
}
func authorizationCallbackURL(ctx domain.RequestContext, s *domain.Store) string {
// NOTE: URL value must have the path and query "/api/public/validate?section=github"
c, _ := s.Setting.Get(meta.ConfigHandle(), "authorizationCallbackURL")
return c
}
func validateToken(ctx provider.Context, s *domain.Store, ptoken string) error {
// Github authorization check
authClient := gogithub.NewClient((&gogithub.BasicAuthTransport{
Username: clientID(ctx.Request, s),
Password: clientSecret(ctx.Request, s),
}).Client())
_, _, err := authClient.Authorizations.Check(context.Background(), clientID(ctx.Request, s), ptoken)
return err
}
func (*Provider) githubClient(config *githubConfig) *gogithub.Client {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: config.Token},
)
tc := oauth2.NewClient(oauth2.NoContext, ts)
return gogithub.NewClient(tc)
}
// Callback is called by a browser redirect from Github, via the validation endpoint
func Callback(rt *env.Runtime, s *domain.Store, res http.ResponseWriter, req *http.Request) error {
ctx := domain.GetRequestContext(req)
code := req.URL.Query().Get("code")
state := req.URL.Query().Get("state")
ghurl := "https://github.com/login/oauth/access_token"
vals := "client_id=" + clientID(ctx, s)
vals += "&client_secret=" + clientSecret(ctx, s)
vals += "&code=" + code
vals += "&state=" + state
req2, err := http.NewRequest("POST", ghurl+"?"+vals, strings.NewReader(vals))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req2.Header.Set("Accept", "application/json")
res2, err := http.DefaultClient.Do(req2)
if err != nil {
return err
}
var gt githubCallbackT
err = json.NewDecoder(res2.Body).Decode(&gt)
if err != nil {
return err
}
err = res2.Body.Close()
if err != nil {
return err
}
returl, err := url.QueryUnescape(state)
if err != nil {
return err
}
up, err := url.Parse(returl)
if err != nil {
return err
}
target := up.Scheme + "://" + up.Host + up.Path + "?mode=edit&code=" + gt.AccessToken
http.Redirect(res, req, target, http.StatusTemporaryRedirect)
return nil
}

View file

@ -1,315 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize unity 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 github
import (
"context"
"fmt"
"html/template"
"sort"
"time"
gogithub "github.com/google/go-github/github"
)
const commitTimeFormat = "2006-01-02, 15:04"
type githubCommit struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
ShowRepo bool `json:"showRepo"`
Branch string `json:"branch"`
ShowBranch bool `json:"showBranch"`
Date string `json:"date"`
BinDate time.Time `json:"-"` // only used for sorting
ShowDate bool `json:"showDate"`
Login string `json:"login"`
Name string `json:"name"`
Avatar string `json:"avatar"`
Message string `json:"message"`
URL template.URL `json:"url"`
}
type githubAuthorStats struct {
Author string `json:"author"`
Login string `json:"login"`
Avatar string `json:"avatar"`
CommitCount int `json:"commitCount"`
Repos []string `json:"repos"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
}
// order commits in a way that makes sense of the table
type orderCommits []githubCommit
func (s orderCommits) Len() int { return len(s) }
func (s orderCommits) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s orderCommits) Less(i, j int) bool {
if s[i].Repo == s[j].Repo {
if s[i].Branch == s[j].Branch {
if s[i].BinDate == s[j].BinDate {
return s[i].Name < s[j].Name
}
return s[i].BinDate.Before(s[j].BinDate)
}
return s[i].Branch < s[j].Branch
}
return s[i].Repo < s[j].Repo
}
// sort stats in order that that should be presented.
type asToSort []githubAuthorStats
func (s asToSort) Len() int { return len(s) }
func (s asToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s asToSort) Less(i, j int) bool {
return s[i].CommitCount > s[j].CommitCount
}
// sort branches in order that that should be presented.
type branchByID []githubBranch
func (s branchByID) Len() int { return len(s) }
func (s branchByID) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s branchByID) Less(i, j int) bool {
return s[i].ID < s[j].ID
}
const tagCommitsData = "commitsData"
func getCommits(client *gogithub.Client, config *githubConfig) ([]githubCommit, []githubAuthorStats, error) {
if !config.ShowCommits {
return nil, nil, nil
}
// first make sure we've got all the branches
for _, orb := range config.Lists {
if orb.Included {
branches, _, err := client.Repositories.ListBranches(context.Background(), orb.Owner, orb.Repo,
&gogithub.ListOptions{PerPage: 100})
if err == nil {
render := make([]githubBranch, len(branches))
for kc, vb := range branches {
for _, existing := range config.Lists {
if orb.Owner == existing.Owner && orb.Repo == existing.Repo && orb.Name == *vb.Name {
goto found
}
}
render[kc] = githubBranch{
Owner: orb.Owner,
Repo: orb.Repo,
Name: *vb.Name,
ID: fmt.Sprintf("%s:%s:%s", orb.Owner, orb.Repo, *vb.Name),
Included: true,
URL: "https://github.com/" + orb.Owner + "/" + orb.Repo + "/tree/" + *vb.Name,
}
found:
}
config.Lists = append(config.Lists, render...)
}
}
}
sort.Sort(branchByID(config.Lists))
config.UserNames = make(map[string]string)
authorStats := make(map[string]githubAuthorStats)
contribBranch := make(map[string]map[string]struct{})
overall := []githubCommit{}
for _, orb := range config.Lists {
if orb.Included {
opts := &gogithub.CommitsListOptions{
SHA: orb.Name,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
if config.SincePtr != nil {
opts.Since = *config.SincePtr
}
guff, _, err := client.Repositories.ListCommits(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
fmt.Println(err)
return nil, nil, err
}
thisBranch := fmt.Sprintf("%s:%s", orb.Repo, orb.Name)
for _, v := range guff {
var d, m, u string
var bd time.Time
if v.Commit != nil {
if v.Commit.Committer.Date != nil {
d = v.Commit.Committer.Date.Format(commitTimeFormat)
bd = *v.Commit.Committer.Date
}
if v.Commit.Message != nil {
m = *v.Commit.Message
}
}
if v.HTMLURL != nil {
u = *v.HTMLURL
}
// author commits
al, an, aa := "", "", githubGravatar
if v.Author != nil {
if v.Author.Login != nil {
al = *v.Author.Login
an = getUserName(client, config, al)
}
if v.Author.AvatarURL != nil {
aa = *v.Author.AvatarURL
}
}
l := al // use author login
overall = append(overall, githubCommit{
Owner: orb.Owner,
Repo: orb.Repo,
Branch: orb.Name,
Name: an,
Login: l,
Message: m,
Date: d,
BinDate: bd,
Avatar: aa,
URL: template.URL(u),
})
if _, ok := contribBranch[l]; !ok {
contribBranch[l] = make(map[string]struct{})
}
contribBranch[l][thisBranch] = struct{}{}
cum := authorStats[l]
cum.Login = l
cum.Author = an
cum.Avatar = aa
cum.CommitCount++
// TODO review, this code removed as too slow
//cmt, _, err := client.Repositories.GetCommit(orb.Owner, orb.Repo, *v.SHA)
//if err == nil {
// if cmt.Stats != nil {
// if cmt.Stats.Total != nil {
// cum.TotalChanges += (*cmt.Stats.Total)
// }
// }
//}
//
authorStats[l] = cum
}
}
}
sort.Sort(orderCommits(overall))
for k := range overall {
overall[k].ShowRepo = true
overall[k].ShowBranch = true
overall[k].ShowDate = true
if k > 0 {
if overall[k].Repo == overall[k-1].Repo {
overall[k].ShowRepo = false
if overall[k].Branch == overall[k-1].Branch {
overall[k].ShowBranch = false
if overall[k].Date == overall[k-1].Date {
overall[k].ShowDate = false
}
}
}
}
}
retStats := make([]githubAuthorStats, 0, len(authorStats))
for _, v := range authorStats {
repos := contribBranch[v.Login]
v.Repos = make([]string, 0, len(repos))
for r := range repos {
v.Repos = append(v.Repos, r)
}
sort.Strings(v.Repos)
retStats = append(retStats, v)
}
sort.Sort(asToSort(retStats))
return overall, retStats, nil
}
func refreshCommits(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowCommits {
return nil
}
gr.BranchCommits, gr.AuthorStats, err = getCommits(client, config)
if err != nil {
return err
}
return nil
}
func renderCommits(payload *githubRender, c *githubConfig) error {
if !c.ShowCommits {
return nil
}
payload.CommitCount = 0
for range payload.BranchCommits {
payload.CommitCount++
}
payload.HasCommits = payload.CommitCount > 0
for i := range payload.Issues {
var author int
for a := range payload.AuthorStats {
if payload.AuthorStats[a].Login == payload.Issues[i].Name ||
(payload.AuthorStats[a].Login == "" && payload.Issues[i].Name == unassignedIssue) {
author = a
goto found
}
}
// no Author found for issue, so create one
payload.AuthorStats = append(payload.AuthorStats, githubAuthorStats{
Author: payload.Issues[i].Name,
Avatar: payload.Issues[i].Avatar,
})
author = len(payload.AuthorStats) - 1
found:
if payload.Issues[i].IsOpen {
payload.AuthorStats[author].OpenIssues++
} else {
payload.AuthorStats[author].ClosedIssues++
}
}
payload.HasAuthorStats = len(payload.AuthorStats) > 0
sort.Sort(asToSort(payload.AuthorStats))
payload.NumContributors = len(payload.AuthorStats) - 1
return nil
}
func init() {
reports[tagCommitsData] = report{refreshCommits, renderCommits, commitsTemplate}
}

View file

@ -1,99 +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
package github
const commitsTemplate = `
<div class="section-github-render">
<!--
{{if .HasAuthorStats}}
<div class="heading">Contributors</div>
<p>
There
{{if eq 1 .NumContributors}}is{{else}}are{{end}}
{{.NumContributors}}
{{if eq 1 .NumContributors}}contributor{{else}}contributors{{end}}
across {{.RepoCount}}
{{if eq 1 .RepoCount}} repository. {{else}} repositories. {{end}}
</p>
<table class="github-table">
<thead>
<tr>
<th class="title">Contributors</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $stats := .AuthorStats}}
<tr>
<td class="no-width">
<img class="github-avatar" alt="@{{$stats.Author}}" src="{{$stats.Avatar}}" />
</td>
<td>
<div class="contributor-name">{{$stats.Author}}</div>
<div class="contributor-meta">
{{if gt $stats.OpenIssues 0}}
assigned {{$stats.OpenIssues}}
{{if eq 1 $stats.OpenIssues}} issue {{else}} issues {{end}}
{{end}}
{{if gt $stats.ClosedIssues 0}}
&middot; {{$stats.ClosedIssues}} closed
{{end}}
{{if gt $stats.CommitCount 0}}
{{if gt $stats.OpenIssues 0}} &middot; {{end}}
{{if gt $stats.ClosedIssues 0}} &middot; {{end}}
made {{$stats.CommitCount}}
{{if eq 1 $stats.CommitCount}} commit {{else}} commits {{end}}
on {{len $stats.Repos}} {{if eq 1 (len $stats.Repos)}} branch {{else}} branches {{end}}
{{range $repo := $stats.Repos}} &middot; {{$repo}} {{end}}
{{end}}
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
-->
{{if .HasCommits}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">Commits <span>&middot; {{len .BranchCommits}} commits</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $commit := .BranchCommits}}
<tr>
<td>
<a href="{{$commit.URL}}">{{$commit.Message}}</a>
<span class="data"> {{$commit.Branch}}</span>
</td>
<td class="right-column">
<div class="contributor-meta">
{{$commit.Date}}
<img class="github-avatar" title="@{{$commit.Name}}" alt="@{{$commit.Name}}" src="{{$commit.Avatar}}" />
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`

View file

@ -1,251 +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
package github
import (
"bytes"
"encoding/json"
"errors"
"html/template"
"io/ioutil"
"net/http"
"strings"
"github.com/documize/community/core/env"
"github.com/documize/community/domain"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
)
// TODO find a smaller image than the one below
const githubGravatar = "https://i2.wp.com/assets-cdn.github.com/images/gravatars/gravatar-user-420.png"
var meta provider.TypeMeta
func init() {
meta = provider.TypeMeta{}
meta.ID = "38c0e4c5-291c-415e-8a4d-262ee80ba5df"
meta.Title = "GitHub"
meta.Description = "Link code commits and issues"
meta.ContentType = "github"
meta.PageType = "tab"
meta.Callback = Callback
}
// Provider represents GitHub
type Provider struct {
Runtime *env.Runtime
Store *domain.Store
}
// Meta describes us.
func (*Provider) Meta() provider.TypeMeta {
return meta
}
// Command to run the various functions required...
func (p *Provider) Command(ctx *provider.Context, w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
method := query.Get("method")
if len(method) == 0 {
msg := "missing method name"
provider.WriteMessage(w, "gitub", msg)
return
}
if method == "config" {
var ret struct {
CID string `json:"clientID"`
URL string `json:"authorizationCallbackURL"`
}
ret.CID = clientID(ctx.Request, p.Store)
ret.URL = authorizationCallbackURL(ctx.Request, p.Store)
provider.WriteJSON(w, ret)
return
}
defer r.Body.Close() // ignore error
body, err := ioutil.ReadAll(r.Body)
if err != nil {
p.Runtime.Log.Error("bad body", errors.New("Missing body"))
provider.WriteMessage(w, "github", "bad body")
return
}
if method == "saveSecret" { // secret Token update code
// write the new one, direct from JS
if err = ctx.SaveSecrets(string(body), p.Store); err != nil {
p.Runtime.Log.Error("github settoken configuration", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
return
}
// load the config from the client-side
config := githubConfig{}
err = json.Unmarshal(body, &config)
if err != nil {
p.Runtime.Log.Error("github Command Unmarshal", err)
provider.WriteError(w, "github", err)
return
}
config.Clean()
// always use DB version of the token
config.Token = ctx.GetSecrets("token", p.Store) // get the secret token in the database
client := p.githubClient(&config)
switch method {
case "checkAuth":
if len(config.Token) == 0 {
err = errors.New("empty github token")
} else {
err = validateToken(*ctx, p.Store, config.Token)
}
if err != nil {
// token now invalid, so wipe it
ctx.SaveSecrets("", p.Store) // ignore error, already in an error state
p.Runtime.Log.Error("github check token validation", err)
provider.WriteError(w, "github", err)
return
}
provider.WriteEmpty(w)
default:
if listFailed(p.Runtime, method, config, client, w) {
gr := githubRender{}
for _, rep := range reports {
rep.refresh(&gr, &config, client)
}
provider.WriteJSON(w, &gr)
}
}
}
// Refresh ... gets the latest version
func (p *Provider) Refresh(ctx *provider.Context, configJSON, data string) string {
var c = githubConfig{}
err := json.Unmarshal([]byte(configJSON), &c)
if err != nil {
p.Runtime.Log.Error("github.Refresh unmarshal", err)
return "internal configuration error '" + err.Error() + "'"
}
c.Clean()
c.Token = ctx.GetSecrets("token", p.Store)
client := p.githubClient(&c)
byts, err := json.Marshal(refreshReportData(&c, client))
if err != nil {
p.Runtime.Log.Error("github.Refresh marshal", err)
return "internal configuration error '" + err.Error() + "'"
}
return string(byts)
}
func refreshReportData(c *githubConfig, client *gogithub.Client) *githubRender {
var gr = githubRender{}
for _, rep := range reports {
rep.refresh(&gr, c, client)
}
return &gr
}
// Render ... just returns the data given, suitably formatted
func (p *Provider) Render(ctx *provider.Context, config, data string) string {
var err error
payload := githubRender{}
var c = githubConfig{}
err = json.Unmarshal([]byte(config), &c)
if err != nil {
return "Please delete and recreate this Github section."
}
c.Clean()
c.Token = ctx.GetSecrets("token", p.Store)
data = strings.TrimSpace(data)
if len(data) == 0 {
p.Runtime.Log.Info("GitHub connector received empty data for rendering")
// TODO review why this error occurs & if it should be reported - seems to occur for new sections
// log.ErrorString(fmt.Sprintf("Rendered empty github JSON payload as '' for owner %s repos %#v", c.Owner, c.Lists))
return ""
}
err = json.Unmarshal([]byte(data), &payload)
if err != nil {
return "Please delete and recreate this Github section."
}
payload.Config = c
payload.Limit = c.BranchLines
payload.List = c.Lists
ret := ""
for _, repID := range c.ReportOrder {
rep, ok := reports[repID]
if !ok {
msg := "github report not found for: " + repID
p.Runtime.Log.Info(msg)
return "Documize internal error: " + msg
}
if err = rep.render(&payload, &c); err != nil {
p.Runtime.Log.Error("unable to render "+repID, err)
return "Documize internal github render " + repID + " error: " + err.Error() + "<BR>" + data
}
t := template.New("github")
t, err = t.Parse(rep.template)
if err != nil {
p.Runtime.Log.Error("github render template.Parse error:", err)
//for k, v := range strings.Split(rep.template, "\n") {
// fmt.Println("DEBUG", k+1, v)
//}
return "Documize internal github template.Parse error: " + err.Error()
}
buffer := new(bytes.Buffer)
err = t.Execute(buffer, payload)
if err != nil {
p.Runtime.Log.Error("github render template.Execute error:", err)
return "Documize internal github template.Execute error: " + err.Error()
}
ret += buffer.String()
}
return ret
}

View file

@ -1,239 +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
package github
import (
"context"
"html/template"
"sort"
"time"
gogithub "github.com/google/go-github/github"
)
type githubIssue struct {
ID int `json:"id"`
Date string `json:"date"`
Updated string `json:"dated"`
Message string `json:"message"`
URL template.URL `json:"url"`
Name string `json:"name"`
Creator string `json:"creator"`
Avatar string `json:"avatar"`
Labels template.HTML `json:"labels"`
LabelNames []string `json:"labelNames"`
LabelColors []string `json:"labelColors"`
IsOpen bool `json:"isopen"`
Repo string `json:"repo"`
Private bool `json:"private"`
Milestone string `json:"milestone"`
}
type githubSharedLabel struct {
Name string `json:"name"`
Count int `json:"count"`
Color string `json:"color"`
Repos template.HTML `json:"Repos"`
}
// sort issues in order that that should be presented - by date updated.
type issuesToSort []githubIssue
func (s issuesToSort) Len() int { return len(s) }
func (s issuesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s issuesToSort) Less(i, j int) bool {
if s[i].Milestone != noMilestone && s[j].Milestone == noMilestone {
return true
}
if s[i].Milestone == noMilestone && s[j].Milestone != noMilestone {
return false
}
if s[i].Milestone != s[j].Milestone {
// TODO should this order be by milestone completion?
return s[i].Milestone < s[j].Milestone
}
if !s[i].IsOpen && s[j].IsOpen {
return true
}
if s[i].IsOpen && !s[j].IsOpen {
return false
}
// TODO this seems a very slow approach
iDate, _ := time.Parse(issuesTimeFormat, s[i].Updated)
jDate, _ := time.Parse(issuesTimeFormat, s[j].Updated)
return iDate.Before(jDate)
}
// sort shared labels alphabetically
type sharedLabelsSort []githubSharedLabel
func (s sharedLabelsSort) Len() int { return len(s) }
func (s sharedLabelsSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s sharedLabelsSort) Less(i, j int) bool { return s[i].Name < s[j].Name }
const (
tagIssuesData = "issuesData"
issuesTimeFormat = "January 2 2006, 15:04"
unassignedIssue = "(unassigned)"
)
func init() {
reports[tagIssuesData] = report{refreshIssues, renderIssues, issuesTemplate}
}
func wrapLabels(labels []gogithub.Label) (l string, labelNames []string, labelColors []string) {
labelNames = make([]string, 0, len(labels))
labelColors = make([]string, 0, len(labels))
for _, ll := range labels {
labelNames = append(labelNames, *ll.Name)
labelColors = append(labelColors, *ll.Color)
l += `<span class="issue-label" style="background-color:#` + *ll.Color + `">` + *ll.Name + `</span> `
}
return l, labelNames, labelColors
}
func getIssues(client *gogithub.Client, config *githubConfig) ([]githubIssue, error) {
if !config.ShowIssues {
return nil, nil
}
ret := []githubIssue{}
hadRepo := make(map[string]bool)
for _, orb := range config.Lists {
if orb.Included {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
for _, state := range []string{"open", "closed"} {
opts := &gogithub.IssueListByRepoOptions{
Sort: "updated",
State: state,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
if config.SincePtr != nil && state == "closed" /* we want all the open ones */ {
opts.Since = *config.SincePtr
}
guff, _, err := client.Issues.ListByRepo(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
return ret, err
}
for _, v := range guff {
n := unassignedIssue
av := githubGravatar
ptr := v.Assignee
if ptr != nil {
if ptr.Login != nil {
n = *ptr.Login
av = *ptr.AvatarURL
}
}
ms := noMilestone
if v.Milestone != nil {
if v.Milestone.Title != nil {
ms = *v.Milestone.Title
}
}
l, ln, lc := wrapLabels(v.Labels)
ret = append(ret, githubIssue{
Name: n,
Creator: getUserName(client, config, *v.User.Login),
Avatar: av,
Message: *v.Title,
Date: v.CreatedAt.Format(issuesTimeFormat),
Updated: v.UpdatedAt.Format(issuesTimeFormat),
URL: template.URL(*v.HTMLURL),
Labels: template.HTML(l),
LabelNames: ln,
LabelColors: lc,
ID: *v.Number,
IsOpen: *v.State == "open",
Repo: repoName(rName),
Private: orb.Private,
Milestone: ms,
})
}
}
}
hadRepo[rName] = true
}
}
sort.Sort(issuesToSort(ret))
return ret, nil
}
func refreshIssues(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowIssues {
return nil
}
gr.Issues, err = getIssues(client, config)
if err != nil {
return err
}
gr.OpenIssues = 0
gr.ClosedIssues = 0
sharedLabels := make(map[string][]string)
sharedLabelColors := make(map[string]string)
for _, v := range gr.Issues {
if v.IsOpen {
gr.OpenIssues++
} else {
gr.ClosedIssues++
}
for i, lab := range v.LabelNames {
sharedLabels[lab] = append(sharedLabels[lab], v.Repo)
if _, exists := sharedLabelColors[lab]; !exists { // use the first one we see
sharedLabelColors[lab] = v.LabelColors[i]
}
}
}
gr.HasIssues = (gr.OpenIssues + gr.ClosedIssues) > 0
gr.SharedLabels = make([]githubSharedLabel, 0, len(sharedLabels)) // will usually be too big
for name, repos := range sharedLabels {
if len(repos) > 1 {
thisLab := githubSharedLabel{Name: name, Count: len(repos), Color: sharedLabelColors[name]}
show := ""
for i, r := range repos {
if i > 0 {
show += ", "
}
show += "<a href='https://github.com/" + config.Owner + "/" + r +
"/issues?q=is%3Aissue+label%3A" + name + "'>" + r + "</a>"
}
thisLab.Repos = template.HTML(show)
gr.SharedLabels = append(gr.SharedLabels, thisLab)
}
}
sort.Sort(sharedLabelsSort(gr.SharedLabels))
gr.HasSharedLabels = len(gr.SharedLabels) > 0
return nil
}
func renderIssues(payload *githubRender, c *githubConfig) error {
return nil
}

View file

@ -1,68 +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
package github
const (
openIsvg = `
<span class="issue-state" title="Open Issue">
<svg height="16" version="1.1" viewBox="0 0 14 16" width="14" class="color:#6cc644;">
<path d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path>
</svg>
</span>
`
closedIsvg = `
<span class="issue-state" title="Closed Issue">
<svg height="16" version="1.1" viewBox="0 0 16 16" width="16" class="color:#bd2c00;">
<path d="M7 10h2v2H7v-2zm2-6H7v5h2V4zm1.5 1.5l-1 1L12 9l4-4.5-1-1L12 7l-1.5-1.5zM8 13.7A5.71 5.71 0 0 1 2.3 8c0-3.14 2.56-5.7 5.7-5.7 1.83 0 3.45.88 4.5 2.2l.92-.92A6.947 6.947 0 0 0 8 1C4.14 1 1 4.14 1 8s3.14 7 7 7 7-3.14 7-7l-1.52 1.52c-.66 2.41-2.86 4.19-5.48 4.19v-.01z"></path>
</svg>
</span>
`
issuesTemplate = `
<div class="section-github-render">
{{if .HasIssues}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">
Issues <span>&middot; {{.ClosedIssues}} closed {{if eq 1 .ClosedIssues}}{{else}}issues{{end}} and {{.OpenIssues}} open
{{if eq 1 .OpenIssues}}issue{{else}}{{end}}</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $data := .Issues}}
<tr>
<td>
{{if $data.IsOpen}}
` + openIsvg + `
{{else}}
` + closedIsvg + `
{{end}}
<a href="{{$data.URL}}">{{$data.Message}}</a> <span class="data">#{{$data.ID}}</span>
{{$data.Labels}}
</td>
<td class="right-column">
<div class="milestone-meta">
<span class="meta-milestone">{{$data.Milestone}}</span> &middot;
<span class="meta-creator">{{$data.Creator}}</span> &middot; <span class="meta-date">{{$data.Date}}</span>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`
)

View file

@ -1,106 +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
package github
import (
"context"
"fmt"
"net/http"
"github.com/documize/community/core/env"
"github.com/documize/community/domain/section/provider"
gogithub "github.com/google/go-github/github"
)
func listFailed(rt *env.Runtime, method string, config githubConfig, client *gogithub.Client, w http.ResponseWriter) (failed bool) {
switch method { // which list to choose?
case "owners":
me, _, err := client.Users.Get(context.Background(), "")
if err != nil {
rt.Log.Error("github get user details:", err)
provider.WriteError(w, "github", err)
return
}
orgs, _, err := client.Organizations.List(context.Background(), "", nil)
if err != nil {
rt.Log.Error("github get user's organisations:", err)
provider.WriteError(w, "github", err)
return
}
owners := make([]githubOwner, 1+len(orgs))
owners[0] = githubOwner{ID: *me.Login, Name: *me.Login}
for ko, vo := range orgs {
id := 1 + ko
owners[id].ID = *vo.Login
owners[id].Name = *vo.Login
}
owners = sortOwners(owners)
provider.WriteJSON(w, owners)
case "orgrepos":
var render []githubBranch
if config.Owner != "" {
me, _, err := client.Users.Get(context.Background(), "")
if err != nil {
rt.Log.Error("github get user details:", err)
provider.WriteError(w, "github", err)
return
}
var repos []*gogithub.Repository
if config.Owner == *me.Login {
repos, _, err = client.Repositories.List(context.Background(), config.Owner, nil)
} else {
opt := &gogithub.RepositoryListByOrgOptions{
ListOptions: gogithub.ListOptions{PerPage: 100},
}
repos, _, err = client.Repositories.ListByOrg(context.Background(), config.Owner, opt)
}
if err != nil {
rt.Log.Error("github get user/org repositories:", err)
provider.WriteError(w, "github", err)
return
}
for _, vr := range repos {
render = append(render,
githubBranch{
Name: "master",
ID: fmt.Sprintf("%s:%s", config.Owner, *vr.Name),
Owner: config.Owner,
Repo: *vr.Name,
Private: *vr.Private,
Included: false,
URL: *vr.HTMLURL,
})
}
}
render = sortBranches(render)
provider.WriteJSON(w, render)
case "content":
provider.WriteJSON(w, refreshReportData(&config, client))
default:
return true // failed to get a list
}
return
}

View file

@ -1,225 +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
package github
import (
"context"
"fmt"
"html/template"
"sort"
gogithub "github.com/google/go-github/github"
)
type githubMilestone struct {
Repo string `json:"repo"`
Private bool `json:"private"`
Name string `json:"name"`
URL template.URL `json:"url"`
IsOpen bool `json:"isopen"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
CompleteMsg string `json:"completeMsg"`
DueDate string `json:"dueDate"`
UpdatedAt string `json:"updatedAt"`
Progress uint `json:"progress"`
IsMilestone bool `json:"isMilestone"`
}
// sort milestones in order that that should be presented.
type milestonesToSort []githubMilestone
func (s milestonesToSort) Len() int { return len(s) }
func (s milestonesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s milestonesToSort) Less(i, j int) bool {
if s[i].Repo < s[j].Repo {
return true
}
if s[i].Repo > s[j].Repo {
return false
}
if !s[i].IsOpen && s[j].IsOpen {
return true
}
if s[i].IsOpen && !s[j].IsOpen {
return false
}
if s[i].Name != noMilestone && s[j].Name == noMilestone {
return true
}
if s[i].Name == noMilestone && s[j].Name != noMilestone {
return false
}
if s[i].Progress == s[j].Progress { // order equal progress milestones
return s[i].Name < s[j].Name
}
return s[i].Progress >= s[j].Progress // put more complete milestones first
}
const (
tagMilestonesData = "milestonesData"
milestonesTimeFormat = "January 2 2006"
noMilestone = "no milestone"
)
func init() {
reports[tagMilestonesData] = report{refreshMilestones, renderMilestones, milestonesTemplate}
}
func getMilestones(client *gogithub.Client, config *githubConfig) ([]githubMilestone, error) {
if !config.ShowMilestones {
return nil, nil
}
ret := []githubMilestone{}
hadRepo := make(map[string]bool)
for _, orb := range config.Lists {
if orb.Included {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
for _, state := range []string{"open", "closed"} {
opts := &gogithub.MilestoneListOptions{
Sort: "updated",
State: state,
ListOptions: gogithub.ListOptions{PerPage: config.BranchLines}}
guff, _, err := client.Issues.ListMilestones(context.Background(), orb.Owner, orb.Repo, opts)
if err != nil {
return ret, err
}
for _, v := range guff {
include := true
if state == "closed" {
if config.SincePtr != nil {
if (*config.SincePtr).After(*v.ClosedAt) {
include = false
}
}
}
if include {
dd := "no due date"
if v.DueOn != nil {
// TODO refactor to add message in red if the milestone is overdue
dd = "due " + (*v.DueOn).Format(milestonesTimeFormat) + ""
}
up := ""
if v.UpdatedAt != nil {
up = (*v.UpdatedAt).Format(milestonesTimeFormat)
}
progress := float64(*v.ClosedIssues*100) / float64(*v.OpenIssues+*v.ClosedIssues)
ret = append(ret, githubMilestone{
Repo: repoName(rName),
Private: orb.Private,
Name: *v.Title,
URL: template.URL(fmt.Sprintf(
"https://github.com/%s/%s/milestone/%d",
orb.Owner, orb.Repo, *v.Number)), // *v.HTMLURL does not give the correct value
IsOpen: *v.State == "open",
OpenIssues: *v.OpenIssues,
ClosedIssues: *v.ClosedIssues,
CompleteMsg: fmt.Sprintf("%2.0f%%", progress),
DueDate: dd,
UpdatedAt: up,
Progress: uint(progress),
IsMilestone: true,
})
}
}
}
}
hadRepo[rName] = true
}
}
return ret, nil
}
func refreshMilestones(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
if !config.ShowMilestones {
return nil
}
gr.Milestones, err = getMilestones(client, config)
if err != nil {
return err
}
gr.OpenMS = 0
gr.ClosedMS = 0
for _, v := range gr.Milestones {
if v.IsOpen {
gr.OpenMS++
} else {
gr.ClosedMS++
}
}
gr.HasMilestones = (gr.OpenMS + gr.ClosedMS) > 0
return nil
}
func renderMilestones(payload *githubRender, c *githubConfig) error {
if !c.ShowMilestones {
return nil
}
hadRepo := make(map[string]bool)
payload.RepoCount = 0
for _, orb := range payload.List {
rName := orb.Owner + "/" + orb.Repo
if !hadRepo[rName] {
if orb.Included {
payload.RepoCount++
issuesOpen, issuesClosed := 0, 0
for _, iss := range payload.Issues {
if iss.Repo == repoName(rName) {
if iss.Milestone == noMilestone {
if iss.IsOpen {
issuesOpen++
} else {
issuesClosed++
}
}
}
}
if issuesClosed+issuesOpen > 0 {
//payload.Milestones = append(payload.Milestones, githubMilestone{
// Repo: orb.Repo, Private: orb.Private, Name: noMilestone, IsOpen: true,
// OpenIssues: issuesOpen, ClosedIssues: issuesClosed, URL: template.URL(orb.URL),
//})
}
hadRepo[rName] = true
}
}
}
sort.Sort(milestonesToSort(payload.Milestones))
return nil
}

View file

@ -1,75 +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
package github
const (
rawMSsvg = `<path d="M8 2H6V0h2v2zm4 5H2c-.55 0-1-.45-1-1V4c0-.55.45-1 1-1h10l2 2-2 2zM8 4H6v2h2V4zM6 16h2V8H6v8z"></path>`
openMSsvg = `
<span class="issue-state" title="Open Milestone">
<svg height="16" width="14" version="1.1" viewBox="0 0 14 16">
` + rawMSsvg + `
</svg>
</span>
`
closedMSsvg = `
<span class="issue-state" title="Closed Milestone">
<svg aria-hidden="true" class="octicon octicon-check" height="16" height="14" version="1.1" viewBox="0 0 12 16">
<path d="M12 5l-8 8-4-4 1.5-1.5L4 10l6.5-6.5z"></path>
</svg>
</span>
`
milestonesTemplate = `
<div class="section-github-render">
{{if .HasMilestones}}
<table class="github-table" style="width: 100%;">
<thead>
<tr>
<th class="title">Milestones <span>&middot; {{.ClosedMS}} closed and {{.OpenMS}} open</span>
</th>
<th></th>
</tr>
</thead>
<tbody>
{{range $data := .Milestones}}
<tr>
<td>
{{if $data.IsMilestone}}
{{if $data.IsOpen}}
` + openMSsvg + `
{{else}}
` + closedMSsvg + `
{{end}}
{{end}}
<a class="link" href="{{$data.URL}}">{{$data.Name}}</a>
<span class="data"> &middot; {{if $data.IsMilestone}} {{$data.DueDate}}{{end}} </span>
</td>
<td class="right-column">
{{if $data.IsMilestone}}
<span class="bold color-off-black">{{$data.CompleteMsg}}</span> complete
<span class="bold color-off-black">{{$data.OpenIssues}}</span> open
<span class="bold color-off-black">{{$data.ClosedIssues}}</span> closed
{{else}}
<span class="bold color-off-black">{{$data.OpenIssues}}</span> open <span class="bold color-off-black">{{$data.ClosedIssues}}</span> closed
{{end}}
<div class="progress-bar">
<div class="progress" style="width:{{$data.Progress}}%;"></div>
</div>
</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
</div>
`
)

View file

@ -1,198 +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
package github
import (
"context"
"sort"
"strings"
"time"
gogithub "github.com/google/go-github/github"
)
type githubRender struct {
Config githubConfig `json:"config"`
List []githubBranch `json:"list"`
RepoCount int `json:"repoCount"`
ShowList bool `json:"showList"`
ShowIssueNumbers bool `json:"showIssueNumbers"`
BranchCommits []githubCommit `json:"branchCommits"`
HasCommits bool `json:"hasCommits"`
CommitCount int `json:"commitCount"`
Issues []githubIssue `json:"issues"`
HasIssues bool `json:"hasIssues"`
SharedLabels []githubSharedLabel `json:"sharedLabels"`
HasSharedLabels bool `json:"hasSharedLabels"`
OpenIssues int `json:"openIssues"`
ClosedIssues int `json:"closedIssues"`
Limit int `json:"limit"`
Milestones []githubMilestone `json:"milestones"`
HasMilestones bool `json:"hasMilestones"`
OpenMS int `json:"openMS"`
ClosedMS int `json:"closedMS"`
OpenPRs int `json:"openPRs"`
ClosedPRs int `json:"closedPRs"`
AuthorStats []githubAuthorStats `json:"authorStats"`
HasAuthorStats bool `json:"hasAuthorStats"`
NumContributors int `json:"numContributors"`
}
type report struct {
refresh func(*githubRender, *githubConfig, *gogithub.Client) error
render func(*githubRender, *githubConfig) error
template string
}
var reports = make(map[string]report)
type githubOwner struct {
ID string `json:"id"`
Name string `json:"name"`
}
type githubBranch struct {
ID string `json:"id"`
Owner string `json:"owner"`
Repo string `json:"repo"`
Name string `json:"name"`
Included bool `json:"included"`
URL string `json:"url"`
Color string `json:"color,omitempty"`
Comma bool `json:"comma"`
Private bool `json:"private"`
}
type githubLabel struct {
ID string `json:"id"`
Owner string `json:"owner"`
Repo string `json:"repo"`
Name string `json:"name"`
Included bool `json:"included"`
URL string `json:"url"`
Color string `json:"color,omitempty"`
}
type githubConfig struct {
Token string `json:"-"` // NOTE very important that the secret Token is not leaked to the client side, so "-"
UserID string `json:"userId"`
PageID string `json:"pageId"`
Owner string `json:"owner_name"`
BranchSince string `json:"branchSince,omitempty"`
SincePtr *time.Time `json:"-"`
Since string `json:"-"`
BranchLines int `json:"branchLines,omitempty,string"`
OwnerInfo githubOwner `json:"owner"`
ClientID string `json:"clientId"`
CallbackURL string `json:"callbackUrl"`
Lists []githubBranch `json:"lists,omitempty"`
ReportOrder []string `json:"-"`
DateMessage string `json:"-"`
UserNames map[string]string `json:"UserNames"`
ShowMilestones bool `json:"showMilestones,omitempty"`
ShowIssues bool `json:"showIssues,omitempty"`
ShowCommits bool `json:"showCommits,omitempty"`
}
func (c *githubConfig) Clean() {
c.Owner = c.OwnerInfo.Name
if len(c.BranchSince) >= len("yyyy/mm/dd hh:ss") {
var since time.Time
tt := []byte("yyyy-mm-ddThh:mm:00Z")
for _, i := range []int{0, 1, 2, 3, 5, 6, 8, 9, 11, 12, 14, 15} {
tt[i] = c.BranchSince[i]
}
err := since.UnmarshalText(tt)
if err != nil {
} else {
c.SincePtr = &since
}
}
if c.SincePtr == nil {
c.DateMessage = " (the last 7 days)"
since := time.Now().AddDate(0, 0, -7)
c.SincePtr = &since
} else {
c.DateMessage = ""
}
c.Since = (*c.SincePtr).Format(issuesTimeFormat)
c.ReportOrder = []string{tagSummaryData}
if c.ShowMilestones {
c.ReportOrder = append(c.ReportOrder, tagMilestonesData)
}
if c.ShowIssues {
c.ReportOrder = append(c.ReportOrder, tagIssuesData)
}
if c.ShowCommits {
c.ReportOrder = append(c.ReportOrder, tagCommitsData)
}
c.BranchLines = 100 // overide any existing value with maximum allowable in one call
sort.Sort(branchesToSort(c.Lists)) // get the configured branches in a sensible order for display
lastItem := 0
for i := range c.Lists {
c.Lists[i].Comma = true
if c.Lists[i].Included {
lastItem = i
}
}
if lastItem < len(c.Lists) {
c.Lists[lastItem].Comma = false
}
if c.UserNames == nil {
c.UserNames = make(map[string]string)
}
}
type githubCallbackT struct {
AccessToken string `json:"access_token"`
}
func repoName(branchName string) string {
bits := strings.Split(branchName, "/")
if len(bits) != 2 {
return branchName + "?repo"
}
pieces := strings.Split(bits[1], ":")
if len(pieces) == 0 {
return branchName + "?repo:?branch"
}
return pieces[0]
}
func getUserName(client *gogithub.Client, config *githubConfig, login string) (fullName string) {
an := login
if content, found := config.UserNames[login]; found {
if len(content) > 0 {
an = content
}
} else {
usr, _, err := client.Users.Get(context.Background(), login)
if err == nil {
if usr.Name != nil {
if len(*usr.Name) > 0 {
config.UserNames[login] = *usr.Name
an = *usr.Name
}
}
} else {
config.UserNames[login] = login // don't look again for a missing name
}
}
return an
}

View file

@ -1,36 +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
package github
import "sort"
// sort owners in order that that should be presented.
type ownersToSort []githubOwner
func (s ownersToSort) Len() int { return len(s) }
func (s ownersToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s ownersToSort) Less(i, j int) bool {
return s[i].Name < s[j].Name
}
func sortOwners(in []githubOwner) []githubOwner {
sts := ownersToSort(in)
sort.Sort(sts)
return []githubOwner(sts)
}
// sort branches in order that that should be presented.
func sortBranches(in []githubBranch) []githubBranch {
sts := branchesToSort(in)
sort.Sort(sts)
return []githubBranch(sts)
}

View file

@ -1,40 +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
package github
import gogithub "github.com/google/go-github/github"
const (
tagSummaryData = "summaryData"
)
// sort branches in order that they should be presented.
type branchesToSort []githubBranch
func (s branchesToSort) Len() int { return len(s) }
func (s branchesToSort) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s branchesToSort) Less(i, j int) bool {
return s[i].URL < s[j].URL
}
func init() {
reports[tagSummaryData] = report{refreshSummary, renderSummary, summaryTemplate}
}
func refreshSummary(gr *githubRender, config *githubConfig, client *gogithub.Client) (err error) {
return nil
}
func renderSummary(payload *githubRender, c *githubConfig) error {
return nil
}

View file

@ -1,48 +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
package github
const summaryTemplate = `
<div class="section-github-render">
<p>Activity since {{.Config.Since}}{{.Config.DateMessage}} for {{.Config.Owner}} repository
{{range $data := .Config.Lists}}
{{if $data.Included}}
<a class="link" href="{{$data.URL}}">
{{$data.Repo}}{{if $data.Comma}},{{end}}
</a>
{{end}}
{{end}}
</p>
<!--
{{if .HasSharedLabels}}
<div class="heading">Labels</div>
<p>There
{{if eq 1 (len .SharedLabels)}} is {{else}} are {{end}}
{{len .SharedLabels}}
shared
{{if eq 1 (len .SharedLabels)}} label {{else}} labels {{end}}
across the repositories.</p>
<table class="github-table">
<tbody>
{{range $slabel := .SharedLabels}}
<tr>
<td class="no-width"><span class="issue-label" style="background-color:#{{$slabel.Color}}">{{$slabel.Name}} ({{$slabel.Count}})</span></td>
<td>{{$slabel.Repos}}</td>
</tr>
{{end}}
</tbody>
</table>
{{end}}
-->
</div>
`

View file

@ -19,6 +19,7 @@ import (
"net/http" "net/http"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
@ -98,7 +99,7 @@ func (h *Handler) SetSMTP(w http.ResponseWriter, r *http.Request) {
Message string `json:"message"` Message string `json:"message"`
} }
result.Message = "Email sent successfully!" result.Message = i18n.Localize(ctx.Locale, "server_smtp_test")
u, err := h.Store.User.Get(ctx, ctx.UserID) u, err := h.Store.User.Get(ctx, ctx.UserID)
if err != nil { if err != nil {
@ -113,8 +114,8 @@ func (h *Handler) SetSMTP(w http.ResponseWriter, r *http.Request) {
h.Runtime.Log.Infof("%v", cfg) h.Runtime.Log.Infof("%v", cfg)
dialer, err := smtp.Connect(cfg) dialer, err := smtp.Connect(cfg)
em := smtp.EmailMessage{} em := smtp.EmailMessage{}
em.Subject = "Documize SMTP Test" em.Subject = i18n.Localize(ctx.Locale, "server_smtp_test_subject")
em.BodyHTML = "<p>This is a test email from Documize using current SMTP settings.</p>" em.BodyHTML = "<p>" + i18n.Localize(ctx.Locale, "server_smtp_test_body") + "</p>"
em.ToEmail = u.Email em.ToEmail = u.Email
em.ToName = u.Fullname() em.ToName = u.Fullname()

View file

@ -36,7 +36,7 @@ func GetSMTPConfig(s *store.Store) (c smtp.Config) {
c.SenderEmail, _ = s.Setting.Get("SMTP", "sender") c.SenderEmail, _ = s.Setting.Get("SMTP", "sender")
c.SenderName, _ = s.Setting.Get("SMTP", "senderName") c.SenderName, _ = s.Setting.Get("SMTP", "senderName")
if c.SenderName == "" { if c.SenderName == "" {
c.SenderName = "Documize" c.SenderName = "Documize Community"
} }
// anon auth? // anon auth?

View file

@ -42,6 +42,7 @@ func inviteNewUserToSharedSpace(ctx domain.RequestContext, rt *env.Runtime, s *s
u.Password = secrets.GeneratePassword(requestedPassword, u.Salt) u.Password = secrets.GeneratePassword(requestedPassword, u.Salt)
userID := uniqueid.Generate() userID := uniqueid.Generate()
u.RefID = userID u.RefID = userID
u.Locale = ctx.OrgLocale
err = s.User.Add(ctx, u) err = s.User.Add(ctx, u)
if err != nil { if err != nil {

View file

@ -22,6 +22,7 @@ import (
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/event" "github.com/documize/community/core/event"
"github.com/documize/community/core/i18n"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
@ -296,7 +297,7 @@ func (h *Handler) Use(w http.ResponseWriter, r *http.Request) {
var d = doc.Document{} var d = doc.Document{}
d.Name = docTitle d.Name = docTitle
d.Location = fmt.Sprintf("template-%s", templateID) d.Location = fmt.Sprintf("template-%s", templateID)
d.Excerpt = "Add detailed description for document..." d.Excerpt = i18n.Localize(ctx.Locale, "description")
d.Slug = stringutil.MakeSlug(d.Name) d.Slug = stringutil.MakeSlug(d.Name)
d.Tags = "" d.Tags = ""
d.SpaceID = spaceID d.SpaceID = spaceID

View file

@ -26,6 +26,7 @@ import (
"github.com/documize/community/core/event" "github.com/documize/community/core/event"
"github.com/documize/community/core/request" "github.com/documize/community/core/request"
"github.com/documize/community/core/response" "github.com/documize/community/core/response"
"github.com/documize/community/core/secrets" "github.com/documize/community/core/secrets"
"github.com/documize/community/core/streamutil" "github.com/documize/community/core/streamutil"
"github.com/documize/community/core/stringutil" "github.com/documize/community/core/stringutil"
@ -135,6 +136,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
if addUser { if addUser {
userID = uniqueid.Generate() userID = uniqueid.Generate()
userModel.RefID = userID userModel.RefID = userID
userModel.Locale = ctx.OrgLocale
err = h.Store.User.Add(ctx, userModel) err = h.Store.User.Add(ctx, userModel)
if err != nil { if err != nil {
@ -780,6 +782,7 @@ func (h *Handler) BulkImport(w http.ResponseWriter, r *http.Request) {
userModel.Firstname = strings.TrimSpace(v[0]) userModel.Firstname = strings.TrimSpace(v[0])
userModel.Lastname = strings.TrimSpace(v[1]) userModel.Lastname = strings.TrimSpace(v[1])
userModel.Email = strings.ToLower(strings.TrimSpace(v[2])) userModel.Email = strings.ToLower(strings.TrimSpace(v[2]))
userModel.Locale = ctx.OrgLocale
if len(userModel.Email) == 0 || len(userModel.Firstname) == 0 || len(userModel.Lastname) == 0 { if len(userModel.Email) == 0 || len(userModel.Firstname) == 0 || len(userModel.Lastname) == 0 {
h.Runtime.Log.Info(method + " missing firstname, lastname, or email") h.Runtime.Log.Info(method + " missing firstname, lastname, or email")

View file

@ -38,8 +38,8 @@ func (s Store) Add(ctx domain.RequestContext, u user.User) (err error) {
u.Created = time.Now().UTC() u.Created = time.Now().UTC()
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
_, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user (c_refid, c_firstname, c_lastname, c_email, c_initials, c_password, c_salt, c_reset, c_lastversion, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"), _, err = ctx.Transaction.Exec(s.Bind("INSERT INTO dmz_user (c_refid, c_firstname, c_lastname, c_email, c_initials, c_password, c_salt, c_reset, c_lastversion, c_locale, c_created, c_revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"),
u.RefID, u.Firstname, u.Lastname, strings.TrimSpace(strings.ToLower(u.Email)), u.Initials, u.Password, u.Salt, "", u.LastVersion, u.Created, u.Revised) u.RefID, u.Firstname, u.Lastname, strings.TrimSpace(strings.ToLower(u.Email)), u.Initials, u.Password, u.Salt, "", u.LastVersion, u.Locale, u.Created, u.Revised)
if err != nil { if err != nil {
err = errors.Wrap(err, "execute user insert") err = errors.Wrap(err, "execute user insert")
@ -53,7 +53,7 @@ func (s Store) Get(ctx domain.RequestContext, id string) (u user.User, err error
err = s.Runtime.Db.Get(&u, s.Bind(` err = s.Runtime.Db.Get(&u, s.Bind(`
SELECT id, c_refid AS refid, c_firstname AS firstname, c_lastname AS lastname, c_email AS email, SELECT id, c_refid AS refid, c_firstname AS firstname, c_lastname AS lastname, c_email AS email,
c_initials AS initials, c_globaladmin AS globaladmin, c_password AS password, c_salt AS salt, c_reset AS reset, c_initials AS initials, c_globaladmin AS globaladmin, c_password AS password, c_salt AS salt, c_reset AS reset,
c_lastversion AS lastversion, c_created AS created, c_revised AS revised c_lastversion AS lastversion, c_locale as locale, c_created AS created, c_revised AS revised
FROM dmz_user FROM dmz_user
WHERE c_refid=?`), WHERE c_refid=?`),
id) id)
@ -72,7 +72,7 @@ func (s Store) GetByDomain(ctx domain.RequestContext, domain, email string) (u u
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u, dmz_user_account a, dmz_org o FROM dmz_user u, dmz_user_account a, dmz_org o
WHERE LOWER(u.c_email)=? AND u.c_refid=a.c_userid AND a.c_orgid=o.c_refid AND LOWER(o.c_domain)=?`), WHERE LOWER(u.c_email)=? AND u.c_refid=a.c_userid AND a.c_orgid=o.c_refid AND LOWER(o.c_domain)=?`),
@ -92,7 +92,7 @@ func (s Store) GetByEmail(ctx domain.RequestContext, email string) (u user.User,
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE LOWER(u.c_email)=?`), WHERE LOWER(u.c_email)=?`),
@ -110,7 +110,7 @@ func (s Store) GetByToken(ctx domain.RequestContext, token string) (u user.User,
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE u.c_reset=?`), WHERE u.c_reset=?`),
@ -130,7 +130,7 @@ func (s Store) GetBySerial(ctx domain.RequestContext, serial string) (u user.Use
err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Get(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised u.c_created AS created, u.c_revised AS revised
FROM dmz_user u FROM dmz_user u
WHERE u.c_salt=?`), WHERE u.c_salt=?`),
@ -151,7 +151,7 @@ func (s Store) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []use
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -176,7 +176,7 @@ func (s Store) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []use
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -210,7 +210,7 @@ func (s Store) GetUsersForSpaces(ctx domain.RequestContext, spaces []string) (u
SELECT u.id, u.c_refid AS refid, SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -244,7 +244,7 @@ func (s Store) UpdateUser(ctx domain.RequestContext, u user.User) (err error) {
u.Revised = time.Now().UTC() u.Revised = time.Now().UTC()
u.Email = strings.ToLower(u.Email) u.Email = strings.ToLower(u.Email)
_, err = ctx.Transaction.NamedExec("UPDATE dmz_user SET c_firstname=:firstname, c_lastname=:lastname, c_email=:email, c_revised=:revised, c_initials=:initials, c_lastversion=:lastversion WHERE c_refid=:refid", &u) _, err = ctx.Transaction.NamedExec("UPDATE dmz_user SET c_firstname=:firstname, c_lastname=:lastname, c_email=:email, c_revised=:revised, c_initials=:initials, c_lastversion=:lastversion, c_locale=:locale WHERE c_refid=:refid", &u)
if err != nil { if err != nil {
err = errors.Wrap(err, fmt.Sprintf("execute user update %s", u.RefID)) err = errors.Wrap(err, fmt.Sprintf("execute user update %s", u.RefID))
} }
@ -313,7 +313,7 @@ func (s Store) GetUsersForOrganization(ctx domain.RequestContext, filter string,
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(limit)+`) u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(limit)+`) u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -323,7 +323,7 @@ func (s Store) GetUsersForOrganization(ctx domain.RequestContext, filter string,
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -357,7 +357,7 @@ func (s Store) MatchUsers(ctx domain.RequestContext, text string, maxMatches int
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(maxMatches)+`) u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT TOP(`+strconv.Itoa(maxMatches)+`) u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a
@ -367,7 +367,7 @@ func (s Store) MatchUsers(ctx domain.RequestContext, text string, maxMatches int
err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid, err = s.Runtime.Db.Select(&u, s.Bind(`SELECT u.id, u.c_refid AS refid,
u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email, u.c_firstname AS firstname, u.c_lastname AS lastname, u.c_email AS email,
u.c_initials AS initials, u.c_globaladmin AS globaladmin, u.c_initials AS initials, u.c_globaladmin AS globaladmin,
u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_password AS password, u.c_salt AS salt, u.c_reset AS reset, u.c_lastversion AS lastversion, u.c_locale as locale,
u.c_created AS created, u.c_revised AS revised, u.c_created AS created, u.c_revised AS revised,
a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics a.c_active AS active, a.c_editor AS editor, a.c_admin AS admin, a.c_users AS viewusers, a.c_analytics AS analytics
FROM dmz_user u, dmz_user_account a FROM dmz_user u, dmz_user_account a

View file

@ -18,6 +18,7 @@ import (
"os" "os"
"github.com/documize/community/core/env" "github.com/documize/community/core/env"
"github.com/documize/community/core/i18n"
"github.com/documize/community/domain" "github.com/documize/community/domain"
"github.com/documize/community/domain/section" "github.com/documize/community/domain/section"
"github.com/documize/community/domain/store" "github.com/documize/community/domain/store"
@ -38,10 +39,10 @@ func main() {
// Specify the product edition. // Specify the product edition.
rt.Product = domain.Product{} rt.Product = domain.Product{}
rt.Product.Major = "4" rt.Product.Major = "5"
rt.Product.Minor = "2" rt.Product.Minor = "0"
rt.Product.Patch = "3" rt.Product.Patch = "0"
rt.Product.Revision = "220214141054" rt.Product.Revision = "220318131033"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch) rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = domain.CommunityEdition rt.Product.Edition = domain.CommunityEdition
rt.Product.Title = "Community" rt.Product.Title = "Community"
@ -62,6 +63,12 @@ func main() {
} }
rt.Log.Info("Configuration: " + rt.Flags.ConfigSource) rt.Log.Info("Configuration: " + rt.Flags.ConfigSource)
// i18n
err := i18n.Initialize(rt.Assets)
if err != nil {
rt.Log.Error("i18n", err)
}
// Start database init. // Start database init.
boot.InitRuntime(&rt, &s) boot.InitRuntime(&rt, &s)

9
go.mod
View file

@ -1,6 +1,6 @@
module github.com/documize/community module github.com/documize/community
go 1.17 go 1.18
require ( require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
@ -15,8 +15,6 @@ require (
github.com/go-ldap/ldap/v3 v3.4.1 github.com/go-ldap/ldap/v3 v3.4.1
github.com/go-sql-driver/mysql v1.6.0 github.com/go-sql-driver/mysql v1.6.0
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
github.com/golang/protobuf v1.4.0 // indirect
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect github.com/google/go-querystring v1.0.0 // indirect
github.com/gorilla/handlers v1.4.2 github.com/gorilla/handlers v1.4.2
github.com/gorilla/mux v1.7.4 github.com/gorilla/mux v1.7.4
@ -31,8 +29,6 @@ require (
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
golang.org/x/net v0.0.0-20210614182718-04defd469f4e golang.org/x/net v0.0.0-20210614182718-04defd469f4e
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
google.golang.org/appengine v1.6.6 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc
gopkg.in/cas.v2 v2.1.0 gopkg.in/cas.v2 v2.1.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
@ -45,8 +41,7 @@ require (
github.com/fatih/structs v1.0.0 // indirect github.com/fatih/structs v1.0.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/google/go-cmp v0.4.0 // indirect
github.com/gorilla/css v1.0.0 // indirect github.com/gorilla/css v1.0.0 // indirect
github.com/trivago/tgo v1.0.1 // indirect github.com/trivago/tgo v1.0.1 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/protobuf v1.21.0 // indirect
) )

30
go.sum
View file

@ -1,4 +1,3 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
@ -35,20 +34,9 @@ github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZ
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@ -90,36 +78,18 @@ golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.6 h1:lMO5rYAqUxkmaj76jAkRUvt5JZgFymx/+Q5Mzfivuhc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0 h1:qdOKuR/EIArgaWNjetjgTzgVTAZ+S/WXVrq9HW9zimw=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/cas.v2 v2.1.0 h1:sbYBMWtpanwLH75GAWjIp5JnON9wa3NodLZhouu0G9I= gopkg.in/cas.v2 v2.1.0 h1:sbYBMWtpanwLH75GAWjIp5JnON9wa3NodLZhouu0G9I=

View file

@ -24,7 +24,11 @@ module.exports = {
"ember/no-get": "off", "ember/no-get": "off",
"ember/no-jquery": "off", "ember/no-jquery": "off",
"ember/no-mixins": "off", "ember/no-mixins": "off",
"ember/no-actions-hash": "off" "ember/no-actions-hash": "off",
"ember/require-computed-macros": "off",
"ember/use-ember-data-rfc-395-imports": "off",
"ember/avoid-leaking-state-in-ember-objects": "off",
"ember/require-return-from-computed": "off"
}, },
overrides: [ overrides: [
// node files // node files

View file

@ -1,3 +1,3 @@
{ {
"ignore_dirs": ["tmp", "dist", "dist-prod", "tests", "node_modules"] "ignore_dirs": ["tmp", "dist", "dist-prod", "tests", "node_modules", "public"]
} }

View file

@ -23,6 +23,7 @@ import Component from '@ember/component';
export default Component.extend(ModalMixin, Notifier, { export default Component.extend(ModalMixin, Notifier, {
appMeta: service(), appMeta: service(),
globalSvc: service('global'), globalSvc: service('global'),
i18n: service(),
isDocumizeProvider: computed('authProvider', function() { isDocumizeProvider: computed('authProvider', function() {
return this.get('authProvider') === this.get('constants').AuthProvider.Documize; return this.get('authProvider') === this.get('constants').AuthProvider.Documize;
@ -90,7 +91,7 @@ export default Component.extend(ModalMixin, Notifier, {
let provider = this.get('authProvider'); let provider = this.get('authProvider');
let constants = this.get('constants'); let constants = this.get('constants');
this.set('ldapPreview', {isError: true, message: 'Unable to connect'}); this.set('ldapPreview', {isError: true, message: this.i18n.localize('auth_ldap_preview_error')});
switch (provider) { switch (provider) {
case constants.AuthProvider.Documize: { case constants.AuthProvider.Documize: {
@ -176,7 +177,7 @@ export default Component.extend(ModalMixin, Notifier, {
this.get('globalSvc').previewLDAP(config).then((preview) => { this.get('globalSvc').previewLDAP(config).then((preview) => {
this.set('ldapPreview', preview); this.set('ldapPreview', preview);
this.modalOpen("#ldap-preview-modal", {"show": true}); this.modalOpen("#ldap-preview-modal", {"show": true});
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
}, },
@ -314,7 +315,7 @@ export default Component.extend(ModalMixin, Notifier, {
}); });
} }
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
} }
} }

View file

@ -18,16 +18,18 @@ import Component from '@ember/component';
export default Component.extend(Notifier, Modal, { export default Component.extend(Notifier, Modal, {
appMeta: service(), appMeta: service(),
router: service(), router: service(),
i18n: service(),
browserSvc: service('browser'), browserSvc: service('browser'),
backupLabel: 'Backup', backupLabel: '',
backupSystemLabel: 'System Backup', backupSystemLabel: '',
backupSpec: null, backupSpec: null,
backupFilename: '', backupFilename: '',
backupError: false, backupError: false,
backupSuccess: false, backupSuccess: false,
backupRunning: false, backupRunning: false,
restoreSpec: null, restoreSpec: null,
restoreButtonLabel: 'Restore', restoreButtonLabel: '',
restoreUploadReady: false, restoreUploadReady: false,
confirmRestore: '', confirmRestore: '',
@ -53,6 +55,10 @@ export default Component.extend(Notifier, Modal, {
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.set('backupLabel', this.i18n.localize('backup'));
this.set('backupSystemLabel', this.i18n.localize('backup_system'));
this.set('restoreButtonLabel', this.i18n.localize('restore'))
$('#restore-file').on('change', function(){ $('#restore-file').on('change', function(){
var fileName = document.getElementById("restore-file").files[0].name; var fileName = document.getElementById("restore-file").files[0].name;
$(this).next('.custom-file-label').html(fileName); $(this).next('.custom-file-label').html(fileName);
@ -68,14 +74,14 @@ export default Component.extend(Notifier, Modal, {
let spec = this.get('backupSpec'); let spec = this.get('backupSpec');
this.get('onBackup')(spec).then((filename) => { this.get('onBackup')(spec).then((filename) => {
this.notifySuccess('Completed'); this.notifySuccess(this.i18n.localize('completed'));
this.set('backupLabel', 'Start Backup'); this.set('backupLabel', this.i18n.localize('backup_start'));
this.set('backupSuccess', true); this.set('backupSuccess', true);
this.set('backupFilename', filename); this.set('backupFilename', filename);
this.set('backupRunning', false); this.set('backupRunning', false);
}, ()=> { }, ()=> {
this.notifyError('Failed'); this.notifyError(this.i18n.localize('backup_failed'));
this.set('backupLabel', 'Run Backup'); this.set('backupLabel', this.i18n.localize('backup_run'));
this.set('backupFailed', true); this.set('backupFailed', true);
this.set('backupRunning', false); this.set('backupRunning', false);
}); });
@ -133,7 +139,7 @@ export default Component.extend(Notifier, Modal, {
} }
// start restore process // start restore process
this.set('restoreButtonLabel', 'Please wait, restore running...'); this.set('restoreButtonLabel', this.i18n.localize('restore_running'));
this.set('restoreSuccess', false); this.set('restoreSuccess', false);
this.set('restoreFailed', false); this.set('restoreFailed', false);
@ -145,13 +151,13 @@ export default Component.extend(Notifier, Modal, {
} }
this.get('onRestore')(spec, filedata).then(() => { this.get('onRestore')(spec, filedata).then(() => {
this.notifySuccess('Completed'); this.notifySuccess(this.i18n.localize('completed'));
this.set('backupLabel', 'Restore'); this.set('backupLabel', this.i18n.localize('restore'));
this.set('restoreSuccess', true); this.set('restoreSuccess', true);
this.get('router').transitionTo('auth.logout'); this.get('router').transitionTo('auth.logout');
}, ()=> { }, ()=> {
this.notifyError('Failed'); this.notifyError(this.i18n.localize('backup_failed'));
this.set('restorbackupLabel', 'Restore'); this.set('restorbackupLabel', this.i18n.localize('restore'));
this.set('restoreFailed', true); this.set('restoreFailed', true);
}); });
}, },

View file

@ -10,6 +10,7 @@
// https://documize.com // https://documize.com
import $ from 'jquery'; import $ from 'jquery';
import { A } from '@ember/array';
import { empty, and } from '@ember/object/computed'; import { empty, and } from '@ember/object/computed';
import { isEmpty } from '@ember/utils'; import { isEmpty } from '@ember/utils';
import { set } from '@ember/object'; import { set } from '@ember/object';
@ -21,6 +22,7 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
router: service(), router: service(),
i18n: service(),
maxTags: 3, maxTags: 3,
domain: '', domain: '',
titleEmpty: empty('model.general.title'), titleEmpty: empty('model.general.title'),
@ -29,12 +31,29 @@ export default Component.extend(Notifier, {
hasTitleInputError: and('titleEmpty', 'titleError'), hasTitleInputError: and('titleEmpty', 'titleError'),
hasMessageInputError: and('messageEmpty', 'messageError'), hasMessageInputError: and('messageEmpty', 'messageError'),
hasConversionEndpointInputError: and('conversionEndpointEmpty', 'conversionEndpointError'), hasConversionEndpointInputError: and('conversionEndpointEmpty', 'conversionEndpointError'),
locale: { name: '' },
locales: A([]),
init(...args) {
this._super(...args);
let l = this.get('appMeta.locales');
let t = A([]);
l.forEach((locale) => {
t.pushObject({ name: locale });
});
this.set('locales', t);
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.set('maxTags', this.get('model.general.maxTags')); this.set('maxTags', this.get('model.general.maxTags'));
this.set('domain', this.get('model.general.domain')); this.set('domain', this.get('model.general.domain'));
this.set('locale', this.locales.findBy('name', this.get('model.general.locale')));
}, },
didInsertElement() { didInsertElement() {
@ -68,7 +87,7 @@ export default Component.extend(Notifier, {
}); });
this.on("queuecomplete", function () { this.on("queuecomplete", function () {
self.notifySuccess('Logo uploaded'); self.notifySuccess(this.i18n.localize('saved'));
}); });
this.on("error", function (error, msg) { this.on("error", function (error, msg) {
@ -148,6 +167,10 @@ export default Component.extend(Notifier, {
}, },
actions: { actions: {
onSelectLocale(locale) {
this.set('model.general.locale', locale.name);
},
change() { change() {
const selectEl = $('#maxTags')[0]; const selectEl = $('#maxTags')[0];
const selection = selectEl.selectedOptions[0].value; const selection = selectEl.selectedOptions[0].value;
@ -186,12 +209,11 @@ export default Component.extend(Notifier, {
this.set('model.general.domain', this.get('domain').toLowerCase()); this.set('model.general.domain', this.get('domain').toLowerCase());
this.get('onUpdate')().then(() => { this.get('onUpdate')().then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
set(this, 'titleError', false); set(this, 'titleError', false);
set(this, 'messageError', false); set(this, 'messageError', false);
set(this, 'conversionEndpointError', false); set(this, 'conversionEndpointError', false);
if (domainChanged) { if (domainChanged) {
let router = this.get('router'); let router = this.get('router');
router.transitionTo('auth.login'); router.transitionTo('auth.login');
@ -206,7 +228,7 @@ export default Component.extend(Notifier, {
onDefaultLogo() { onDefaultLogo() {
this.get('onDefaultLogo')(this.get('appMeta.orgId')); this.get('onDefaultLogo')(this.get('appMeta.orgId'));
this.notifySuccess('Using default logo'); this.notifySuccess(this.i18n.localize('saved'));
} }
} }
}); });

View file

@ -61,7 +61,7 @@ export default Component.extend(Notifier, {
this.get('orgSvc').saveGlobalSetting('SECTION-TRELLO', this.get('trelloCreds')); this.get('orgSvc').saveGlobalSetting('SECTION-TRELLO', this.get('trelloCreds'));
} }
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
} }
} }

View file

@ -22,10 +22,13 @@ export default Component.extend(Notifier, Modals, {
subscription: null, subscription: null,
planCloud: false, planCloud: false,
planSelfhost: false, planSelfhost: false,
comment: 'Nothing in particular -- just passing through. Please close my Documize account.', comment: '',
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.set('comment', this.i18n.localize('close_account'));
this.get('global').getSubscription().then((subs) => { this.get('global').getSubscription().then((subs) => {
this.set('subscription', subs); this.set('subscription', subs);
if (subs.plan === 'Installed') { if (subs.plan === 'Installed') {
@ -41,7 +44,7 @@ export default Component.extend(Notifier, Modals, {
actions: { actions: {
saveLicense() { saveLicense() {
this.get('global').setLicense(this.get('license')).then(() => { this.get('global').setLicense(this.get('license')).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
window.location.reload(); window.location.reload();
}); });
}, },
@ -55,7 +58,7 @@ export default Component.extend(Notifier, Modals, {
let comment = this.get('comment'); let comment = this.get('comment');
this.get('global').deactivate(comment).then(() => { this.get('global').deactivate(comment).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
this.modalOpen("#deactivation-confirmation-modal", {"show": true}); this.modalOpen("#deactivation-confirmation-modal", {"show": true});
}); });
} }

View file

@ -15,14 +15,21 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
buttonLabel: 'Rebuild', i18n: service(),
buttonLabel: '',
init() {
this._super(...arguments);
this.buttonLabel = this.i18n.localize('search_reindex_rebuild');
},
actions: { actions: {
reindex() { reindex() {
this.set('buttonLabel', 'Running...'); this.set('buttonLabel', this.i18n.localize('running'));
this.notifyInfo("Starting search re-index process"); this.notifyInfo(this.i18n.localize('search_reindex_start'));
this.get('reindex')(() => { this.get('reindex')(() => {
this.notifySuccess("Search re-indexing complete"); this.notifySuccess(this.i18n.localize('search_reindex_finish'));
this.set('buttonLabel', this.i18n.localize('search_reindex_rebuild'));
}); });
} }
} }

View file

@ -17,14 +17,20 @@ import Component from '@ember/component';
export default Component.extend(Notifier, { export default Component.extend(Notifier, {
appMeta: service(), appMeta: service(),
i18n: service(),
SMTPHostEmptyError: empty('model.smtp.host'), SMTPHostEmptyError: empty('model.smtp.host'),
SMTPPortEmptyError: empty('model.smtp.port'), SMTPPortEmptyError: empty('model.smtp.port'),
SMTPSenderEmptyError: empty('model.smtp.sender'), SMTPSenderEmptyError: empty('model.smtp.sender'),
senderNameError: empty('model.smtp.senderName'), senderNameError: empty('model.smtp.senderName'),
buttonText: 'Save & Test', buttonText: 'Save & Test',
testSMTP: null, testSMTP: null,
init() {
this._super(...arguments);
this.buttonText = this.i18n.localize('smtp_save_test');
},
actions: { actions: {
saveSMTP() { saveSMTP() {
if (this.get('SMTPHostEmptyError')) { if (this.get('SMTPHostEmptyError')) {
@ -50,11 +56,11 @@ export default Component.extend(Notifier, {
}, },
); );
this.set('buttonText', 'Please wait...'); this.set('buttonText', this.i18n.localize('please_test'));
this.notifyInfo('Sending test email to you'); this.notifyInfo(this.i18n.localize('smtp_sent_test_email'));
this.get('saveSMTP')().then((result) => { this.get('saveSMTP')().then((result) => {
this.set('buttonText', 'Save & Test'); this.set('buttonText', this.i18n.localize('smtp_save_test'));
this.set('testSMTP', result); this.set('testSMTP', result);
this.set('appMeta.configured', true); this.set('appMeta.configured', true);

View file

@ -20,6 +20,7 @@ export default Component.extend(Notifier, Modals, {
spaceSvc: service('folder'), spaceSvc: service('folder'),
browserSvc: service('browser'), browserSvc: service('browser'),
documentSvc: service('document'), documentSvc: service('document'),
i18n: service(),
spaces: null, spaces: null,
label: computed('model', function() { label: computed('model', function() {
@ -75,7 +76,7 @@ export default Component.extend(Notifier, Modals, {
this.set('deleteSpace.id', ''); this.set('deleteSpace.id', '');
this.set('deleteSpace.name', ''); this.set('deleteSpace.name', '');
this.loadData(); this.loadData();
this.notifySuccess('Deleted'); this.notifySuccess(this.i18n.localize('deleted'));
}); });
}, },
@ -86,17 +87,17 @@ export default Component.extend(Notifier, Modals, {
filterType: 'space', filterType: 'space',
}; };
this.notifyInfo('Export running...'); this.notifyInfo(this.i18n.localize('space_admin_export_running'));
this.get('documentSvc').export(spec).then((htmlExport) => { this.get('documentSvc').export(spec).then((htmlExport) => {
this.get('browserSvc').downloadFile(htmlExport, 'documize.html'); this.get('browserSvc').downloadFile(htmlExport, 'documize-community.html');
this.notifySuccess('Export completed'); this.notifySuccess(this.i18n.localize('completed'));
}); });
}, },
onOwner(spaceId) { onOwner(spaceId) {
this.get('spaceSvc').grantOwnerPermission(spaceId).then(() => { /* jshint ignore:line */ this.get('spaceSvc').grantOwnerPermission(spaceId).then(() => { /* jshint ignore:line */
this.notifySuccess('Added as owner'); this.notifySuccess(this.i18n.localize('completed'));
}); });
} }
} }

View file

@ -15,10 +15,12 @@ import ModalMixin from '../../mixins/modal';
import Notifier from '../../mixins/notifier'; import Notifier from '../../mixins/notifier';
import stringUtil from '../../utils/string'; import stringUtil from '../../utils/string';
import Component from '@ember/component'; import Component from '@ember/component';
import { inject as service } from '@ember/service';
export default Component.extend(AuthProvider, ModalMixin, Notifier, { export default Component.extend(AuthProvider, ModalMixin, Notifier, {
bulkUsers: '', bulkUsers: '',
newUser: null, newUser: null,
i18n: service(),
init() { init() {
this._super(...arguments); this._super(...arguments);
@ -53,7 +55,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.get('onAddUser')(user).then(() => { this.get('onAddUser')(user).then(() => {
this.set('newUser', { firstname: '', lastname: '', email: '', active: true }); this.set('newUser', { firstname: '', lastname: '', email: '', active: true });
this.notifySuccess('Added user'); this.notifySuccess(this.i18n.localize('added'));
}); });
this.modalClose("#add-user-modal"); this.modalClose("#add-user-modal");
@ -68,7 +70,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.get('onAddUsers')(this.get('bulkUsers')).then(() => { this.get('onAddUsers')(this.get('bulkUsers')).then(() => {
this.set('bulkUsers', ''); this.set('bulkUsers', '');
this.notifySuccess('Added users'); this.notifySuccess(this.i18n.localize('added'));
}); });
this.modalClose("#add-user-modal"); this.modalClose("#add-user-modal");

View file

@ -174,7 +174,7 @@ export default Component.extend(AuthProvider, ModalMixin, {
}, },
onSearch() { onSearch() {
debounce(this, function() { this.loadGroupInfo(); }, 450); debounce(this, this.loadGroupInfo, 450);
}, },
onLeaveGroup(userId) { onLeaveGroup(userId) {

View file

@ -20,6 +20,8 @@ import Component from '@ember/component';
export default Component.extend(AuthProvider, ModalMixin, Notifier, { export default Component.extend(AuthProvider, ModalMixin, Notifier, {
groupSvc: service('group'), groupSvc: service('group'),
i18n: service(),
editUser: null, editUser: null,
deleteUser: null, deleteUser: null,
filter: '', filter: '',
@ -183,7 +185,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
let cb = this.get('onDelete'); let cb = this.get('onDelete');
cb(this.get('deleteUser.id')); cb(this.get('deleteUser.id'));
this.notifySuccess("Deleted user"); this.notifySuccess(this.i18n.localize('deleted'));
return true; return true;
}, },
@ -203,7 +205,7 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
this.set('selectedUsers', []); this.set('selectedUsers', []);
this.set('hasSelectedUsers', false); this.set('hasSelectedUsers', false);
this.notifySuccess("Deleted selected users"); this.notifySuccess(this.i18n.localize('deleted'));
this.modalClose('#admin-user-delete-modal'); this.modalClose('#admin-user-delete-modal');
}, },
@ -222,6 +224,8 @@ export default Component.extend(AuthProvider, ModalMixin, Notifier, {
}) })
this.set('groups', groups); this.set('groups', groups);
if (_.isNull(groups)) return;
this.modalOpen("#group-member-modal", {"show": true}); this.modalOpen("#group-member-modal", {"show": true});
}, },

View file

@ -25,6 +25,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
pinned: service(), pinned: service(),
browserSvc: service('browser'), browserSvc: service('browser'),
documentSvc: service('document'), documentSvc: service('document'),
i18n: service(),
showRevisions: computed('permissions', 'document.protection', function() { showRevisions: computed('permissions', 'document.protection', function() {
if (!this.get('session.authenticated')) return false; if (!this.get('session.authenticated')) return false;
if (!this.get('session.viewUsers')) return false; if (!this.get('session.viewUsers')) return false;
@ -218,7 +219,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
this.get('documentSvc').export(spec).then((htmlExport) => { this.get('documentSvc').export(spec).then((htmlExport) => {
this.get('browserSvc').downloadFile(htmlExport, this.get('document.slug') + '.html'); this.get('browserSvc').downloadFile(htmlExport, this.get('document.slug') + '.html');
this.notifySuccess('Exported'); this.notifySuccess(this.i18n.localize('exported'));
}); });
} }
} }

View file

@ -23,6 +23,7 @@ export default Component.extend(Notifier, ModalMixin, {
searchService: service('search'), searchService: service('search'),
router: service(), router: service(),
appMeta: service(), appMeta: service(),
i18n: service(),
deleteChildren: false, deleteChildren: false,
blockTitle: "", blockTitle: "",
blockExcerpt: "", blockExcerpt: "",
@ -79,13 +80,12 @@ export default Component.extend(Notifier, ModalMixin, {
this._super(...arguments); this._super(...arguments);
let pageId = this.get('page.id'); let pageId = this.get('page.id');
let url = window.location.protocol + '//' + this.get('appMeta.appHost') + let url = window.location.protocol + '//' + this.get('appMeta.appHost') + this.get('router').generate('document.index', {queryParams: {currentPageId: pageId}});
this.get('router').generate('document.index', {queryParams: {currentPageId: pageId}});
let self = this; let self = this;
let clip = new ClipboardJS('#page-copy-link-' + pageId, { let clip = new ClipboardJS('#page-copy-link-' + pageId, {
text: function() { text: function() {
self.notifySuccess('Link copied to clipboard'); self.notifySuccess(this.i18n.localize('copied'));
return url; return url;
} }
}); });

View file

@ -18,13 +18,20 @@ import Component from '@ember/component';
export default Component.extend(Modals, Notifier, { export default Component.extend(Modals, Notifier, {
appMeta: service(), appMeta: service(),
session: service(), session: service(),
i18n: service(),
editMode: false, editMode: false,
downloadQuery: '', downloadQuery: '',
uploadId: computed('page', function () { uploadId: computed('page', function () {
let page = this.get('page'); let page = this.get('page');
return `page-uploader-${page.id}`; return `page-uploader-${page.id}`;
}), }),
uploadLabel: 'Upload Attachments', uploadLabel: '',
init(...args) {
this._super(...args);
this.uploadLabel = this.i18n.localize('upload_attachment');
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -95,7 +102,7 @@ export default Component.extend(Modals, Notifier, {
}); });
this.on("queuecomplete", function () { this.on("queuecomplete", function () {
self.notifySuccess('Uploaded file'); self.notifySuccess(this.i18n.localize('uploaded'));
self.get('onAttachmentUpload')(); self.get('onAttachmentUpload')();
}); });
@ -120,7 +127,7 @@ export default Component.extend(Modals, Notifier, {
actions: { actions: {
onDelete(attachment) { onDelete(attachment) {
this.notifySuccess('File deleted'); this.notifySuccess(this.i18n.localize('deleted'));
this.get('onAttachmentDelete')(attachment.id); this.get('onAttachmentDelete')(attachment.id);
} }
} }

View file

@ -22,6 +22,7 @@ export default Component.extend(Modals, Notifier, {
browserSvc: service('browser'), browserSvc: service('browser'),
appMeta: service(), appMeta: service(),
session: service(), session: service(),
i18n: service(),
hasAttachments: notEmpty('files'), hasAttachments: notEmpty('files'),
canEdit: computed('permissions.{documentApprove,documentEdit}', 'document.protection', function() { canEdit: computed('permissions.{documentApprove,documentEdit}', 'document.protection', function() {
// Check to see if specific scenarios prevent us from changing doc level attachments. // Check to see if specific scenarios prevent us from changing doc level attachments.
@ -86,7 +87,7 @@ export default Component.extend(Modals, Notifier, {
}); });
this.on("queuecomplete", function () { this.on("queuecomplete", function () {
self.notifySuccess('Uploaded file'); self.notifySuccess(this.i18n.localize('uploaded'));
self.getAttachments(); self.getAttachments();
}); });
@ -115,7 +116,7 @@ export default Component.extend(Modals, Notifier, {
actions: { actions: {
onDelete(attachment) { onDelete(attachment) {
this.get('documentService').deleteAttachment(this.get('document.id'), attachment.id).then(() => { this.get('documentService').deleteAttachment(this.get('document.id'), attachment.id).then(() => {
this.notifySuccess('File deleted'); this.notifySuccess(this.i18n.localize('deleted'));
this.getAttachments(); this.getAttachments();
}); });
} }

View file

@ -16,10 +16,11 @@ import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
localStorage: service(), localStorage: service(),
i18n: service(),
showDeleteDialog: false, showDeleteDialog: false,
showMoveDialog: false, showMoveDialog: false,
selectedDocuments: A([]), selectedDocuments: A([]),
selectedCaption: 'document', selectedCaption: '',
viewDensity: "1", viewDensity: "1",
showAdd: computed('permissions.documentAdd', 'documents', function() { showAdd: computed('permissions.documentAdd', 'documents', function() {
@ -35,6 +36,11 @@ export default Component.extend({
return _.isEmpty(this.get('categoryFilter')) && this.get('documents').length == this.get("numDocuments"); return _.isEmpty(this.get('categoryFilter')) && this.get('documents').length == this.get("numDocuments");
}), }),
init() {
this._super(...arguments);
this.selectedCaption = this.i18n.localize('document');
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -162,7 +168,7 @@ export default Component.extend({
list = _.without(list, documentId); list = _.without(list, documentId);
} }
this.set('selectedCaption', list.length > 1 ? 'documents' : 'document'); this.set('selectedCaption', list.length > 1 ? this.i18n.localize('document') : this.i18n.localize('documents'));
this.set('selectedDocuments', A(list)); this.set('selectedDocuments', A(list));
}, },

View file

@ -19,6 +19,7 @@ export default Component.extend(AuthMixin, Notifier, {
router: service(), router: service(),
spaceSvc: service('folder'), spaceSvc: service('folder'),
sectionSvc: service('section'), sectionSvc: service('section'),
i18n: service(),
showDeleteDialog: false, showDeleteDialog: false,
deleteBlockId: '', deleteBlockId: '',
@ -54,7 +55,7 @@ export default Component.extend(AuthMixin, Notifier, {
this.get('sectionSvc').deleteBlock(id).then(() => { this.get('sectionSvc').deleteBlock(id).then(() => {
this.set('deleteBlockId', ''); this.set('deleteBlockId', '');
this.notifySuccess('Deleted'); this.notifySuccess(this.i18n.localize('deleted'));
this.get('sectionSvc').getSpaceBlocks(this.get('space.id')).then((blocks) => { this.get('sectionSvc').getSpaceBlocks(this.get('space.id')).then((blocks) => {
this.set('blocks', blocks); this.set('blocks', blocks);

View file

@ -22,6 +22,7 @@ export default Component.extend(ModalMixin, Notifer, {
categorySvc: service('category'), categorySvc: service('category'),
appMeta: service(), appMeta: service(),
store: service(), store: service(),
i18n: service(),
editId: '', editId: '',
editName: '', editName: '',
editDefault: false, editDefault: false,
@ -120,7 +121,7 @@ export default Component.extend(ModalMixin, Notifer, {
this.get('categorySvc').add(c).then(() => { this.get('categorySvc').add(c).then(() => {
this.load(); this.load();
this.notifySuccess('Category added'); this.notifySuccess(this.i18n.localize('added'));
}); });
}, },

View file

@ -24,6 +24,7 @@ export default Component.extend(AuthMixin, Notifier, {
spaceSvc: service('folder'), spaceSvc: service('folder'),
iconSvc: service('icon'), iconSvc: service('icon'),
localStorage: service('localStorage'), localStorage: service('localStorage'),
i18n: service(),
isSpaceAdmin: computed('permissions', function() { isSpaceAdmin: computed('permissions', function() {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage'); return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}), }),
@ -120,9 +121,9 @@ export default Component.extend(AuthMixin, Notifier, {
let folder = this.get('space'); let folder = this.get('space');
let spaceTypeOptions = A([]); let spaceTypeOptions = A([]);
spaceTypeOptions.pushObject({id: constants.SpaceType.Private, label: 'Private - viewable only by me'}); spaceTypeOptions.pushObject({id: constants.SpaceType.Private, label: this.i18n.localize('personal_explain')});
spaceTypeOptions.pushObject({id: constants.SpaceType.Protected, label: 'Protected - access is restricted to selected users'}); spaceTypeOptions.pushObject({id: constants.SpaceType.Protected, label: this.i18n.localize('protected_explain')});
spaceTypeOptions.pushObject({id: constants.SpaceType.Public, label: 'Public - can be seen by everyone'}); spaceTypeOptions.pushObject({id: constants.SpaceType.Public, label: this.i18n.localize('public_explain')});
this.set('spaceTypeOptions', spaceTypeOptions); this.set('spaceTypeOptions', spaceTypeOptions);
this.set('spaceType', spaceTypeOptions.findBy('id', folder.get('spaceType'))); this.set('spaceType', spaceTypeOptions.findBy('id', folder.get('spaceType')));
@ -131,7 +132,7 @@ export default Component.extend(AuthMixin, Notifier, {
if (this.get('allowLikes')) { if (this.get('allowLikes')) {
this.set('likes', folder.get('likes')); this.set('likes', folder.get('likes'));
} else { } else {
this.set('likes', 'Did this help you?'); this.set('likes', this.i18n.localize('likes_prompt'));
} }
this.set('spaceName', this.get('space.name')); this.set('spaceName', this.get('space.name'));
@ -184,7 +185,7 @@ export default Component.extend(AuthMixin, Notifier, {
space.set('labelId', this.get('spaceLabel')); space.set('labelId', this.get('spaceLabel'));
this.get('spaceSvc').save(space).then(() => { this.get('spaceSvc').save(space).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
}); });
} }
} }

View file

@ -27,6 +27,7 @@ export default Component.extend(Notifier, Modals, AuthProvider, {
router: service(), router: service(),
appMeta: service(), appMeta: service(),
store: service(), store: service(),
i18n: service(),
spacePermissions: null, spacePermissions: null,
users: null, users: null,
searchText: '', searchText: '',
@ -129,7 +130,7 @@ export default Component.extend(Notifier, Modals, AuthProvider, {
}, },
getDefaultInvitationMessage() { getDefaultInvitationMessage() {
return "Hey there, I am sharing the " + this.get('folder.name') + " space (in " + this.get("appMeta.title") + ") with you so we can both collaborate on documents."; return this.i18n.localize('space_invite_message', this.get('folder.name'), this.get("appMeta.title"));
}, },
matchUsers(s) { matchUsers(s) {
@ -216,7 +217,7 @@ export default Component.extend(Notifier, Modals, AuthProvider, {
} }
this.get('spaceSvc').savePermissions(folder.get('id'), payload).then(() => { this.get('spaceSvc').savePermissions(folder.get('id'), payload).then(() => {
this.notifySuccess('Saved'); this.notifySuccess(this.i18n.localize('saved'));
this.get('onRefresh')(); this.get('onRefresh')();
}); });
}, },
@ -284,7 +285,7 @@ export default Component.extend(Notifier, Modals, AuthProvider, {
this.set('inviteEmail', ''); this.set('inviteEmail', '');
this.get('spaceSvc').share(this.get('folder.id'), result).then(() => { this.get('spaceSvc').share(this.get('folder.id'), result).then(() => {
this.notifySuccess('Invites sent'); this.notifySuccess(this.i18n.localize('sent'));
$('#space-invite-email').removeClass('is-invalid'); $('#space-invite-email').removeClass('is-invalid');
this.modalClose("#space-invite-user-modal"); this.modalClose("#space-invite-user-modal");
this.load(); this.load();

View file

@ -27,6 +27,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
session: service(), session: service(),
appMeta: service(), appMeta: service(),
pinned: service(), pinned: service(),
i18n: service(),
spaceName: '', spaceName: '',
copyTemplate: true, copyTemplate: true,
copyPermission: true, copyPermission: true,
@ -252,7 +253,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
let status = this.get('importStatus'); let status = this.get('importStatus');
let documents = this.get('importedDocuments'); let documents = this.get('importedDocuments');
status.pushObject(`Converting ${filename}...`); status.pushObject(this.i18n.localize('import_convert', filename));
documents.push(filename); documents.push(filename);
this.set('importStatus', status); this.set('importStatus', status);
@ -263,7 +264,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
let status = this.get('importStatus'); let status = this.get('importStatus');
let documents = this.get('importedDocuments'); let documents = this.get('importedDocuments');
status.pushObject(`Successfully converted ${filename}`); status.pushObject(this.i18n.localize('import_success', filename));
documents.pop(filename); documents.pop(filename);
this.set('importStatus', status); this.set('importStatus', status);
@ -301,7 +302,7 @@ export default Component.extend(ModalMixin, AuthMixin, Notifier, {
this.get('documentSvc').export(spec).then((htmlExport) => { this.get('documentSvc').export(spec).then((htmlExport) => {
this.get('browserSvc').downloadFile(htmlExport, this.get('space.slug') + '.html'); this.get('browserSvc').downloadFile(htmlExport, this.get('space.slug') + '.html');
this.notifySuccess('Exported'); this.notifySuccess(this.i18n.localize('exported'));
}); });
this.modalClose("#space-export-modal"); this.modalClose("#space-export-modal");

View file

@ -20,7 +20,9 @@ export default Component.extend({
slug: "", slug: "",
processing: false, processing: false,
didRender() { didRender(...args) {
this._super(...args);
let self = this; let self = this;
$("#stage-1-firstname").focus(); $("#stage-1-firstname").focus();
@ -97,8 +99,6 @@ export default Component.extend({
if ($("#stage-2-password-confirm").val() !== $("#stage-2-password").val()) { if ($("#stage-2-password-confirm").val() !== $("#stage-2-password").val()) {
$("#stage-2-password").addClass("is-invalid"); $("#stage-2-password").addClass("is-invalid");
$("#stage-2-password-confirm").addClass("is-invalid"); $("#stage-2-password-confirm").addClass("is-invalid");
// $(".mismatch").show();
// $(".password-status").attr("src", "/assets/img/onboard/lock-red.png");
return; return;
} }
@ -115,7 +115,6 @@ export default Component.extend({
self.set('processing', true); self.set('processing', true);
$(".stage-3").fadeIn(); $(".stage-3").fadeIn();
// $("#spinner-1").show();
var payload = '{ "password": "' + $("#stage-2-password").val() + '", "serial": "' + self.serial + '", "firstname": "' + $("#stage-1-firstname").val() + '", "lastname": "' + $("#stage-1-lastname").val() + '" }'; var payload = '{ "password": "' + $("#stage-2-password").val() + '", "serial": "' + self.serial + '", "firstname": "' + $("#stage-1-firstname").val() + '", "lastname": "' + $("#stage-1-lastname").val() + '" }';
var password = $("#stage-2-password").val(); var password = $("#stage-2-password").val();
@ -126,11 +125,6 @@ export default Component.extend({
self.get('session').authenticate('authenticator:documize', creds).then(() => { self.get('session').authenticate('authenticator:documize', creds).then(() => {
window.location.href = '//' + window.location.host + '/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);
// self.session.sso(credentials).then(function() {
// window.location.href = 's/' + self.folderId + "/" + self.slug;
// });
}, function() { }, function() {
window.location.href = "/"; window.location.href = "/";
}); });

View file

@ -15,11 +15,11 @@ import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
localStorage: service('localStorage'), localStorage: service('localStorage'),
i18n: service(),
resultPhrase: '', resultPhrase: '',
searchQuery: computed('keywords', function() { searchQuery: computed('keywords', function() {
return encodeURIComponent(this.get('keywords')); return encodeURIComponent(this.get('keywords'));
}), }),
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
sortBy: { sortBy: {
name: true, name: true,
created: false, created: false,
@ -33,15 +33,15 @@ export default Component.extend({
let docs = this.get('results'); let docs = this.get('results');
let duped = []; let duped = [];
let phrase = 'Nothing found'; let phrase = this.i18n.localize('nothing_found');
if (docs.length > 0) { if (docs.length > 0) {
duped = _.uniqBy(docs, function(item) { duped = _.uniqBy(docs, function(item) {
return item.get('documentId'); return item.get('documentId');
}); });
let references = docs.length === 1 ? "reference" : "references"; let references = docs.length === 1 ? this.i18n.localize('reference') : this.i18n.localize('references');
let docLabel = duped.length === 1 ? "document" : "documents"; let docLabel = duped.length === 1 ? this.i18n.localize('document') : this.i18n.localize('documents');
let i = docs.length; let i = docs.length;
let j = duped.length; let j = duped.length;
phrase = `${i} ${references} in ${j} ${docLabel}`; phrase = `${i} ${references} in ${j} ${docLabel}`;

View file

@ -15,6 +15,7 @@ export default Component.extend({
data: "", data: "",
didReceiveAttrs() { didReceiveAttrs() {
this._super();
this.set("data", this.get("meta.rawBody")); this.set("data", this.get("meta.rawBody"));
}, },

View file

@ -34,7 +34,7 @@ export default Component.extend(Modals, Notifier, {
pageTitle: '', pageTitle: '',
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super();
this.set('pageTitle', this.get('page.title')); this.set('pageTitle', this.get('page.title'));
}, },

View file

@ -21,7 +21,9 @@ export default Component.extend(ModalMixin, {
hasNameError: empty('page.title'), hasNameError: empty('page.title'),
hasDescError: empty('page.excerpt'), hasDescError: empty('page.excerpt'),
didRender() { didRender(...args) {
this._super(...args);
let self = this; let self = this;
Mousetrap.bind('esc', function () { Mousetrap.bind('esc', function () {
self.send('onCancel'); self.send('onCancel');

View file

@ -67,7 +67,8 @@ export default Component.extend({
} }
}, },
didInsertElement() { didInsertElement(...args) {
this._super(...args);
var editor = CodeMirror.fromTextArea(document.getElementById(this.get('editorId')), { var editor = CodeMirror.fromTextArea(document.getElementById(this.get('editorId')), {
theme: "material", theme: "material",
lineNumbers: true, lineNumbers: true,
@ -91,7 +92,8 @@ export default Component.extend({
this.set('codeEditor', editor); this.set('codeEditor', editor);
}, },
willDestroyElement() { willDestroyElement(...args) {
this._super(...args);
let editor = this.get('codeEditor'); let editor = this.get('codeEditor');
if (!_.isNull(editor)) { if (!_.isNull(editor)) {

View file

@ -17,13 +17,14 @@ import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
appMeta: service(), appMeta: service(),
sectionSvc: service('section'), sectionSvc: service('section'),
i18n: service(),
isDirty: false, isDirty: false,
waiting: false, waiting: false,
diagram: '', diagram: '',
diagramXML: '', diagramXML: '',
title: '', title: '',
readyToSave: false, readyToSave: false,
previewButtonCaption: 'Preview', previewButtonCaption: '',
flowCallback: null, flowCallback: null,
editorId: computed('page', function () { editorId: computed('page', function () {
let page = this.get('page'); let page = this.get('page');
@ -40,6 +41,8 @@ export default Component.extend({
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.previewButtonCaption = this.i18n.localize('preview');
schedule('afterRender', () => { schedule('afterRender', () => {
this.setupEditor(); this.setupEditor();
}); });
@ -125,7 +128,7 @@ export default Component.extend({
action: 'export', action: 'export',
format: 'xmlpng', format: 'xmlpng',
xml: this.get('diagramXML'), xml: this.get('diagramXML'),
spin: 'Updating' spin: this.i18n.localize('updating')
} }
), '*'); ), '*');
}, },

View file

@ -15,6 +15,8 @@ export default Component.extend({
data: "", data: "",
didReceiveAttrs() { didReceiveAttrs() {
this._super();
this.set("data", this.get("meta.rawBody")); this.set("data", this.get("meta.rawBody"));
}, },

View file

@ -30,6 +30,8 @@ export default Component.extend(SectionMixin, {
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
let config = {}; let config = {};
try { try {

View file

@ -36,11 +36,13 @@ export default Component.extend({
this.set('pageBody', body); this.set('pageBody', body);
}, },
didInsertElement() { didInsertElement(...args) {
this._super(...args);
this.attachEditor(); this.attachEditor();
}, },
willDestroyElement() { willDestroyElement(...args) {
this._super(...args);
let editor = this.get('codeEditor'); let editor = this.get('codeEditor');
if (this.get('editMode')) { if (this.get('editMode')) {

View file

@ -28,6 +28,7 @@ export default Component.extend(SectionMixin, NotifierMixin, {
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
let config = {}; let config = {};
try { try {

View file

@ -28,6 +28,8 @@ export default Component.extend({
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
let pdfOption = {}; let pdfOption = {};
try { try {

View file

@ -17,11 +17,12 @@ import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
appMeta: service(), appMeta: service(),
sectionSvc: service('section'), sectionSvc: service('section'),
i18n: service(),
isDirty: false, isDirty: false,
waiting: false, waiting: false,
diagramText: '', diagramText: '',
diagramPreview: null, diagramPreview: null,
previewButtonCaption: 'Preview', previewButtonCaption: '',
editorId: computed('page', function () { editorId: computed('page', function () {
let page = this.get('page'); let page = this.get('page');
return `plantuml-editor-${page.id}`; return `plantuml-editor-${page.id}`;
@ -34,9 +35,14 @@ export default Component.extend({
return _.isEmpty(this.get('diagramText')); return _.isEmpty(this.get('diagramText'));
}), }),
init(...args) {
this._super(...args);
this.previewButtonCaption = this.i18n.localize('preview');
},
generatePreview() { generatePreview() {
this.set('waiting', true); this.set('waiting', true);
this.set('previewButtonCaption', 'Generating preview...'); this.set('previewButtonCaption', this.i18n.localize('preview_wait'));
let self = this; let self = this;
let data = { data: this.get('diagramText') }; let data = { data: this.get('diagramText') };
@ -45,11 +51,11 @@ export default Component.extend({
this.get('sectionSvc').fetch(this.get('page'), 'preview', data).then(function (response) { this.get('sectionSvc').fetch(this.get('page'), 'preview', data).then(function (response) {
self.set('diagramPreview', response.data); self.set('diagramPreview', response.data);
self.set('waiting', false); self.set('waiting', false);
self.set('previewButtonCaption', 'Preview'); self.set('previewButtonCaption', this.i18n.localize('preview'));
}, function (reason) { // eslint-disable-line no-unused-vars }, function (reason) { // eslint-disable-line no-unused-vars
self.set('diagramPreview', null); self.set('diagramPreview', null);
self.set('waiting', false); self.set('waiting', false);
self.set('previewButtonCaption', 'Preview'); self.set('previewButtonCaption', this.i18n.localize('preview'));
}); });
}); });
}, },

View file

@ -47,6 +47,8 @@ export default Component.extend(SectionMixin, NotifierMixin, {
}, },
didReceiveAttrs() { didReceiveAttrs() {
this._super();
let page = this.get('page'); let page = this.get('page');
let config = {}; let config = {};
let self = this; let self = this;

View file

@ -1,34 +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 Modals from '../../mixins/modal';
import Component from '@ember/component';
export default Component.extend(Modals, {
localStorage: service(),
didInsertElement() {
this._super(...arguments);
let firstRun = this.get('localStorage').isFirstRun();
if (firstRun) {
this.modalOpen('#first-run-modal', {'show': true});
}
},
actions: {
onCloseModal() {
this.modalClose('#first-run-modal');
}
}
});

View file

@ -10,6 +10,7 @@
// https://documize.com // https://documize.com
import $ from 'jquery'; import $ from 'jquery';
import { A } from '@ember/array';
import { empty } from '@ember/object/computed'; import { empty } from '@ember/object/computed';
import { computed, set } from '@ember/object'; import { computed, set } from '@ember/object';
import { isPresent, isEqual, isEmpty } from '@ember/utils'; import { isPresent, isEqual, isEmpty } from '@ember/utils';
@ -46,13 +47,34 @@ export default Component.extend(AuthProvider, {
return ''; return '';
} }
}), }),
locale: { name: '' },
locales: null,
init() { init() {
this._super(...arguments); this._super(...arguments);
this.password = { password: "", confirmation: "" }; this.password = { password: "", confirmation: "" };
let l = this.get('appMeta.locales');
let t = A([]);
l.forEach((locale) => {
t.pushObject( {name: locale} );
});
this.set('locales', t);
},
didReceiveAttrs() {
this._super(...arguments);
this.set('locale', this.locales.findBy('name', this.get('model.locale')));
}, },
actions: { actions: {
onSelectLocale(locale) {
this.set('model.locale', locale.name);
},
save() { save() {
let password = this.get('password.password'); let password = this.get('password.password');
let confirmation = this.get('password.confirmation'); let confirmation = this.get('password.confirmation');
@ -87,10 +109,5 @@ export default Component.extend(AuthProvider, {
set(this, 'password.confirmation', ''); set(this, 'password.confirmation', '');
}); });
} }
// onThemeChange(theme) {
// this.get('appMeta').setTheme(theme);
// this.set('model.theme', theme);
// }
} }
}); });

View file

@ -68,10 +68,6 @@ let constants = EmberObject.extend({
None: 0, None: 0,
Lock: 1, Lock: 1,
Review: 2, Review: 2,
NoneLabel: 'Changes permitted without approval',
LockLabel: 'Locked, changes not permitted',
ReviewLabel: 'Changes require approval before publication'
}, },
// Document // Document
@ -80,10 +76,6 @@ let constants = EmberObject.extend({
Anybody: 1, Anybody: 1,
Majority: 2, Majority: 2,
Unanimous: 3, Unanimous: 3,
AnybodyLabel: 'Approval required from any approver',
MajorityLabel: 'Majority approval required from approvers',
UnanimousLabel: 'Unanimous approval required from all approvers'
}, },
// Section // Section
@ -115,10 +107,6 @@ let constants = EmberObject.extend({
Draft: 0, Draft: 0,
Live: 1, Live: 1,
Archived: 2, Archived: 2,
DraftLabel: 'Draft',
LiveLabel: 'Live',
ArchivedLabel: 'Archived',
}, },
// Document Version -- document.groupId links different versions of documents together // Document Version -- document.groupId links different versions of documents together
@ -318,51 +306,6 @@ let constants = EmberObject.extend({
Yellow: 'yellow', Yellow: 'yellow',
Gray: 'gray' Gray: 'gray'
}, },
Label: { // eslint-disable-line ember/avoid-leaking-state-in-ember-objects
Add: 'Add',
Activate: "Activate",
Approve: 'Approve',
Authenticate: 'Authenticate',
Cancel: 'Cancel',
Close: 'Close',
Copy: 'Copy',
Delete: 'Delete',
Duplicate: 'Duplicate',
Edit: 'Edit',
Export: 'Export',
File: 'File',
Import: 'Import',
Insert: 'Insert',
Invite: 'Invite',
Join: 'Join',
Leave: 'Leave',
Login: 'Login',
Move: 'Move',
Next: 'Next',
OK: 'OK',
Preview: 'Preview',
Print: 'Print',
Publish: 'Publish',
Reject: 'Reject',
Remove: 'Remove',
Reply: 'Reply',
Reset: 'Reset',
Restore: 'Restore',
Request: 'Request',
Save: 'Save',
Search: 'Search',
Send: 'Send',
Share: 'Share',
SignIn: 'Sign In',
Sort: 'Sort',
Space: 'Space',
Spaces: 'Spaces',
Unassigned: 'Unassigned',
Update: 'Update',
Upload: 'Upload',
Version: 'Version'
}
}); });
export default { constants }; export default { constants };

View file

@ -0,0 +1,17 @@
// Copyright 2022 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
//
// https://www.documize.com
import Helper from '@ember/component/helper';
import { inject as service } from '@ember/service';
export default Helper.extend({
i18n: service(),
compute([key, ...rest]) {
return this.i18n.localize(key, ...rest);
}
});

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