branch develop updated (64b9409 -> aba84cc)
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 64b9409 add tooltip in questions list new c88a0b0 add question view page new a515fab fixes #6019 participants on the question can add new documents in it new a5b20ba prepare full edit of question new 349b65c prepare full edit of question new dfe0dab fix question edit mode new e3b0a23 add filter on users during question edition new 88130f2 manage some restriction on question content new aa7617e add information when question is not available new 66df66b filter questions list with privacy for an expert user new aba84cc Merge branch 'feature/6017-edit-question' into develop The 10 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 aba84cc0d06c13274dd9a4bc0b40dd4abdc5aea8 Merge: 64b9409 66df66b Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 19:12:48 2014 +0100 Merge branch 'feature/6017-edit-question' into develop commit 66df66b1695b14075986e840fa33b5356c274cb5 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 19:12:14 2014 +0100 filter questions list with privacy for an expert user commit aa7617eaf99c9b9f9f47cab7c359f2aacbc0d6fc Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 18:44:06 2014 +0100 add information when question is not available commit 88130f2c7e9b86bbdc7ac7659730e96972829a6b Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 18:38:58 2014 +0100 manage some restriction on question content commit e3b0a235757decb746b3ff011c28d31b05d5a96d Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 17:42:47 2014 +0100 add filter on users during question edition commit dfe0dab0b637e914b2e9ebca53557c57f16dd056 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 16:11:20 2014 +0100 fix question edit mode commit 349b65cf339f83d0436baa217614de7e90ef448d Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 12:12:08 2014 +0100 prepare full edit of question commit a5b20ba0ab8753ea6133750b37f2a26f4aad3091 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 10:58:13 2014 +0100 prepare full edit of question commit a515fab40bd481eee110db24c4a9ac1a319d6406 Author: Yannick Martel <martel@©odelutin.com> Date: Tue Dec 9 16:12:44 2014 +0100 fixes #6019 participants on the question can add new documents in it commit c88a0b07c6a348bb6bd94c314bc3523e7741c6d3 Author: Yannick Martel <martel@©odelutin.com> Date: Mon Dec 8 17:25:01 2014 +0100 add question view page Summary of changes: .../coselmar/persistence/SearchRequestBean.java | 55 +++ .../persistence/entity/CoselmarUserTopiaDao.java | 37 +- .../persistence/entity/QuestionTopiaDao.java | 35 ++ .../src/main/xmi/coselmar-model.properties | 3 +- .../src/main/xmi/coselmar-model.zargo | Bin 9735 -> 9741 bytes .../fr/ifremer/coselmar/beans/QuestionBean.java | 10 + .../fr/ifremer/coselmar/beans/UserSearchBean.java | 76 ++++ .../coselmar/converter/BeanEntityConverter.java | 44 ++- .../services/CoselmarRestApplicationListener.java | 4 +- .../coselmar/services/v1/QuestionsWebService.java | 393 ++++++++++++++++++++- .../coselmar/services/v1/UsersWebService.java | 16 +- coselmar-rest/src/main/resources/mapping | 6 +- .../src/main/webapp/js/coselmar-controllers.js | 348 ++++++++++++++---- .../main/webapp/js/coselmar-questions-services.js | 30 +- coselmar-ui/src/main/webapp/js/coselmar.js | 6 +- .../main/webapp/views/questions/editquestion.html | 28 +- .../webapp/views/questions/newDocumentsPart.html | 38 ++ .../main/webapp/views/questions/newquestion.html | 14 +- .../src/main/webapp/views/questions/question.html | 59 ++++ .../views/questions/viewRestrictedQuestion.html | 15 + .../main/webapp/views/questions/viewquestion.html | 151 ++++++++ 21 files changed, 1247 insertions(+), 121 deletions(-) create mode 100644 coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/SearchRequestBean.java create mode 100644 coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/QuestionTopiaDao.java create mode 100644 coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java create mode 100644 coselmar-ui/src/main/webapp/views/questions/newDocumentsPart.html create mode 100644 coselmar-ui/src/main/webapp/views/questions/question.html create mode 100644 coselmar-ui/src/main/webapp/views/questions/viewRestrictedQuestion.html create mode 100644 coselmar-ui/src/main/webapp/views/questions/viewquestion.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 c88a0b07c6a348bb6bd94c314bc3523e7741c6d3 Author: Yannick Martel <martel@©odelutin.com> Date: Mon Dec 8 17:25:01 2014 +0100 add question view page --- .../src/main/xmi/coselmar-model.properties | 3 +- .../coselmar/services/v1/QuestionsWebService.java | 41 ++++++ coselmar-rest/src/main/resources/mapping | 2 +- .../src/main/webapp/js/coselmar-controllers.js | 42 +++++- .../main/webapp/js/coselmar-questions-services.js | 5 + coselmar-ui/src/main/webapp/js/coselmar.js | 4 + .../main/webapp/views/questions/editquestion.html | 4 +- .../src/main/webapp/views/questions/question.html | 30 ++++ .../main/webapp/views/questions/viewquestion.html | 151 +++++++++++++++++++++ 9 files changed, 278 insertions(+), 4 deletions(-) diff --git a/coselmar-persistence/src/main/xmi/coselmar-model.properties b/coselmar-persistence/src/main/xmi/coselmar-model.properties index 1835e8e..6f8eef4 100644 --- a/coselmar-persistence/src/main/xmi/coselmar-model.properties +++ b/coselmar-persistence/src/main/xmi/coselmar-model.properties @@ -27,4 +27,5 @@ package.fr.ifremer.coselmar.persistence.entity.stereotype=entity model.tagvalue.useEnumerationName=true # Text -fr.ifremer.coselmar.persistence.entity.Document.attribute.summary.tagValue.hibernateAttributeType=text \ No newline at end of file +fr.ifremer.coselmar.persistence.entity.Document.attribute.summary.tagValue.hibernateAttributeType=text +fr.ifremer.coselmar.persistence.entity.Question.attribute.summary.tagValue.hibernateAttributeType=text \ No newline at end of file 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 adb7cbb..13939d0 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 @@ -264,6 +264,47 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { commit(); } + public QuestionBean getQuestion(String questionId) throws InvalidCredentialException, UnauthorizedException { + + // Check authentication + String authorization = getContext().getHeader("Authorization"); + UserWebToken userWebToken = checkAuthentication(authorization); + + // Only Supervisor can add question + String userRole = userWebToken.getRole(); + + if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) + && StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { + String message = String.format("User %s %s ('%s') is not allowed to delete question", + userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + + String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + + try { + getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + } catch (TopiaNoResultException tnre) { + // Should not happened, cause user are not really deleted + String message = String.format("Logged user ('%s') does not exist.", fullUserId); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new InvalidCredentialException(message); + } + + // Retrieve Question + String fullQuestionId = getFullIdFromShort(Question.class, questionId); + Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + + QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), question); + return result; + } + //////////////////////////////////////////////////////////////////////////// /////////////////////// Internal Parts ///////////////////////////// diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 173dc88..fe16e65 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -46,7 +46,7 @@ DELETE /v1/users/{userId} UsersWebService.deleteUser # Questions Api GET /v1/questions QuestionsWebService.getQuestions -#GET /v1/questions/{questionId} QuestionsWebService.getQuestion +GET /v1/questions/{questionId} QuestionsWebService.getQuestion #POST /v1/questions/{questionId} QuestionsWebService.saveQuestion #POST /v1/questions/{questionId}/documents QuestionsWebService.addDocuments POST /v1/questions QuestionsWebService.addQuestion diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 0d03c42..13e9f77 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -373,7 +373,7 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio }]); -// Controller for All User View +// Controller for All Question View coselmarControllers.controller("QuestionsCtrl", ['$scope', '$route', '$routeParams', '$location', 'questionsService', function($scope, $route, $routeParams, $location, questionsService){ @@ -422,6 +422,46 @@ coselmarControllers.controller("QuestionsCtrl", ['$scope', '$route', '$routePara }]); +// Controller for All User View +coselmarControllers.controller("QuestionViewCtrl", ['$scope', '$route', '$routeParams', '$location', 'questionsService', + function($scope, $route, $routeParams, $location, questionsService){ + + $scope.editMode = $routeParams.edit ? $routeParams.edit : false; + + $scope.edit = function() { + $location.search("edit"); + } + + questionsService.getQuestion($routeParams.questionId, + function(question) { + // success : just get the questions + $scope.question = question; + + }, function(error) { + // Fail function : TODO + console.log("error during request"); + console.log(error); + }); + + + $scope.saveQuestion = function(isValidForm){ + console.log($scope.question); + }; + + $scope.deleteQuestion = function(){ + questionsService.deleteQuestion($routeParams.questionId, function() { + // success : goto questions list + $location('#/questions'); + + }, function(error) { + // Fail function : TODO + console.log(error); + }); + }; + + +}]); + coselmarControllers.controller('ModalSearchDocumentsCtrl', function ($scope, $modalInstance, documentService) { $scope.searchKeywords = []; 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 2675751..60c219f 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -47,4 +47,9 @@ function Question(resource, config){ var questionResource = resource(baseURL + "/" + questionId); questionResource.delete().$promise.then(successFunction, failFunction); } + + this.getQuestion = function(questionId, successFunction, failFunction) { + var questionResource = resource(baseURL + "/" + questionId); + questionResource.get().$promise.then(successFunction, failFunction); + } }; \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/js/coselmar.js b/coselmar-ui/src/main/webapp/js/coselmar.js index 15fe537..38b2605 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar.js +++ b/coselmar-ui/src/main/webapp/js/coselmar.js @@ -60,6 +60,10 @@ coselmarApp.config(['$routeProvider', function($routeProvider) { controller : 'NewQuestionCtrl', templateUrl : 'views/questions/newquestion.html' + }).when('/questions/:questionId', { + controller : 'QuestionViewCtrl', + templateUrl : 'views/questions/question.html' + }).otherwise({ redirectTo: '/', diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index 745cc33..c43759d 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -239,7 +239,9 @@ <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> + <td><a class="btn fa fa-minus" title="Remove document" + ng-click="removeDocument(document)" + ng-if="currentUser.role == 'SUPERVISOR'"/></td> </tr> </table> </div> diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html new file mode 100644 index 0000000..33225d5 --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -0,0 +1,30 @@ +<div style="padding: 0px 0px 0px 30px"> + <div class="page-header" style="margin: 0"> + <h2>{{question.title}} + <a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true"> + <span class="fa fa-edit" aria-hidden="true"></span>Edit + </a></h2> + </div> + + <div style="padding-bottom: 50px" ng-include="src='views/questions/viewquestion.html'" ng-if="editMode == false"> + </div> + + <div class="text-center" ng-if="question.closingDate"> + Closed on {{question.closingDate}}. + </div> + + <div class="text-center" ng-if="!question.closingDate && currentUser.role == 'SUPERVISOR'"> + <a class="btn btn-action btn-success" ng-click="closeQuestion()"> + <span class="fa fa-check-square-o" aria-hidden="true"></span>Close + </a> + - + <a class="btn btn-action btn-edit" ng-click="adjournQuestion()"> + <span class="fa fa-edit" aria-hidden="true"></span>Adjourn + </a> + - + <a class="btn btn-action btn-disable" ng-click="deleteQuestion()"> + <span class="fa fa-remove" aria-hidden="true"></span>Delete + </a> + </div> + +</div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/viewquestion.html b/coselmar-ui/src/main/webapp/views/questions/viewquestion.html new file mode 100644 index 0000000..69e088c --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/questions/viewquestion.html @@ -0,0 +1,151 @@ + +<div class=""> + + <div class="form-group col-md-12"> + <div class="col-md-3"> + <dl> + <dt>Question Type</dt> + <dd>{{question.type}}</dd> + </dl> + </div> + <div class="col-md-3"> + <dl> + <dt>Privacy</dt> + <dd>{{question.privacy | lowercase}}</dd> + </dl> + </div> + <div class="col-md-3"> + <dl> + <dt>Submission Date</dt> + <dd>{{question.submissionDate | date:'mediumDate'}}</dd> + </dl> + </div> + <div class="col-md-3" ng-if="question.deadline"> + <dl> + <dt>Deadline</dt> + <dd>{{question.deadline | date:'mediumDate'}}</dd> + </dl> + </div> + </div> + + <div class="form-group col-md-12"> + <div class="col-md-12" > + <dl> + <dt>Summary</dt> + <dd>{{question.summary}}</dd> + </dl> + </div> + </div> + + <div class="form-group col-md-12"> + <div class="col-md-4"> + <dl> + <dt>Themes</dt> + <dd> + <ul> + <li ng-if="question.themes" ng-repeat="theme in question.themes"> + {{theme}} + </li> + </ul> + </dd> + </dl> + </div> + + <div class="col-md-4"> + <dl> + <dt>Supervisors</dt> + <dd> + <ul> + <li ng-if="question.supervisors" ng-repeat="supervisor in question.supervisors"> + <a href="#/users/{{supervisor.id}}" target="_blank">{{supervisor.firstName}} {{supervisor.name}} ({{supervisor.organization}})</a> + </li> + </ul> + </dd> + </dl> + </div> + + <div class="cold-md-4"> + <dl> + <dt>Status</dt> + <dd>{{question.status}}</dd> + </dl> + </div> + </div> + + <div class="form-group col-md-12"> + <div class="col-md-4"> + <dl> + <dt>Experts</dt> + <dd> + <ul> + <li ng-if="question.participants" ng-repeat="participant in question.participants"> + <a href="#/users/{{participant.id}}" target="_blank">{{participant.firstName}} {{participant.name}} ({{participant.organization}})</a> + </li> + <li ng-if="question.externalExperts" ng-repeat="participant in question.externalExperts"> + {{participant}} + </ul> + </dd> + </dl> + </div> + + <div class="col-md-4"> + <dl> + <dt>Clients</dt> + <dd> + <ul> + <li ng-if="question.clients" ng-repeat="client in question.clients"> + <a href="#/users/{{client.id}}" target="_blank">{{client.firstName}} {{client.name}} ({{client.organization}})</a> + </li> + </ul> + </dd> + </dl> + </div> + + <div class="col-md-4"> + <dl> + <dt>Contributors</dt> + <dd> + <ul> + <li ng-if="question.contributors" ng-repeat="contributor in question.contributors"> + <a href="#/users/{{contributor.id}}" target="_blank">{{contributor.firstName}} {{contributor.name}} ({{contributor.organization}})</a> + </li> + </ul> + </dd> + </dl> + </div> + </div> + + <div class="form-group col-md-12" + ng-if="currentUser.role == 'SUPERVISOR' || currentUser.role == 'EXPERT'"> + + <dl> + <dt>Related Documents</dt> + + <dd> + <table class="table table-bordered table-condensed"> + <tr> + <th>Name</th> + <th>Owner</th> + <th>Keywords</th> + <th>Deposit Date</th> + </tr> + <tr ng-repeat="document in question.relatedDocuments"> + <td><a href="#/documents/{{document.id}}" target="_blank">{{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> + </tr> + </table> + </dd> + </dl> + + </div> + + <!-- TODO ymartel 20141208 : manage parents and children + <div class="form-group col-md-12"> + <div class="col-md-6">TODO Parents</div> + <div class="col-md-6">TODO Children</div> + </div> + --> + +</div> \ No newline at end of file -- 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 a515fab40bd481eee110db24c4a9ac1a319d6406 Author: Yannick Martel <martel@©odelutin.com> Date: Tue Dec 9 16:12:44 2014 +0100 fixes #6019 participants on the question can add new documents in it --- .../coselmar/services/v1/QuestionsWebService.java | 56 +++++++++++++ coselmar-rest/src/main/resources/mapping | 2 +- .../src/main/webapp/js/coselmar-controllers.js | 94 ++++++++++++++++++++-- .../main/webapp/js/coselmar-questions-services.js | 23 +++++- coselmar-ui/src/main/webapp/js/coselmar.js | 2 +- .../main/webapp/views/questions/editquestion.html | 4 +- .../webapp/views/questions/newDocumentsPart.html | 38 +++++++++ .../src/main/webapp/views/questions/question.html | 29 +++++-- 8 files changed, 228 insertions(+), 20 deletions(-) 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 13939d0..61e1b2e 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 @@ -9,6 +9,7 @@ import java.util.Set; import com.google.common.base.Function; import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; import fr.ifremer.coselmar.beans.DocumentBean; import fr.ifremer.coselmar.beans.QuestionBean; import fr.ifremer.coselmar.beans.UserBean; @@ -305,6 +306,61 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { return result; } + public void addDocuments(String questionId, DocumentBean[] documents) throws InvalidCredentialException, UnauthorizedException { + + // Check authentication + String authorization = getContext().getHeader("Authorization"); + UserWebToken userWebToken = checkAuthentication(authorization); + + // Only Supervisor can add question + String userRole = userWebToken.getRole(); + + if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) + && StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { + String message = String.format("User %s %s ('%s') is not allowed to delete question", + userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + + String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + + try { + getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + } catch (TopiaNoResultException tnre) { + // Should not happened, cause user are not really deleted + String message = String.format("Logged user ('%s') does not exist.", fullUserId); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new InvalidCredentialException(message); + } + + // Retrieve Question + String fullQuestionId = getFullIdFromShort(Question.class, questionId); + Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + + // Retrieve all documents + Collection<Document> questionDocuments = question.getRelatedDocuments(); + if (documents != null && documents.length > 0) { + List<Document> documentEntities = retrieveDocuments(Lists.newArrayList(documents)); + // Manage restriction list for document with Privacy.RESTRICTED + for (Document document : documentEntities) { + if (document.getPrivacy() == Privacy.RESTRICTED) { + document.addRestrictedList(question.getParticipants()); + } + if (!questionDocuments.contains(document)) { + question.addRelatedDocuments(document); + } + } + } + + commit(); + } + //////////////////////////////////////////////////////////////////////////// /////////////////////// Internal Parts ///////////////////////////// diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index fe16e65..49e9c52 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -48,7 +48,7 @@ DELETE /v1/users/{userId} UsersWebService.deleteUser 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/{questionId}/documents QuestionsWebService.addDocuments POST /v1/questions QuestionsWebService.addQuestion DELETE /v1/questions/{questionId} QuestionsWebService.deleteQuestion diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 13e9f77..0150ba5 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -423,10 +423,12 @@ coselmarControllers.controller("QuestionsCtrl", ['$scope', '$route', '$routePara }]); // Controller for All User View -coselmarControllers.controller("QuestionViewCtrl", ['$scope', '$route', '$routeParams', '$location', 'questionsService', - function($scope, $route, $routeParams, $location, questionsService){ +coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParams', '$location', '$modal', 'questionsService', + function($scope, $route, $routeParams, $location, $modal, questionsService){ $scope.editMode = $routeParams.edit ? $routeParams.edit : false; + $scope.isCurrentParticipant = false; + $scope.question = { 'newRelatedDocuments': []}; $scope.edit = function() { $location.search("edit"); @@ -437,17 +439,23 @@ coselmarControllers.controller("QuestionViewCtrl", ['$scope', '$route', '$routeP // success : just get the questions $scope.question = question; + // update scope about current user : if he is participant, more option enable in ui + for (var i = 0; i < $scope.question.participants.length; i++) { + if ($scope.question.participants[i].id == $scope.currentUser.userId) { + $scope.isCurrentParticipant = true; + // Should be able to put new documents in question + $scope.question.newRelatedDocuments = []; + break; + } + } + }, function(error) { // Fail function : TODO console.log("error during request"); console.log(error); }); - - $scope.saveQuestion = function(isValidForm){ - console.log($scope.question); - }; - + //Deletion $scope.deleteQuestion = function(){ questionsService.deleteQuestion($routeParams.questionId, function() { // success : goto questions list @@ -459,6 +467,76 @@ coselmarControllers.controller("QuestionViewCtrl", ['$scope', '$route', '$routeP }); }; + $scope.saveQuestion = function(isValidForm){ + //TODO + }; + + // New documents added + $scope.validateNewDocuments = function(){ + + if ($scope.question.newRelatedDocuments + && $scope.question.newRelatedDocuments.length > 0) { + + questionsService.addNewDocuments($routeParams.questionId, $scope.question.newRelatedDocuments, function() { + $route.reload(); + + }, function(error) { + // Fail function : TODO + console.log(error); + }); + } + }; + + // Modals part for documents + $scope.modalSearchDocuments = function (documentList) { + + var modalInstance = $modal.open({ + templateUrl: 'views/documents/modalDocumentSearch.html', + controller: 'ModalSearchDocumentsCtrl', + size: 'lg' + }); + + modalInstance.result.then(function (selectedDocument) { + var already = false; + for (var i = 0; i < documentList.length; i++) { + if (documentList[i].id == selectedDocument.id) { + already = true; + } + } + if (!already) { + documentList.push(selectedDocument); + } + }); + }; + + $scope.modalCreateDocument = function (documentList) { + + 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 < documentList.length; i++) { + if (documentList[i].id == selectedDocument.id) { + already = true + } + } + if (!already) { + documentList.push(selectedDocument); + } + }); + }; + + $scope.removeDocument = function(document, documentList) { + var position = documentList.indexOf(document); + if (document && position != -1) { + documentList.splice(position, 1); + } + } + }]); @@ -493,7 +571,7 @@ coselmarControllers.controller('ModalCreateDocumentsCtrl', function ($scope, $mo $scope.create = function (isValidForm) { - if (angular.isDate($scope.question.publicationDate)) { + if (angular.isDate($scope.document.publicationDate)) { $scope.document.publicationDate = $scope.document.publicationDate.getTime(); } 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 60c219f..5aa3448 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -15,7 +15,7 @@ function Question(resource, config){ var formData = new FormData(); formData.append("question", JSON.stringify(question)); - // Save the User + // Save the Question var serviceURl = baseURL; if (question.id) { serviceURl = baseURL + "/" + question.id @@ -34,7 +34,7 @@ function Question(resource, config){ this.findUsers = function(example, successFunction) { var userResource = resource(usersURL, {'like': example}); - userResource.query().$promise.then(successFunction); + userResource.query(successFunction); }; @@ -52,4 +52,23 @@ function Question(resource, config){ var questionResource = resource(baseURL + "/" + questionId); questionResource.get().$promise.then(successFunction, failFunction); } + + this.addNewDocuments = function(questionId, documents, successFunction, failFunction) { + + var formData = new FormData(); + formData.append("documents", JSON.stringify(documents)); + + var serviceURl = baseURL + "/" + questionId + "/documents"; + var questionResource = resource(serviceURl, null, { + 'save': { + method:'POST', + transformRequest: angular.identity, + isArray: true, + headers:{ + 'Content-Type': undefined + } + } + }); + questionResource.save(null, formData, successFunction, failFunction); + } }; \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/js/coselmar.js b/coselmar-ui/src/main/webapp/js/coselmar.js index 38b2605..cf7cd2f 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar.js +++ b/coselmar-ui/src/main/webapp/js/coselmar.js @@ -61,7 +61,7 @@ coselmarApp.config(['$routeProvider', function($routeProvider) { templateUrl : 'views/questions/newquestion.html' }).when('/questions/:questionId', { - controller : 'QuestionViewCtrl', + controller : 'QuestionCtrl', templateUrl : 'views/questions/question.html' diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index c43759d..c42e8e2 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -131,7 +131,7 @@ </ui-select-match> <ui-select-choices - repeat="expert in users.participants | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + repeat="expert in users.participants track by expert.id | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> {{expert.firstName}} {{expert.name}} ({{expert.organization}}) </ui-select-choices> </ui-select> @@ -200,7 +200,7 @@ </ui-select-match> <ui-select-choices - repeat="expert in users.supervisors | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + repeat="expert in users.supervisors track by expert.id | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> {{expert.firstName}} {{expert.name}} ({{expert.organization}}) </ui-select-choices> </ui-select> diff --git a/coselmar-ui/src/main/webapp/views/questions/newDocumentsPart.html b/coselmar-ui/src/main/webapp/views/questions/newDocumentsPart.html new file mode 100644 index 0000000..a742523 --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/questions/newDocumentsPart.html @@ -0,0 +1,38 @@ +<div class=""> + + <div class="form-group col-md-12"> + + <dl> + + <dt>Contribute with new Documents</dt> + + <dd> + <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="modalSearchDocuments(question.newRelatedDocuments)"/> + <a class="btn fa fa-plus" title="Add new Document" + ng-click="modalCreateDocument(question.newRelatedDocuments)"/> + </th> + </tr> + <tr ng-repeat="document in question.newRelatedDocuments"> + <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, question.newRelatedDocuments)" /></td> + </tr> + </table> + </dd> + </dl> + + </div> + +</div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html index 33225d5..d5e53b8 100644 --- a/coselmar-ui/src/main/webapp/views/questions/question.html +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -1,19 +1,30 @@ <div style="padding: 0px 0px 0px 30px"> <div class="page-header" style="margin: 0"> - <h2>{{question.title}} - <a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true"> - <span class="fa fa-edit" aria-hidden="true"></span>Edit - </a></h2> + <h2> + {{question.title}} + <!--<a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true">--> + <!--<span class="fa fa-edit" aria-hidden="true"></span>Edit--> + <!--</a>--> + </h2> </div> - <div style="padding-bottom: 50px" ng-include="src='views/questions/viewquestion.html'" ng-if="editMode == false"> + <div style="padding-bottom: 50px" ng-if="editMode == false"> + <div ng-include="src='views/questions/viewquestion.html'"></div> + <div ng-include="src='views/questions/newDocumentsPart.html'" ng-if="isCurrentParticipant"></div> + </div> + + <div style="padding-bottom: 50px" ng-if="editMode == true"> + <div ng-include="src='views/questions/editquestion.html'"></div> + <div><a class="btn btn-action btn-success" ng-click="saveQuestion()"> + <span class="fa fa-check-square-o" aria-hidden="true"></span>Close + </a></div> </div> <div class="text-center" ng-if="question.closingDate"> Closed on {{question.closingDate}}. </div> - <div class="text-center" ng-if="!question.closingDate && currentUser.role == 'SUPERVISOR'"> + <div class="text-center" ng-if="!question.closingDate && currentUser.role == 'SUPERVISOR' && editMode == false"> <a class="btn btn-action btn-success" ng-click="closeQuestion()"> <span class="fa fa-check-square-o" aria-hidden="true"></span>Close </a> @@ -27,4 +38,10 @@ </a> </div> + <div class="text-center" ng-if="!question.closingDate && isCurrentParticipant && editMode == false && question.newRelatedDocuments.length > 0"> + <a class="btn btn-action btn-success" ng-click="validateNewDocuments()"> + <span class="fa fa-check-square-o" aria-hidden="true"></span>Validate new documents + </a> + </div> + </div> \ No newline at end of file -- 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 a5b20ba0ab8753ea6133750b37f2a26f4aad3091 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 10:58:13 2014 +0100 prepare full edit of question --- .../coselmar/services/v1/QuestionsWebService.java | 5 + .../src/main/webapp/js/coselmar-controllers.js | 140 +++++++++++++++++++-- .../main/webapp/js/coselmar-questions-services.js | 4 +- .../main/webapp/views/questions/editquestion.html | 9 +- .../main/webapp/views/questions/newquestion.html | 6 + .../src/main/webapp/views/questions/question.html | 10 +- 6 files changed, 154 insertions(+), 20 deletions(-) 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 61e1b2e..e00230c 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 @@ -346,6 +346,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve all documents Collection<Document> questionDocuments = question.getRelatedDocuments(); if (documents != null && documents.length > 0) { + // now with expert documents, question is in progress + if (question.getStatus() == Status.OPEN) { + question.setStatus(Status.IN_PROGRESS); + } + List<Document> documentEntities = retrieveDocuments(Lists.newArrayList(documents)); // Manage restriction list for document with Privacy.RESTRICTED for (Document document : documentEntities) { diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 0150ba5..793cd38 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -430,25 +430,103 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam $scope.isCurrentParticipant = false; $scope.question = { 'newRelatedDocuments': []}; + //to enter in edit mode from view $scope.edit = function() { $location.search("edit"); } + // Participants, clients and supervisors management for ui-select + $scope.participantsIndex = {}; + $scope.clientsIndex = {}; + $scope.supervisorsIndex = {}; + + $scope.users = { 'participants' : [], 'clients': [], 'supervisors' : []}; + + $scope.refreshExperts = function(searchKeyword) { + + questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, searchKeyword, function(users) { + $scope.users.participants = users; + $scope.participantsIndex = {}; + angular.forEach($scope.users.participants, function(participant) { + $scope.participantsIndex[participant.id] = participant; + }); + bindUsers($scope.question.participants, $scope.participantsIndex); + }); + } + + $scope.refreshClients = function(searchKeyword) { + + questionsService.findUsers({'role': 'CLIENT', 'active': 'true'}, searchKeyword, function(users) { + $scope.users.clients = users; + $scope.clientsIndex = {}; + angular.forEach($scope.users.clients, function(client) { + $scope.clientsIndex[client.id] = client; + }); + bindUsers($scope.question.clients, $scope.clientsIndex); + }); + } + + $scope.refreshSupervisors = function(searchKeyword) { + + questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true'}, searchKeyword, function(users) { + $scope.users.supervisors = users; + $scope.supervisorsIndex = {}; + angular.forEach($scope.users.supervisors, function(supervisor) { + $scope.supervisorsIndex[supervisor.id] = supervisor; + }); + bindUsers($scope.question.supervisors, $scope.supervisorsIndex); + }); + } + + // call refresh for init + $scope.refreshExperts(""); + $scope.refreshClients(""); + $scope.refreshSupervisors(""); + + // function to be sure to have same user objects in list + var bindUsers = function(toDeal, index) { + if (toDeal) { + for(var i = 0; i < toDeal.length; i++) { + var user = toDeal[i]; + if (index[user.id]) { + toDeal[i] = index[user.id]; + } + } + } + + }; + + questionsService.getQuestion($routeParams.questionId, function(question) { // success : just get the questions $scope.question = question; // update scope about current user : if he is participant, more option enable in ui - for (var i = 0; i < $scope.question.participants.length; i++) { - if ($scope.question.participants[i].id == $scope.currentUser.userId) { - $scope.isCurrentParticipant = true; - // Should be able to put new documents in question - $scope.question.newRelatedDocuments = []; - break; + if ($scope.question.participants) { + for (var i = 0; i < $scope.question.participants.length; i++) { + if ($scope.question.participants[i].id == $scope.currentUser.userId) { + $scope.isCurrentParticipant = true; + // Should be able to put new documents in question + $scope.question.newRelatedDocuments = []; + break; + } } } + if (question.participants) { + $scope.users.participants = question.participants; + bindUsers($scope.question.participants, $scope.participantsIndex); + } + if (question.clients) { + $scope.users.clients = question.clients; + bindUsers($scope.question.clients, $scope.clientsIndex); + } + if (question.supervisors) { + $scope.users.supervisors = question.supervisors; + bindUsers($scope.question.supervisors, $scope.supervisorsIndex); + } + }, function(error) { // Fail function : TODO console.log("error during request"); @@ -468,7 +546,42 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam }; $scope.saveQuestion = function(isValidForm){ - //TODO + if (angular.isDate($scope.question.deadline)) { + $scope.question.deadline = $scope.question.deadline.getTime(); + } + + // Call service to create a new user + if(isValidForm) { +// questionsService.saveQuestion($scope.question, function() { + $location.search(""); +// },function(error) { +// //TODO ymartel 20141118 : deal with error.status or statusText +// console.log("error occurs"); +// console.log(error.s); +// }); + } + }; + + $scope.closeQuestion = function(){ + $scope.question.status = "CLOSED"; + questionsService.saveQuestion($scope.question, function() { + $location.search(""); + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); + }; + + $scope.adjournQuestion = function(){ + $scope.question.status = "ADJOURNED"; + questionsService.saveQuestion($scope.question, function() { + $location.search(""); + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); }; // New documents added @@ -487,6 +600,19 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam } }; + $scope.addTheme = function(theme) { + if (theme && $scope.question.themes.indexOf(theme) == -1) { + $scope.question.themes.push(theme); + } + } + + $scope.removeTheme = function(theme) { + var position = $scope.question.themes.indexOf(theme); + if (theme && position != -1) { + $scope.question.themes.splice(position, 1); + } + } + // Modals part for documents $scope.modalSearchDocuments = function (documentList) { 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 5aa3448..873c215 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -32,8 +32,8 @@ function Question(resource, config){ questionResource.save(null, formData, successFunction, failFunction); }; - this.findUsers = function(example, successFunction) { - var userResource = resource(usersURL, {'like': example}); + this.findUsers = function(example, searchKeyword, successFunction) { + var userResource = resource(usersURL, {'like': example, 'fullSearch': searchKeyword}); userResource.query(successFunction); }; diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index c42e8e2..b8e5f70 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -131,7 +131,9 @@ </ui-select-match> <ui-select-choices - repeat="expert in users.participants track by expert.id | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}"> + repeat="expert in users.participants track by expert.id | propsFilter: {name: $select.search, firstName: $select.search, organization: $select.search}" + refresh="refreshExperts($select.search)" + refresh-delay="1000"> {{expert.firstName}} {{expert.name}} ({{expert.organization}}) </ui-select-choices> </ui-select> @@ -250,10 +252,5 @@ <!-- End Line with Related Document --> - <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)"/> - </div> - </div> </form> </div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/newquestion.html b/coselmar-ui/src/main/webapp/views/questions/newquestion.html index b05689b..7518ede 100644 --- a/coselmar-ui/src/main/webapp/views/questions/newquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/newquestion.html @@ -5,6 +5,12 @@ <div style="padding-bottom: 50px" ng-include="src='views/questions/editquestion.html'" ng-if="currentUser.role == 'SUPERVISOR'"> + + <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)"/> + </div> + </div> </div> <div style="padding-bottom: 50px" ng-if="currentUser.role != 'SUPERVISOR'"> diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html index d5e53b8..28351c4 100644 --- a/coselmar-ui/src/main/webapp/views/questions/question.html +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -2,9 +2,9 @@ <div class="page-header" style="margin: 0"> <h2> {{question.title}} - <!--<a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true">--> - <!--<span class="fa fa-edit" aria-hidden="true"></span>Edit--> - <!--</a>--> + <a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true"> + <span class="fa fa-edit" aria-hidden="true"></span>Edit + </a> </h2> </div> @@ -15,8 +15,8 @@ <div style="padding-bottom: 50px" ng-if="editMode == true"> <div ng-include="src='views/questions/editquestion.html'"></div> - <div><a class="btn btn-action btn-success" ng-click="saveQuestion()"> - <span class="fa fa-check-square-o" aria-hidden="true"></span>Close + <div class="form-group"><a class="btn btn-action btn-success" ng-click="saveQuestion()"> + <span class="fa fa-check-square-o" aria-hidden="true"></span>Validate changes </a></div> </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 349b65cf339f83d0436baa217614de7e90ef448d Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 12:12:08 2014 +0100 prepare full edit of question --- .../src/main/xmi/coselmar-model.zargo | Bin 9735 -> 9741 bytes .../coselmar/converter/BeanEntityConverter.java | 3 +- .../coselmar/services/v1/QuestionsWebService.java | 186 ++++++++++++++++++++- coselmar-rest/src/main/resources/mapping | 2 +- .../src/main/webapp/js/coselmar-controllers.js | 24 ++- .../main/webapp/views/questions/editquestion.html | 7 - .../main/webapp/views/questions/newquestion.html | 8 +- .../src/main/webapp/views/questions/question.html | 6 +- 8 files changed, 203 insertions(+), 33 deletions(-) diff --git a/coselmar-persistence/src/main/xmi/coselmar-model.zargo b/coselmar-persistence/src/main/xmi/coselmar-model.zargo index 830802b..9e32b2a 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/converter/BeanEntityConverter.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/converter/BeanEntityConverter.java index 76449ea..5f20852 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 @@ -113,6 +113,7 @@ public class BeanEntityConverter { result.setSummary(question.getSummary()); result.setType(question.getType()); result.setPrivacy(question.getPrivacy().name()); + result.setStatus(question.getStatus().name()); Collection<String> theme = question.getTheme(); if (theme != null && !theme.isEmpty()) { @@ -166,7 +167,7 @@ public class BeanEntityConverter { for (CoselmarUser contributor : contributors) { String lightId = idFactory.getRandomPart(contributor.getTopiaId()); UserBean contributorBean = toBean(lightId, contributor); - result.addClient(contributorBean); + result.addContributor(contributorBean); } } 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 e00230c..661775c 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 @@ -151,7 +151,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Hierarchy of questions Set<QuestionBean> parents = question.getParents(); if (parents != null && !parents.isEmpty()) { - List<Question> questions = retrieveQuestions(parents); + Set<Question> questions = retrieveQuestions(parents); questionEntity.addAllParents(questions); } @@ -159,7 +159,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Documents on init Set<DocumentBean> relatedDocuments = question.getRelatedDocuments(); if (relatedDocuments != null && !relatedDocuments.isEmpty()) { - List<Document> documents = retrieveDocuments(relatedDocuments); + Set<Document> documents = retrieveDocuments(relatedDocuments); // Manage restriction list for document with Privacy.RESTRICTED for (Document document : documents) { if (document.getPrivacy() == Privacy.RESTRICTED) { @@ -351,7 +351,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { question.setStatus(Status.IN_PROGRESS); } - List<Document> documentEntities = retrieveDocuments(Lists.newArrayList(documents)); + Set<Document> documentEntities = retrieveDocuments(Lists.newArrayList(documents)); // Manage restriction list for document with Privacy.RESTRICTED for (Document document : documentEntities) { if (document.getPrivacy() == Privacy.RESTRICTED) { @@ -366,6 +366,178 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { commit(); } + public void saveQuestion(QuestionBean question) throws InvalidCredentialException, UnauthorizedException { + + // Check authentication + String authorization = getContext().getHeader("Authorization"); + UserWebToken userWebToken = checkAuthentication(authorization); + + // Only Supervisor can add question + String userRole = userWebToken.getRole(); + + if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole)) { + String message = String.format("User %s %s ('%s') is not allowed to save question", + userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + + // retrieve user who will be assigned as question supervisor + String fullUserId = getFullUserIdFromShort(userWebToken.getUserId()); + + CoselmarUser supervisor; + try { + supervisor = getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + } catch (TopiaNoResultException tnre) { + // Should not happened, cause user are not really deleted + String message = String.format("Logged user ('%s') does not exist.", fullUserId); + if (log.isErrorEnabled()) { + log.error(message); + } + throw new InvalidCredentialException(message); + } + + // let's go + Question questionEntity; + // An update + String questionId = question.getId(); + boolean inEdition = StringUtils.isNotBlank(questionId); + if (inEdition) { + String fullQuestionId = getFullIdFromShort(Question.class, questionId); + questionEntity = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + + } else { + // or a create + questionEntity = getQuestionDao().create(); + } + + // Question basics + + questionEntity.setTitle(question.getTitle()); + + questionEntity.setSummary(question.getSummary()); + + questionEntity.setType(question.getType()); + + Set<String> themes = question.getThemes(); + if (themes != null) { + questionEntity.setTheme(new HashSet(themes)); + } + + // By default, privacy is private + String privacy = question.getPrivacy(); + Privacy realPrivacy = privacy != null ? Privacy.valueOf(privacy.toUpperCase()) : Privacy.PRIVATE; + questionEntity.setPrivacy(realPrivacy); + + // On creation, Status is Open + if (inEdition) { + questionEntity.setStatus(question.getStatus() != null ? Status.valueOf(question.getStatus().toUpperCase()) : Status.OPEN); + } else { + questionEntity.setStatus(Status.OPEN); + } + + // Manage Dates : submission & deadline + Date submissionDate = question.getSubmissionDate(); + if (submissionDate != null) { + questionEntity.setSubmissionDate(new Date(submissionDate.getTime())); + } else { + questionEntity.setSubmissionDate(new Date()); + } + + Date deadline = question.getDeadline(); + if (deadline != null) { + questionEntity.setDeadline(new Date(deadline.getTime())); + } + + + // Users around the question + + // First Supervisor is the one creating this question + questionEntity.addSupervisors(supervisor); + + questionEntity.addAllExternalExperts(question.getExternalExperts()); + + // Retrieve the clients + Set<UserBean> clients = question.getClients(); + if (clients != null && !clients.isEmpty()) { + 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 = question.getParticipants(); + CoselmarUserGroup participantGroup; + + //On update, get the existing group, make a diff : removed participants become contributor + if (inEdition) { + participantGroup = questionEntity.getParticipants(); + + if (participants != null && !participants.isEmpty()) { + Set<CoselmarUser> expertEntities = retrieveUsers(participants); + + // For each already assigned participants, if not in new list, assign them as contributors + for (CoselmarUser participantBefore : participantGroup.getMembers()) { + if (!expertEntities.contains(participantBefore)) { + questionEntity.addContributors(participantBefore); + } + } + + participantGroup.clearMembers(); + participantGroup.addAllMembers(expertEntities); + } + + } else { + // If not update, create new group + participantGroup = getCoselmarUserGroupDao().create(); + participantGroup.setName(question.getTitle()); + + if (participants != null && !participants.isEmpty()) { + Set<CoselmarUser> expertEntities = retrieveUsers(participants); + participantGroup.addAllMembers(expertEntities); + } + } + questionEntity.setParticipants(participantGroup); + + // 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, + // excluded from process + + + // Hierarchy of questions + Set<QuestionBean> parents = question.getParents(); + if (parents != null && !parents.isEmpty()) { + Set<Question> questions = retrieveQuestions(parents); + questionEntity.addAllParents(questions); + } + + + // Documents on init + Set<DocumentBean> relatedDocuments = question.getRelatedDocuments(); + if (relatedDocuments != null && !relatedDocuments.isEmpty()) { + Set<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); + } + + commit(); + } + //////////////////////////////////////////////////////////////////////////// /////////////////////// Internal Parts ///////////////////////////// @@ -386,7 +558,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } - protected List<Question> retrieveQuestions(Collection<QuestionBean> questionBeans) { + protected Set<Question> retrieveQuestions(Collection<QuestionBean> questionBeans) { Function<QuestionBean, String> getIds = new Function<QuestionBean, String>() { @Override public String apply(QuestionBean questionBean) { @@ -396,11 +568,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Collection<String> questionIds = Collections2.transform(questionBeans, getIds); List<Question> questions = getQuestionDao().forTopiaIdIn(questionIds).findAll(); - return questions; + return new HashSet<>(questions); } - protected List<Document> retrieveDocuments(Collection<DocumentBean> documentBeans) { + protected Set<Document> retrieveDocuments(Collection<DocumentBean> documentBeans) { Function<DocumentBean, String> getIds = new Function<DocumentBean, String>() { @Override public String apply(DocumentBean documentBean) { @@ -410,7 +582,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Collection<String> documentIds = Collections2.transform(documentBeans, getIds); List<Document> documents = getDocumentDao().forTopiaIdIn(documentIds).findAll(); - return documents; + return new HashSet<>(documents); } diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index 49e9c52..0268034 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -47,7 +47,7 @@ DELETE /v1/users/{userId} UsersWebService.deleteUser GET /v1/questions QuestionsWebService.getQuestions GET /v1/questions/{questionId} QuestionsWebService.getQuestion -#POST /v1/questions/{questionId} QuestionsWebService.saveQuestion +POST /v1/questions/{questionId} QuestionsWebService.saveQuestion POST /v1/questions/{questionId}/documents QuestionsWebService.addDocuments POST /v1/questions QuestionsWebService.addQuestion DELETE /v1/questions/{questionId} QuestionsWebService.deleteQuestion diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 793cd38..993e497 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -281,13 +281,13 @@ coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$locatio 'clients' : [], 'relatedDocuments': [] }; $scope.users = { 'participants' : [], 'clients': [], 'supervisors' : []}; - questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, function(users) { + questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, '', function(users) { $scope.users.participants = users; }); - questionsService.findUsers({'role': 'CLIENT', 'active': 'true'}, function(users) { + questionsService.findUsers({'role': 'CLIENT', 'active': 'true'}, '', function(users) { $scope.users.clients = users; }); - questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true'}, function(users) { + questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true'}, '', function(users) { $scope.users.supervisors = users; }); @@ -545,21 +545,19 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam }); }; - $scope.saveQuestion = function(isValidForm){ + $scope.saveQuestion = function(){ if (angular.isDate($scope.question.deadline)) { $scope.question.deadline = $scope.question.deadline.getTime(); } // Call service to create a new user - if(isValidForm) { -// questionsService.saveQuestion($scope.question, function() { - $location.search(""); -// },function(error) { -// //TODO ymartel 20141118 : deal with error.status or statusText -// console.log("error occurs"); -// console.log(error.s); -// }); - } + questionsService.saveQuestion($scope.question, function() { + $location.search(""); + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); }; $scope.closeQuestion = function(){ diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index b8e5f70..f58aedb 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -1,8 +1,4 @@ -<div class=""> - - <form name="questionForm" class="form-horizontal" role="form"> - <!-- Line with Title --> <div class="form-group row" ng-class="{'has-error' : questionForm.title.$invalid && !questionForm.title.$pristine }"> <label class="col-md-2 control-label">Title *</label> @@ -251,6 +247,3 @@ </div> <!-- End Line with Related Document --> - - </form> -</div> \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/questions/newquestion.html b/coselmar-ui/src/main/webapp/views/questions/newquestion.html index 7518ede..37db08d 100644 --- a/coselmar-ui/src/main/webapp/views/questions/newquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/newquestion.html @@ -3,14 +3,18 @@ <h2>Add a question</h2> </div> - <div style="padding-bottom: 50px" ng-include="src='views/questions/editquestion.html'" - ng-if="currentUser.role == 'SUPERVISOR'"> + <div style="padding-bottom: 50px" ng-if="currentUser.role == 'SUPERVISOR'"> + + <form name="questionForm" class="form-horizontal" role="form"> + + <div ng-include="src='views/questions/editquestion.html'"></div> <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)"/> </div> </div> + </form> </div> <div style="padding-bottom: 50px" ng-if="currentUser.role != 'SUPERVISOR'"> diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html index 28351c4..b0d5ab5 100644 --- a/coselmar-ui/src/main/webapp/views/questions/question.html +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -14,14 +14,16 @@ </div> <div style="padding-bottom: 50px" ng-if="editMode == true"> + <form name="questionForm" class="form-horizontal" role="form"> <div ng-include="src='views/questions/editquestion.html'"></div> - <div class="form-group"><a class="btn btn-action btn-success" ng-click="saveQuestion()"> + <div class="form-group" style="padding-left: 200px"><a class="btn btn-action btn-success" ng-click="saveQuestion()"> <span class="fa fa-check-square-o" aria-hidden="true"></span>Validate changes </a></div> + </form> </div> <div class="text-center" ng-if="question.closingDate"> - Closed on {{question.closingDate}}. + Closed on {{question.closingDate | date:'medium' }}. </div> <div class="text-center" ng-if="!question.closingDate && currentUser.role == 'SUPERVISOR' && editMode == false"> -- 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 dfe0dab0b637e914b2e9ebca53557c57f16dd056 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 16:11:20 2014 +0100 fix question edit mode --- .../coselmar/services/v1/QuestionsWebService.java | 24 +++- .../src/main/webapp/js/coselmar-controllers.js | 158 +++++++-------------- coselmar-ui/src/main/webapp/js/coselmar.js | 2 +- .../main/webapp/views/questions/editquestion.html | 8 +- .../src/main/webapp/views/questions/question.html | 8 +- 5 files changed, 84 insertions(+), 116 deletions(-) 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 661775c..d2c900b 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 @@ -434,7 +434,18 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // On creation, Status is Open if (inEdition) { - questionEntity.setStatus(question.getStatus() != null ? Status.valueOf(question.getStatus().toUpperCase()) : Status.OPEN); + String status = question.getStatus(); + questionEntity.setStatus(status != null ? Status.valueOf(status.toUpperCase()) : Status.OPEN); + + // If it is a close or adjourn update, put a closing date, if it is a reopen, remove closing date + if (Lists.newArrayList(Status.CLOSED.name(), Status.ADJOURNED.name()).contains(status) && questionEntity.getClosingDate() == null) { + questionEntity.setClosingDate(new Date()); + + // it could be a reopen ... + } else if (questionEntity.getClosingDate() != null && !Lists.newArrayList(Status.CLOSED.name(), Status.ADJOURNED.name()).contains(status)) { + questionEntity.setClosingDate(null); + } + } else { questionEntity.setStatus(Status.OPEN); } @@ -462,6 +473,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve the clients Set<UserBean> clients = question.getClients(); + questionEntity.clearClients(); if (clients != null && !clients.isEmpty()) { Set<CoselmarUser> clientEntities = retrieveUsers(clients); questionEntity.addAllClients(clientEntities); @@ -488,6 +500,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { participantGroup.clearMembers(); participantGroup.addAllMembers(expertEntities); + } else { + participantGroup.clearMembers(); } } else { @@ -504,6 +518,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { // Retrieve the supervisor Set<UserBean> supervisors = question.getSupervisors(); + questionEntity.clearSupervisors(); if (supervisors != null && !supervisors.isEmpty()) { Set<CoselmarUser> supervisorEntities = retrieveUsers(supervisors); questionEntity.addAllSupervisors(supervisorEntities); @@ -517,7 +532,11 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { Set<QuestionBean> parents = question.getParents(); if (parents != null && !parents.isEmpty()) { Set<Question> questions = retrieveQuestions(parents); + questionEntity.addAllParents(questions); + + } else if(inEdition) { + questionEntity.clearParents(); } @@ -532,7 +551,10 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } } + questionEntity.clearRelatedDocuments(); questionEntity.addAllRelatedDocuments(documents); + } else if (inEdition) { + questionEntity.clearRelatedDocuments(); } commit(); diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 993e497..d063fac 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -270,109 +270,6 @@ coselmarControllers.controller("UserViewCtrl", //////////// Questions Part /////////////////// ///////////////////////////////////////////////// -// Controller for new question View -coselmarControllers.controller("NewQuestionCtrl", ['$scope', '$route', '$location', '$modal', - 'questionsService', 'userService', 'documentService', - function($scope, $route, $location, $modal, - questionsService, userService, documentService){ - - $scope.question = {'privacy' : 'PUBLIC', - 'themes' : [], 'participants' : [], 'externalExperts' : [], - 'clients' : [], 'relatedDocuments': [] }; - - $scope.users = { 'participants' : [], 'clients': [], 'supervisors' : []}; - questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, '', function(users) { - $scope.users.participants = 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){ - - if (angular.isDate($scope.question.deadline)) { - $scope.question.deadline = $scope.question.deadline.getTime(); - } - - // Call service to create a new user - if(isValidForm) { - questionsService.saveQuestion($scope.question, function() { - $location.path("/questions"); - },function(error) { - //TODO ymartel 20141118 : deal with error.status or statusText - console.log("error occurs"); - console.log(error.s); - }); - } - } - - $scope.addTheme = function(theme) { - if (theme && $scope.question.themes.indexOf(theme) == -1) { - $scope.question.themes.push(theme); - } - } - - $scope.removeTheme = function(theme) { - var position = $scope.question.themes.indexOf(theme); - if (theme && position != -1) { - $scope.question.themes.splice(position, 1); - } - } - - $scope.searchDocuments = function () { - - var modalInstance = $modal.open({ - templateUrl: 'views/documents/modalDocumentSearch.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.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); - } - } - -}]); - // Controller for All Question View coselmarControllers.controller("QuestionsCtrl", ['$scope', '$route', '$routeParams', '$location', 'questionsService', function($scope, $route, $routeParams, $location, questionsService){ @@ -428,7 +325,10 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam $scope.editMode = $routeParams.edit ? $routeParams.edit : false; $scope.isCurrentParticipant = false; - $scope.question = { 'newRelatedDocuments': []}; + + $scope.question = {'privacy' : 'PUBLIC', + 'themes' : [], 'participants' : [], 'externalExperts' : [], + 'clients' : [], 'relatedDocuments': [], 'newRelatedDocuments' : [] }; //to enter in edit mode from view $scope.edit = function() { @@ -496,9 +396,9 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam }; - - questionsService.getQuestion($routeParams.questionId, - function(question) { + if ($routeParams.questionId) { + questionsService.getQuestion($routeParams.questionId, + function(question) { // success : just get the questions $scope.question = question; @@ -514,6 +414,10 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam } } + if(!question.relatedDocuments) { + $scope.question.relatedDocuments = []; + } + if (question.participants) { $scope.users.participants = question.participants; bindUsers($scope.question.participants, $scope.participantsIndex); @@ -531,7 +435,8 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam // Fail function : TODO console.log("error during request"); console.log(error); - }); + }); + }; //Deletion $scope.deleteQuestion = function(){ @@ -560,10 +465,43 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam }); }; + $scope.saveQuestion = function(isValidForm){ + + if (angular.isDate($scope.question.deadline)) { + $scope.question.deadline = $scope.question.deadline.getTime(); + } + + // Call service to create a new user + if(isValidForm) { + questionsService.saveQuestion($scope.question, function() { + if ($routeParams.questionId) { + $location.search(""); + } else { + $location.path("/questions"); + } + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); + } + } + $scope.closeQuestion = function(){ $scope.question.status = "CLOSED"; questionsService.saveQuestion($scope.question, function() { - $location.search(""); + $route.reload(); + },function(error) { + //TODO ymartel 20141118 : deal with error.status or statusText + console.log("error occurs"); + console.log(error.s); + }); + }; + + $scope.reopenQuestion = function(){ + $scope.question.status = "IN_PROGRESS"; + questionsService.saveQuestion($scope.question, function() { + $route.reload(); },function(error) { //TODO ymartel 20141118 : deal with error.status or statusText console.log("error occurs"); @@ -574,7 +512,7 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam $scope.adjournQuestion = function(){ $scope.question.status = "ADJOURNED"; questionsService.saveQuestion($scope.question, function() { - $location.search(""); + $route.reload(); },function(error) { //TODO ymartel 20141118 : deal with error.status or statusText console.log("error occurs"); diff --git a/coselmar-ui/src/main/webapp/js/coselmar.js b/coselmar-ui/src/main/webapp/js/coselmar.js index cf7cd2f..208594c 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar.js +++ b/coselmar-ui/src/main/webapp/js/coselmar.js @@ -57,7 +57,7 @@ coselmarApp.config(['$routeProvider', function($routeProvider) { templateUrl : 'views/questions/questions.html' }).when('/questions/new', { - controller : 'NewQuestionCtrl', + controller : 'QuestionCtrl', templateUrl : 'views/questions/newquestion.html' }).when('/questions/:questionId', { diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index f58aedb..4a95d72 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -228,8 +228,10 @@ <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 new Document" ng-click="createDocument()"/> + <a class="btn fa fa-search" title="Search document" + ng-click="modalSearchDocuments(question.relatedDocuments)" /> + <a class="btn fa fa-plus" title="Add new Document" + ng-click="modalCreateDocument(question.relatedDocuments)"/> </th> </tr> <tr ng-repeat="document in question.relatedDocuments"> @@ -238,7 +240,7 @@ <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)" + ng-click="removeDocument(document, question.relatedDocuments)" ng-if="currentUser.role == 'SUPERVISOR'"/></td> </tr> </table> diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html index b0d5ab5..dcf238c 100644 --- a/coselmar-ui/src/main/webapp/views/questions/question.html +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -16,7 +16,7 @@ <div style="padding-bottom: 50px" ng-if="editMode == true"> <form name="questionForm" class="form-horizontal" role="form"> <div ng-include="src='views/questions/editquestion.html'"></div> - <div class="form-group" style="padding-left: 200px"><a class="btn btn-action btn-success" ng-click="saveQuestion()"> + <div class="form-group" style="padding-left: 200px"><a class="btn btn-action btn-success" ng-click="saveQuestion(true)"> <span class="fa fa-check-square-o" aria-hidden="true"></span>Validate changes </a></div> </form> @@ -26,6 +26,12 @@ Closed on {{question.closingDate | date:'medium' }}. </div> + <div class="text-center" ng-if="question.closingDate && currentUser.role == 'SUPERVISOR' && editMode == false"> + <a class="btn btn-action btn-success" ng-click="reopenQuestion()"> + <span class="fa fa-check-square-o" aria-hidden="true"></span>Reopen + </a> + </div> + <div class="text-center" ng-if="!question.closingDate && currentUser.role == 'SUPERVISOR' && editMode == false"> <a class="btn btn-action btn-success" ng-click="closeQuestion()"> <span class="fa fa-check-square-o" aria-hidden="true"></span>Close -- 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 e3b0a235757decb746b3ff011c28d31b05d5a96d Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 17:42:47 2014 +0100 add filter on users during question edition --- .../coselmar/persistence/SearchRequestBean.java | 55 ++++++++++++++++ .../persistence/entity/CoselmarUserTopiaDao.java | 37 ++++++++++- .../fr/ifremer/coselmar/beans/UserSearchBean.java | 76 ++++++++++++++++++++++ .../services/CoselmarRestApplicationListener.java | 4 +- .../coselmar/services/v1/UsersWebService.java | 16 +++-- .../src/main/webapp/js/coselmar-controllers.js | 18 ++++- .../main/webapp/js/coselmar-questions-services.js | 4 +- 7 files changed, 198 insertions(+), 12 deletions(-) diff --git a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/SearchRequestBean.java b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/SearchRequestBean.java new file mode 100644 index 0000000..6d38cc0 --- /dev/null +++ b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/SearchRequestBean.java @@ -0,0 +1,55 @@ +package fr.ifremer.coselmar.persistence; + +import java.io.Serializable; +import java.util.List; + +/** + * @author ymartel <martel@codelutin.com> + */ +public class SearchRequestBean implements Serializable { + + private static final long serialVersionUID = -5556404267735629642L; + + // Pagination Parameters + protected int limit; + protected int page; + + // Global onFields request parameters + protected List<String> fullTextSearch;// if this is given, make fullText search for all given string + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public int getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + if (limit == null) { + this.limit = 10; + } else { + this.limit = limit; + } + } + + public int getPage() { + return page; + } + + public void setPage(Integer page) { + if (page == null) { + this.page = 0; + } else { + this.page = page; + } + } + + public List<String> getFullTextSearch() { + return fullTextSearch; + } + + public void setFullTextSearch(List<String> fullTextSearch) { + this.fullTextSearch = fullTextSearch; + } +} 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 6bcfbbc..ddf63ca 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 @@ -29,6 +29,8 @@ import java.util.List; import java.util.Map; import fr.ifremer.coselmar.persistence.DaoUtils; +import fr.ifremer.coselmar.persistence.SearchRequestBean; +import org.nuiton.util.pagination.PaginationParameter; public class CoselmarUserTopiaDao extends AbstractCoselmarUserTopiaDao<CoselmarUser> { @@ -117,7 +119,7 @@ public class CoselmarUserTopiaDao extends AbstractCoselmarUserTopiaDao<CoselmarU * @return Users thats contains in a property at least all given keywords * */ - public List<CoselmarUser> findAllByExample(CoselmarUser example, boolean onlyActive) { + public List<CoselmarUser> findAllByExample(CoselmarUser example, boolean onlyActive, SearchRequestBean searchRequest) { StringBuilder hqlBuilder = new StringBuilder("FROM " + CoselmarUser.class.getName() + " CU"); hqlBuilder.append(" WHERE 1=1 "); // Just because next clause will begin with operator @@ -177,7 +179,38 @@ public class CoselmarUserTopiaDao extends AbstractCoselmarUserTopiaDao<CoselmarU } - List<CoselmarUser> coselmarUsers = forHql(hqlBuilder.toString(), args).findAll(); + // Now lets start keywords on each text fields on User ! + List<String> keywords = searchRequest.getFullTextSearch(); + if (keywords != null) { + for (String keyword : keywords) { + // a keyword is an AND clause with lot of OR clauses inside ! + hqlBuilder.append(" AND ( 1=0 "); // Same as previously : need to have an insignificant clause to add all OR after + String orFirstname = DaoUtils.orAttributeLike("CU", CoselmarUser.PROPERTY_FIRSTNAME, args, keyword); + hqlBuilder.append(orFirstname); + + // Name + String orName = DaoUtils.orAttributeLike("CU", CoselmarUser.PROPERTY_NAME, args, keyword); + hqlBuilder.append(orName); + + // Name + String orMail = DaoUtils.orAttributeLike("CU", CoselmarUser.PROPERTY_MAIL, args, keyword); + hqlBuilder.append(orMail); + + // Name + String orOrganization = DaoUtils.orAttributeLike("CU", CoselmarUser.PROPERTY_ORGANIZATION, args, keyword); + hqlBuilder.append(orOrganization); + + // Name + String orQualification = DaoUtils.orAttributeLike("CU", CoselmarUser.PROPERTY_QUALIFICATION, args, keyword); + hqlBuilder.append(orQualification); + + hqlBuilder.append(" ) "); // Close this keywords clause + } + } + + PaginationParameter paginationParameter = PaginationParameter.of(searchRequest.getPage(), searchRequest.getLimit(), CoselmarUser.PROPERTY_MAIL, false); + + List<CoselmarUser> coselmarUsers = forHql(hqlBuilder.toString(), args).find(paginationParameter); return coselmarUsers; } diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java new file mode 100644 index 0000000..2604033 --- /dev/null +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/UserSearchBean.java @@ -0,0 +1,76 @@ +package fr.ifremer.coselmar.beans; + +/* + * #%L + * Coselmar :: Rest Services + * $Id:$ + * $HeadURL:$ + * %% + * Copyright (C) 2014 Ifremer, Code Lutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/gpl-3.0.html>. + * #L% + */ + +import java.util.List; + +/** + * @author ymartel <martel@codelutin.com> + */ +public class UserSearchBean extends UserBean { + + private static final long serialVersionUID = -2665085000774964576L; + + protected Integer limit; + protected Integer page; + protected List<String> fullTextSearch; + protected boolean onlyActive; + + public UserSearchBean(String id, String firstName, String name, String mail, String role, String qualification, String organization, boolean active) { + super(id, firstName, name, mail, role, qualification, organization, active); + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } + + public Integer getPage() { + return page; + } + + public void setPage(Integer page) { + this.page = page; + } + + public List<String> getFullTextSearch() { + return fullTextSearch; + } + + public void setFullTextSearch(List<String> fullTextSearch) { + this.fullTextSearch = fullTextSearch; + } + + public boolean isOnlyActive() { + return onlyActive; + } + + public void setOnlyActive(boolean onlyActive) { + this.onlyActive = onlyActive; + } +} 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 94799bc..8af741c 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 @@ -31,6 +31,7 @@ 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.beans.UserSearchBean; import fr.ifremer.coselmar.converter.DateConverter; import fr.ifremer.coselmar.converter.JsonArrayConverter; import fr.ifremer.coselmar.converter.JsonConverter; @@ -48,7 +49,8 @@ public class CoselmarRestApplicationListener implements WebMotionServerListener protected static final Set<Class<?>> BEAN_TYPES = Sets.<Class<?>>newHashSet( DocumentBean.class, UserBean.class, - QuestionBean.class + QuestionBean.class, + UserSearchBean.class ); @Override 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 6007e40..d9f9f98 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 @@ -41,8 +41,10 @@ import com.github.mustachejava.MustacheFactory; import com.google.common.base.Preconditions; import fr.ifremer.coselmar.beans.UserAccountCreatedMail; import fr.ifremer.coselmar.beans.UserBean; +import fr.ifremer.coselmar.beans.UserSearchBean; import fr.ifremer.coselmar.beans.UserWebToken; import fr.ifremer.coselmar.converter.BeanEntityConverter; +import fr.ifremer.coselmar.persistence.SearchRequestBean; import fr.ifremer.coselmar.persistence.entity.CoselmarUser; import fr.ifremer.coselmar.persistence.entity.CoselmarUserRole; import fr.ifremer.coselmar.services.CoselmarTechnicalException; @@ -92,16 +94,22 @@ public class UsersWebService extends CoselmarWebServiceSupport { return userBean; } - public List<UserBean> getUsers(String searchKeyword, UserBean like, boolean onlyActive) { + public List<UserBean> getUsers(String searchKeyword, UserSearchBean search, 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 (search != null) { + // Search default parameter if not given + SearchRequestBean requestBean = new SearchRequestBean(); + requestBean.setLimit(search.getLimit()); + requestBean.setPage(search.getPage()); + requestBean.setFullTextSearch(search.getFullTextSearch()); + CoselmarUser example = BeanEntityConverter.fromBean(search); + + userList = getCoselmarUserDao().findAllByExample(example, search.isOnlyActive(), requestBean); } else { if (onlyActive) { diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index d063fac..e655776 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -343,8 +343,12 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam $scope.users = { 'participants' : [], 'clients': [], 'supervisors' : []}; $scope.refreshExperts = function(searchKeyword) { + var searchKeywords = []; + if (searchKeyword && searchKeyword.length > 0) { + searchKeywords.push(searchKeyword); + } - questionsService.findUsers({'role': 'EXPERT', 'active': 'true'}, searchKeyword, function(users) { + questionsService.findUsers({'role': 'EXPERT', 'active': true, 'fullTextSearch' : searchKeywords}, function(users) { $scope.users.participants = users; $scope.participantsIndex = {}; angular.forEach($scope.users.participants, function(participant) { @@ -355,8 +359,12 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam } $scope.refreshClients = function(searchKeyword) { + var searchKeywords = []; + if (searchKeyword && searchKeyword.length > 0) { + searchKeywords.push(searchKeyword); + } - questionsService.findUsers({'role': 'CLIENT', 'active': 'true'}, searchKeyword, function(users) { + questionsService.findUsers({'role': 'CLIENT', 'active': 'true', 'fullTextSearch' : searchKeywords}, function(users) { $scope.users.clients = users; $scope.clientsIndex = {}; angular.forEach($scope.users.clients, function(client) { @@ -367,8 +375,12 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam } $scope.refreshSupervisors = function(searchKeyword) { + var searchKeywords = []; + if (searchKeyword && searchKeyword.length > 0) { + searchKeywords.push(searchKeyword); + } - questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true'}, searchKeyword, function(users) { + questionsService.findUsers({'role': 'SUPERVISOR', 'active': 'true', 'fullTextSearch' : searchKeywords}, function(users) { $scope.users.supervisors = users; $scope.supervisorsIndex = {}; angular.forEach($scope.users.supervisors, function(supervisor) { 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 873c215..8856a1f 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -32,8 +32,8 @@ function Question(resource, config){ questionResource.save(null, formData, successFunction, failFunction); }; - this.findUsers = function(example, searchKeyword, successFunction) { - var userResource = resource(usersURL, {'like': example, 'fullSearch': searchKeyword}); + this.findUsers = function(example, successFunction) { + var userResource = resource(usersURL, {'search': example}); userResource.query(successFunction); }; -- 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 88130f2c7e9b86bbdc7ac7659730e96972829a6b Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 18:38:58 2014 +0100 manage some restriction on question content --- .../fr/ifremer/coselmar/beans/QuestionBean.java | 10 ++ .../coselmar/converter/BeanEntityConverter.java | 41 ++++++++ .../coselmar/services/v1/QuestionsWebService.java | 103 ++++++++++++++++++--- 3 files changed, 143 insertions(+), 11 deletions(-) diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionBean.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionBean.java index 3cab9f2..ff9e9d2 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionBean.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/beans/QuestionBean.java @@ -42,6 +42,9 @@ public class QuestionBean implements Serializable { protected Set<String> externalExperts; + protected Boolean isRestricted; + + public String getId() { return id; } @@ -235,4 +238,11 @@ public class QuestionBean implements Serializable { this.externalExperts = externalExperts; } + public Boolean getIsRestricted() { + return isRestricted; + } + + public void setRestricted(boolean isRestricted) { + this.isRestricted = isRestricted; + } } //Question 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 5f20852..1875092 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 @@ -191,4 +191,45 @@ public class BeanEntityConverter { return result; } + public static QuestionBean toLightBean(TopiaIdFactory idFactory, Question question) { + QuestionBean result = new QuestionBean(); + result.setId(idFactory.getRandomPart(question.getTopiaId())); + + result.setTitle(question.getTitle()); + result.setSummary(question.getSummary()); + result.setType(question.getType()); + result.setPrivacy(question.getPrivacy().name()); + result.setStatus(question.getStatus().name()); + + Collection<String> theme = question.getTheme(); + if (theme != null && !theme.isEmpty()) { + result.setThemes(new HashSet(theme)); + } + + Date submissionDate = question.getSubmissionDate(); + if (submissionDate != null){ + result.setSubmissionDate(new Date(submissionDate.getTime())); + } + + Date deadline = question.getDeadline(); + if (deadline != null){ + result.setDeadline(new Date(deadline.getTime())); + } + + Date closingDate = question.getClosingDate(); + if (closingDate != null){ + result.setClosingDate(new Date(closingDate.getTime())); + } + + Collection<Question> parents = question.getParents(); + if (parents != null && !parents.isEmpty()) { + for (Question parent : parents) { + QuestionBean questionBean = toBean(idFactory, parent); + result.addParent(questionBean); + } + } + + return result; + } + } 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 d2c900b..f10b8a3 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 @@ -38,6 +38,8 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { private static final Log log = LogFactory.getLog(QuestionsWebService.class); + protected static final List<String> RESTRICTED_ACCESS_USERS = Lists.newArrayList(CoselmarUserRole.CLIENT.name(), CoselmarUserRole.MEMBER.name()); + public void addQuestion(QuestionBean question) throws InvalidCredentialException, UnauthorizedException { // Check authentication @@ -194,6 +196,10 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.MEMBER.name(), currentUserRole)) { questionList = getQuestionDao().forPrivacyEquals(Privacy.PUBLIC).findAll(); + } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), currentUserRole)) { + questionList = getQuestionDao().findAll(); + //TODO ymartel : manage privacy for experts + } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.CLIENT.name(), currentUserRole)) { questionList = getQuestionDao().forClientsContains(currentUser).findAll(); } else { @@ -208,7 +214,15 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { for (Question question : questionList) { TopiaIdFactory topiaIdFactory = getPersistenceContext().getTopiaIdFactory(); - QuestionBean questionBean = BeanEntityConverter.toBean(topiaIdFactory, question); + + QuestionBean questionBean; + if (RESTRICTED_ACCESS_USERS.contains(currentUserRole)) { + questionBean = BeanEntityConverter.toLightBean(topiaIdFactory, question); + + } else { + questionBean = BeanEntityConverter.toBean(topiaIdFactory, question); + } + result.add(questionBean); } @@ -221,7 +235,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Only Supervisor can add question + // Only Supervisor can delete question String userRole = userWebToken.getRole(); if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) @@ -271,12 +285,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Only Supervisor can add question + // Supervisor can get all the question elements + // Expert can get all the question elements if public or if he is participant of the question + // Client can get the question (not the documents) if he is client of the question. String userRole = userWebToken.getRole(); if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) && StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { - String message = String.format("User %s %s ('%s') is not allowed to delete question", + String message = String.format("User %s %s ('%s') is not allowed to view question", userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); if (log.isWarnEnabled()) { log.warn(message); @@ -287,8 +303,9 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + CoselmarUser currentUser; try { - getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + currentUser = getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Logged user ('%s') does not exist.", fullUserId); @@ -302,7 +319,30 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String fullQuestionId = getFullIdFromShort(Question.class, questionId); Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); - QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), question); + // Check client authorization on the document + checkIsClient(question, currentUser); + + QuestionBean result = BeanEntityConverter.toBean(getPersistenceContext().getTopiaIdFactory(), question);; + + // Client is not allowed to see documents + if (CoselmarUserRole.CLIENT == currentUser.getRole()) { + result.setRelatedDocuments(null); + + // If document is private, only participants could check it + } else if (CoselmarUserRole.EXPERT == currentUser.getRole() && question.getPrivacy() == Privacy.PRIVATE) { + CoselmarUserGroup participants = question.getParticipants(); + if (participants == null || !participants.getMembers().contains(currentUser)) { + result = new QuestionBean(); + result.setTitle(question.getTitle()); + result.setPrivacy(question.getPrivacy().name()); + result.setRestricted(true); + for (Question parent : question.getParents()) { + result.addParent(BeanEntityConverter.toLightBean(getPersistenceContext().getTopiaIdFactory(), parent)); + } + //TODO ymartel : manager children + } + } + return result; } @@ -312,12 +352,14 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Only Supervisor can add question + // Only Supervisor can add documents String userRole = userWebToken.getRole(); if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole) - && StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole)) { - String message = String.format("User %s %s ('%s') is not allowed to delete question", + && !StringUtils.equalsIgnoreCase(CoselmarUserRole.ADMIN.name(), userRole) + && !StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), userRole)) { + + String message = String.format("User %s %s ('%s') is not allowed to add document", userWebToken.getFirstName(), userWebToken.getLastName(), userWebToken.getUserId()); if (log.isWarnEnabled()) { log.warn(message); @@ -328,8 +370,9 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String fullUserId = getFullIdFromShort(CoselmarUser.class, userWebToken.getUserId()); + CoselmarUser currentUser; try { - getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); + currentUser = getCoselmarUserDao().forTopiaIdEquals(fullUserId).findUnique(); } catch (TopiaNoResultException tnre) { // Should not happened, cause user are not really deleted String message = String.format("Logged user ('%s') does not exist.", fullUserId); @@ -343,6 +386,10 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String fullQuestionId = getFullIdFromShort(Question.class, questionId); Question question = getQuestionDao().forTopiaIdEquals(fullQuestionId).findUnique(); + // Check expert authorization on the document + checkIsParticipant(question, currentUser); + + // Retrieve all documents Collection<Document> questionDocuments = question.getRelatedDocuments(); if (documents != null && documents.length > 0) { @@ -372,7 +419,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { String authorization = getContext().getHeader("Authorization"); UserWebToken userWebToken = checkAuthentication(authorization); - // Only Supervisor can add question + // Only Supervisor can save question String userRole = userWebToken.getRole(); if (!StringUtils.equalsIgnoreCase(CoselmarUserRole.SUPERVISOR.name(), userRole)) { @@ -566,6 +613,40 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { //////////////////////////////////////////////////////////////////////////// + protected void checkIsParticipant(Question question, CoselmarUser currentUser) throws UnauthorizedException { + String userRole = currentUser.getRole().name(); + Set<CoselmarUser> questionsParticipants = question.getParticipants().getMembers(); + + if (StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), userRole) + && !questionsParticipants.contains(currentUser)) { + + String message = String.format("Expert %s %s ('%s') is not allowed to add document", + currentUser.getFirstname(), currentUser.getName(), currentUser.getTopiaId()); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + } + + protected void checkIsClient(Question question, CoselmarUser currentUser) throws UnauthorizedException { + String userRole = currentUser.getRole().name(); + Set<CoselmarUser> questionsClients = question.getClients(); + + if (StringUtils.equalsIgnoreCase(CoselmarUserRole.CLIENT.name(), userRole) + && !questionsClients.contains(currentUser)) { + + String message = String.format("Client %s %s ('%s') is not allowed to access question %s", + currentUser.getFirstname(), currentUser.getName(), currentUser.getTopiaId(), question.getTopiaId()); + if (log.isWarnEnabled()) { + log.warn(message); + } + throw new UnauthorizedException(message); + + } + } + protected Set<CoselmarUser> retrieveUsers(Collection<UserBean> userBeans) { Function<UserBean, String> getIds = new Function<UserBean, String>() { @Override -- 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 aa7617eaf99c9b9f9f47cab7c359f2aacbc0d6fc Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 18:44:06 2014 +0100 add information when question is not available --- coselmar-ui/src/main/webapp/views/questions/question.html | 8 ++++++-- .../webapp/views/questions/viewRestrictedQuestion.html | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/coselmar-ui/src/main/webapp/views/questions/question.html b/coselmar-ui/src/main/webapp/views/questions/question.html index dcf238c..3093209 100644 --- a/coselmar-ui/src/main/webapp/views/questions/question.html +++ b/coselmar-ui/src/main/webapp/views/questions/question.html @@ -2,13 +2,17 @@ <div class="page-header" style="margin: 0"> <h2> {{question.title}} - <a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true"> + <a class="btn btn-action btn-edit pull-right" ng-click="edit()" ng-if="editMode != true && currentUser.role == 'SUPERVISOR'"> <span class="fa fa-edit" aria-hidden="true"></span>Edit </a> </h2> </div> - <div style="padding-bottom: 50px" ng-if="editMode == false"> + <div style="padding-bottom: 50px" ng-if="editMode == false && question.isRestricted"> + <div ng-include="src='views/questions/viewRestrictedQuestion.html'"></div> + </div> + + <div style="padding-bottom: 50px" ng-if="editMode == false && !question.isRestricted"> <div ng-include="src='views/questions/viewquestion.html'"></div> <div ng-include="src='views/questions/newDocumentsPart.html'" ng-if="isCurrentParticipant"></div> </div> diff --git a/coselmar-ui/src/main/webapp/views/questions/viewRestrictedQuestion.html b/coselmar-ui/src/main/webapp/views/questions/viewRestrictedQuestion.html new file mode 100644 index 0000000..f48f801 --- /dev/null +++ b/coselmar-ui/src/main/webapp/views/questions/viewRestrictedQuestion.html @@ -0,0 +1,15 @@ + +<div class=""> + + Cette question n'est pas disponible à la consultation. + + </div> + + <!-- TODO ymartel 20141208 : manage parents and children + <div class="form-group col-md-12"> + <div class="col-md-6">TODO Parents</div> + <div class="col-md-6">TODO Children</div> + </div> + --> + +</div> \ No newline at end of file -- 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 66df66b1695b14075986e840fa33b5356c274cb5 Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 19:12:14 2014 +0100 filter questions list with privacy for an expert user --- .../persistence/entity/QuestionTopiaDao.java | 35 ++++++++++++++++++++++ .../coselmar/services/v1/QuestionsWebService.java | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/QuestionTopiaDao.java b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/QuestionTopiaDao.java new file mode 100644 index 0000000..0d01ed1 --- /dev/null +++ b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/QuestionTopiaDao.java @@ -0,0 +1,35 @@ +package fr.ifremer.coselmar.persistence.entity; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import fr.ifremer.coselmar.persistence.DaoUtils; + +public class QuestionTopiaDao extends AbstractQuestionTopiaDao<Question> { + + public List<Question> findForExpert(CoselmarUser expert) { + + StringBuilder hqlBuilder = new StringBuilder("SELECT Q FROM " + Question.class.getName() + " Q " + + " INNER JOIN Q.participants CUG "); + + Map<String, Object> args = new HashMap<>(); + + String publicCondition = DaoUtils.getQueryForAttributeEquals("Q", Question.PROPERTY_PRIVACY, args, Privacy.PUBLIC, ""); + + hqlBuilder.append(" WHERE (" + publicCondition + " ) "); + + String privateCondition = DaoUtils.getQueryForAttributeEquals("Q", Question.PROPERTY_PRIVACY, args, Privacy.PRIVATE, ""); + + hqlBuilder.append(" OR (" + privateCondition); + + String userCondition = DaoUtils.andAttributeContains("CUG", CoselmarUserGroup.PROPERTY_MEMBERS, args, expert); + + hqlBuilder.append(userCondition + ")"); + + List<Question> questions = forHql(hqlBuilder.toString(), args).findAll(); + + return questions; + } + +} //QuestionTopiaDao 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 f10b8a3..92706ff 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 @@ -197,7 +197,7 @@ public class QuestionsWebService extends CoselmarWebServiceSupport { questionList = getQuestionDao().forPrivacyEquals(Privacy.PUBLIC).findAll(); } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.EXPERT.name(), currentUserRole)) { - questionList = getQuestionDao().findAll(); + questionList = getQuestionDao().findForExpert(currentUser); //TODO ymartel : manage privacy for experts } else if (StringUtils.equalsIgnoreCase(CoselmarUserRole.CLIENT.name(), currentUserRole)) { -- 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 aba84cc0d06c13274dd9a4bc0b40dd4abdc5aea8 Merge: 64b9409 66df66b Author: Yannick Martel <martel@©odelutin.com> Date: Wed Dec 10 19:12:48 2014 +0100 Merge branch 'feature/6017-edit-question' into develop .../coselmar/persistence/SearchRequestBean.java | 55 +++ .../persistence/entity/CoselmarUserTopiaDao.java | 37 +- .../persistence/entity/QuestionTopiaDao.java | 35 ++ .../src/main/xmi/coselmar-model.properties | 3 +- .../src/main/xmi/coselmar-model.zargo | Bin 9735 -> 9741 bytes .../fr/ifremer/coselmar/beans/QuestionBean.java | 10 + .../fr/ifremer/coselmar/beans/UserSearchBean.java | 76 ++++ .../coselmar/converter/BeanEntityConverter.java | 44 ++- .../services/CoselmarRestApplicationListener.java | 4 +- .../coselmar/services/v1/QuestionsWebService.java | 393 ++++++++++++++++++++- .../coselmar/services/v1/UsersWebService.java | 16 +- coselmar-rest/src/main/resources/mapping | 6 +- .../src/main/webapp/js/coselmar-controllers.js | 348 ++++++++++++++---- .../main/webapp/js/coselmar-questions-services.js | 30 +- coselmar-ui/src/main/webapp/js/coselmar.js | 6 +- .../main/webapp/views/questions/editquestion.html | 28 +- .../webapp/views/questions/newDocumentsPart.html | 38 ++ .../main/webapp/views/questions/newquestion.html | 14 +- .../src/main/webapp/views/questions/question.html | 59 ++++ .../views/questions/viewRestrictedQuestion.html | 15 + .../main/webapp/views/questions/viewquestion.html | 151 ++++++++ 21 files changed, 1247 insertions(+), 121 deletions(-) -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.
participants (1)
-
codelutin.com scm