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

All new space permission managament

Using ember-toggle add-on.
This commit is contained in:
sauls8t 2018-06-27 18:56:03 +01:00
parent 13deb55cbb
commit d5be8ec843
11 changed files with 231 additions and 152 deletions

View file

@ -1,89 +0,0 @@
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
//
// This software (Documize Community Edition) is licensed under
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
//
// You can operate outside the AGPL restrictions by purchasing
// Documize Enterprise Edition and obtaining a commercial license
// by contacting <sales@documize.com>.
//
// https://documize.com
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import AuthMixin from '../../mixins/auth';
import Notifier from '../../mixins/notifier';
import Component from '@ember/component';
export default Component.extend(AuthMixin, Notifier, {
spaceSvc: service('folder'),
isSpaceAdmin: computed('permissions', function() {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
}),
inviteEmail: '',
inviteMessage: '',
init() {
this._super(...arguments);
},
didReceiveAttrs() {
this._super(...arguments);
if (this.get('inviteMessage').length === 0) {
this.set('inviteMessage', this.getDefaultInvitationMessage());
}
},
getDefaultInvitationMessage() {
return "Hey there, I am sharing the " + this.get('space.name') + " space (in " + this.get("appMeta.title") + ") with you so we can both collaborate on documents.";
},
actions: {
onSpaceInvite(e) {
e.preventDefault();
var email = this.get('inviteEmail').trim().replace(/ /g, '');
var message = this.get('inviteMessage').trim();
if (message.length === 0) {
this.set('inviteMessage', this.getDefaultInvitationMessage());
message = this.getDefaultInvitationMessage();
}
if (email.length === 0) {
this.$('#space-invite-email').addClass('is-invalid').focus();
return;
}
this.showWait();
var result = {
Message: message,
Recipients: []
};
// Check for multiple email addresses
if (email.indexOf(",") > -1) {
result.Recipients = email.split(',');
}
if (email.indexOf(";") > -1 && result.Recipients.length === 0) {
result.Recipients = email.split(';');
}
// Handle just one email address
if (result.Recipients.length === 0 && email.length > 0) {
result.Recipients.push(email);
}
this.set('inviteEmail', '');
this.get('spaceSvc').share(this.get('space.id'), result).then(() => {
this.showDone();
this.$('#space-invite-email').removeClass('is-invalid');
});
},
}
});

View file

