mirror of
https://github.com/sismics/docs.git
synced 2024-11-22 05:57:57 +01:00
Closes #83: Edit ACLs for tags in UI + batch for old DB
This commit is contained in:
parent
b851fd0ecc
commit
a55c55bbdb
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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() {
|
||||
});
|
@ -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);
|
||||
};
|
||||
});
|
@ -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;
|
||||
})
|
||||
});
|
112
docs-web/src/main/webapp/src/app/docs/directive/AclEdit.js
Normal file
112
docs-web/src/main/webapp/src/app/docs/directive/AclEdit.js
Normal 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) {
|
||||
}
|
||||
}
|
||||
});
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
@ -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>
|
||||
|
@ -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>
|
@ -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>
|
@ -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>
|
4
docs-web/src/main/webapp/src/partial/docs/tag.edit.html
Normal file
4
docs-web/src/main/webapp/src/partial/docs/tag.edit.html
Normal file
@ -0,0 +1,4 @@
|
||||
<acl-edit source="tag.id"
|
||||
acls="tag.acls"
|
||||
writable="tag.writable"
|
||||
creator="tag.creator"></acl-edit>
|
@ -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 }"> </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 }"> </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>
|
@ -0,0 +1,2 @@
|
||||
<h1>Users & Groups</h1>
|
||||
<p>Here you can view informations about users and groups.</p>
|
@ -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)
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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");
|
||||
|
Loading…
Reference in New Issue
Block a user