branch develop updated (15ec2ea -> aac2c1f)
This is an automated email from the git hooks/post-receive script. New change to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git from 15ec2ea tidy poms adds 0f5aa27 add question in entities model adds 7998ad3 prepare web service to add question adds 30703a4 prepare page form for questions adds 2c4c138 Fix initialisation : account lambda expert has no password new 74d3dc1 manage users (experts, clients and supervisors) in Question creation UI with auto-search new 27fa957 can add existing documents in questions new f7060c2 add modal for document creation during question edit new d21577d from question edit, can create a document to attach it on question : request on service returns the document new 5f2069c #6016 can create new question new 58ade4f Merge branch 'feature/6016-add-question' into develop new aac2c1f Merge branch 'develop' of https://git.codelutin.com/coselmar into develop The 7 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit aac2c1ffb77d83dc70d5d40f77e6a29eb42d7220 Merge: 58ade4f 15ec2ea Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:14:09 2014 +0100 Merge branch 'develop' of https://git.codelutin.com/coselmar into develop Conflicts: coselmar-ui/src/main/webapp/css/coselmar.css coselmar-ui/src/main/webapp/index.html commit 58ade4f032622eedf21df74b4de4dc394a757ee1 Merge: 533dc4f 5f2069c Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:04:14 2014 +0100 Merge branch 'feature/6016-add-question' into develop commit 5f2069c3bfb78d3134e831a6d52072c614b005e0 Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:03:49 2014 +0100 #6016 can create new question commit d21577dcbd5610154880ab9dc637d254d62350ba Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 18:24:21 2014 +0100 from question edit, can create a document to attach it on question : request on service returns the document commit f7060c24edbf0cb3870a0d02f299dd7718d6f248 Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 17:41:19 2014 +0100 add modal for document creation during question edit commit 27fa957f3263c14ac4eb532c53e9a4b313807a67 Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 16:16:01 2014 +0100 can add existing documents in questions commit 74d3dc1a7bdb5860163e2b6b9070a8d5154a6a7f Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 3 17:49:23 2014 +0100 manage users (experts, clients and supervisors) in Question creation UI with auto-search Summary of changes: .../persistence/entity/CoselmarUserTopiaDao.java | 86 +++++++ .../persistence/entity/DocumentTopiaDao.java | 4 +- .../src/main/xmi/coselmar-model.zargo | Bin 6557 -> 9735 bytes .../fr/ifremer/coselmar/beans/QuestionBean.java | 188 +++++++++++++++ .../coselmar/converter/BeanEntityConverter.java | 16 ++ .../services/CoselmarRestApplicationListener.java | 4 +- .../services/CoselmarWebServiceSupport.java | 22 ++ .../coselmar/services/v1/DocumentsWebService.java | 16 +- .../services/v1/InitialisationService.java | 16 +- .../coselmar/services/v1/QuestionsWebService.java | 216 +++++++++++++++++ .../coselmar/services/v1/UsersWebService.java | 8 +- coselmar-rest/src/main/resources/mapping | 11 +- coselmar-ui/pom.xml | 6 + coselmar-ui/src/main/webapp/css/coselmar.css | 5 + coselmar-ui/src/main/webapp/index.html | 6 + .../src/main/webapp/js/coselmar-controllers.js | 200 +++++++++++++++- .../main/webapp/js/coselmar-questions-services.js | 41 ++++ .../src/main/webapp/js/coselmar-services.js | 1 - coselmar-ui/src/main/webapp/js/coselmar.js | 10 + .../webapp/views/documents/modalDocumentEdit.html | 198 ++++++++++++++++ .../views/documents/modalDocumentSearch.html | 45 ++++ .../main/webapp/views/questions/editquestion.html | 257 +++++++++++++++++++++ .../main/webapp/views/questions/newquestion.html | 11 + pom.xml | 10 +- 24 files changed, 1354 insertions(+), 23 deletions(-) create mode 100644 coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionBean.java create mode 100644 coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java create mode 100644 coselmar-ui/src/main/webapp/js/coselmar-questions-services.js create mode 100644 coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html create mode 100644 coselmar-ui/src/main/webapp/views/documents/modalDocumentSearch.html create mode 100644 coselmar-ui/src/main/webapp/views/questions/editquestion.html create mode 100644 coselmar-ui/src/main/webapp/views/questions/newquestion.html -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 74d3dc1a7bdb5860163e2b6b9070a8d5154a6a7f Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 3 17:49:23 2014 +0100 manage users (experts, clients and supervisors) in Question creation UI with auto-search --- .../persistence/entity/CoselmarUserTopiaDao.java | 86 ++++++++++++++++ .../coselmar/converter/BeanEntityConverter.java | 16 +++ .../coselmar/services/v1/UsersWebService.java | 8 +- coselmar-rest/src/main/resources/mapping | 2 +- coselmar-ui/pom.xml | 6 ++ coselmar-ui/src/main/webapp/index.html | 5 + .../src/main/webapp/js/coselmar-controllers.js | 55 ++++++++++- .../main/webapp/js/coselmar-questions-services.js | 10 +- .../main/webapp/views/questions/editquestion.html | 109 ++++++++++++++++++++- pom.xml | 9 ++ 10 files changed, 299 insertions(+), 7 deletions(-) diff --git a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/CoselmarUserTopiaDao.java b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/CoselmarUserTopiaDao.java index e691bec..6bcfbbc 100644 --- a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/CoselmarUserTopiaDao.java +++ b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/CoselmarUserTopiaDao.java @@ -96,4 +96,90 @@ public class CoselmarUserTopiaDao extends AbstractCoselmarUserTopiaDao<CoselmarU return coselmarUsers; } + /** + * Retrieve all user that are like the example. We search all + * {@link fr.ifremer.coselmar.persistence.entity.CoselmarUser} that have for + * each properties values like ones from Example + * + * For Example, a search with an example {firstName :'John', name: 'Doe'} + * will return users all users with a firstName containing 'John' and name + * containing 'Doe' : + * <ul> + * <li>User with firstName John and name Doe</li> + * <li>User with firstName Johnathan and Name doe</li> + * <li>User with firstName John and name Doenyon</li> + * <li>...</li> + * </ul> + * + * @param example : Prototype of User to find. We search all users that + * can have properties like ones in example. + * + * @return Users thats contains in a property at least all given keywords + * + */ + public List<CoselmarUser> findAllByExample(CoselmarUser example, boolean onlyActive) { + + StringBuilder hqlBuilder = new StringBuilder("FROM " + CoselmarUser.class.getName() + " CU"); + hqlBuilder.append(" WHERE 1=1 "); // Just because next clause will begin with operator + + Map<String, Object> args = new HashMap<>(); + + // only active ? + if (onlyActive) { + String activeCondition = DaoUtils.andAttributeEquals("CU", CoselmarUser.PROPERTY_ACTIVE, args, true); + hqlBuilder.append(activeCondition); + + } else { + String activeCondition = DaoUtils.andAttributeEquals("CU", CoselmarUser.PROPERTY_ACTIVE, args, example.isActive()); + hqlBuilder.append(activeCondition); + + } + + // Search on FirstName ? + if (example.getFirstname() != null) { + String firstNameCondition = DaoUtils.andAttributeLike("CU", CoselmarUser.PROPERTY_FIRSTNAME, args, example.getFirstname()); + hqlBuilder.append(firstNameCondition); + + } + + // Search on Name ? + if (example.getName() != null) { + String nameCondition = DaoUtils.andAttributeLike("CU", CoselmarUser.PROPERTY_NAME, args, example.getName()); + hqlBuilder.append(nameCondition); + + } + + // Search on Mail ? + if (example.getMail() != null) { + String mailCondition = DaoUtils.andAttributeLike("CU", CoselmarUser.PROPERTY_MAIL, args, example.getMail()); + hqlBuilder.append(mailCondition); + + } + + // Search on Organization ? + if (example.getOrganization() != null) { + String organizationCondition = DaoUtils.andAttributeLike("CU", CoselmarUser.PROPERTY_ORGANIZATION, args, example.getOrganization()); + hqlBuilder.append(organizationCondition); + + } + + // Search on Qualification ? + if (example.getQualification() != null) { + String qualificationCondition = DaoUtils.andAttributeLike("CU", CoselmarUser.PROPERTY_QUALIFICATION, args, example.getQualification()); + hqlBuilder.append(qualificationCondition); + + } + + // Search on Qualification ? + if (example.getRole() != null) { + String roleCondition = DaoUtils.andAttributeEquals("CU", CoselmarUser.PROPERTY_ROLE, args, example.getRole()); + hqlBuilder.append(roleCondition); + + } + + List<CoselmarUser> coselmarUsers = forHql(hqlBuilder.toString(), args).findAll(); + + return coselmarUsers; + } + } //CoselmarUserTopiaDao \ No newline at end of file diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/BeanEntityConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/BeanEntityConverter.java index fcebb4a..0eae916 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/BeanEntityConverter.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/BeanEntityConverter.java @@ -29,6 +29,8 @@ import java.util.Date; import fr.ifremer.coselmar.beans.DocumentBean; import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; +import fr.ifremer.coselmar.persistence.entity.CoselmarUserImpl; +import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.persistence.entity.Document; import org.apache.commons.lang3.StringUtils; @@ -82,4 +84,18 @@ public class BeanEntityConverter { user.isActive()); } + public static CoselmarUser fromBean(UserBean userBean) { + CoselmarUser user = new CoselmarUserImpl(); + + user.setFirstname(userBean.getFirstName()); + user.setName(userBean.getName()); + user.setMail(userBean.getMail()); + user.setRole(CoselmarUserRole.valueOf(userBean.getRole())); + user.setQualification(userBean.getQualification()); + user.setOrganization(userBean.getOrganization()); + user.setActive(userBean.isActive()); + + return user; + } + } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java index 42a7770..6007e40 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/UsersWebService.java @@ -92,12 +92,17 @@ public class UsersWebService extends CoselmarWebServiceSupport { return userBean; } - public List<UserBean> getUsers(String searchKeyword, boolean onlyActive) { + public List<UserBean> getUsers(String searchKeyword, UserBean like, boolean onlyActive) { List<CoselmarUser> userList; if (StringUtils.isNotBlank(searchKeyword)) { userList = getCoselmarUserDao().findAllLikeKeywords(Arrays.asList(searchKeyword), onlyActive); + } else if (like != null) { + CoselmarUser example = BeanEntityConverter.fromBean(like); + userList = getCoselmarUserDao().findAllByExample(example, onlyActive); + + } else { if (onlyActive) { userList = getCoselmarUserDao().forActiveEquals(true).findAll(); @@ -105,6 +110,7 @@ public class UsersWebService extends CoselmarWebServiceSupport { } else { userList = getCoselmarUserDao().findAll(); } + } List<UserBean> result = new ArrayList<>(userList.size()); diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 27b2522..3e698ff 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -36,7 +36,7 @@ DELETE /v1/documents/{documentId} DocumentsWebService.deleteDocume # Users Api -GET /v1/users UsersWebService.getUsers +GET /v1/users UsersWebService.getUsers onlyActive=true GET /v1/users/{userId} UsersWebService.getUser POST /v1/users/login UsersWebService.login POST /v1/users/{userId} UsersWebService.modifyUser diff --git a/coselmar-ui/pom.xml b/coselmar-ui/pom.xml index 3b21d3e..01c8fe1 100644 --- a/coselmar-ui/pom.xml +++ b/coselmar-ui/pom.xml @@ -79,6 +79,12 @@ <version>4.2.0-1</version> <scope>runtime</scope> </dependency> + + <dependency> + <groupId>org.webjars</groupId> + <artifactId>angular-ui-select</artifactId> + <scope>runtime</scope> + </dependency> </dependencies> <build> diff --git a/coselmar-ui/src/main/webapp/index.html b/coselmar-ui/src/main/webapp/index.html index 9a7953a..b81be28 100644 --- a/coselmar-ui/src/main/webapp/index.html +++ b/coselmar-ui/src/main/webapp/index.html @@ -35,6 +35,11 @@ <script src="nuiton-js/angular-messages.js"></script> <script src="nuiton-js/angular-ui-bootstrap.js"></script> <script src="nuiton-js/bootstrap.js"></script> + + <!--TODO ymartel 20141203 : extract version, or use wro --> + <script src="webjars/angular-ui-select/0.9.0/select.js"></script> + <link rel="stylesheet" href="webjars/angular-ui-select/0.9.0/select.css"> + <script src="js/angular-jwt.js"></script> <script src="js/coselmar.js"></script> <script src="js/coselmar-constants.js"></script> diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 39243b3..2587787 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -21,7 +21,7 @@ * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ -var coselmarControllers = angular.module('coselmarControllers', ['ui.bootstrap']); +var coselmarControllers = angular.module('coselmarControllers', ['ui.bootstrap', 'ui.select']); // Controller when the main page/view loads coselmarControllers.controller("HomeCtrl", ['$scope', '$http', '$location', 'userService', 'jwtHelper', @@ -269,7 +269,18 @@ coselmarControllers.controller("UserViewCtrl", // Controller for new question View coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$location', 'questionsService', function($scope, $route, $location, questionsService){ - $scope.question = {'privacy' : 'PUBLIC', 'themes' : []}; + $scope.question = {'privacy' : 'PUBLIC', 'themes' : [], 'experts' : [], 'clients' : [], 'externalExperts' : []}; + + $scope.users = { 'experts' : [], 'clients': [], 'supervisors' : []}; + questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, function(users) { + $scope.users.experts = users; + }); + questionsService.findUsers({'role': 'CLIENT', 'active': 'true'}, function(users) { + $scope.users.clients = users; + }); + questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true'}, function(users) { + $scope.users.supervisors = users; + }); $scope.saveQuestion = function(isValidForm){ @@ -299,3 +310,43 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio } }]); + +/** + * AngularJS default filter with the following expression: + * "person in people | filter: {name: $select.search, age: $select.search}" + * performs a AND between 'name: $select.search' and 'age: $select.search'. + * We want to perform a OR. + * + * Extract from angular-ui-select demo : + * https://github.com/angular-ui/ui-select/blob/master/examples/demo.js + */ +coselmarControllers.filter('propsFilter', function() { + return function(items, props) { + var out = []; + + if (angular.isArray(items)) { + items.forEach(function(item) { + var itemMatches = false; + + var keys = Object.keys(props); + for (var i = 0; i < keys.length; i++) { + var prop = keys[i]; + var text = props[prop].toLowerCase(); + if (item[prop] && item[prop].toString().toLowerCase().indexOf(text) !== -1) { + itemMatches = true; + break; + } + } + + if (itemMatches) { + out.push(item); + } + }); + } else { + // Let the output be the input untouched + out = items; + } + + return out; + }; +}); diff --git a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js index de81f2b..7b91279 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -8,6 +8,7 @@ function Question(resource, config){ this.resource = resource; var baseURL = config.BASE_URL + "/questions"; + var usersURL = config.BASE_URL + "/users"; this.saveQuestion = function(question, successFunction, failFunction){ @@ -29,5 +30,12 @@ function Question(resource, config){ } }); questionResource.save(null, formData, successFunction, failFunction); - } + }; + + this.findUsers = function(example, successFunction) { + var userResource = resource(usersURL, {'like': example}); + userResource.query().$promise.then(successFunction); + + }; + }; \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index d284563..ebc2702 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -82,7 +82,6 @@ <div class="form-group" ng-class="{'has-error' : question.themes.$invalid && question.themes.length < 1 && !questionForm.themes.$pristine}"> - <form name="questionThemesForm" > <label class="col-md-2 control-label">Themes *</label> @@ -98,7 +97,6 @@ <button class="btn btn-primary" value="add" ng-click="addTheme(toAddTheme); toAddTheme=''">add</button> </div> - </form> <div class="col-md-7"> <span ng-repeat="theme in question.themes" class="" aria-hidden="true"> @@ -114,6 +112,113 @@ <!-- End Line with Themes --> + <!-- Line with Experts and clients --> + + <div class="form-group row" > + + <div class=""> + + <label class="col-md-2 control-label">Experts</label> + + <div class="col-md-4"> + + <ui-select multiple ng-model="question.experts" + theme="bootstrap" reset-search-input="true" + ng-disabled="disabled" class="form-control"> + + <ui-select-match placeholder="Select expert..."> + {{$item.firstName}} {{$item.name}} ({{$item.organization}}) + </ui-select-match> + + <ui-select-choices + repeat="expert in users.experts | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + {{expert.firstName}} {{expert.name}} ({{expert.organization}}) + </ui-select-choices> + </ui-select> + + </div> + + </div> + + <div class=""> + <label class="col-md-2 control-label">Clients</label> + + <div class="col-md-4"> + + <ui-select multiple ng-model="question.clients" + theme="bootstrap" reset-search-input="true" + ng-disabled="disabled" class="form-control"> + + <ui-select-match placeholder="Select client..."> + {{$item.firstName}} {{$item.name}} ({{$item.organization}}) + </ui-select-match> + + <ui-select-choices + repeat="expert in users.clients | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + {{expert.firstName}} {{expert.name}} ({{expert.organization}}) + </ui-select-choices> + </ui-select> + + </div> + + </div> + + + </div> + + <!-- End Line with Experts --> + + + <!-- Line with External Experts and Supervisor --> + + <div class="form-group row" > + + <div class=""> + + <label class="col-md-2 control-label">External Experts</label> + + <div class="col-md-4"> + + <input type="text" ng-model="question.externalExperts" + class="form-control" name="externalExperts" + placeholder="expert1, expert2, ..." list > + </div> + + </div> + + <div class=""> + <label class="col-md-2 control-label">Supervisors</label> + + <div class="col-md-4"> + + <ui-select multiple ng-model="question.supervisors" + theme="bootstrap" reset-search-input="true" + ng-disabled="disabled" class="form-control"> + + <ui-select-match placeholder="Select supervisor..."> + {{$item.firstName}} {{$item.name}} ({{$item.organization}}) + </ui-select-match> + + <ui-select-choices + repeat="expert in users.supervisors | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + {{expert.firstName}} {{expert.name}} ({{expert.organization}}) + </ui-select-choices> + </ui-select> + + </div> + + </div> + + + </div> + + <!-- End Line with External Experts and Supervisor --> + + <!-- datalist for Experts --> + <datalist id="expertList"> + <option data-ng-repeat="expert in users.experts" value="{{expert}}"> pouet </option> + </datalist> + <div class="form-group" ng-if="questionForm.$valid && question.themes.length > 0"> <div style="padding-left: 200px"> <input type="submit" value="Validate" class="btn btn-primary" ng-click="saveQuestion(questionForm.$valid)"/> diff --git a/pom.xml b/pom.xml index 9b21f6c..106641c 100644 --- a/pom.xml +++ b/pom.xml @@ -145,6 +145,8 @@ <tomcatEmbedVersion>7.0.50</tomcatEmbedVersion> + <angularUiSelectVersion>0.9.0</angularUiSelectVersion> + </properties> @@ -341,6 +343,13 @@ <version>${slf4jVersion}</version> </dependency> + <!-- JS librairie --> + <dependency> + <groupId>org.webjars</groupId> + <artifactId>angular-ui-select</artifactId> + <version>${angularUiSelectVersion}</version> + </dependency> + <!-- Others --> <dependency> <groupId>com.github.spullara.mustache.java</groupId> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 27fa957f3263c14ac4eb532c53e9a4b313807a67 Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 16:16:01 2014 +0100 can add existing documents in questions --- coselmar-ui/src/main/webapp/index.html | 2 +- .../src/main/webapp/js/coselmar-controllers.js | 58 +++++++++++++++++++++- .../src/main/webapp/js/coselmar-services.js | 1 - .../views/documents/smallDocumentSearch.html | 42 ++++++++++++++++ .../main/webapp/views/questions/editquestion.html | 37 ++++++++++++-- 5 files changed, 132 insertions(+), 8 deletions(-) diff --git a/coselmar-ui/src/main/webapp/index.html b/coselmar-ui/src/main/webapp/index.html index b81be28..7bf3104 100644 --- a/coselmar-ui/src/main/webapp/index.html +++ b/coselmar-ui/src/main/webapp/index.html @@ -102,7 +102,7 @@ </div> <form class="navbar-form navbar-right" role="form" ng-if="currentUser"> - <div class="form-group">{{currentUser.firstName}} {{currentUser.lastName}} <a href="#/users/{{currentUser.userId}}?edit" class="glyphicon glyphicon-pencil"></a></div> + <div class="form-group">{{currentUser.firstName}} {{currentUser.lastName}} <a href="#/users/{{currentUser.userId}}?edit" class="fa fa-pencil"></a></div> <button type="submit" class="btn btn-danger" ng-click="logout()">Logout</button> </form> </nav> diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 2587787..0cb8cf2 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -267,9 +267,14 @@ coselmarControllers.controller("UserViewCtrl", ///////////////////////////////////////////////// // Controller for new question View -coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$location', 'questionsService', function($scope, $route, $location, questionsService){ +coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$location', '$modal', + 'questionsService', 'userService', 'documentService', + function($scope, $route, $location, $modal, + questionsService, userService, documentService){ - $scope.question = {'privacy' : 'PUBLIC', 'themes' : [], 'experts' : [], 'clients' : [], 'externalExperts' : []}; + $scope.question = {'privacy' : 'PUBLIC', + 'themes' : [], 'experts' : [], 'externalExperts' : [], + 'clients' : [], 'relatedDocuments': [] }; $scope.users = { 'experts' : [], 'clients': [], 'supervisors' : []}; questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, function(users) { @@ -309,8 +314,57 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio } } + $scope.searchDocuments = function () { + + var modalInstance = $modal.open({ + templateUrl: 'views/documents/smallDocumentSearch.html', + controller: 'ModalSearchDocumentsCtrl', + size: 'lg' + }); + + modalInstance.result.then(function (selectedDocument) { + var already = false; + for (var i = 0; i < $scope.question.relatedDocuments.length; i++) { + if ($scope.question.relatedDocuments[i].id == selectedDocument.id) { + already = true + } + } + if (!already) { + $scope.question.relatedDocuments.push(selectedDocument); + } + }); + }; + + $scope.removeDocument = function(document) { + var position = $scope.question.relatedDocuments.indexOf(document); + if (document && position != -1) { + $scope.question.relatedDocuments.splice(position, 1); + } + } }]); +coselmarControllers.controller('ModalSearchDocumentsCtrl', function ($scope, $modalInstance, documentService) { + + $scope.searchKeywords = []; + + documentService.getDocuments($scope); + + $scope.searchDocuments = function(searchKeywords) { + $scope.searchKeywords = searchKeywords; + documentService.getDocuments($scope); + + } + + $scope.select = function (document) { + $modalInstance.close(document); + }; + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + +}); + /** * AngularJS default filter with the following expression: * "person in people | filter: {name: $select.search, age: $select.search}" diff --git a/coselmar-ui/src/main/webapp/js/coselmar-services.js b/coselmar-ui/src/main/webapp/js/coselmar-services.js index 2d236b3..709b9e9 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-services.js @@ -80,7 +80,6 @@ function Document(resource, config){ this.getDocuments = function(scope){ // Load all documents - console.log("loading all documents"); var docResource = resource(baseURL, {searchKeywords : scope.searchKeywords}); docResource.query(function(documents){ diff --git a/coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html b/coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html new file mode 100644 index 0000000..97fe884 --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html @@ -0,0 +1,42 @@ + +<div style="padding: 0px 0px 0px 30px"> + <div class="page-header" style="margin: 0"> + <h1> + <!-- Heading goes here --> + Search Documents + </h1> + </div> + + <div> + <div> + <form class="form-inline pull-right" role="documentOptions" ng-submit="searchDocuments(searchKeywords)"> + <div class="form-group"> + <input type="search" class="form-control" placeholder="keyword1,keyword2,..." ng-model="searchKeywords" ng-list /> + </div> + <div class="form-group"> + <button type="submit" class="btn btn-default fa fa-search"></button> + </div> + </form> + </div> + <br/> + <table class="table"> + <tr> + <th>Name</th> + <th>Owner</th> + <th>Privacy</th> + <th>Keywords</th> + <th>Deposit Date</th> + <th></th> + </tr> + <tr ng-repeat="document in documents"> + <td><a href="#/documents/{{document.id}}" target="_blank">{{document.name}}</a></td> + <td>{{document.ownerName}}</td> + <td>{{document.privacy}}</td> + <td><span ng-repeat="keyword in document.keywords">{{keyword}} ,</span></td> + <td>{{document.depositDate | date:'mediumDate'}}</td> + <td> + <a class="btn fa fa-plus" title="Add Document" ng-click="select(document)"/></td> + </tr> + </table> + </div> +</div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index ebc2702..0d4ea7f 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -214,10 +214,39 @@ <!-- End Line with External Experts and Supervisor --> - <!-- datalist for Experts --> - <datalist id="expertList"> - <option data-ng-repeat="expert in users.experts" value="{{expert}}"> pouet </option> - </datalist> + + <!-- Line with Related Document --> + + <div class="form-group" > + + + <label class="col-md-2 control-label">Related Documents</label> + + <div class="col-md-10"> + <table class="table table-bordered table-condensed"> + <tr> + <th>Name</th> + <th>Owner</th> + <th>Keywords</th> + <th>Deposit Date</th> + <th> + <a class="btn fa fa-search" title="Search document" ng-click="searchDocuments()" /> + <!--<a class="btn fa fa-plus" title="Add Document" ng-click=""/>--> + </th> + </tr> + <tr ng-repeat="document in question.relatedDocuments"> + <td>{{document.name}}</a></td> + <td>{{document.ownerName}}</td> + <td><span ng-repeat="keyword in document.keywords">{{keyword}}, </span></td> + <td>{{document.depositDate | date:'mediumDate'}}</td> + <td><a class="btn fa fa-minus" title="Remove document" ng-click="removeDocument(document)"/></td> + </tr> + </table> + </div> + + </div> + + <!-- End Line with Related Document --> <div class="form-group" ng-if="questionForm.$valid && question.themes.length > 0"> <div style="padding-left: 200px"> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit f7060c24edbf0cb3870a0d02f299dd7718d6f248 Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 17:41:19 2014 +0100 add modal for document creation during question edit --- .../src/main/webapp/js/coselmar-controllers.js | 51 ++++- .../webapp/views/documents/modalDocumentEdit.html | 205 +++++++++++++++++++++ ...ocumentSearch.html => modalDocumentSearch.html} | 5 +- .../main/webapp/views/questions/editquestion.html | 2 +- 4 files changed, 260 insertions(+), 3 deletions(-) diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 0cb8cf2..c103428 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -317,7 +317,7 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio $scope.searchDocuments = function () { var modalInstance = $modal.open({ - templateUrl: 'views/documents/smallDocumentSearch.html', + templateUrl: 'views/documents/modalDocumentSearch.html', controller: 'ModalSearchDocumentsCtrl', size: 'lg' }); @@ -335,12 +335,34 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio }); }; + $scope.createDocument = function () { + + var modalInstance = $modal.open({ + templateUrl: 'views/documents/modalDocumentEdit.html', + controller: 'ModalCreateDocumentsCtrl', + size: 'lg' + }); + + modalInstance.result.then(function (selectedDocument) { + var already = false; + for (var i = 0; i < $scope.question.relatedDocuments.length; i++) { + if ($scope.question.relatedDocuments[i].id == selectedDocument.id) { + already = true + } + } + if (!already) { + $scope.question.relatedDocuments.push(selectedDocument); + } + }); + }; + $scope.removeDocument = function(document) { var position = $scope.question.relatedDocuments.indexOf(document); if (document && position != -1) { $scope.question.relatedDocuments.splice(position, 1); } } + }]); coselmarControllers.controller('ModalSearchDocumentsCtrl', function ($scope, $modalInstance, documentService) { @@ -365,6 +387,33 @@ coselmarControllers.controller('ModalSearchDocumentsCtrl', function ($scope, $mo }); +coselmarControllers.controller('ModalCreateDocumentsCtrl', function ($scope, $modalInstance, documentService) { + + $scope.document = {'privacy': 'PUBLIC'}; + $scope.upload = {}; + + $scope.existingKeywords = []; + + $scope.create = function (isValidForm) { + if (isValidForm) { + documentService.createDocument( + $scope.document, $scope.upload.file, function(document) { + $modalInstance.close(document); + + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); + } + }; + + $scope.cancel = function () { + $modalInstance.dismiss('cancel'); + }; + +}); + /** * AngularJS default filter with the following expression: * "person in people | filter: {name: $select.search, age: $select.search}" diff --git a/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html new file mode 100644 index 0000000..ba57ce6 --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html @@ -0,0 +1,205 @@ +<div xmlns="http://www.w3.org/1999/html"> + <div class="modal-title"> + <h2>New Document</h2> + Field with <strong><big>*</big></strong> are mandatory. + </div> + + <form class="form-horizontal" name="documentForm" role="form" + ng-submit="createNewDocument(documentForm.$valid)"> + + <div class="modal-body"> + + + <!-- Line with Name and Type --> + <div class="form-group row"> + + <div class="" + ng-class="{'has-error' : documentForm.name.$invalid && !documentForm.name.$pristine}"> + <label class="col-md-2 control-label">Name *</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="name" + ng-model="document.name" required/> + + <p ng-show="documentForm.name.$invalid && !documentForm.name.$pristine" + class="help-block">Document name is required.</p> + </div> + </div> + + <div class="" + ng-class="{'has-error' : documentForm.type.$invalid && !documentForm.type.$pristine}"> + <label class="col-md-2 control-label">Type *</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="type" + ng-model="document.type" required/> + + <p ng-show="documentForm.type.$invalid && !documentForm.type.$pristine" + class="help-block">Document type is required.</p> + </div> + </div> + </div> + <!-- End Line with name and Type --> + + <!-- Line with file and externalURL --> + <div class="form-group" + ng-class="{'has-error' : + !documentForm.externalUrl.$invalid && !documentForm.externalUrl.$pristine && + !upload.file}"> + <div class=""> + <label class="col-md-2 control-label">File</label> + + <div class="col-md-4"> + <input type="file" class="form-control" name="uploadFile" + ng-file-model="upload.file"/> + </div> + </div> + + <div class=""> + <label class="col-md-2 control-label">External URL</label> + + <div class="col-md-4"> + <input type="input" class="form-control" name="externalUrl" + ng-model="document.externalUrl" ng-minlength="6"/> + + </div> + </div> + <p ng-show="!document.externalUrl && !file.upload" + class="help-block text-center">A File or an external URL is + required.</p> + </div> + <!-- End Line with file and externalURL --> + + + <!-- Line with keywords and privacy --> + <div class="form-group"> + <div + ng-class="{'has-error' : documentForm.keywords.$invalid && !documentForm.keywords.$pristine}"> + <label class="col-md-2 control-label">keywords *</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="keywords" + ng-model="document.keywords" ng-list required + placeholder="keyword 1, keyword2"/> + + <p ng-show="documentForm.keywords.$invalid && !documentForm.keywords.$pristine" + class="help-block">At least one keyword is required.</p> + </div> + </div> + + <div class=""> + <label class="col-md-2 control-label">Privacy</label> + + <div class="col-md-4"> + <select class="form-control" name="privacy" + ng-model="document.privacy"> + <option value="PRIVATE">Private</option> + <option value="PUBLIC">Public</option> + <option value="RESTRICTED">Restricted to current Question</option> + </select> + + </div> + + </div> + </div> + <!-- End Line with keywords and privacy --> + + <!-- Line with publication Date, Authors and Language --> + <div class="form-group"> + <div class=""> + <label class="col-md-2 control-label">Publication date</label> + + <div class="col-md-2"> + <div class="input-group"> + <input type="text" class="form-control" name="publicationDate" + ng-model="document.publicationDate" + datepicker-popup="dd/MM/yyyy" + is-open="publicationDateOpened" + ng-click="publicationDateOpened = true"/> + <span class="input-group-addon"><span class="fa fa-calendar" + aria-hidden="true"></span></span> + </div> + </div> + </div> + + <div class="" + ng-class="{'has-error' : documentForm.authors.$invalid && !documentForm.authors.$pristine}"> + <label class="col-md-1 control-label">Authors</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="authors" + ng-model="document.authors" required/> + + <p ng-show="documentForm.authors.$invalid && !documentForm.authors.$pristine" + class="help-block">Document authors is required.</p> + </div> + </div> + + <div class=""> + <label class="col-md-1 control-label">Language</label> + + <div class="col-md-2"> + <input type="text" class="form-control" name="language" + ng-model="document.language"/> + </div> + </div> + </div> + <!-- End Line with publication Date, Authors and Language --> + + <!-- Line with Copyright and Licence --> + <div class="form-group"> + <div + ng-class="{'has-error' : documentForm.copyright.$invalid && !documentForm.copyright.$pristine}"> + <label class="col-md-2 control-label">Copyright *</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="copyright" + ng-model="document.copyright" required/> + + <p ng-show="documentForm.copyright.$invalid && !documentForm.copyright.$pristine" + class="help-block">Copyright is required.</p> + </div> + </div> + + <div class="form-group"> + <label class="col-md-2 control-label">Licence</label> + + <div class="col-md-4"> + <input type="text" class="form-control" name="licence" + ng-model="document.licence"/> + </div> + </div> + </div> + <!-- End Line with Copyright and Licence --> + + <!-- End Line with Summary --> + <div class="form-group" + ng-class="{'has-error' : documentForm.summary.$invalid && !documentForm.summary.$pristine}"> + <label class="col-md-2 control-label">Summary</label> + + <div class="col-md-10"> + <textarea type="text" class="form-control" name="summary" rows="5" + ng-model="document.summary" required/> + + <p ng-show="documentForm.summary.$invalid && !documentForm.summary.$pristine" + class="help-block">A summary is required.</p> + </div> + </div> + + <div class="form-group"> + <div style="float-right col-md-4"> + <input type="submit" value="Submit" class="btn btn-primary" + ng-if="documentForm.$valid && privacy != 'RESTRICTED'"/> + </div> + </div> + + </div> + + <div class="modal-footer"> + <input type="submit" class="btn btn-primary" ng-click="create()"/> + <button class="btn btn-warning" ng-click="cancel()">Cancel</button> + </div> + + </form> + +</div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html b/coselmar-ui/src/main/webapp/views/documents/modalDocumentSearch.html similarity index 89% rename from coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html rename to coselmar-ui/src/main/webapp/views/documents/modalDocumentSearch.html index 97fe884..009d325 100644 --- a/coselmar-ui/src/main/webapp/views/documents/smallDocumentSearch.html +++ b/coselmar-ui/src/main/webapp/views/documents/modalDocumentSearch.html @@ -1,6 +1,6 @@ <div style="padding: 0px 0px 0px 30px"> - <div class="page-header" style="margin: 0"> + <div class="page-header"> <h1> <!-- Heading goes here --> Search Documents @@ -39,4 +39,7 @@ </tr> </table> </div> + <div class="modal-footer"> + <button class="btn btn-warning" ng-click="cancel()">Cancel</button> + </div> </div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index 0d4ea7f..9dc0507 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -231,7 +231,7 @@ <th>Deposit Date</th> <th> <a class="btn fa fa-search" title="Search document" ng-click="searchDocuments()" /> - <!--<a class="btn fa fa-plus" title="Add Document" ng-click=""/>--> + <a class="btn fa fa-plus" title="Add new Document" ng-click="createDocument()"/> </th> </tr> <tr ng-repeat="document in question.relatedDocuments"> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit d21577dcbd5610154880ab9dc637d254d62350ba Author: Yannick Martel <martel@©odelutin.com> Date: Thu Dec 4 18:24:21 2014 +0100 from question edit, can create a document to attach it on question : request on service returns the document --- .../src/main/xmi/coselmar-model.zargo | Bin 9632 -> 9660 bytes .../coselmar/services/v1/DocumentsWebService.java | 8 ++++++-- .../src/main/webapp/js/coselmar-controllers.js | 3 +++ .../webapp/views/documents/modalDocumentEdit.html | 17 +++++------------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/coselmar-persistence/src/main/xmi/coselmar-model.zargo b/coselmar-persistence/src/main/xmi/coselmar-model.zargo index bfdc485..9dd868f 100644 Binary files a/coselmar-persistence/src/main/xmi/coselmar-model.zargo and b/coselmar-persistence/src/main/xmi/coselmar-model.zargo differ diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java index dbb5182..f3940ab 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java @@ -64,7 +64,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { private static final Log log = getLog(DocumentsWebService.class); public static final List<String> DOCUMENT_EDIT_ALLOWED_USER_ROLES = - Lists.newArrayList(CoselmarUserRole.EXPERT.name()); + Lists.newArrayList(CoselmarUserRole.EXPERT.name(), CoselmarUserRole.SUPERVISOR.name()); public static final List<String> DOCUMENT_VIEW_ALLOWED_USER_ROLES = Lists.newArrayList( @@ -136,7 +136,7 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { return result; } - public void addDocument(DocumentBean document, UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { + public DocumentBean addDocument(DocumentBean document, UploadFile uploadFile) throws InvalidCredentialException, UnauthorizedException { // Check authentication String authorization = getContext().getHeader("Authorization"); @@ -217,6 +217,10 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { } commit(); + String lightId = getPersistenceContext().getTopiaIdFactory().getRandomPart(documentEntity.getTopiaId()); + DocumentBean result = BeanEntityConverter.toBean(lightId, documentEntity); + return result; + } public Render getDocumentFile(String documentId) { diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index c103428..e19dc55 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -396,6 +396,9 @@ coselmarControllers.controller('ModalCreateDocumentsCtrl', function ($scope, $mo $scope.create = function (isValidForm) { if (isValidForm) { + if ($scope.upload.file) { + $scope.document.withFile = true; + } documentService.createDocument( $scope.document, $scope.upload.file, function(document) { $modalInstance.close(document); diff --git a/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html index ba57ce6..2d115ac 100644 --- a/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html +++ b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html @@ -5,7 +5,7 @@ </div> <form class="form-horizontal" name="documentForm" role="form" - ng-submit="createNewDocument(documentForm.$valid)"> + ng-submit="create(documentForm.$valid)"> <div class="modal-body"> @@ -44,7 +44,7 @@ <!-- Line with file and externalURL --> <div class="form-group" ng-class="{'has-error' : - !documentForm.externalUrl.$invalid && !documentForm.externalUrl.$pristine && + documentForm.externalUrl.$invalid && !documentForm.externalUrl.$pristine && !upload.file}"> <div class=""> <label class="col-md-2 control-label">File</label> @@ -124,7 +124,7 @@ <div class="" ng-class="{'has-error' : documentForm.authors.$invalid && !documentForm.authors.$pristine}"> - <label class="col-md-1 control-label">Authors</label> + <label class="col-md-1 control-label">Authors *</label> <div class="col-md-4"> <input type="text" class="form-control" name="authors" @@ -175,7 +175,7 @@ <!-- End Line with Summary --> <div class="form-group" ng-class="{'has-error' : documentForm.summary.$invalid && !documentForm.summary.$pristine}"> - <label class="col-md-2 control-label">Summary</label> + <label class="col-md-2 control-label">Summary *</label> <div class="col-md-10"> <textarea type="text" class="form-control" name="summary" rows="5" @@ -186,17 +186,10 @@ </div> </div> - <div class="form-group"> - <div style="float-right col-md-4"> - <input type="submit" value="Submit" class="btn btn-primary" - ng-if="documentForm.$valid && privacy != 'RESTRICTED'"/> - </div> - </div> - </div> <div class="modal-footer"> - <input type="submit" class="btn btn-primary" ng-click="create()"/> + <input type="submit" class="btn btn-primary" /> <button class="btn btn-warning" ng-click="cancel()">Cancel</button> </div> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 5f2069c3bfb78d3134e831a6d52072c614b005e0 Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:03:49 2014 +0100 #6016 can create new question --- .../src/main/xmi/coselmar-model.zargo | Bin 9660 -> 9735 bytes .../services/CoselmarRestApplicationListener.java | 4 +- .../services/CoselmarWebServiceSupport.java | 12 +++ .../coselmar/services/v1/QuestionsWebService.java | 104 ++++++++++++++------- coselmar-rest/src/main/resources/mapping | 9 ++ 5 files changed, 95 insertions(+), 34 deletions(-) diff --git a/coselmar-persistence/src/main/xmi/coselmar-model.zargo b/coselmar-persistence/src/main/xmi/coselmar-model.zargo index 9dd868f..830802b 100644 Binary files a/coselmar-persistence/src/main/xmi/coselmar-model.zargo and b/coselmar-persistence/src/main/xmi/coselmar-model.zargo differ diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java index e16b7a3..94799bc 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarRestApplicationListener.java @@ -29,6 +29,7 @@ import java.util.Set; import com.google.common.collect.Sets; import fr.ifremer.coselmar.beans.DocumentBean; +import fr.ifremer.coselmar.beans.QuestionBean; import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.converter.DateConverter; import fr.ifremer.coselmar.converter.JsonArrayConverter; @@ -46,7 +47,8 @@ public class CoselmarRestApplicationListener implements WebMotionServerListener protected static final Set<Class<?>> BEAN_TYPES = Sets.<Class<?>>newHashSet( DocumentBean.class, - UserBean.class + UserBean.class, + QuestionBean.class ); @Override diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java index 17e5af2..120e10e 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/CoselmarWebServiceSupport.java @@ -170,4 +170,16 @@ public abstract class CoselmarWebServiceSupport extends WebMotionController impl protected String getFullUserIdFromShort(String shortUserId) { return CoselmarUser.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortUserId; } + + /** + * Construct the full id of an entity from the random part contained in a Bean. + * + * @param clazz : the entity class, we want full id (use to compose the id) + * @param shortId : the short id, corresponding to the random part of the id + * + * @return the complete id, with class FQN. + */ + protected String getFullIdFromShort(Class clazz, String shortId) { + return clazz.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortId; + } } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java index 2d1791c..19fa8ef 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/QuestionsWebService.java @@ -2,17 +2,20 @@ package fr.ifremer.coselmar.services.v1; import java.util.Collection; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Set; import com.google.common.base.Function; import com.google.common.collect.Collections2; +import fr.ifremer.coselmar.beans.DocumentBean; import fr.ifremer.coselmar.beans.QuestionBean; import fr.ifremer.coselmar.beans.UserBean; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserGroup; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; +import fr.ifremer.coselmar.persistence.entity.Document; import fr.ifremer.coselmar.persistence.entity.Privacy; import fr.ifremer.coselmar.persistence.entity.Question; import fr.ifremer.coselmar.persistence.entity.Status; @@ -32,16 +35,16 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { private static final Log log = LogFactory.getLog(QuestionsWebService.class); - public void addQuestion(QuestionBean questionBean) throws InvalidCredentialException, UnauthorizedException { + public void addQuestion(QuestionBean question) throws InvalidCredentialException, UnauthorizedException { // Check authentication String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Only Expert or Supervisor can add document + // Only Supervisor can add question String userRole = userWebToken.getRole(); - if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { + if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole)) { String message = String.format("User %s %s ('%s') is not allowed to add question", userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); if (log.isWarnEnabled()) { @@ -67,58 +70,68 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } // let's go - Question question = getQuestionDao().create(); + Question questionEntity = getQuestionDao().create(); - question.setTitle(questionBean.getTitle()); + // Question basics - question.setSummary(questionBean.getSummary()); + questionEntity.setTitle(question.getTitle()); - question.setType(questionBean.getType()); + questionEntity.setSummary(question.getSummary()); + + questionEntity.setType(question.getType()); // By default, privacy is private - String privacy = questionBean.getPrivacy(); + String privacy = question.getPrivacy(); Privacy realPrivacy = privacy != null ? Privacy.valueOf(privacy.toUpperCase()) : Privacy.PRIVATE; - question.setPrivacy(realPrivacy); + questionEntity.setPrivacy(realPrivacy); // On creation, Status is Open - question.setStatus(Status.OPEN); + questionEntity.setStatus(Status.OPEN); // Manage Dates : submission & deadline - Date submissionDate = questionBean.getSubmissionDate(); + Date submissionDate = question.getSubmissionDate(); if (submissionDate != null) { - question.setSubmissionDate(new Date(submissionDate.getTime())); + questionEntity.setSubmissionDate(new Date(submissionDate.getTime())); } - Date deadline = questionBean.getDeadline(); + Date deadline = question.getDeadline(); if (deadline != null) { - question.setDeadline(new Date(deadline.getTime())); + questionEntity.setDeadline(new Date(deadline.getTime())); } // Users around the question // First Supervisor is the one creating this question - question.addSupervisors(supervisor); + questionEntity.addSupervisors(supervisor); + + questionEntity.addAllExternalExperts(question.getExternalExperts()); - question.addAllExternalExperts(questionBean.getExternalExperts()); // Retrieve the clients - Set<UserBean> clients = questionBean.getClients(); + Set<UserBean> clients = question.getClients(); if (clients != null && !clients.isEmpty()) { - List<CoselmarUser> clientEntities = retrieveUsers(clients); - question.addAllClients(clientEntities); + Set<CoselmarUser> clientEntities = retrieveUsers(clients); + questionEntity.addAllClients(clientEntities); } // For participants, should create a dedicated group, // that could be used to restrict documents access - Set<UserBean> participants = questionBean.getParticipants(); + Set<UserBean> participants = question.getParticipants(); + CoselmarUserGroup participantGroup = getCoselmarUserGroupDao().create(); if (participants != null && !participants.isEmpty()) { - CoselmarUserGroup userGroup = getCoselmarUserGroupDao().create(); - userGroup.setName(questionBean.getTitle()); + participantGroup.setName(question.getTitle()); - List<CoselmarUser> expertEntities = retrieveUsers(participants); - userGroup.addAllMembers(expertEntities); + Set<CoselmarUser> expertEntities = retrieveUsers(participants); + participantGroup.addAllMembers(expertEntities); + + } + questionEntity.setParticipants(participantGroup); - question.setParticipants(userGroup); + // Retrieve the supervisor + Set<UserBean> supervisors = question.getSupervisors(); + if (supervisors != null && !supervisors.isEmpty()) { + Set<CoselmarUser> supervisorEntities = retrieveUsers(supervisors); + questionEntity.addAllSupervisors(supervisorEntities); } // Note : no contributors now, contributors are old participants, @@ -126,15 +139,26 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Hierarchy of questions - Set<QuestionBean> parents = questionBean.getParents(); + Set<QuestionBean> parents = question.getParents(); if (parents != null && !parents.isEmpty()) { List<Question> questions = retrieveQuestions(parents); - question.addAllParents(questions); + questionEntity.addAllParents(questions); } -// // Documents on init -// question.addAllRelatedDocuments(); + // Documents on init + Set<DocumentBean> relatedDocuments = question.getRelatedDocuments(); + if (relatedDocuments != null && !relatedDocuments.isEmpty()) { + List<Document> documents = retrieveDocuments(relatedDocuments); + // Manage restriction list for document with Privacy.RESTRICTED + for (Document document : documents) { + if (document.getPrivacy() == Privacy.RESTRICTED) { + document.addRestrictedList(participantGroup); + } + } + + questionEntity.addAllRelatedDocuments(documents); + } } @@ -144,17 +168,17 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { //////////////////////////////////////////////////////////////////////////// - protected List<CoselmarUser> retrieveUsers(Collection<UserBean> userBeans) { + protected Set<CoselmarUser> retrieveUsers(Collection<UserBean> userBeans) { Function<UserBean, String> getIds = new Function<UserBean, String>() { @Override public String apply(UserBean userBean) { - return getFullUserIdFromShort(userBean.getId()); + return getFullIdFromShort(CoselmarUser.class, userBean.getId()); } }; Collection<String> userIds = Collections2.transform(userBeans, getIds); List<CoselmarUser> coselmarUsers = getCoselmarUserDao().forTopiaIdIn(userIds).findAll(); - return coselmarUsers; + return new HashSet<>(coselmarUsers); } @@ -162,7 +186,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Function<QuestionBean, String> getIds = new Function<QuestionBean, String>() { @Override public String apply(QuestionBean questionBean) { - return getFullUserIdFromShort(questionBean.getId()); + return getFullIdFromShort(Question.class, questionBean.getId()); } }; @@ -172,6 +196,20 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } + protected List<Document> retrieveDocuments(Collection<DocumentBean> documentBeans) { + Function<DocumentBean, String> getIds = new Function<DocumentBean, String>() { + @Override + public String apply(DocumentBean documentBean) { + return getFullIdFromShort(Document.class, documentBean.getId()); + } + }; + + Collection<String> documentIds = Collections2.transform(documentBeans, getIds); + List<Document> documents = getDocumentDao().forTopiaIdIn(documentIds).findAll(); + return documents; + + } + protected String getFullQuestionIdFromShort(String shortQuestionId) { return Question.class.getCanonicalName() + getPersistenceContext().getTopiaIdFactory().getSeparator() + shortQuestionId; } diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 3e698ff..8e557c1 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -43,3 +43,12 @@ POST /v1/users/{userId} UsersWebService.modifyUser POST /v1/users UsersWebService.addUser DELETE /v1/users/{userId} UsersWebService.deleteUser +# Questions Api + +#GET /v1/questions QuestionsWebService.getQuestions +#GET /v1/questions/{questionId} QuestionsWebService.getQuestion +#POST /v1/questions/{questionId} QuestionsWebService.saveQuestion +#POST /v1/questions/{questionId}/documents QuestionsWebService.addDocuments +POST /v1/questions QuestionsWebService.addQuestion +#DELETE /v1/questions/{questionId} QuestionsWebService.closeQuestion + -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit 58ade4f032622eedf21df74b4de4dc394a757ee1 Merge: 533dc4f 5f2069c Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:04:14 2014 +0100 Merge branch 'feature/6016-add-question' into develop .../persistence/entity/CoselmarUserTopiaDao.java | 86 +++++++ .../persistence/entity/DocumentTopiaDao.java | 4 +- .../src/main/xmi/coselmar-model.zargo | Bin 6557 -> 9735 bytes .../fr/ifremer/coselmar/beans/QuestionBean.java | 188 +++++++++++++++ .../coselmar/converter/BeanEntityConverter.java | 16 ++ .../services/CoselmarRestApplicationListener.java | 4 +- .../services/CoselmarWebServiceSupport.java | 22 ++ .../coselmar/services/v1/DocumentsWebService.java | 16 +- .../services/v1/InitialisationService.java | 16 +- .../coselmar/services/v1/QuestionsWebService.java | 216 +++++++++++++++++ .../coselmar/services/v1/UsersWebService.java | 8 +- coselmar-rest/src/main/resources/mapping | 11 +- coselmar-ui/pom.xml | 6 + coselmar-ui/src/main/webapp/css/coselmar.css | 5 + coselmar-ui/src/main/webapp/index.html | 8 +- .../src/main/webapp/js/coselmar-controllers.js | 200 +++++++++++++++- .../main/webapp/js/coselmar-questions-services.js | 41 ++++ .../src/main/webapp/js/coselmar-services.js | 1 - coselmar-ui/src/main/webapp/js/coselmar.js | 10 + .../webapp/views/documents/modalDocumentEdit.html | 198 ++++++++++++++++ .../views/documents/modalDocumentSearch.html | 45 ++++ .../main/webapp/views/questions/editquestion.html | 257 +++++++++++++++++++++ .../main/webapp/views/questions/newquestion.html | 11 + pom.xml | 10 +- 24 files changed, 1355 insertions(+), 24 deletions(-) -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop in repository coselmar. See http://git.codelutin.com/coselmar.git commit aac2c1ffb77d83dc70d5d40f77e6a29eb42d7220 Merge: 58ade4f 15ec2ea Author: Yannick Martel <martel@©odelutin.com> Date: Fri Dec 5 11:14:09 2014 +0100 Merge branch 'develop' of https://git.codelutin.com/coselmar into develop Conflicts: coselmar-ui/src/main/webapp/css/coselmar.css coselmar-ui/src/main/webapp/index.html coselmar-bundle/pom.xml | 8 +- coselmar-persistence/pom.xml | 1 - coselmar-rest/pom.xml | 8 +- coselmar-ui/pom.xml | 3 - coselmar-ui/src/main/webapp/css/coselmar.css | 126 ++++++++++++++++++--- coselmar-ui/src/main/webapp/index.html | 12 +- .../src/main/webapp/views/documents/document.html | 2 +- .../src/main/webapp/views/documents/documents.html | 4 +- .../main/webapp/views/documents/newdocument.html | 15 ++- coselmar-ui/src/main/webapp/views/home.html | 2 +- .../src/main/webapp/views/users/edituser.html | 2 +- .../src/main/webapp/views/users/newuser.html | 2 +- coselmar-ui/src/main/webapp/views/users/user.html | 2 +- coselmar-ui/src/main/webapp/views/users/users.html | 8 +- pom.xml | 49 +------- 15 files changed, 149 insertions(+), 95 deletions(-) diff --cc coselmar-ui/src/main/webapp/css/coselmar.css index fa181c4,b79fd0a..1e82598 --- a/coselmar-ui/src/main/webapp/css/coselmar.css +++ b/coselmar-ui/src/main/webapp/css/coselmar.css @@@ -51,25 -71,94 +71,99 @@@ body > .container color: #999; text-align: center; z-index:1000; + border-top: 1px solid #aaa; + font-size: 90%; + } + + .footer .separator { + padding: 0 15px; + } + + + + /* General + -------------------------------------------------- */ + a { + color: #008EB4; + } + + .float-right { + float: right; + } + + .info-panel { + border: 1px solid #EEE; + border-left: 3px solid #39a0a8; + margin: 30px 0; + padding: 5px 20px; + } + + /* Titles + -------------------------------------------------- */ + h1, h2 { + color: #39a0a8; + border-bottom: 1px solid rgba( 57, 160, 168,0.2); + padding-left: 5px; + } + + + /* Icons / Buttons + -------------------------------------------------- */ + .btn .glyphicon { + padding-right: 8px; + } + + .btn-action { + background-color: transparent; + transition: all 0.4s ease-in-out 0s; + border: 1px solid #39a0a8; + color: #39a0a8; + margin-left: 15px; + } + + .btn-action:hover { + background-color: #39a0a8; + color: #FFF; + } + + .btn-edit { + color: #f0ad4e; + border-color: #f0ad4e; + } + .btn-edit:hover { + background-color: #f0ad4e; + } + + .btn-disable { + color: #d9534f; + border-color: #d9534f; + } + .btn-disable:hover { + background-color: #d9534f; + } + + /* Override Boostrap default classes + -------------------------------------------------- */ + .btn-primary { + background-color: #39a0a8; + border-color: #3a8a8a; + } + + .page-header { + background-color: #FAFAFA; + border: 0; + padding: 5px 0 0 0; + } + + .navbar-form.navbar-right { + padding: 0; + } - .separator { - padding-left:.75%; - padding-right:.75%; - } - - a { - color: #999; - text-decoration:none; - - &:hover { - color:#fff; - background-color:transparent; - text-decoration:none; - } - } + body > .container .btn-primary { + padding: 3px 10px; } + +/* remove the stupid float on right with bootstrap close class */ +.close { + float: none; +} diff --cc pom.xml index 106641c,4d69b33..7f6febc --- a/pom.xml +++ b/pom.xml @@@ -145,11 -142,8 +142,10 @@@ <tomcatEmbedVersion>7.0.50</tomcatEmbedVersion> + <angularUiSelectVersion>0.9.0</angularUiSelectVersion> + </properties> - <repositories> <repository> <id>coselmar-nexus-repository</id> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
participants (1)
-
codelutin.com scm