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

New config setting: how many tags per document?

This commit is contained in:
Harvey Kandola 2018-07-05 12:02:10 -04:00
parent 0743ae002c
commit 19736aab04
17 changed files with 827 additions and 731 deletions

View file

@ -58,9 +58,9 @@ Space view.
## Latest version
[Community edition: v1.66.0](https://github.com/documize/community/releases)
[Community edition: v1.67.0](https://github.com/documize/community/releases)
[Enterprise edition: v1.68.0](https://documize.com/downloads)
[Enterprise edition: v1.69.0](https://documize.com/downloads)
## OS support

View file

@ -0,0 +1,6 @@
/* community edition */
-- max tags per document setting
ALTER TABLE organization ADD COLUMN `maxtags` INT NOT NULL DEFAULT 3 AFTER `authconfig`;
-- deprecations

View file

@ -54,6 +54,7 @@ func (h *Handler) Meta(w http.ResponseWriter, r *http.Request) {
data.AllowAnonymousAccess = org.AllowAnonymousAccess
data.AuthProvider = org.AuthProvider
data.AuthConfig = org.AuthConfig
data.MaxTags = org.MaxTags
data.Version = h.Runtime.Product.Version
data.Edition = h.Runtime.Product.License.Edition
data.Valid = h.Runtime.Product.License.Valid

View file

@ -36,9 +36,9 @@ func (s Scope) AddOrganization(ctx domain.RequestContext, org org.Organization)
org.Revised = time.Now().UTC()
_, err = ctx.Transaction.Exec(
"INSERT INTO organization (refid, company, title, message, url, domain, email, allowanonymousaccess, serial, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
"INSERT INTO organization (refid, company, title, message, url, domain, email, allowanonymousaccess, serial, maxtags, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
org.RefID, org.Company, org.Title, org.Message, strings.ToLower(org.URL), strings.ToLower(org.Domain),
strings.ToLower(org.Email), org.AllowAnonymousAccess, org.Serial, org.Created, org.Revised)
strings.ToLower(org.Email), org.AllowAnonymousAccess, org.Serial, org.MaxTags, org.Created, org.Revised)
if err != nil {
err = errors.Wrap(err, "unable to execute insert for org")
@ -49,7 +49,7 @@ func (s Scope) AddOrganization(ctx domain.RequestContext, org org.Organization)
// GetOrganization returns the Organization reocrod from the organization database table with the given id.
func (s Scope) GetOrganization(ctx domain.RequestContext, id string) (org org.Organization, err error) {
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE refid=?")
stmt, err := s.Runtime.Db.Preparex("SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE refid=?")
defer streamutil.Close(stmt)
if err != nil {
@ -80,14 +80,14 @@ func (s Scope) GetOrganizationByDomain(subdomain string) (o org.Organization, er
}
// match on given domain name
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE domain=? AND active=1", subdomain)
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain=? AND active=1", subdomain)
if err == nil {
return
}
err = nil
// match on empty domain as last resort
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, created, revised FROM organization WHERE domain='' AND active=1")
err = s.Runtime.Db.Get(&o, "SELECT id, refid, company, title, message, url, domain, service as conversionendpoint, email, serial, active, allowanonymousaccess, authprovider, coalesce(authconfig,JSON_UNQUOTE('{}')) as authconfig, maxtags, created, revised FROM organization WHERE domain='' AND active=1")
if err != nil && err != sql.ErrNoRows {
err = errors.Wrap(err, "unable to execute select for empty subdomain")
}
@ -99,7 +99,7 @@ func (s Scope) GetOrganizationByDomain(subdomain string) (o org.Organization, er
func (s Scope) UpdateOrganization(ctx domain.RequestContext, org org.Organization) (err error) {
org.Revised = time.Now().UTC()
_, err = ctx.Transaction.NamedExec("UPDATE organization SET title=:title, message=:message, service=:conversionendpoint, email=:email, allowanonymousaccess=:allowanonymousaccess, revised=:revised WHERE refid=:refid",
_, err = ctx.Transaction.NamedExec("UPDATE organization SET title=:title, message=:message, service=:conversionendpoint, email=:email, allowanonymousaccess=:allowanonymousaccess, maxtags=:maxtags, revised=:revised WHERE refid=:refid",
&org)
if err != nil {

View file

@ -41,7 +41,7 @@ func main() {
// product details
rt.Product = env.ProdInfo{}
rt.Product.Major = "1"
rt.Product.Minor = "66"
rt.Product.Minor = "67"
rt.Product.Patch = "0"
rt.Product.Version = fmt.Sprintf("%s.%s.%s", rt.Product.Major, rt.Product.Minor, rt.Product.Patch)
rt.Product.Edition = "Community"

File diff suppressed because one or more lines are too long

View file

@ -17,6 +17,7 @@ import Notifier from '../../mixins/notifier';
import Component from '@ember/component';
export default Component.extend(Notifier, {
maxTags: 3,
titleEmpty: empty('model.general.title'),
messageEmpty: empty('model.general.message'),
conversionEndpointEmpty: empty('model.general.conversionEndpoint'),
@ -24,7 +25,19 @@ export default Component.extend(Notifier, {
hasMessageInputError: and('messageEmpty', 'messageError'),
hasConversionEndpointInputError: and('conversionEndpointEmpty', 'conversionEndpointError'),
didReceiveAttrs() {
this._super(...arguments);
this.set('maxTags', this.get('model.general.maxTags'));
},
actions: {
change() {
const selectEl = this.$('#maxTags')[0];
const selection = selectEl.selectedOptions[0].value;
this.set('maxTags', parseInt(selection));
},
save() {
if (isEmpty(this.get('model.general.title'))) {
set(this, 'titleError', true);
@ -46,6 +59,7 @@ export default Component.extend(Notifier, {
this.set('model.general.conversionEndpoint', e.substring(0, e.length-1));
}
this.set('model.general.maxTags', this.get('maxTags'));
this.model.general.set('allowAnonymousAccess', $("#allowAnonymousAccess").prop('checked'));
this.showWait();

View file

@ -18,9 +18,11 @@ import Notifier from '../../mixins/notifier';
import Component from '@ember/component';
export default Component.extend(Notifier, {
appMeta: service(),
documentSvc: service('document'),
categoryService: service('category'),
tagz: A([]),
categories: A([]),
newCategory: '',
showCategoryModal: false,
@ -34,11 +36,6 @@ export default Component.extend(Notifier, {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}),
maxTags: 3,
tag1: '',
tag2: '',
tag3: '',
didReceiveAttrs() {
this._super(...arguments);
this.load();
@ -48,7 +45,7 @@ export default Component.extend(Notifier, {
this._super(...arguments);
schedule('afterRender', () => {
$("#add-tag-field0").focus();
$("#add-tag-field-1").focus();
$(".tag-input").off("keydown").on("keydown", function(e) {
if (e.shiftKey && e.which === 9) {
@ -59,7 +56,20 @@ export default Component.extend(Notifier, {
return false;
}
if (e.which === 9 || e.which === 13 || e.which === 16 || e.which === 45 || e.which === 189 || e.which === 8 || e.which === 127 || (e.which >= 65 && e.which <= 90) || (e.which >= 97 && e.which <= 122) || (e.which >= 48 && e.which <= 57)) {
if (e.which === 9 ||
e.which === 13 ||
e.which === 16 ||
e.which === 37 ||
e.which === 38 ||
e.which === 39 ||
e.which === 40 ||
e.which === 45 ||
e.which === 189 ||
e.which === 8 ||
e.which === 127 ||
(e.which >= 65 && e.which <= 90) ||
(e.which >= 97 && e.which <= 122) ||
(e.which >= 48 && e.which <= 57)) {
return true;
}
@ -90,17 +100,27 @@ export default Component.extend(Notifier, {
});
});
let counter = 1;
let tagz = A([]);
let maxTags = this.get('appMeta.maxTags');
if (!_.isUndefined(this.get('document.tags')) && this.get('document.tags').length > 1) {
let tags = this.get('document.tags').split('#');
let counter = 1;
_.each(tags, (tag) => {
tag = tag.trim();
if (tag.length > 0) {
this.set('tag' + counter, tag);
if (tag.length > 0 && counter <= maxTags) {
tagz.pushObject({number: counter, value: tag});
counter++;
}
});
}
for (let index = counter; index <= maxTags; index++) {
tagz.pushObject({number: index, value: ''});
}
this.set('tagz', tagz);
},
actions: {
@ -142,27 +162,26 @@ export default Component.extend(Notifier, {
});
});
let tag1 = this.get("tag1").toLowerCase().trim();
let tag2 = this.get("tag2").toLowerCase().trim();
let tag3 = this.get("tag3").toLowerCase().trim();
let tagz = this.get('tagz');
let tagzToSave = [];
_.each(tagz, (t) => {
let tag = t.value.toLowerCase().trim();
if (tag.length> 0) {
if (!_.contains(tagzToSave, tag) && is.not.startWith(tag, '-')) {
tagzToSave.push(tag);
this.$('#add-tag-field-' + t.number).removeClass('is-invalid');
} else {
this.$('#add-tag-field-' + t.number).addClass('is-invalid');
}
}
});
let save = "#";
if (is.startWith(tag1, '-')) {
$('#add-tag-field1').addClass('is-invalid');
return;
}
if (is.startWith(tag2, '-')) {
$('#add-tag-field2').addClass('is-invalid');
return;
}
if (is.startWith(tag3, '-')) {
$('#add-tag-field3').addClass('is-invalid');
return;
}
(tag1.length > 0 ) ? save += (tag1 + "#") : this.set('tag1', '');
(tag2.length > 0 && tag2 !== tag1) ? save += (tag2 + "#") : this.set('tag2', '');
(tag3.length > 0 && tag3 !== tag1 && tag3 !== tag2) ? save += (tag3 + "#") : this.set('tag3', '');
_.each(tagzToSave, (t) => {
save += t;
save += '#';
});
let doc = this.get('document');
doc.set('tags', save);

View file

@ -11,7 +11,6 @@
import Model from 'ember-data/model';
import attr from 'ember-data/attr';
// import { belongsTo, hasMany } from 'ember-data/relationships';
export default Model.extend({
title: attr('string'),
@ -19,6 +18,7 @@ export default Model.extend({
email: attr('string'),
conversionEndpoint: attr('string'),
allowAnonymousAccess: attr('boolean', { defaultValue: false }),
maxTags: attr('number', {defaultValue: 3}),
created: attr(),
revised: attr()
});

View file

@ -29,7 +29,7 @@ export default Service.extend({
message: '',
edition: 'Community',
// for major.minor semver release detection
// for bugfix releases, only admin is made aware of new release and end users see no Whats New messaging
// for bugfix releases, only admin is made aware of new release and end users see no What's New messaging
updateAvailable: false,
valid: true,
allowAnonymousAccess: false,
@ -37,6 +37,7 @@ export default Service.extend({
authConfig: null,
setupMode: false,
secureMode: false,
maxTags: 3,
invalidLicense() {
return this.valid === false;

View file

@ -34,6 +34,7 @@ export default Service.extend({
this.get('appMeta').setProperties({
message: org.get('message'),
title: org.get('title'),
maxTags: org.get('maxTags'),
conversionEndpoint: org.get('conversionEndpoint')
});

View file

@ -10,22 +10,22 @@
<div class="view-customize">
<form class="mt-5">
<div class="form-group row">
<label for="siteTitle" class="col-sm-2 col-form-label">Instance Name</label>
<div class="col-sm-10">
<label for="siteTitle" class="col-sm-4 col-form-label">Site Name</label>
<div class="col-sm-7">
{{focus-input id="siteTitle" type="text" value=model.general.title class=(if hasTitleInputError 'form-control is-invalid' 'form-control')}}
<small class="form-text text-muted">Provide short title for this Documize instance</small>
</div>
</div>
<div class="form-group row">
<label id="siteMessage" class="col-sm-2 col-form-label">Message</label>
<div class="col-sm-10">
<label id="siteMessage" class="col-sm-4 col-form-label">Site Message</label>
<div class="col-sm-7">
{{textarea id="siteMessage" rows="3" value=model.general.message class=(if hasMessageInputError 'form-control is-invalid' 'form-control')}}
<small class="form-text text-muted">Provide short message explaining this Documize instance</small>
</div>
</div>
<div class="form-group row">
<label class="col-sm-2 col-form-label">Anonymous Access</label>
<div class="col-sm-10">
<label class="col-sm-4 col-form-label">Anonymous Access</label>
<div class="col-sm-7">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="allowAnonymousAccess" checked= {{model.general.allowAnonymousAccess}}
/>
@ -36,8 +36,8 @@
</div>
</div>
<div class="form-group row">
<label for="conversionEndpoint" class="col-sm-2 col-form-label">Conversion Service URL</label>
<div class="col-sm-10">
<label for="conversionEndpoint" class="col-sm-4 col-form-label">Conversion Service URL</label>
<div class="col-sm-7">
{{input id="conversionEndpoint" type="text" value=model.general.conversionEndpoint class=(if hasConversionEndpointInputError 'form-control is-invalid' 'form-control')}}
<small class="form-text text-muted">
Endpoint for handling import/export (e.g. https://api.documize.com,
@ -45,6 +45,22 @@
</small>
</div>
</div>
<div class="btn btn-success mt-4" {{action 'save'}}>Save</div>
<div class="form-group row">
<label for="maxTags" class="col-sm-4 col-form-label">Maximum Tags Per Document</label>
<div class="col-sm-7">
<select class="form-control" id="maxTags" {{action 'change' on='change'}}>
<option selected={{is-equal 3 maxTags}} value=3>3</option>
<option selected={{is-equal 4 maxTags}} value=4>4</option>
<option selected={{is-equal 5 maxTags}} value=5>5</option>
<option selected={{is-equal 6 maxTags}} value=6>6</option>
<option selected={{is-equal 7 maxTags}} value=7>7</option>
<option selected={{is-equal 8 maxTags}} value=8>8</option>
<option selected={{is-equal 9 maxTags}} value=9>9</option>
<option selected={{is-equal 10 maxTags}} value=10>10</option>
</select>
<small class="form-text text-muted">How many tags can be assigned to a document (between 3 and 10 tags)</small>
</div>
</div>
<div class="btn btn-success font-weight-bold text-uppercase mt-4" {{action 'save'}}>Save</div>
</form>
</div>

View file

@ -2,27 +2,17 @@
<div class="explainer-header">Categories & Tags</div>
<p class="explainer-text explainer-gap">Categorize your content, assign tags to suppliment</p>
<h1>Specify up to three tags</h1>
<h1>Specify up to {{appMeta.maxTags}} tags</h1>
<p class="form-text text-muted">Lowercase, characters, numbers, hyphens only</p>
<form>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">#</span>
{{#each tagz as |tag|}}
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">#</span>
</div>
{{input type='text' id=(concat 'add-tag-field-' tag.number) class="form-control mousetrap tag-input" placeholder="Tag name" value=tag.value}}
</div>
{{input type='text' id='add-tag-field1' class="form-control mousetrap tag-input" placeholder="Tag name" value=tag1}}
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">#</span>
</div>
{{input type='text' id='add-tag-field2' class="form-control mousetrap tag-input" placeholder="Tag name" value=tag2}}
</div>
<div class="input-group mb-3">
<div class="input-group-prepend">
<span class="input-group-text">#</span>
</div>
{{input type='text' id='add-tag-field3' class="form-control mousetrap tag-input" placeholder="Tag name" value=tag3}}
</div>
{{/each}}
</form>
<div class="mt-5" />

View file

@ -75,7 +75,7 @@
<td>{{x-toggle value=permission.spaceView onToggle=(action (mut permission.spaceView))}}</td>
<td>{{x-toggle value=permission.spaceManage onToggle=(action (mut permission.spaceManage))}}</td>
<td>{{x-toggle value=permission.spaceOwner onToggle=(action (mut permission.spaceOwner))}}</td>
<td>{{x-toggle value=permission.documentAdd onToggle=(action (mut permission.spacdocumentAddView))}}</td>
<td>{{x-toggle value=permission.documentAdd onToggle=(action (mut permission.documentAdd))}}</td>
<td>{{x-toggle value=permission.documentEdit onToggle=(action (mut permission.documentEdit))}}</td>
<td>{{x-toggle value=permission.documentDelete onToggle=(action (mut permission.documentDelete))}}</td>
<td>{{x-toggle value=permission.documentMove onToggle=(action (mut permission.documentMove))}}</td>

View file

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

View file

@ -32,6 +32,7 @@ type SiteMeta struct {
AuthProvider string `json:"authProvider"`
AuthConfig string `json:"authConfig"`
Version string `json:"version"`
MaxTags int `json:"maxTags"`
Edition string `json:"edition"`
Valid bool `json:"valid"`
ConversionEndpoint string `json:"conversionEndpoint"`

View file

@ -26,6 +26,7 @@ type Organization struct {
AuthProvider string `json:"authProvider"`
AuthConfig string `json:"authConfig"`
ConversionEndpoint string `json:"conversionEndpoint"`
MaxTags int `json:"maxTags"`
Serial string `json:"-"`
Active bool `json:"-"`
}