@ -14,10 +14,11 @@ import { A } from '@ember/array';
import { debounce } from '@ember/runloop';
import { computed } from '@ember/object';
import Notifier from '../../mixins/notifier';
import Modals from '../../mixins/modal';
import stringUtil from '../../utils/string';
import Component from '@ember/component';
export default Component.extend(Notifier, {
export default Component.extend(Notifier, Modals, {
groupSvc: service('group'),
spaceSvc: service('folder'),
userSvc: service('user'),
@ -27,6 +28,8 @@ export default Component.extend(Notifier, {
spacePermissions: null,
users: null,
searchText: '',
inviteEmail: '',
inviteMessage: '',
isSpaceAdmin: computed('permissions', function() {
return this.get('permissions.spaceOwner') || this.get('permissions.spaceManage');
@ -82,6 +85,10 @@ export default Component.extend(Notifier, {
});
this.set('searchText', '');
if (this.get('inviteMessage').length === 0) {
this.set('inviteMessage', this.getDefaultInvitationMessage());
}
},
permissionRecord(who, whoId, name) {
@ -118,8 +125,9 @@ export default Component.extend(Notifier, {
let spacePermissions = this.get('spacePermissions');
let filteredUsers = A([]);
this.get('userSvc').matchUsers(s).then((users) => {
this.showWait();
this.get('userSvc').matchUsers(s).then((users) => {
users.forEach((user) => {
let exists = spacePermissions.findBy('whoId', user.get('id'));
@ -129,10 +137,19 @@ export default Component.extend(Notifier, {
});
this.set('filteredUsers', filteredUsers);
this.showDone();
});
},
actions: {
onShowInviteModal() {
this.modalOpen("#space-invite-user-modal", {"show": true}, '#space-invite-email');
},
onShowAddModal() {
this.modalOpen("#space-add-user-modal", {"show": true}, '#user-search');
},
onSave() {
if (!this.get('isSpaceAdmin')) return;
@ -195,11 +212,77 @@ export default Component.extend(Notifier, {
let spacePermissions = this.get('spacePermissions');
let constants = this.get('constants');
this.showWait();
let exists = spacePermissions.findBy('whoId', user.get('id'));
if (is.undefined(exists)) {
spacePermissions.pushObject(this.permissionRecord(constants.WhoType.User, user.get('id'), user.get('fullname')));
this.set('spacePermissions', spacePermissions);
this.send('onSearch');
}
this.showDone();
},
onSpaceInvite(e) {
e.preventDefault();
var email = this.get('inviteEmail').trim().replace(/ /g, '');
var message = this.get('inviteMessage').trim();
if (message.length === 0) {
this.set('inviteMessage', this.getDefaultInvitationMessage());
message = this.getDefaultInvitationMessage();
}
if (email.length === 0) {
this.$('#space-invite-email').addClass('is-invalid').focus();
return;
}
this.showWait();
var result = {
Message: message,
Recipients: []
};
// Check for multiple email addresses
if (email.indexOf(",") > -1) {
result.Recipients = email.split(',');
}
if (email.indexOf(";") > -1 && result.Recipients.length === 0) {
result.Recipients = email.split(';');
}
// Handle just one email address
if (result.Recipients.length === 0 && email.length > 0) {
result.Recipients.push(email);
}
this.set('inviteEmail', '');
this.get('spaceSvc').share(this.get('folder.id'), result).then(() => {
this.showDone();
this.$('#space-invite-email').removeClass('is-invalid');
this.modalClose("#space-invite-user-modal");
});
},
onBulkPermission(p, state) {
p.set('spaceView', state);
p.set('spaceManage', state);
p.set('spaceOwner', state);
p.set('documentAdd', state);
p.set('documentEdit', state);
p.set('documentDelete', state);
p.set('documentMove', state);
p.set('documentCopy', state);
p.set('documentTemplate', state);
p.set('documentApprove', state);
p.set('documentLifecycle', state);
p.set('documentVersion', state);
}
}
});

View file

@ -39,7 +39,7 @@
<div id="sidebar" class="sidebar">
<ul class="tabnav-control tabnav-control-centered w-75">
<li class="tab tab-vertical {{if (eq tab 'general') 'selected'}}" {{action 'onTab' 'general'}}>General</li>
<li class="tab tab-vertical {{if (eq tab 'permissions') 'selected'}}" {{action 'onTab' 'meta'}}>Categories & Tags</li>
<li class="tab tab-vertical {{if (eq tab 'meta') 'selected'}}" {{action 'onTab' 'meta'}}>Categories & Tags</li>
</ul>
</div>
{{/layout/middle-zone-sidebar}}

View file

@ -21,10 +21,6 @@
{{folder/settings-permissions permissions=model.permissions folders=model.folders folder=model.folder onRefresh=(action 'onRefresh')}}
{{/if}}
{{#if (eq tab 'invitations')}}
{{folder/settings-invitations permissions=model.permissions space=model.folder}}
{{/if}}
{{#if (eq tab 'templates')}}
{{folder/settings-templates permissions=model.permissions space=model.folder templates=model.templates}}
{{/if}}
@ -42,8 +38,7 @@
<div id="sidebar" class="sidebar">
<ul class="tabnav-control tabnav-control-centered w-75">
<li class="tab tab-vertical {{if (eq tab 'general') 'selected'}}" {{action 'onTab' 'general'}}>Options</li>
<li class="tab tab-vertical {{if (eq tab 'permissions') 'selected'}}" {{action 'onTab' 'permissions'}}>Permissions</li>
<li class="tab tab-vertical {{if (eq tab 'invitations') 'selected'}}" {{action 'onTab' 'invitations'}}>Invite Users</li>
<li class="tab tab-vertical {{if (eq tab 'permissions') 'selected'}}" {{action 'onTab' 'permissions'}}>User Permissions</li>
<li class="tab tab-vertical {{if (eq tab 'templates') 'selected'}}" {{action 'onTab' 'templates'}}>Document Templates</li>
<li class="tab tab-vertical {{if (eq tab 'blocks') 'selected'}}" {{action 'onTab' 'blocks'}}>Content Blocks</li>
<li class="tab tab-vertical {{if (eq tab 'categories') 'selected'}}" {{action 'onTab' 'categories'}}>Categories</li>

View file

@ -22,7 +22,7 @@
<div class="d-sm-inline-block margin-left-20" />
{{#each selectedCategories as |cat|}}
<div class="document-category {{if isSpaceAdmin 'cursor-pointer'}}" data-toggle="tooltip" data-placement="top" title="Category">
<div class="document-category {{if isSpaceAdmin 'cursor-pointer'}}" data-toggle="tooltip" data-placement="top" title="Category" {{action 'onEditCategory'}}>
{{cat.category}}
</div>
{{/each}}
@ -36,7 +36,7 @@
{{/each}}
<div class="document-meta">
{{document/view-attachment document=document permissions=permissions {{action 'onEditCategory'}}}}
{{document/view-attachment document=document permissions=permissions}}
</div>
<div class="margin-top-70" />

View file

@ -1,16 +0,0 @@
<div class="content-zone">
<div class="explainer-header">Invite new users to this space</div>
<p class="explainer-text explainer-gap">Email invite leads to a smooth onboarding process</p>
<form onsubmit={{action 'onSpaceInvite'}}>
<div class="form-group">
<label for="space-invite-email">Email for space invitation</label>
{{input id="space-invite-email" type='email' class="form-control mousetrap" placeholder="Enter email" value=inviteEmail}}
<small class="form-text text-muted">Comma separate multiple email addresses</small>
</div>
<div class="form-group">
<label for="space-invite-msg">Message explaining space invitation</label>
{{textarea id="space-invite-msg" value=inviteMessage class="form-control" rows="5"}}
</div>
</form>
<button type="button" class="btn btn-success mt-3" onclick={{action 'onSpaceInvite'}}>Invite</button>
</div>

View file

@ -1,22 +1,12 @@
<div class="content-zone">
<div class="explainer-header">Space and document permissions</div>
<div class="explainer-header">Who can see this space and perform actions</div>
</div>
<div class="container-fluid my-5">
<div class="container-fluid my-3">
<div class="row justify-content-center">
<div class="col-6">
<div class="form-group">
{{focus-input id="user-search" type="text" class="form-control mousetrap" placeholder="Search for users by firstname, lastname, email" value=searchText key-up=(action 'onSearch')}}
</div>
{{#each filteredUsers as |user|}}
<div class="row my-3">
<div class="col-10">{{user.fullname}}</div>
<div class="col-2 text-right">
<button class="btn btn-primary" {{action 'onAdd' user}}>Add</button>
</div>
</div>
{{/each}}
</div>
<button type="button" class="btn btn-info font-weight-bold text-uppercase my-3" onclick={{action 'onShowAddModal'}}>Add existing users</button>
&nbsp;&nbsp;
<button type="button" class="btn btn-info font-weight-bold text-uppercase my-3" onclick={{action 'onShowInviteModal'}}>Invite new users</button>
</div>
</div>
@ -25,14 +15,14 @@
<thead>
<tr>
<th></th>
<th colspan="3">Spaces</th>
<th colspan="3" class="text-warning">Spaces</th>
<th colspan="9" class="text-info">Documents</th>
</tr>
<tr>
<th></th>
<th>View</th>
<th>Manage</th>
<th>Owner</th>
<th class="text-warning">View</th>
<th class="text-warning">Manage</th>
<th class="text-warning">Owner</th>
<th class="text-info">Create</th>
<th class="text-info">Edit</th>
<th class="text-info">Delete</th>
@ -47,7 +37,16 @@
<tbody>
{{#each spacePermissions as |permission|}}
<tr>
<td>
<td class="no-wrap no-width">
<i class="material-icons align-top text-secondary cursor-pointer" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false">
more_vert
</i>
<div class="dropdown-menu dropdown-menu-left" aria-labelledby="permission-dropdown-{{permission.whoId}}">
<a class="dropdown-item" href="#" {{action 'onBulkPermission' permission true}}>Grant all</a>
<a class="dropdown-item" href="#" {{action 'onBulkPermission' permission false}}>Revoke all</a>
</div>
{{#if (eq permission.who "role")}}
<span class="button-icon-blue button-icon-small align-middle">
<i class="material-icons">people</i>
@ -73,25 +72,25 @@
{{/if}}
{{/if}}
</td>
<td>{{input type="checkbox" id=(concat 'space-role-view-' permission.whoId) checked=permission.spaceView}}</td>
<td>{{input type="checkbox" id=(concat 'space-role-manage-' permission.whoId) checked=permission.spaceManage}}</td>
<td>{{input type="checkbox" id=(concat 'space-role-owner-' permission.whoId) checked=permission.spaceOwner}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-add-' permission.whoId) checked=permission.documentAdd}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-edit-' permission.whoId) checked=permission.documentEdit}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-delete-' permission.whoId) checked=permission.documentDelete}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-move-' permission.whoId) checked=permission.documentMove}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-copy-' permission.whoId) checked=permission.documentCopy}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-template-' permission.whoId) checked=permission.documentTemplate}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-approve-' permission.whoId) checked=permission.documentApprove}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-lifecycle-' permission.whoId) checked=permission.documentLifecycle}}</td>
<td>{{input type="checkbox" id=(concat 'doc-role-version-' permission.whoId) checked=permission.documentVersion}}</td>
<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.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>
<td>{{x-toggle value=permission.documentCopy onToggle=(action (mut permission.documentCopy))}}</td>
<td>{{x-toggle value=permission.documentTemplate onToggle=(action (mut permission.documentTemplate))}}</td>
<td>{{x-toggle value=permission.documentApprove onToggle=(action (mut permission.documentApprove))}}</td>
<td>{{x-toggle value=permission.documentLifecycle onToggle=(action (mut permission.documentLifecycle))}}</td>
<td>{{x-toggle value=permission.documentVersion onToggle=(action (mut permission.documentVersion))}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
<button type="button" class="btn btn-success my-3" onclick= {{action 'onSave'}}>Save</button>
<button type="button" class="btn btn-success font-weight-bold text-uppercase my-3" onclick={{action 'onSave'}}>SAVE</button>
<div class="row my-3">
<div class="col-12 col-md-6">
@ -121,3 +120,51 @@
</div>
</div>
</div>
<div id="space-add-user-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">Space Deletion</div>
<div class="modal-body">
{{input id="user-search" type="text" class="form-control mousetrap" placeholder="Search for users by firstname, lastname, email" value=searchText key-up=(action 'onSearch')}}
{{#each filteredUsers as |user|}}
<div class="row my-3">
<div class="col-10">{{user.fullname}}</div>
<div class="col-2 text-right">
<button class="btn btn-success" {{action 'onAdd' user}}>Add</button>
</div>
</div>
{{/each}}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="space-invite-user-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">Invite Users to Space</div>
<div class="modal-body">
<p>Email invite leads to a smooth onboarding process</p>
<form onsubmit={{action 'onSpaceInvite'}}>
<div class="form-group">
<label for="space-invite-email">Email</label>
{{input id="space-invite-email" type='email' class="form-control mousetrap" placeholder="Enter email" value=inviteEmail}}
<small class="form-text text-muted">Comma separate multiple email addresses</small>
</div>
<div class="form-group">
<label for="space-invite-msg">Message</label>
{{textarea id="space-invite-msg" value=inviteMessage class="form-control" rows="5"}}
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-success" onclick={{action 'onSpaceInvite'}}>Invite</button>
</div>
</div>
</div>
</div>

View file

@ -35,6 +35,16 @@ module.exports = function (environment) {
routeAfterAuthentication: 'folders',
routeIfAlreadyAuthenticated: 'folders'
},
'ember-toggle': {
includedThemes: ['light', 'ios', 'flip'],
excludedThemes: ['skewed'],
excludeBaseStyles: false,
defaultShowLabels: false,
defaultTheme: 'ios',
defaultSize: 'small',
defaultOffLabel: 'Off',
defaultOnLabel: 'On'
},
APP: {
}
};

View file

@ -44,6 +44,7 @@
"ember-resolver": "^4.0.0",
"ember-simple-auth": "^1.4.0",
"ember-source": "3.1.2",
"ember-toggle": "^5.2.4",
"ember-truth-helpers": "^2.0.0",
"eslint": "^4.19.1",
"eslint-plugin-ember": "^5.0.0",

View file

@ -158,6 +158,10 @@ amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
animation-frame@~0.2.4:
version "0.2.5"
resolved "https://registry.yarnpkg.com/animation-frame/-/animation-frame-0.2.5.tgz#cdf5a91a69ad2c85ddac2b82daf7290300f05960"
ansi-align@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
@ -3062,7 +3066,22 @@ ember-factory-for-polyfill@^1.3.1:
node-fetch "^2.0.0-alpha.9"
whatwg-fetch "^2.0.3"
ember-get-config@^0.2.2:
ember-gestures@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ember-gestures/-/ember-gestures-1.1.0.tgz#4e6cc10dcb42c4d149af72b8ac88e37d2da3c7c2"
dependencies:
animation-frame "~0.2.4"
broccoli-funnel "^2.0.1"
broccoli-merge-trees "^2.0.0"
broccoli-stew "^1.5.0"
ember-cli-babel "^6.8.2"
ember-cli-htmlbars "^2.0.1"
ember-cli-version-checker "^2.1.0"
ember-getowner-polyfill "^2.2.0"
hammerjs "^2.0.8"
rsvp "^3.1.0"
ember-get-config@^0.2.2, ember-get-config@^0.2.4:
version "0.2.4"
resolved "https://registry.yarnpkg.com/ember-get-config/-/ember-get-config-0.2.4.tgz#118492a2a03d73e46004ed777928942021fe1ecd"
dependencies:
@ -3077,13 +3096,24 @@ ember-getowner-polyfill@^1.1.0, ember-getowner-polyfill@^1.2.2:
ember-cli-version-checker "^1.2.0"
ember-factory-for-polyfill "^1.1.0"
ember-getowner-polyfill@^2.0.0:
ember-getowner-polyfill@^2.0.0, ember-getowner-polyfill@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ember-getowner-polyfill/-/ember-getowner-polyfill-2.2.0.tgz#38e7dccbcac69d5ec694000329ec0b2be651d2b2"
dependencies:
ember-cli-version-checker "^2.1.0"
ember-factory-for-polyfill "^1.3.1"
ember-hammertime@^1.4.1:
version "1.5.0"
resolved "https://registry.yarnpkg.com/ember-hammertime/-/ember-hammertime-1.5.0.tgz#205a9b4afd83e580b37e86b69cd79ebfa34b3bfa"
dependencies:
broccoli-funnel "^2.0.1"
broccoli-merge-trees "^2.0.0"
broccoli-stew "^1.5.0"
ember-cli-babel "^6.6.0"
ember-get-config "^0.2.4"
hammer-timejs "^1.1.0"
ember-inflector@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ember-inflector/-/ember-inflector-2.0.1.tgz#e9ac469ffa17992a43276bb1c9b8d87992b10d37"
@ -3194,6 +3224,16 @@ ember-source@3.1.2:
jquery "^3.3.1"
resolve "^1.5.0"
ember-toggle@^5.2.4:
version "5.2.4"
resolved "https://registry.yarnpkg.com/ember-toggle/-/ember-toggle-5.2.4.tgz#71e23333fe0276155e3cb4c4fcf14f703b649acc"
dependencies:
ember-cli-babel "^6.6.0"
ember-cli-htmlbars "^2.0.1"
ember-gestures "^1.1.0"
ember-hammertime "^1.4.1"
ember-runtime-enumerable-includes-polyfill "^2.0.0"
ember-truth-helpers@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ember-truth-helpers/-/ember-truth-helpers-2.0.0.tgz#f3e2eef667859197f1328bb4f83b0b35b661c1ac"
@ -4309,6 +4349,14 @@ growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
hammer-timejs@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/hammer-timejs/-/hammer-timejs-1.1.0.tgz#fc07ec1e4012228fe51b6a51218ec12661a32a42"
hammerjs@^2.0.8:
version "2.0.8"
resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1"
handlebars@^4.0.4:
version "4.0.11"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc"