Closes #83: Edit ACLs for tags in UI + batch for old DB

This commit is contained in:
jendib 2016-05-08 00:46:32 +02:00
parent b851fd0ecc
commit a55c55bbdb
No known key found for this signature in database
GPG Key ID: 06EE7F699579166F
23 changed files with 531 additions and 398 deletions

View File

@ -176,8 +176,9 @@ public class TagDao {
Map<String, Object> parameterMap = new HashMap<>();
List<String> criteriaList = new ArrayList<>();
StringBuilder sb = new StringBuilder("select distinct t.TAG_ID_C as c0, t.TAG_NAME_C as c1, t.TAG_COLOR_C as c2, t.TAG_IDPARENT_C as c3 ");
StringBuilder sb = new StringBuilder("select distinct t.TAG_ID_C as c0, t.TAG_NAME_C as c1, t.TAG_COLOR_C as c2, t.TAG_IDPARENT_C as c3, u.USE_USERNAME_C as c4 ");
sb.append(" from T_TAG t ");
sb.append(" join T_USER u on t.TAG_IDUSER_C = u.USE_ID_C ");
// Add search criterias
if (criteria.getId() != null) {
@ -223,7 +224,8 @@ public class TagDao {
.setId((String) o[i++])
.setName((String) o[i++])
.setColor((String) o[i++])
.setParentId((String) o[i]);
.setParentId((String) o[i++])
.setCreator((String) o[i]);
tagDtoList.add(tagDto);
}

View File

@ -26,6 +26,11 @@ public class TagDto {
*/
private String parentId;
/**
* Creator.
*/
private String creator;
public String getId() {
return id;
}
@ -61,4 +66,13 @@ public class TagDto {
this.parentId = parentId;
return this;
}
public String getCreator() {
return creator;
}
public TagDto setCreator(String creator) {
this.creator = creator;
return this;
}
}

View File

@ -21,29 +21,20 @@ public class AclUtil {
*
* @param json JSON
* @param sourceId Source ID
* @param principal Principal
* @param targetIdList List of target ID
*/
public static void addAcls(JsonObjectBuilder json, String sourceId, IPrincipal principal) {
public static void addAcls(JsonObjectBuilder json, String sourceId, List<String> targetIdList) {
AclDao aclDao = new AclDao();
List<AclDto> aclDtoList = aclDao.getBySourceId(sourceId);
JsonArrayBuilder aclList = Json.createArrayBuilder();
boolean writable = false;
for (AclDto aclDto : aclDtoList) {
aclList.add(Json.createObjectBuilder()
.add("perm", aclDto.getPerm().name())
.add("id", aclDto.getTargetId())
.add("name", JsonUtil.nullable(aclDto.getTargetName()))
.add("type", aclDto.getTargetType()));
if (!principal.isAnonymous()
&& (aclDto.getTargetId().equals(principal.getId())
|| principal.getGroupIdSet().contains(aclDto.getTargetId()))
&& aclDto.getPerm() == PermType.WRITE) {
// The source is writable for the current user
writable = true;
}
}
json.add("acls", aclList)
.add("writable", writable);
.add("writable", aclDao.checkPermission(sourceId, PermType.WRITE, targetIdList));
}
}

View File

@ -20,6 +20,13 @@ import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.TagDao;
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
import com.sismics.docs.core.dao.jpa.dto.AclDto;
import com.sismics.docs.core.dao.jpa.dto.TagDto;
import com.sismics.docs.core.model.jpa.Acl;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Appender;
import org.apache.log4j.Level;
@ -284,12 +291,12 @@ public class AppResource extends BaseResource {
Map<String, User> userMap = new HashMap<>();
for (File file : fileList) {
java.nio.file.Path storedFile = DirectoryUtil.getStorageDirectory().resolve(file.getId());
User user = null;
User user;
if (userMap.containsKey(file.getUserId())) {
user = userMap.get(file.getUserId());
} else {
user = userDao.getById(file.getUserId());
user.setStorageCurrent(0l);
user.setStorageCurrent(0L);
userMap.put(user.getId(), user);
}
@ -312,4 +319,49 @@ public class AppResource extends BaseResource {
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
/**
* Add base ACLs to tags.
*
* @return Response
*/
@POST
@Path("batch/tag_acls")
public Response batchTagAcls() {
if (!authenticate()) {
throw new ForbiddenClientException();
}
checkBaseFunction(BaseFunction.ADMIN);
// Get all tags
TagDao tagDao = new TagDao();
List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria(), null);
// Add READ and WRITE ACLs
for (TagDto tagDto : tagDtoList) {
AclDao aclDao = new AclDao();
List<AclDto> aclDtoList = aclDao.getBySourceId(tagDto.getId());
if (aclDtoList.size() == 0) {
// Create read ACL
Acl acl = new Acl();
acl.setPerm(PermType.READ);
acl.setSourceId(tagDto.getId());
acl.setTargetId(principal.getId());
aclDao.create(acl, principal.getId());
// Create write ACL
acl = new Acl();
acl.setPerm(PermType.WRITE);
acl.setSourceId(tagDto.getId());
acl.setTargetId(principal.getId());
aclDao.create(acl, principal.getId());
}
}
// Always return OK
JsonObjectBuilder response = Json.createObjectBuilder()
.add("status", "ok");
return Response.ok().entity(response.build()).build();
}
}

View File

@ -69,7 +69,6 @@ public class DocumentResource extends BaseResource {
authenticate();
DocumentDao documentDao = new DocumentDao();
AclDao aclDao = new AclDao();
DocumentDto documentDto = documentDao.getDocument(documentId, PermType.READ, getTargetIdList(shareId));
if (documentDto == null) {
throw new NotFoundException();
@ -90,7 +89,11 @@ public class DocumentResource extends BaseResource {
} else {
// Add tags added by the current user on this document
TagDao tagDao = new TagDao();
List<TagDto> tagDtoList = tagDao.findByCriteria(new TagCriteria().setTargetIdList(getTargetIdList(null)).setDocumentId(documentId), new SortCriteria(1, true));
List<TagDto> tagDtoList = tagDao.findByCriteria(
new TagCriteria()
.setTargetIdList(getTargetIdList(shareId))
.setDocumentId(documentId),
new SortCriteria(1, true));
JsonArrayBuilder tags = Json.createArrayBuilder();
for (TagDto tagDto : tagDtoList) {
tags.add(Json.createObjectBuilder()
@ -113,7 +116,7 @@ public class DocumentResource extends BaseResource {
document.add("creator", documentDto.getCreator());
// Add ACL
AclUtil.addAcls(document, documentId, principal);
AclUtil.addAcls(document, documentId, getTargetIdList(shareId));
// Add contributors
ContributorDao contributorDao = new ContributorDao();

View File

@ -5,7 +5,6 @@ import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.docs.core.dao.jpa.TagDao;
import com.sismics.docs.core.dao.jpa.criteria.TagCriteria;
import com.sismics.docs.core.dao.jpa.dto.AclDto;
import com.sismics.docs.core.dao.jpa.dto.TagDto;
import com.sismics.docs.core.model.jpa.Acl;
import com.sismics.docs.core.model.jpa.Tag;
@ -13,14 +12,12 @@ import com.sismics.docs.core.util.jpa.SortCriteria;
import com.sismics.rest.exception.ClientException;
import com.sismics.rest.exception.ForbiddenClientException;
import com.sismics.rest.util.AclUtil;
import com.sismics.rest.util.JsonUtil;
import com.sismics.rest.util.ValidationUtil;
import org.apache.commons.lang.StringUtils;
import javax.json.Json;
import javax.json.JsonArrayBuilder;
import javax.json.JsonObjectBuilder;
import javax.json.JsonValue;
import javax.ws.rs.*;
import javax.ws.rs.core.Response;
import java.text.MessageFormat;
@ -64,8 +61,6 @@ public class TagResource extends BaseResource {
.add("color", tagDto.getColor());
if (tagIdSet.contains(tagDto.getParentId())) {
item.add("parent", tagDto.getParentId());
} else {
item.add("parent", JsonValue.NULL);
}
items.add(item);
}
@ -98,11 +93,12 @@ public class TagResource extends BaseResource {
TagDto tagDto = tagDtoList.get(0);
JsonObjectBuilder tag = Json.createObjectBuilder()
.add("id", tagDto.getId())
.add("creator", tagDto.getCreator())
.add("name", tagDto.getName())
.add("color", tagDto.getColor());
// Add ACL
AclUtil.addAcls(tag, id, principal);
AclUtil.addAcls(tag, id, getTargetIdList(null));
return Response.ok().entity(tag.build()).build();
}

View File

@ -13,6 +13,7 @@ angular.module('docs',
* Configuring modules.
*/
.config(function($stateProvider, $httpProvider, RestangularProvider) {
// Configuring UI Router
$stateProvider
.state('main', {
@ -26,6 +27,7 @@ angular.module('docs',
})
.state('tag', {
url: '/tag',
abstract: true,
views: {
'page': {
templateUrl: 'partial/docs/tag.html',
@ -33,6 +35,23 @@ angular.module('docs',
}
}
})
.state('tag.default', {
url: '',
views: {
'tag': {
templateUrl: 'partial/docs/tag.default.html'
}
}
})
.state('tag.edit', {
url: '/:id',
views: {
'tag': {
templateUrl: 'partial/docs/tag.edit.html',
controller: 'TagEdit'
}
}
})
.state('settings', {
url: '/settings',
abstract: true,
@ -264,6 +283,7 @@ angular.module('docs',
})
.state('user', {
url: '/user',
abstract: true,
views: {
'page': {
templateUrl: 'partial/docs/usergroup.html',
@ -271,6 +291,14 @@ angular.module('docs',
}
}
})
.state('user.default', {
url: '',
views: {
'sub': {
templateUrl: 'partial/docs/usergroup.default.html'
}
}
})
.state('user.profile', {
url: '/:username',
views: {
@ -282,6 +310,7 @@ angular.module('docs',
})
.state('group', {
url: '/group',
abstract: true,
views: {
'page': {
templateUrl: 'partial/docs/usergroup.html',
@ -289,6 +318,14 @@ angular.module('docs',
}
}
})
.state('group.default', {
url: '',
views: {
'sub': {
templateUrl: 'partial/docs/usergroup.default.html'
}
}
})
.state('group.profile', {
url: '/:name',
views: {
@ -298,7 +335,6 @@ angular.module('docs',
}
}
});
// Configuring Restangular
RestangularProvider.setBaseUrl('../api');

View File

@ -3,95 +3,5 @@
/**
* Document view permissions controller.
*/
angular.module('docs').controller('DocumentViewPermissions', function ($scope, $stateParams, Restangular, $q) {
// Watch for ACLs change and group them for easy displaying
$scope.$watch('document.acls', function(acls) {
$scope.acls = _.groupBy(acls, function(acl) {
return acl.id;
});
});
// Initialize add ACL
$scope.acl = { perm: 'READ' };
/**
* Delete an ACL.
*/
$scope.deleteAcl = function(acl) {
Restangular.one('acl/' + $stateParams.id + '/' + acl.perm + '/' + acl.id, null).remove().then(function () {
$scope.document.acls = _.reject($scope.document.acls, function(s) {
return angular.equals(acl, s);
});
});
};
/**
* Add an ACL.
*/
$scope.addAcl = function() {
// Compute ACLs to add
$scope.acl.source = $stateParams.id;
var acls = [];
if ($scope.acl.perm == 'READWRITE') {
acls = [{
source: $stateParams.id,
target: $scope.acl.target.name,
perm: 'READ',
type: $scope.acl.target.type
}, {
source: $stateParams.id,
target: $scope.acl.target.name,
perm: 'WRITE',
type: $scope.acl.target.type
}];
} else {
acls = [{
source: $stateParams.id,
target: $scope.acl.target.name,
perm: $scope.acl.perm,
type: $scope.acl.target.type
}];
}
// Add ACLs
_.each(acls, function(acl) {
Restangular.one('acl').put(acl).then(function(acl) {
if (_.isUndefined(acl.id)) {
return;
}
$scope.document.acls.push(acl);
$scope.document.acls = angular.copy($scope.document.acls);
});
});
// Reset form
$scope.acl = { perm: 'READ' };
};
/**
* Auto-complete on ACL target.
*/
$scope.getTargetAclTypeahead = function($viewValue) {
var deferred = $q.defer();
Restangular.one('acl/target/search')
.get({
search: $viewValue
}).then(function(data) {
var output = [];
// Add the type to use later
output.push.apply(output, _.map(data.users, function(user) {
user.type = 'USER';
return user;
}));
output.push.apply(output, _.map(data.groups, function(group) {
group.type = 'GROUP';
return group;
}));
// Send the data to the typeahead directive
deferred.resolve(output, true);
});
return deferred.promise;
};
angular.module('docs').controller('DocumentViewPermissions', function() {
});

View File

@ -3,37 +3,14 @@
/**
* Tag controller.
*/
angular.module('docs').controller('Tag', function($scope, $dialog, Tag, Restangular) {
angular.module('docs').controller('Tag', function($scope, $dialog, Restangular) {
$scope.tag = { name: '', color: '#3a87ad' };
// Retrieve tags
Tag.tags().then(function(data) {
Restangular.one('tag/list').get().then(function(data) {
$scope.tags = data.tags;
});
// Retrieve tag stats
Restangular.one('tag/stats').get().then(function(data) {
$scope.stats = data.stats;
});
/**
* Returns total number of document from tag stats.
*/
$scope.getStatCount = function() {
return _.reduce($scope.stats, function(memo, stat) {
return memo + stat.count
}, 0);
};
/**
* Validate a tag name for duplicate.
*/
$scope.validateDuplicate = function(name) {
return !_.find($scope.tags, function(tag) {
return tag.name == name;
});
};
/**
* Add a tag.
*/
@ -71,15 +48,6 @@ angular.module('docs').controller('Tag', function($scope, $dialog, Tag, Restangu
*/
$scope.updateTag = function(tag) {
// Update the server
return Restangular.one('tag', tag.id).post('', tag).then(function () {
// Update the stat object
var stat = _.find($scope.stats, function (t) {
return tag.id == t.id;
});
if (stat) {
_.extend(stat, tag);
}
});
return Restangular.one('tag', tag.id).post('', tag);
};
});

View File

@ -0,0 +1,10 @@
'use strict';
/**
* Tag edit controller.
*/
angular.module('docs').controller('TagEdit', function($scope, $stateParams, Restangular) {
Restangular.one('tag', $stateParams.id).get().then(function(data) {
$scope.tag = data;
})
});

View File

@ -0,0 +1,112 @@
'use strict';
/**
* ACL edit directive.
*/
angular.module('docs').directive('aclEdit', function() {
return {
restrict: 'E',
templateUrl: 'partial/docs/directive.acledit.html',
replace: true,
scope: {
source: '=',
acls: '=',
writable: '=',
creator: '='
},
controller: function($scope, Restangular, $q) {
// Watch for ACLs change and group them for easy displaying
$scope.$watch('acls', function(acls) {
$scope.groupedAcls = _.groupBy(acls, function(acl) {
return acl.id;
});
});
// Initialize add ACL
$scope.acl = { perm: 'READ' };
/**
* Delete an ACL.
*/
$scope.deleteAcl = function(acl) {
Restangular.one('acl/' + $scope.source + '/' + acl.perm + '/' + acl.id, null).remove().then(function () {
$scope.acls = _.reject($scope.acls, function(s) {
return angular.equals(acl, s);
});
});
};
/**
* Add an ACL.
*/
$scope.addAcl = function() {
// Compute ACLs to add
$scope.acl.source = $scope.source;
var acls = [];
if ($scope.acl.perm == 'READWRITE') {
acls = [{
source: $scope.source,
target: $scope.acl.target.name,
perm: 'READ',
type: $scope.acl.target.type
}, {
source: $scope.source,
target: $scope.acl.target.name,
perm: 'WRITE',
type: $scope.acl.target.type
}];
} else {
acls = [{
source: $scope.source,
target: $scope.acl.target.name,
perm: $scope.acl.perm,
type: $scope.acl.target.type
}];
}
// Add ACLs
_.each(acls, function(acl) {
Restangular.one('acl').put(acl).then(function(acl) {
if (_.isUndefined(acl.id)) {
return;
}
$scope.acls.push(acl);
$scope.acls = angular.copy($scope.acls);
});
});
// Reset form
$scope.acl = { perm: 'READ' };
};
/**
* Auto-complete on ACL target.
*/
$scope.getTargetAclTypeahead = function($viewValue) {
var deferred = $q.defer();
Restangular.one('acl/target/search')
.get({
search: $viewValue
}).then(function(data) {
var output = [];
// Add the type to use later
output.push.apply(output, _.map(data.users, function(user) {
user.type = 'USER';
return user;
}));
output.push.apply(output, _.map(data.groups, function(group) {
group.type = 'GROUP';
return group;
}));
// Send the data to the typeahead directive
deferred.resolve(output, true);
});
return deferred.promise;
};
},
link: function(scope, element, attr, ctrl) {
}
}
});

View File

@ -13,9 +13,9 @@ angular.module('docs').directive('selectTag', function() {
ref: '@',
ngDisabled: '='
},
controller: function($scope, Tag) {
controller: function($scope, Restangular) {
// Retrieve tags
Tag.tags().then(function(data) {
Restangular.one('tag/list').get().then(function(data) {
$scope.allTags = data.tags;
});
@ -48,7 +48,7 @@ angular.module('docs').directive('selectTag', function() {
if ($event) {
$event.preventDefault();
}
}
};
/**
* Remove a tag.

View File

@ -1,18 +0,0 @@
'use strict';
/**
* Tag service.
*/
angular.module('docs').factory('Tag', function(Restangular) {
var tags = null;
return {
/**
* Returns tags.
* @param force If true, force reloading data
*/
tags: function(force) {
return Restangular.one('tag/list').get();
}
}
});

View File

@ -55,6 +55,7 @@
<script src="app/docs/controller/document/FileModalView.js" type="text/javascript"></script>
<script src="app/docs/controller/Login.js" type="text/javascript"></script>
<script src="app/docs/controller/tag/Tag.js" type="text/javascript"></script>
<script src="app/docs/controller/tag/TagEdit.js" type="text/javascript"></script>
<script src="app/docs/controller/Navigation.js" type="text/javascript"></script>
<script src="app/docs/controller/settings/Settings.js" type="text/javascript"></script>
<script src="app/docs/controller/settings/SettingsDefault.js" type="text/javascript"></script>
@ -73,7 +74,6 @@
<script src="app/docs/controller/usergroup/UserProfile.js" type="text/javascript"></script>
<script src="app/docs/controller/usergroup/GroupProfile.js" type="text/javascript"></script>
<script src="app/docs/service/User.js" type="text/javascript"></script>
<script src="app/docs/service/Tag.js" type="text/javascript"></script>
<script src="app/docs/filter/Newline.js" type="text/javascript"></script>
<script src="app/docs/filter/Shorten.js" type="text/javascript"></script>
<script src="app/docs/filter/Filesize.js" type="text/javascript"></script>
@ -84,6 +84,7 @@
<script src="app/docs/directive/InlineEdit.js" type="text/javascript"></script>
<script src="app/docs/directive/ImgError.js" type="text/javascript"></script>
<script src="app/docs/directive/Acl.js" type="text/javascript"></script>
<script src="app/docs/directive/AclEdit.js" type="text/javascript"></script>
<!-- endref -->
</head>
<body>

View File

@ -0,0 +1,61 @@
<div>
<table class="table">
<tr>
<th style="width: 40%">For</th>
<th style="width: 40%">Permission</th>
</tr>
<tr ng-repeat="(id, acl) in groupedAcls">
<td><acl data="acl[0]"></acl></td>
<td>
<span class="label label-default" style="margin-right: 6px;" ng-repeat="a in acl | orderBy: 'perm'">
{{ a.perm }}
<span ng-show="(creator != a.name && a.type == 'USER' || a.type != 'USER') && writable"
class="glyphicon glyphicon-remove pointer"
ng-click="deleteAcl(a)"></span>
</span>
</td>
</tr>
</table>
<div ng-show="writable">
<h4>Add a permission</h4>
<form name="aclForm" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label" for="inputTarget">For</label>
<div class="col-sm-3">
<input required ng-maxlength="50" class="form-control" type="text" id="inputTarget"
placeholder="Search a user or group" name="target" ng-model="acl.target" autocomplete="off"
typeahead="target as target.name for target in getTargetAclTypeahead($viewValue) | filter: $viewValue"
typeahead-template-url="partial/docs/directive.typeahead.acl.html"
typeahead-wait-ms="200" />
</div>
<div class="col-sm-4">
<span class="btn btn-primary" ng-if="acl.target.type" ng-click="acl.target = null">
<acl data="acl.target"></acl>
</span>
</div>
</div>
<div class="form-group">
<label class=" col-sm-2 control-label" for="inputPermission">Permission</label>
<div class="col-sm-3">
<select class="form-control" ng-model="acl.perm" id="inputPermission">
<option value="READ">Can read</option>
<option value="READWRITE">Can edit</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" ng-disabled="!acl.target.type" ng-click="addAcl()">
<span class="glyphicon glyphicon-plus"></span>
Add
</button>
</div>
</div>
</form>
</div>
</div>

View File

@ -1,59 +1,4 @@
<table class="table">
<tr>
<th style="width: 40%">For</th>
<th style="width: 40%">Permission</th>
</tr>
<tr ng-repeat="(id, acl) in acls">
<td><acl data="acl[0]"></acl></td>
<td>
<span class="label label-default" style="margin-right: 6px;" ng-repeat="a in acl | orderBy: 'perm'">
{{ a.perm }}
<span ng-show="(document.creator != a.name && a.type == 'USER' || a.type != 'USER') && document.writable"
class="glyphicon glyphicon-remove pointer"
ng-click="deleteAcl(a)"></span>
</span>
</td>
</tr>
</table>
<div ng-show="document.writable">
<h4>Add a permission</h4>
<form name="aclForm" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label" for="inputTarget">For</label>
<div class="col-sm-3">
<input required ng-maxlength="50" class="form-control" type="text" id="inputTarget"
placeholder="Search a user or group" name="target" ng-model="acl.target" autocomplete="off"
typeahead="target as target.name for target in getTargetAclTypeahead($viewValue) | filter: $viewValue"
typeahead-template-url="partial/docs/directive.typeahead.acl.html"
typeahead-wait-ms="200" />
</div>
<div class="col-sm-4">
<span class="btn btn-primary" ng-if="acl.target.type" ng-click="acl.target = null">
<acl data="acl.target"></acl>
</span>
</div>
</div>
<div class="form-group">
<label class=" col-sm-2 control-label" for="inputPermission">Permission</label>
<div class="col-sm-3">
<select class="form-control" ng-model="acl.perm" id="inputPermission">
<option value="READ">Can read</option>
<option value="READWRITE">Can edit</option>
</select>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary" ng-disabled="!acl.target.type" ng-click="addAcl()">
<span class="glyphicon glyphicon-plus"></span>
Add
</button>
</div>
</div>
</form>
</div>
<acl-edit source="document.id"
acls="document.acls"
writable="document.writable"
creator="document.creator"></acl-edit>

View File

@ -0,0 +1,6 @@
<h1>Tags</h1>
<p><strong>Tags</strong> are labels associated to documents.</p>
<p>A document can be tagged by multiple tags, and a tag can be applied to multiple documents.</p>
<p>Using the <span class="glyphicon glyphicon-user"></span> button, you can edit permissions on a tag.</p>
<p>If a tag can be read by another user or group, associated documents can also be read by those people.</p>
<p>For example, tag your company documents with a tag <span class="label label-info">MyCompany</span> and add the permission <strong>Read</strong> to a group <span class="btn btn-default">employees</span></p>

View File

@ -0,0 +1,4 @@
<acl-edit source="tag.id"
acls="tag.acls"
writable="tag.writable"
creator="tag.creator"></acl-edit>

View File

@ -5,10 +5,9 @@
<p class="input-group" ng-class="{ 'has-error': !tagForm.name.$valid }">
<span colorpicker class="input-group-addon btn btn-default" data-color="#3a87ad" ng-model="tag.color" ng-style="{ 'background': tag.color }">&nbsp;</span>
<input type="text" name="name" placeholder="New tag" class="form-control"
ng-maxlength="36" required ng-model="tag.name" ui-validate="{duplicate: 'validateDuplicate($value)', space: '!$value || $value.indexOf(\' \') == -1' }">
ng-maxlength="36" required ng-model="tag.name" ui-validate="{ space: '!$value || $value.indexOf(\' \') == -1' }">
<span class="input-group-addon btn btn-primary" ng-disabled="!tagForm.$valid" ng-click="addTag()">Add</span>
</p>
<span class="help-block" ng-show="tagForm.name.$error.duplicate">This tag already exists</span>
<span class="help-block" ng-show="tagForm.name.$error.space">Space are not allowed</span>
</form>
@ -31,22 +30,15 @@
</select>
</td>
<td class="col-xs-1"><span colorpicker class="btn" on-hide="updateTag(tag)" data-color="" ng-model="tag.color" ng-style="{ 'background': tag.color }">&nbsp;</span></td>
<td class="col-xs-1"><button class="btn btn-danger pull-right" ng-click="deleteTag(tag)"><span class="glyphicon glyphicon-trash"></span></button></td>
<td class="col-xs-1"><a href="#/tag/{{ tag.id }}" class="btn btn-default pull-right" title="Edit permissions"><span class="glyphicon glyphicon-user"></span></a></td>
<td class="col-xs-1"><button class="btn btn-danger pull-right" ng-click="deleteTag(tag)" title="Delete this tag"><span class="glyphicon glyphicon-trash"></span></button></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-8" ng-if="stats.length >= 0">
<h1>{{ tags.length }} <small>tag{{ tags.length > 1 ? 's' : '' }}</small></h1>
<dl class="dl-horizontal" ng-repeat="stat in stats | orderBy: '-count'">
<dt>{{ stat.name }} <span class="badge badge-info" ng-style="{ 'background': stat.color }">{{ stat.count }}</span></dt>
<dd><progressbar value="stat.count / getStatCount() * 100" class="progress-info"></progressbar></dd>
</dl>
</div>
<div class="col-md-8" ng-if="!stats">
<img src="img/loader.gif" />
<div class="col-md-8">
<div ui-view="tag"></div>
</div>
</div>

View File

@ -0,0 +1,2 @@
<h1>Users & Groups</h1>
<p>Here you can view informations about users and groups.</p>

View File

@ -357,12 +357,13 @@ public class TestAclResource extends BaseJerseyTest {
.param("language", "eng")));
Assert.assertEquals(Status.FORBIDDEN.getStatusCode(), response.getStatus());
// acltag2 can see document1 with tag1
// acltag2 can see document1 with tag1 (non-writable)
json = target().path("/document/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token)
.get(JsonObject.class);
tags = json.getJsonArray("tags");
Assert.assertEquals(1, tags.size());
Assert.assertFalse(json.getBoolean("writable"));
Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id"));
// acltag2 can see tag1
@ -392,6 +393,15 @@ public class TestAclResource extends BaseJerseyTest {
.param("target", "acltag2")
.param("type", "USER")), JsonObject.class);
// acltag2 can see document1 with tag1 (writable)
json = target().path("/document/" + document1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token)
.get(JsonObject.class);
tags = json.getJsonArray("tags");
Assert.assertEquals(1, tags.size());
Assert.assertTrue(json.getBoolean("writable"));
Assert.assertEquals(tag1Id, tags.getJsonObject(0).getString("id"));
// acltag2 can see and edit tag1
json = target().path("/tag/" + tag1Id).request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, acltag2Token)

View File

@ -2,11 +2,17 @@ package com.sismics.docs.rest;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Form;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import com.sismics.docs.core.constant.PermType;
import com.sismics.docs.core.dao.jpa.AclDao;
import com.sismics.util.context.ThreadLocalContext;
import com.sismics.util.jpa.EMF;
import org.junit.Assert;
import org.junit.Test;
@ -57,6 +63,35 @@ public class TestAppResource extends BaseJerseyTest {
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.post(Entity.form(new Form()));
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
// Create a tag
json = target().path("/tag").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.put(Entity.form(new Form()
.param("name", "Tag4")
.param("color", "#00ff00")), JsonObject.class);
String tagId = json.getString("id");
// Init transactional context
EntityManager em = EMF.get().createEntityManager();
ThreadLocalContext context = ThreadLocalContext.get();
context.setEntityManager(em);
EntityTransaction tx = em.getTransaction();
tx.begin();
// Remove base ACLs
AclDao aclDao = new AclDao();
aclDao.delete(tagId, PermType.READ, "admin", "admin");
aclDao.delete(tagId, PermType.WRITE, "admin", "admin");
Assert.assertEquals(0, aclDao.getBySourceId(tagId).size());
tx.commit();
// Add base ACLs to tags
response = target().path("/app/batch/tag_acls").request()
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, adminToken)
.post(Entity.form(new Form()));
Assert.assertEquals(Status.OK, Status.fromStatusCode(response.getStatus()));
Assert.assertEquals(2, aclDao.getBySourceId(tagId).size());
}
/**

View File

@ -52,6 +52,7 @@ public class TestTagResource extends BaseJerseyTest {
.cookie(TokenBasedSecurityFilter.COOKIE_NAME, tag1Token)
.get(JsonObject.class);
Assert.assertEquals("Tag4", json.getString("name"));
Assert.assertEquals("tag1", json.getString("creator"));
Assert.assertEquals("#00ff00", json.getString("color"));
Assert.assertTrue(json.getBoolean("writable"));
JsonArray acls = json.getJsonArray("acls");