mirror of
https://github.com/documize/community.git
synced 2025-07-19 13:19:43 +02:00
Provide in-app What's New & new release notifications
This commit is contained in:
parent
97cc5374f0
commit
eaf46f06c1
25 changed files with 1166 additions and 772 deletions
|
@ -24,4 +24,7 @@ INSERT INTO search (orgid, documentid, itemid, itemtype, content)
|
||||||
SELECT orgid, documentid, refid as itemid, "page" as itemtype, title as content
|
SELECT orgid, documentid, refid as itemid, "page" as itemtype, title as content
|
||||||
FROM page WHERE status=0
|
FROM page WHERE status=0
|
||||||
|
|
||||||
|
-- whats new support
|
||||||
|
ALTER TABLE user ADD COLUMN `lastversion` CHAR(16) NOT NULL DEFAULT '' AFTER `active`;
|
||||||
|
|
||||||
-- deprecations
|
-- deprecations
|
||||||
|
|
|
@ -39,6 +39,7 @@ type RequestContext struct {
|
||||||
Expires time.Time
|
Expires time.Time
|
||||||
Fullname string
|
Fullname string
|
||||||
Transaction *sqlx.Tx
|
Transaction *sqlx.Tx
|
||||||
|
AppVersion string
|
||||||
}
|
}
|
||||||
|
|
||||||
//GetAppURL returns full HTTP url for the app
|
//GetAppURL returns full HTTP url for the app
|
||||||
|
|
|
@ -100,6 +100,7 @@ func (h *Handler) Add(w http.ResponseWriter, r *http.Request) {
|
||||||
requestedPassword := secrets.GenerateRandomPassword()
|
requestedPassword := secrets.GenerateRandomPassword()
|
||||||
userModel.Salt = secrets.GenerateSalt()
|
userModel.Salt = secrets.GenerateSalt()
|
||||||
userModel.Password = secrets.GeneratePassword(requestedPassword, userModel.Salt)
|
userModel.Password = secrets.GeneratePassword(requestedPassword, userModel.Salt)
|
||||||
|
userModel.LastVersion = ctx.AppVersion
|
||||||
|
|
||||||
// only create account if not dupe
|
// only create account if not dupe
|
||||||
addUser := true
|
addUser := true
|
||||||
|
|
|
@ -35,8 +35,8 @@ func (s Scope) 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("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
_, err = ctx.Transaction.Exec("INSERT INTO user (refid, firstname, lastname, email, initials, password, salt, reset, lastversion, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||||
u.RefID, u.Firstname, u.Lastname, strings.ToLower(u.Email), u.Initials, u.Password, u.Salt, "", u.Created, u.Revised)
|
u.RefID, u.Firstname, u.Lastname, strings.ToLower(u.Email), u.Initials, u.Password, u.Salt, "", u.LastVersion, u.Created, u.Revised)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, "execute user insert")
|
err = errors.Wrap(err, "execute user insert")
|
||||||
|
@ -47,7 +47,7 @@ func (s Scope) Add(ctx domain.RequestContext, u user.User) (err error) {
|
||||||
|
|
||||||
// Get returns the user record for the given id.
|
// Get returns the user record for the given id.
|
||||||
func (s Scope) Get(ctx domain.RequestContext, id string) (u user.User, err error) {
|
func (s Scope) Get(ctx domain.RequestContext, id string) (u user.User, err error) {
|
||||||
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE refid=?", id)
|
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, lastversion, created, revised FROM user WHERE refid=?", id)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for user %s", id))
|
err = errors.Wrap(err, fmt.Sprintf("unable to execute select for user %s", id))
|
||||||
|
@ -60,7 +60,7 @@ func (s Scope) Get(ctx domain.RequestContext, id string) (u user.User, err error
|
||||||
func (s Scope) GetByDomain(ctx domain.RequestContext, domain, email string) (u user.User, err error) {
|
func (s Scope) GetByDomain(ctx domain.RequestContext, domain, email string) (u user.User, err error) {
|
||||||
email = strings.TrimSpace(strings.ToLower(email))
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
err = s.Runtime.Db.Get(&u, "SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?",
|
err = s.Runtime.Db.Get(&u, "SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.global, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised FROM user u, account a, organization o WHERE TRIM(LOWER(u.email))=? AND u.refid=a.userid AND a.orgid=o.refid AND TRIM(LOWER(o.domain))=?",
|
||||||
email, domain)
|
email, domain)
|
||||||
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
@ -74,7 +74,7 @@ func (s Scope) GetByDomain(ctx domain.RequestContext, domain, email string) (u u
|
||||||
func (s Scope) GetByEmail(ctx domain.RequestContext, email string) (u user.User, err error) {
|
func (s Scope) GetByEmail(ctx domain.RequestContext, email string) (u user.User, err error) {
|
||||||
email = strings.TrimSpace(strings.ToLower(email))
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE TRIM(LOWER(email))=?", email)
|
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, lastversion, created, revised FROM user WHERE TRIM(LOWER(email))=?", email)
|
||||||
|
|
||||||
if err != nil && err != sql.ErrNoRows {
|
if err != nil && err != sql.ErrNoRows {
|
||||||
err = errors.Wrap(err, fmt.Sprintf("execute select user by email %s", email))
|
err = errors.Wrap(err, fmt.Sprintf("execute select user by email %s", email))
|
||||||
|
@ -85,7 +85,7 @@ func (s Scope) GetByEmail(ctx domain.RequestContext, email string) (u user.User,
|
||||||
|
|
||||||
// GetByToken returns a user record given a reset token value.
|
// GetByToken returns a user record given a reset token value.
|
||||||
func (s Scope) GetByToken(ctx domain.RequestContext, token string) (u user.User, err error) {
|
func (s Scope) GetByToken(ctx domain.RequestContext, token string) (u user.User, err error) {
|
||||||
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE reset=?", token)
|
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, lastversion, created, revised FROM user WHERE reset=?", token)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, fmt.Sprintf("execute user select by token %s", token))
|
err = errors.Wrap(err, fmt.Sprintf("execute user select by token %s", token))
|
||||||
|
@ -98,7 +98,7 @@ func (s Scope) GetByToken(ctx domain.RequestContext, token string) (u user.User,
|
||||||
// This occurs when we you share a folder with a new user and they have to complete
|
// This occurs when we you share a folder with a new user and they have to complete
|
||||||
// the onboarding process.
|
// the onboarding process.
|
||||||
func (s Scope) GetBySerial(ctx domain.RequestContext, serial string) (u user.User, err error) {
|
func (s Scope) GetBySerial(ctx domain.RequestContext, serial string) (u user.User, err error) {
|
||||||
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, created, revised FROM user WHERE salt=?", serial)
|
err = s.Runtime.Db.Get(&u, "SELECT id, refid, firstname, lastname, email, initials, global, password, salt, reset, lastversion, created, revised FROM user WHERE salt=?", serial)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Wrap(err, fmt.Sprintf("execute user select by serial %s", serial))
|
err = errors.Wrap(err, fmt.Sprintf("execute user select by serial %s", serial))
|
||||||
|
@ -111,7 +111,7 @@ func (s Scope) GetBySerial(ctx domain.RequestContext, serial string) (u user.Use
|
||||||
// identified in the Persister.
|
// identified in the Persister.
|
||||||
func (s Scope) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []user.User, err error) {
|
func (s Scope) GetActiveUsersForOrganization(ctx domain.RequestContext) (u []user.User, err error) {
|
||||||
err = s.Runtime.Db.Select(&u,
|
err = s.Runtime.Db.Select(&u,
|
||||||
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised,
|
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised,
|
||||||
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
||||||
FROM user u, account a
|
FROM user u, account a
|
||||||
WHERE u.refid=a.userid AND a.orgid=? AND a.active=1
|
WHERE u.refid=a.userid AND a.orgid=? AND a.active=1
|
||||||
|
@ -139,7 +139,7 @@ func (s Scope) GetUsersForOrganization(ctx domain.RequestContext, filter string)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Runtime.Db.Select(&u,
|
err = s.Runtime.Db.Select(&u,
|
||||||
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised,
|
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised,
|
||||||
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
||||||
FROM user u, account a
|
FROM user u, account a
|
||||||
WHERE u.refid=a.userid AND a.orgid=? `+likeQuery+
|
WHERE u.refid=a.userid AND a.orgid=? `+likeQuery+
|
||||||
|
@ -160,7 +160,7 @@ func (s Scope) GetUsersForOrganization(ctx domain.RequestContext, filter string)
|
||||||
// GetSpaceUsers returns a slice containing all user records for given space.
|
// GetSpaceUsers returns a slice containing all user records for given space.
|
||||||
func (s Scope) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []user.User, err error) {
|
func (s Scope) GetSpaceUsers(ctx domain.RequestContext, spaceID string) (u []user.User, err error) {
|
||||||
err = s.Runtime.Db.Select(&u, `
|
err = s.Runtime.Db.Select(&u, `
|
||||||
SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised, u.global,
|
SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.lastversion, u.revised, u.global,
|
||||||
a.active, a.users AS viewusers, a.editor, a.admin
|
a.active, a.users AS viewusers, a.editor, a.admin
|
||||||
FROM user u, account a
|
FROM user u, account a
|
||||||
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN (
|
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN (
|
||||||
|
@ -189,7 +189,7 @@ func (s Scope) GetUsersForSpaces(ctx domain.RequestContext, spaces []string) (u
|
||||||
}
|
}
|
||||||
|
|
||||||
query, args, err := sqlx.In(`
|
query, args, err := sqlx.In(`
|
||||||
SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised, u.global,
|
SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised, u.global,
|
||||||
a.active, a.users AS viewusers, a.editor, a.admin
|
a.active, a.users AS viewusers, a.editor, a.admin
|
||||||
FROM user u, account a
|
FROM user u, account a
|
||||||
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN (
|
WHERE a.orgid=? AND u.refid = a.userid AND a.active=1 AND u.refid IN (
|
||||||
|
@ -219,7 +219,7 @@ func (s Scope) UpdateUser(ctx domain.RequestContext, u user.User) (err error) {
|
||||||
u.Email = strings.ToLower(u.Email)
|
u.Email = strings.ToLower(u.Email)
|
||||||
|
|
||||||
_, err = ctx.Transaction.NamedExec(
|
_, err = ctx.Transaction.NamedExec(
|
||||||
"UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, initials=:initials WHERE refid=:refid", &u)
|
"UPDATE user SET firstname=:firstname, lastname=:lastname, email=:email, revised=:revised, initials=:initials, lastversion=:lastversion WHERE 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))
|
||||||
|
@ -289,7 +289,7 @@ func (s Scope) MatchUsers(ctx domain.RequestContext, text string, maxMatches int
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.Runtime.Db.Select(&u,
|
err = s.Runtime.Db.Select(&u,
|
||||||
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.created, u.revised,
|
`SELECT u.id, u.refid, u.firstname, u.lastname, u.email, u.initials, u.password, u.salt, u.reset, u.lastversion, u.created, u.revised,
|
||||||
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
u.global, a.active, a.editor, a.admin, a.users as viewusers
|
||||||
FROM user u, account a
|
FROM user u, account a
|
||||||
WHERE a.orgid=? AND u.refid=a.userid AND a.active=1 `+likeQuery+
|
WHERE a.orgid=? AND u.refid=a.userid AND a.active=1 `+likeQuery+
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -9,11 +9,30 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
import { empty } from '@ember/object/computed';
|
import { empty } from '@ember/object/computed';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
import Component from '@ember/component';
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
appMeta: service(),
|
||||||
LicenseError: empty('model.license'),
|
LicenseError: empty('model.license'),
|
||||||
|
changelog: '',
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let cacheBuster = + new Date();
|
||||||
|
$.ajax({
|
||||||
|
url: `https://storage.googleapis.com/documize/downloads/updates/summary.html?cb=${cacheBuster}`,
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'html',
|
||||||
|
success: function (response) {
|
||||||
|
self.set('changelog', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
saveLicense() {
|
saveLicense() {
|
||||||
|
|
|
@ -9,12 +9,14 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
import Component from '@ember/component';
|
import $ from 'jquery';
|
||||||
import { notEmpty } from '@ember/object/computed';
|
import { notEmpty } from '@ember/object/computed';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service'
|
||||||
|
import ModalMixin from '../../mixins/modal';
|
||||||
import constants from '../../utils/constants';
|
import constants from '../../utils/constants';
|
||||||
|
import Component from '@ember/component';
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend(ModalMixin, {
|
||||||
folderService: service('folder'),
|
folderService: service('folder'),
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
session: service(),
|
session: service(),
|
||||||
|
@ -24,6 +26,8 @@ export default Component.extend({
|
||||||
hasPins: notEmpty('pins'),
|
hasPins: notEmpty('pins'),
|
||||||
hasSpacePins: notEmpty('spacePins'),
|
hasSpacePins: notEmpty('spacePins'),
|
||||||
hasDocumentPins: notEmpty('documentPins'),
|
hasDocumentPins: notEmpty('documentPins'),
|
||||||
|
hasWhatsNew: false,
|
||||||
|
newsContent: '',
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
@ -34,6 +38,23 @@ export default Component.extend({
|
||||||
config = JSON.parse(config);
|
config = JSON.parse(config);
|
||||||
this.set('enableLogout', !config.disableLogout);
|
this.set('enableLogout', !config.disableLogout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.get('session').hasWhatsNew().then((v) => {
|
||||||
|
this.set('hasWhatsNew', v);
|
||||||
|
});
|
||||||
|
|
||||||
|
let version = this.get('appMeta.version');
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let cacheBuster = + new Date();
|
||||||
|
$.ajax({
|
||||||
|
url: `https://storage.googleapis.com/documize/downloads/updates/${version}.html?cb=${cacheBuster}`,
|
||||||
|
type: 'GET',
|
||||||
|
dataType: 'html',
|
||||||
|
success: function (response) {
|
||||||
|
self.set('newsContent', response);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
@ -80,6 +101,12 @@ export default Component.extend({
|
||||||
let folder = this.get('store').peekRecord('folder', folderId);
|
let folder = this.get('store').peekRecord('folder', folderId);
|
||||||
this.get('router').transitionTo('document', folderId, folder.get('slug'), documentId, 'document');
|
this.get('router').transitionTo('document', folderId, folder.get('slug'), documentId, 'document');
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onShowWhatsNewModal() {
|
||||||
|
this.modalOpen("#whats-new-modal", { "show": true });
|
||||||
|
this.get('session').seenNewVersion();
|
||||||
|
this.set('hasWhatsNew', false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,6 +25,7 @@ export default Model.extend({
|
||||||
global: attr('boolean', { defaultValue: false }),
|
global: attr('boolean', { defaultValue: false }),
|
||||||
accounts: attr(),
|
accounts: attr(),
|
||||||
groups: attr(),
|
groups: attr(),
|
||||||
|
lastVersion: attr('string'),
|
||||||
created: attr(),
|
created: attr(),
|
||||||
revised: attr(),
|
revised: attr(),
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
import { Promise as EmberPromise } from 'rsvp';
|
import { Promise as EmberPromise } from 'rsvp';
|
||||||
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import NotifierMixin from "../../../mixins/notifier";
|
import NotifierMixin from "../../../mixins/notifier";
|
||||||
|
@ -18,13 +17,13 @@ import NotifierMixin from "../../../mixins/notifier";
|
||||||
|
|
||||||
export default Controller.extend(NotifierMixin, {
|
export default Controller.extend(NotifierMixin, {
|
||||||
global: service(),
|
global: service(),
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
session: service(),
|
session: service(),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
onSave(data) {
|
onSave(data) {
|
||||||
return new EmberPromise((resolve) => {
|
return new EmberPromise((resolve) => {
|
||||||
if(!this.get('session.isGlobalAdmin')) {
|
if (!this.get('session.isGlobalAdmin')) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
this.get('global').saveAuthConfig(data).then(() => {
|
this.get('global').saveAuthConfig(data).then(() => {
|
||||||
|
@ -46,7 +45,7 @@ export default Controller.extend(NotifierMixin, {
|
||||||
this.get('session').logout();
|
this.get('session').logout();
|
||||||
this.set('appMeta.authProvider', data.authProvider);
|
this.set('appMeta.authProvider', data.authProvider);
|
||||||
this.set('appMeta.authConfig', data.authConfig);
|
this.set('appMeta.authConfig', data.authConfig);
|
||||||
window.location.href= '/';
|
window.location.href = '/';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{{#toolbar/t-toolbar}}
|
{{#toolbar/t-toolbar}}
|
||||||
{{#toolbar/t-links}}
|
{{#toolbar/t-links}}
|
||||||
{{#link-to "folders" class="link" tagName="li"}}Spaces{{/link-to}}
|
{{#link-to "folders" class="link" tagName="li" }}Spaces{{/link-to}}
|
||||||
{{/toolbar/t-links}}
|
{{/toolbar/t-links}}
|
||||||
{{#toolbar/t-actions}}
|
{{#toolbar/t-actions}}
|
||||||
{{/toolbar/t-actions}}
|
{{/toolbar/t-actions}}
|
||||||
|
@ -14,24 +14,20 @@
|
||||||
<div class="form-group mt-4">
|
<div class="form-group mt-4">
|
||||||
{{focus-input type="text" value=filter class="form-control mb-4" placeholder='a OR b, x AND y, "phrase mat*"'}}
|
{{focus-input type="text" value=filter class="form-control mb-4" placeholder='a OR b, x AND y, "phrase mat*"'}}
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
{{input type="checkbox" id="search-1" class="form-check-input" checked=matchDoc}}
|
||||||
{{input type="checkbox" id=checkId class="form-check-input" checked=matchDoc}} document title
|
<label class="form-check-label" for="search-1"> document title</label>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
{{input type="checkbox" id="search-2" class="form-check-input" checked=matchContent}}
|
||||||
{{input type="checkbox" id=checkId class="form-check-input" checked=matchContent}} content
|
<label class="form-check-label" for="search-2"> content</label>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
{{input type="checkbox" id="search-3" class="form-check-input" checked=matchTag}}
|
||||||
{{input type="checkbox" id=checkId class="form-check-input" checked=matchTag}} tag name
|
<label class="form-check-label" for="search-3"> tag name</label>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check form-check-inline">
|
<div class="form-check form-check-inline">
|
||||||
<label class="form-check-label">
|
{{input type="checkbox" id="search-4" class="form-check-input" checked=matchFile}}
|
||||||
{{input type="checkbox" id=checkId class="form-check-input" checked=matchFile}} attachment name
|
<label class="form-check-label" for="search-4"> attachment name</label>
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
|
import $ from 'jquery';
|
||||||
import { htmlSafe } from '@ember/string';
|
import { htmlSafe } from '@ember/string';
|
||||||
import { resolve } from 'rsvp';
|
import { resolve } from 'rsvp';
|
||||||
import Service, { inject as service } from '@ember/service';
|
import Service, { inject as service } from '@ember/service';
|
||||||
|
@ -63,6 +64,7 @@ export default Service.extend({
|
||||||
|
|
||||||
return this.get('ajax').request('public/meta').then((response) => {
|
return this.get('ajax').request('public/meta').then((response) => {
|
||||||
this.setProperties(response);
|
this.setProperties(response);
|
||||||
|
this.set('version', 'v' + this.get('version'));
|
||||||
|
|
||||||
if (requestedRoute === 'secure') {
|
if (requestedRoute === 'secure') {
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
|
@ -76,6 +78,26 @@ export default Service.extend({
|
||||||
this.get('localStorage').storeSessionItem('entryUrl', requestedUrl);
|
this.get('localStorage').storeSessionItem('entryUrl', requestedUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
let cacheBuster = + new Date();
|
||||||
|
$.getJSON(`https://storage.googleapis.com/documize/downloads/updates/meta.json?cb=${cacheBuster}`, function (versions) {
|
||||||
|
let cv = 'v' + versions.community.version;
|
||||||
|
let ev = 'v' + versions.enterprise.version;
|
||||||
|
let re = self.get('edition');
|
||||||
|
let rv = self.get('version');
|
||||||
|
|
||||||
|
self.set('communityLatest', cv);
|
||||||
|
self.set('enterpriseLatest', ev);
|
||||||
|
self.set('updateAvailable', false); // set to true for testing
|
||||||
|
|
||||||
|
if (re === 'Community' && rv < cv) {
|
||||||
|
self.set('updateAvailable', true);
|
||||||
|
}
|
||||||
|
if (re === 'Enterprise' && rv < ev) {
|
||||||
|
self.set('updateAvailable', true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,13 @@
|
||||||
|
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { computed } from '@ember/object';
|
import { computed } from '@ember/object';
|
||||||
|
import { Promise as EmberPromise } from 'rsvp';
|
||||||
import SimpleAuthSession from 'ember-simple-auth/services/session';
|
import SimpleAuthSession from 'ember-simple-auth/services/session';
|
||||||
|
|
||||||
export default SimpleAuthSession.extend({
|
export default SimpleAuthSession.extend({
|
||||||
ajax: service(),
|
ajax: service(),
|
||||||
appMeta: service(),
|
appMeta: service(),
|
||||||
|
userSvc: service('user'),
|
||||||
store: service(),
|
store: service(),
|
||||||
localStorage: service(),
|
localStorage: service(),
|
||||||
folderPermissions: null,
|
folderPermissions: null,
|
||||||
|
@ -23,11 +25,11 @@ export default SimpleAuthSession.extend({
|
||||||
isMac: false,
|
isMac: false,
|
||||||
isMobile: false,
|
isMobile: false,
|
||||||
|
|
||||||
hasAccounts: computed('isAuthenticated', 'session.content.authenticated.user', function() {
|
hasAccounts: computed('isAuthenticated', 'session.content.authenticated.user', function () {
|
||||||
return this.get('session.authenticator') !== 'authenticator:anonymous' && this.get('session.content.authenticated.user.accounts').length > 0;
|
return this.get('session.authenticator') !== 'authenticator:anonymous' && this.get('session.content.authenticated.user.accounts').length > 0;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
accounts: computed('hasAccounts', function() {
|
accounts: computed('hasAccounts', function () {
|
||||||
return this.get('session.content.authenticated.user.accounts');
|
return this.get('session.content.authenticated.user.accounts');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -35,7 +37,9 @@ export default SimpleAuthSession.extend({
|
||||||
if (this.get('isAuthenticated')) {
|
if (this.get('isAuthenticated')) {
|
||||||
let user = this.get('session.content.authenticated.user') || { id: '0' };
|
let user = this.get('session.content.authenticated.user') || { id: '0' };
|
||||||
let data = this.get('store').normalize('user', user);
|
let data = this.get('store').normalize('user', user);
|
||||||
return this.get('store').push(data);
|
let um = this.get('store').push(data)
|
||||||
|
|
||||||
|
return um;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
@ -71,5 +75,21 @@ export default SimpleAuthSession.extend({
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
this.get('localStorage').clearAll();
|
this.get('localStorage').clearAll();
|
||||||
|
},
|
||||||
|
|
||||||
|
seenNewVersion() {
|
||||||
|
this.get('userSvc').getUser(this.get('user.id')).then((user) => {
|
||||||
|
user.set('lastVersion', this.get('appMeta.version'));
|
||||||
|
this.get('userSvc').save(user);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// set what's new indicator
|
||||||
|
hasWhatsNew() {
|
||||||
|
return new EmberPromise((resolve) => {
|
||||||
|
return this.get('userSvc').getUser(this.get('user.id')).then((user) => {
|
||||||
|
resolve(user.get('lastVersion') !== this.get('appMeta.version'));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
@import "section/plantuml.scss";
|
@import "section/plantuml.scss";
|
||||||
@import "section/papertrail.scss";
|
@import "section/papertrail.scss";
|
||||||
@import "section/wysiwyg.scss";
|
@import "section/wysiwyg.scss";
|
||||||
|
@import "news.scss";
|
||||||
@import "enterprise/all.scss";
|
@import "enterprise/all.scss";
|
||||||
|
|
||||||
// Bootstrap override that removes gutter space on smaller screens
|
// Bootstrap override that removes gutter space on smaller screens
|
||||||
|
|
18
gui/app/styles/bootstrap.scss
vendored
18
gui/app/styles/bootstrap.scss
vendored
|
@ -101,8 +101,7 @@ $link-hover-decoration: none;
|
||||||
@import "node_modules/bootstrap/scss/tooltip";
|
@import "node_modules/bootstrap/scss/tooltip";
|
||||||
@import "node_modules/bootstrap/scss/tables";
|
@import "node_modules/bootstrap/scss/tables";
|
||||||
@import "node_modules/bootstrap/scss/badge";
|
@import "node_modules/bootstrap/scss/badge";
|
||||||
// @import "node_modules/bootstrap/scss/navbar";
|
|
||||||
// @import "node_modules/bootstrap/scss/images";
|
|
||||||
|
|
||||||
.modal-80 {
|
.modal-80 {
|
||||||
max-width: 80% !important;
|
max-width: 80% !important;
|
||||||
|
@ -111,3 +110,18 @@ $link-hover-decoration: none;
|
||||||
body.modal-open {
|
body.modal-open {
|
||||||
padding-right: 0 !important;
|
padding-right: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.modal-header-white {
|
||||||
|
background-color: $color-white !important;
|
||||||
|
border: none !important;
|
||||||
|
|
||||||
|
> .close {
|
||||||
|
background-color: $color-white !important;
|
||||||
|
border: none !important;
|
||||||
|
|
||||||
|
> span {
|
||||||
|
color: $color-gray;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ $color-red: #9E0D1F;
|
||||||
$color-green: #348A37;
|
$color-green: #348A37;
|
||||||
$color-blue: #2667af;
|
$color-blue: #2667af;
|
||||||
$color-goldy: #FFD700;
|
$color-goldy: #FFD700;
|
||||||
|
$color-orange: #FFAD15;
|
||||||
|
|
||||||
// widgets
|
// widgets
|
||||||
$color-checkbox: #2667af;
|
$color-checkbox: #2667af;
|
||||||
|
@ -40,6 +41,7 @@ $color-symbol-box: #dce5e8;
|
||||||
$color-symbol-icon: #a4b8be;
|
$color-symbol-icon: #a4b8be;
|
||||||
$color-card: #F9F9F9;
|
$color-card: #F9F9F9;
|
||||||
$color-stroke: #e1e1e1;
|
$color-stroke: #e1e1e1;
|
||||||
|
$color-whats-new: #fc1530;
|
||||||
|
|
||||||
// Color utility classes for direct usage in HTML
|
// Color utility classes for direct usage in HTML
|
||||||
.color-white {
|
.color-white {
|
||||||
|
@ -75,6 +77,12 @@ $color-stroke: #e1e1e1;
|
||||||
.color-gold {
|
.color-gold {
|
||||||
color: $color-goldy !important;
|
color: $color-goldy !important;
|
||||||
}
|
}
|
||||||
|
.color-orange {
|
||||||
|
color: $color-orange !important;
|
||||||
|
}
|
||||||
|
.color-whats-new {
|
||||||
|
color: $color-whats-new !important;
|
||||||
|
}
|
||||||
|
|
||||||
.background-color-white {
|
.background-color-white {
|
||||||
background-color: $color-white !important;
|
background-color: $color-white !important;
|
||||||
|
|
175
gui/app/styles/news.scss
Normal file
175
gui/app/styles/news.scss
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
.product-update {
|
||||||
|
text-align: left;
|
||||||
|
margin: 50px 0;
|
||||||
|
|
||||||
|
> .update-summary {
|
||||||
|
padding: 25px;
|
||||||
|
border: 1px solid $color-orange;
|
||||||
|
background-color: $color-off-white;
|
||||||
|
@include border-radius(2px);
|
||||||
|
|
||||||
|
> .caption {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: $color-orange;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .instructions {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: $color-gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .version {
|
||||||
|
margin: 30px 0 0 20px;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
color: $color-gray;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .changes {
|
||||||
|
margin: 10px 0 0 40px;
|
||||||
|
|
||||||
|
> li {
|
||||||
|
list-style-type: disc;
|
||||||
|
padding: 5px 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: $color-black;
|
||||||
|
|
||||||
|
> .tag-edition {
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: $color-gray-light;
|
||||||
|
color: $color-primary;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-about {
|
||||||
|
text-align: center;
|
||||||
|
margin: 30px 30px;
|
||||||
|
|
||||||
|
> .edition {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
color: $color-black;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .version {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
color: $color-gray;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .dotcom {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: $color-link;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .copyright {
|
||||||
|
text-align: center;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $color-off-black;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .license {
|
||||||
|
text-align: left;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: $color-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-available-dot {
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: $color-orange;
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.whats-new-dot {
|
||||||
|
border-radius: 10px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
background-color: $color-whats-new;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.product-news {
|
||||||
|
text-align: left;
|
||||||
|
margin: 0 30px;
|
||||||
|
|
||||||
|
> h2 {
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: $color-off-black;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .news-item {
|
||||||
|
padding: 30px 0;
|
||||||
|
border-bottom: 1px solid $color-border;
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
> .title {
|
||||||
|
color: $color-primary;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .date {
|
||||||
|
color: $color-gray;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .info {
|
||||||
|
color: $color-black;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> .tag-edition {
|
||||||
|
margin: 10px 10px 10px 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: $color-off-white;
|
||||||
|
color: $color-gray;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
> img {
|
||||||
|
max-width: 450px;
|
||||||
|
max-height: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .action {
|
||||||
|
margin: 20px 0;
|
||||||
|
text-align: center;
|
||||||
|
color: $color-gray;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -201,7 +201,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
// overflow: hidden; // kills update dots
|
||||||
width: 35px;
|
width: 35px;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
line-height: 34px;
|
line-height: 34px;
|
||||||
|
|
|
@ -8,9 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view-customize">
|
<div class="view-customize">
|
||||||
|
|
||||||
<form class="mt-5">
|
<form class="mt-5">
|
||||||
|
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Provider</label>
|
<label class="col-sm-2 col-form-label">Provider</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
@ -62,7 +60,8 @@
|
||||||
<label for="keycloak-admin-user" class="col-sm-2 col-form-label">Keycloak Username</label>
|
<label for="keycloak-admin-user" class="col-sm-2 col-form-label">Keycloak Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
{{input id="keycloak-admin-user" type="text" value=keycloakConfig.adminUser class=(if KeycloakAdminUserError 'form-control is-invalid' 'form-control')}}
|
{{input id="keycloak-admin-user" type="text" value=keycloakConfig.adminUser class=(if KeycloakAdminUserError 'form-control is-invalid' 'form-control')}}
|
||||||
<small class="form-text text-muted">Used to connect with Keycloak and sync users with Documize (create user under Master Realm and assign 'view-users' role against Realm specified above)</small>
|
<small class="form-text text-muted">Used to connect with Keycloak and sync users with Documize (create user under Master Realm and assign 'view-users' role
|
||||||
|
against Realm specified above)</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -75,24 +74,26 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="keycloak-admin-password" class="col-sm-2 col-form-label">Logout</label>
|
<label for="keycloak-admin-password" class="col-sm-2 col-form-label">Logout</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<label class="form-check-label">
|
<div class="form-check">
|
||||||
{{input type="checkbox" class="form-check-input" checked=keycloakConfig.disableLogout}}
|
{{input type="checkbox" class="form-check-input" id="keycloak-logout" checked=keycloakConfig.disableLogout}}
|
||||||
Hide the logout button for Keycloak users
|
<label class="form-check-label" for="keycloak-logout">
|
||||||
</label>
|
Hide the logout button for Keycloak users
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="keycloak-admin-password" class="col-sm-2 col-form-label">Space Permission</label>
|
<label for="keycloak-admin-password" class="col-sm-2 col-form-label">Space Permission</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<label class="form-check-label">
|
<div class="form-check">
|
||||||
{{input type="checkbox" class="form-check-input" checked=keycloakConfig.defaultPermissionAddSpace}}
|
{{input type="checkbox" class="form-check-input" id="keycloak-perm" checked=keycloakConfig.defaultPermissionAddSpace}}
|
||||||
Can add spaces
|
<label class="form-check-label" for="keycloak-perm">
|
||||||
</label>
|
Can add spaces
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="btn btn-success mt-4" {{action 'onSave'}}>Save</div>
|
<div class="btn btn-success mt-4" {{action 'onSave'}}>Save</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
|
@ -26,10 +26,13 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-2 col-form-label">Anonymous Access</label>
|
<label class="col-sm-2 col-form-label">Anonymous Access</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<label class="form-check-label">
|
<div class="form-check">
|
||||||
<input type="checkbox" class="form-check-input" id="allowAnonymousAccess" checked={{model.general.allowAnonymousAccess}} />
|
<input type="checkbox" class="form-check-input" id="allowAnonymousAccess" checked= {{model.general.allowAnonymousAccess}}
|
||||||
Make content marked as "Everyone" available to anonymous users
|
/>
|
||||||
</label>
|
<label class="form-check-label" for="allowAnonymousAccess">
|
||||||
|
Make content marked as "Everyone" available to anonymous users
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
|
@ -37,7 +40,8 @@
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
{{input id="conversionEndpoint" type="text" value=model.general.conversionEndpoint class=(if hasConversionEndpointInputError 'form-control is-invalid' 'form-control')}}
|
{{input id="conversionEndpoint" type="text" value=model.general.conversionEndpoint class=(if hasConversionEndpointInputError 'form-control is-invalid' 'form-control')}}
|
||||||
<small class="form-text text-muted">
|
<small class="form-text text-muted">
|
||||||
Endpoint for handling import/export (e.g. https://api.documize.com, <a href="https://docs.documize.com/s/WNEpptWJ9AABRnha/administration-guides/d/WO0pt_MXigAB6sJ7/general-options">view documentation</a>)
|
Endpoint for handling import/export (e.g. https://api.documize.com,
|
||||||
|
<a href="https://docs.documize.com/s/WNEpptWJ9AABRnha/administration-guides/d/WO0pt_MXigAB6sJ7/general-options">view documentation</a>)
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,21 +1,55 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="view-customize">
|
<div class="view-customize">
|
||||||
<h1 class="admin-heading">Product License</h1>
|
<h1 class="admin-heading">{{appMeta.edition}} Edition {{appMeta.version}}</h1>
|
||||||
<h2 class="sub-heading">Optional Enterprise Edition license</h2>
|
<h2 class="sub-heading">Enterprise Edition unlocks
|
||||||
|
<a class="" href="https://documize.com/pricing">premium capabilities and product support</a>
|
||||||
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="view-customize">
|
<div class="view-customize">
|
||||||
<form class="mt-5">
|
<form class="mt-5 ">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="smtp-host" class="col-sm-2 col-form-label">License Key</label>
|
<label for="smtp-host " class="col-sm-2 col-form-label ">Enterprise Edition License Key</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10 ">
|
||||||
{{textarea value=model.license rows="15" class=(if LicenseError 'form-control is-invalid' 'form-control')}}
|
{{textarea value=model.license rows="10" class=(if LicenseError 'form-control is-invalid' 'form-control')}}
|
||||||
<small class="form-text text-muted">XML format</small>
|
<small class="form-text text-muted ">XML format</small>
|
||||||
|
{{#if appMeta.valid}}
|
||||||
|
<p class="mt-2 color-green">Valid</p>
|
||||||
|
{{else}}
|
||||||
|
<p class="mt-2 color-red">Invalid</p>
|
||||||
|
{{/if}}
|
||||||
|
<div class="btn btn-success mt-3" {{action 'saveLicense'}}>Save</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn btn-success mt-4" {{action 'saveLicense'}}>Save</div>
|
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="product-update">
|
||||||
|
<div class="update-summary">
|
||||||
|
{{#if appMeta.updateAvailable}}
|
||||||
|
<a href="https://documize.com/downloads" class="caption">New version available</a>
|
||||||
|
<p class="instructions">
|
||||||
|
To upgrade, replace existing binary and restart Documize. Migrate between Community and Enterprise editions seamlessly.
|
||||||
|
</p>
|
||||||
|
{{else}}
|
||||||
|
<div class="caption">Release Summary</div>
|
||||||
|
{{/if}}
|
||||||
|
<p>
|
||||||
|
<span class="color-off-black">Community Edition {{appMeta.communityLatest}}</span>
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-community-windows-amd64.exe" class="font-weight-bold">Windows</a> ·
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-community-linux-amd64" class="font-weight-bold">Linux</a> ·
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-community-darwin-amd64" class="font-weight-bold">macOS</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span class="color-off-black">Enterprise Edition {{appMeta.enterpriseLatest}}</span>
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-enterprise-windows-amd64.exe" class="font-weight-bold color-blue">Windows</a> ·
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-enterprise-linux-amd64" class="font-weight-bold color-blue">Linux</a> ·
|
||||||
|
<a href="https://storage.googleapis.com/documize/downloads/documize-enterprise-darwin-amd64" class="font-weight-bold color-blue">macOS</a>
|
||||||
|
</p>
|
||||||
|
<div class="my-5" />
|
||||||
|
{{{changelog}}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-4 col-form-label">SSL</label>
|
<label class="col-sm-4 col-form-label">SSL</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
{{input id="smtp-usessl" type="checkbox" checked=model.smtp.usessl class='form-check-input'}}
|
{{input id="smtp-usessl" type="checkbox" checked=model.smtp.usessl class='form-check-input'}}
|
||||||
<label class="form-check-label" for="smtp-usessl">Use SSL</label>
|
<label class="form-check-label" for="smtp-usessl">Use SSL</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<div id="nav-bar" class="nav-bar clearfix container-fluid">
|
<div id="nav-bar" class="nav-bar clearfix container-fluid">
|
||||||
<div class="row no-gutters">
|
<div class="row no-gutters">
|
||||||
<div class="col col-sm-9">
|
<div class="col col-sm-9">
|
||||||
{{#link-to "folders" class='nav-link'}}
|
{{#link-to "folders" class='nav-link' }}
|
||||||
<div class="nav-title">{{appMeta.title}}</div>
|
<div class="nav-title">{{appMeta.title}}</div>
|
||||||
<div class="nav-msg text-truncate">{{appMeta.message}}</div>
|
<div class="nav-msg text-truncate">{{appMeta.message}}</div>
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
<div class="col col-sm-3">
|
<div class="col col-sm-3">
|
||||||
<div class="nav-right">
|
<div class="nav-right">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
{{#link-to "search" class="button-icon-white"}}
|
{{#link-to "search" class="button-icon-white" }}
|
||||||
<i class="material-icons">search</i>
|
<i class="material-icons">search</i>
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,13 +25,13 @@
|
||||||
{{#if hasSpacePins}}
|
{{#if hasSpacePins}}
|
||||||
<h6 class="dropdown-header">Spaces</h6>
|
<h6 class="dropdown-header">Spaces</h6>
|
||||||
{{#each spacePins as |pin|}}
|
{{#each spacePins as |pin|}}
|
||||||
<a class="dropdown-item" href="#" {{action 'jumpToPin' pin}} data-id={{pin.id}} id="pin-{{pin.id}}">{{pin.pin}}</a>
|
<a class="dropdown-item" href="#" {{action 'jumpToPin' pin}} data-id= {{pin.id}} id="pin-{{pin.id}}">{{pin.pin}}</a>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if hasDocumentPins}}
|
{{#if hasDocumentPins}}
|
||||||
<h6 class="dropdown-header">Documents</h6>
|
<h6 class="dropdown-header">Documents</h6>
|
||||||
{{#each documentPins as |pin|}}
|
{{#each documentPins as |pin|}}
|
||||||
<a class="dropdown-item" href="#" {{action 'jumpToPin' pin}} data-id={{pin.id}} id="pin-{{pin.id}}">{{pin.pin}}</a>
|
<a class="dropdown-item" href="#" {{action 'jumpToPin' pin}} data-id= {{pin.id}} id="pin-{{pin.id}}">{{pin.pin}}</a>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -42,22 +42,38 @@
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<div class="button-gravatar-white align-text-bottom" id="profile-button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
<div class="button-gravatar-white align-text-bottom" id="profile-button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
{{session.user.initials}}
|
{{session.user.initials}}
|
||||||
|
{{#if hasWhatsNew}}
|
||||||
|
<div class="whats-new-dot" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if session.isAdmin}}
|
||||||
|
{{#if appMeta.updateAvailable}}
|
||||||
|
<div class="update-available-dot" />
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="profile-button">
|
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="profile-button">
|
||||||
{{#link-to 'profile' class="dropdown-item"}}Profile{{/link-to}}
|
{{#link-to 'profile' class="dropdown-item" }}Profile{{/link-to}}
|
||||||
{{#if session.isAdmin}}
|
{{#if session.isAdmin}}
|
||||||
{{#link-to 'customize.general' class="dropdown-item"}}Settings{{/link-to}}
|
{{#link-to 'customize.general' class="dropdown-item" }}Settings{{/link-to}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
<div class="dropdown-divider"></div>
|
||||||
|
{{#if session.isAdmin}}
|
||||||
|
{{#if appMeta.updateAvailable}}
|
||||||
|
{{#link-to 'customize.license' class="dropdown-item font-weight-bold color-orange" }}Update available{{/link-to}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
<a href="#" class="dropdown-item {{if hasWhatsNew 'color-whats-new font-weight-bold'}}" {{action 'onShowWhatsNewModal'}}>What's New</a>
|
||||||
|
<a href="#" class="dropdown-item" data-toggle="modal" data-target="#about-documize-modal" data-backdrop="static">About Documize</a>
|
||||||
{{#if enableLogout}}
|
{{#if enableLogout}}
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
{{#link-to 'auth.logout' class="dropdown-item"}}Logout{{/link-to}}
|
{{#link-to 'auth.logout' class="dropdown-item" }}Logout{{/link-to}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="button-icon-gap" />
|
<div class="button-icon-gap" />
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
{{#link-to 'auth.login' class="button-icon-white "}}
|
{{#link-to 'auth.login' class="button-icon-white " }}
|
||||||
<i class="material-icons">lock_open</i>
|
<i class="material-icons">lock_open</i>
|
||||||
{{/link-to}}
|
{{/link-to}}
|
||||||
</div>
|
</div>
|
||||||
|
@ -66,3 +82,67 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if session.authenticated}}
|
||||||
|
<div id="whats-new-modal" class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-header modal-header-white">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true" data-dismiss="modal" aria-label="Close">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="product-news">
|
||||||
|
<h2>What's New</h2>
|
||||||
|
|
||||||
|
{{{newsContent}}}
|
||||||
|
|
||||||
|
<div class="action">
|
||||||
|
Have an idea? Suggestion or feedback? <a href="mailto:support@documize.com">Get in touch!</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="about-documize-modal" class="modal" tabindex="-1" role="dialog">
|
||||||
|
<div class="modal-dialog modal-dialog-centered" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="product-about">
|
||||||
|
<div class="edition">
|
||||||
|
Documize {{appMeta.edition}} Edition
|
||||||
|
</div>
|
||||||
|
<div class="version">
|
||||||
|
{{appMeta.version}}
|
||||||
|
</div>
|
||||||
|
<div class="dotcom">
|
||||||
|
<a href="https://documize.com">https://documize.com</a>
|
||||||
|
</div>
|
||||||
|
{{#if (eq appMeta.edition 'Community')}}
|
||||||
|
<div class="copyright">
|
||||||
|
© Documize Inc. All rights reserved.
|
||||||
|
</div>
|
||||||
|
<div class="license">
|
||||||
|
<br/>
|
||||||
|
<br/> This software (Documize Community Edition) is licensed under
|
||||||
|
<a href="http://www.gnu.org/licenses/agpl-3.0.en.html">GNU AGPL v3</a>
|
||||||
|
You can operate outside the AGPL restrictions by purchasing Documize Enterprise Edition and obtaining a commercial licenseby
|
||||||
|
contacting
|
||||||
|
<a href="mailto:sales@documize.com">sales@documize.com</a>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
14
meta.json
14
meta.json
|
@ -1,14 +0,0 @@
|
||||||
{
|
|
||||||
"community": {
|
|
||||||
"version": "1.59.0",
|
|
||||||
"major": 1,
|
|
||||||
"minor": 59,
|
|
||||||
"patch": 0
|
|
||||||
},
|
|
||||||
"enterprise": {
|
|
||||||
"version": "1.61.0",
|
|
||||||
"major": 1,
|
|
||||||
"minor": 61,
|
|
||||||
"patch": 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,20 +22,21 @@ import (
|
||||||
// User defines a login.
|
// User defines a login.
|
||||||
type User struct {
|
type User struct {
|
||||||
model.BaseEntity
|
model.BaseEntity
|
||||||
Firstname string `json:"firstname"`
|
Firstname string `json:"firstname"`
|
||||||
Lastname string `json:"lastname"`
|
Lastname string `json:"lastname"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Initials string `json:"initials"`
|
Initials string `json:"initials"`
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
Editor bool `json:"editor"`
|
Editor bool `json:"editor"`
|
||||||
Admin bool `json:"admin"`
|
Admin bool `json:"admin"`
|
||||||
ViewUsers bool `json:"viewUsers"`
|
ViewUsers bool `json:"viewUsers"`
|
||||||
Global bool `json:"global"`
|
Global bool `json:"global"`
|
||||||
Password string `json:"-"`
|
Password string `json:"-"`
|
||||||
Salt string `json:"-"`
|
Salt string `json:"-"`
|
||||||
Reset string `json:"-"`
|
Reset string `json:"-"`
|
||||||
Accounts []account.Account `json:"accounts"`
|
LastVersion string `json:"lastVersion"`
|
||||||
Groups []group.Record `json:"groups"`
|
Accounts []account.Account `json:"accounts"`
|
||||||
|
Groups []group.Record `json:"groups"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtectSecrets blanks sensitive data.
|
// ProtectSecrets blanks sensitive data.
|
||||||
|
|
|
@ -144,6 +144,7 @@ func (m *middleware) Authorize(w http.ResponseWriter, r *http.Request, next http
|
||||||
rc.AppURL = r.Host
|
rc.AppURL = r.Host
|
||||||
rc.Subdomain = organization.GetSubdomainFromHost(r)
|
rc.Subdomain = organization.GetSubdomainFromHost(r)
|
||||||
rc.SSL = r.TLS != nil
|
rc.SSL = r.TLS != nil
|
||||||
|
rc.AppVersion = fmt.Sprintf("v%s", m.Runtime.Product.Version)
|
||||||
|
|
||||||
// get user IP from request
|
// get user IP from request
|
||||||
i := strings.LastIndex(r.RemoteAddr, ":")
|
i := strings.LastIndex(r.RemoteAddr, ":")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue