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 d6e163cbfee762f2796dbc1cb25ab4fe6d7b4e79 Author: Yannick Martel <martel@©odelutin.com> Date: Mon Dec 15 17:23:16 2014 +0100 add completion on question.type, question.themes, document.type (plain and modal) and document.keywords(modal) --- .../persistence/entity/DocumentTopiaDao.java | 23 +++++++ .../coselmar/services/v1/DocumentsWebService.java | 14 ++++ coselmar-rest/src/main/resources/mapping | 8 ++- .../src/main/webapp/js/coselmar-controllers.js | 43 +++++++++++- .../main/webapp/js/coselmar-questions-services.js | 19 ++++-- .../src/main/webapp/js/coselmar-services.js | 21 ++++-- .../webapp/views/documents/modalDocumentEdit.html | 77 +++++++++++++++------- .../main/webapp/views/documents/newdocument.html | 5 +- .../main/webapp/views/questions/editquestion.html | 10 ++- 9 files changed, 178 insertions(+), 42 deletions(-) diff --git a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/DocumentTopiaDao.java b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/DocumentTopiaDao.java index cfc4a60..ef773ee 100644 --- a/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/DocumentTopiaDao.java +++ b/coselmar-persistence/src/main/java/fr/ifremer/coselmar/persistence/entity/DocumentTopiaDao.java @@ -72,4 +72,27 @@ public class DocumentTopiaDao extends AbstractDocumentTopiaDao<Document> { return documents; } + public List<String> findAllKeywords() { + + StringBuilder hqlBuilder = + new StringBuilder("SELECT DISTINCT(keywords)" + + " FROM " + Document.class.getName() + " D " + + " INNER JOIN D." + Document.PROPERTY_KEYWORDS + " keywords "); + + List<String> values = findAll(hqlBuilder.toString()); + + return values; + } + + public List<String> findAllTypes() { + + StringBuilder hqlBuilder = + new StringBuilder("SELECT DISTINCT(D. " + Document.PROPERTY_TYPE + ")" + + " FROM " + Document.class.getName() + " D "); + + List<String> values = findAll(hqlBuilder.toString()); + + return values; + } + } //DocumentTopiaDao diff --git a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java index b7cafe9..7bd42f7 100644 --- a/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java +++ b/coselmar-rest/src/main/java/fr/ifremer/coselmar/services/v1/DocumentsWebService.java @@ -289,6 +289,20 @@ public class DocumentsWebService extends CoselmarWebServiceSupport { commit(); } + public List<String> getKeywords() throws InvalidCredentialException, UnauthorizedException { + + List<String> themes = getDocumentDao().findAllKeywords(); + + return themes; + } + + public List<String> getTypes() throws InvalidCredentialException, UnauthorizedException { + + List<String> types = getDocumentDao().findAllTypes(); + + return types; + } + //////////////////////////////////////////////////////////////////////////// /////////////////////// Internal Parts ///////////////////////////// diff --git a/coselmar-rest/src/main/resources/mapping b/coselmar-rest/src/main/resources/mapping index a43119f..5c89620 100644 --- a/coselmar-rest/src/main/resources/mapping +++ b/coselmar-rest/src/main/resources/mapping @@ -14,10 +14,10 @@ default.render=fr.ifremer.coselmar.services.CoselmarRender [errors] -#fr.ifremer.coselmar.services.errors.InvalidCredentialException ErrorAction.on401 +#fr.ifremer.coselmar.services.errors.InvalidCredentialException ErrorAction.on401 #fr.ifremer.coselmar.services.errors.UnauthorizedException ErrorAction.on403 -#fr.ifremer.coselmar.services.CoselmarTechnicalException ErrorAction.on500 -#org.nuiton.topia.persistence.TopiaNoResultException ErrorAction.on404 +#fr.ifremer.coselmar.services.CoselmarTechnicalException ErrorAction.on500 +#org.nuiton.topia.persistence.TopiaNoResultException ErrorAction.on404 [actions] @@ -30,6 +30,8 @@ GET /v1/doc DocApi.showMapping # Documents Api GET /v1/documents DocumentsWebService.getDocuments +GET /v1/documents/keywords DocumentsWebService.getKeywords +GET /v1/documents/types DocumentsWebService.getTypes GET /v1/documents/{documentId} DocumentsWebService.getDocument GET /v1/documents/{documentId}/file DocumentsWebService.getDocumentFile POST /v1/documents DocumentsWebService.addDocument diff --git a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js index 9d03c2b..7a15b4c 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-controllers.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-controllers.js @@ -99,6 +99,15 @@ coselmarControllers.controller("NewDocumentCtrl", ['$scope', '$location', 'docum $scope.document = {'privacy': 'PUBLIC'}; $scope.upload = {}; + $scope.existing = {'types' : [], 'keywords' : []}; + + documentService.findAllTypes(function(results) { + $scope.existing.types = results; + }); + + documentService.findAllKeywords(function(results) { + $scope.existing.keywords = results; + }); $scope.createNewDocument = function(isValidForm){ @@ -329,6 +338,16 @@ coselmarControllers.controller("QuestionCtrl", ['$scope', '$route', '$routeParam $scope.question = {'privacy' : 'PUBLIC', 'themes' : [], 'participants' : [], 'externalExperts' : [], 'clients' : [], 'relatedDocuments': [], 'newRelatedDocuments' : [] }; + $scope.existing = {'types' : [], 'themes' : []}; + + // Preload exiting types and themes + questionsService.findAllTypes(function(results) { + $scope.existing.types = results; + }); + + questionsService.findAllThemes(function(results) { + $scope.existing.themes = results; + }); //to enter in edit mode from view $scope.edit = function() { @@ -646,10 +665,30 @@ coselmarControllers.controller('ModalSearchDocumentsCtrl', function ($scope, $mo coselmarControllers.controller('ModalCreateDocumentsCtrl', function ($scope, $modalInstance, documentService) { - $scope.document = {'privacy': 'PUBLIC'}; + $scope.document = {'privacy': 'PUBLIC', 'keywords' : []}; $scope.upload = {}; + $scope.modalExisting = {'keywords' : [], 'types' : []}; - $scope.existingKeywords = []; + documentService.findAllKeywords(function(result) { + $scope.modalExisting.keywords = result; + }); + + documentService.findAllTypes(function(result) { + $scope.modalExisting.types = result; + }); + + $scope.addKeyword = function(keyword) { + if (keyword && $scope.document.keywords.indexOf(keyword) == -1) { + $scope.document.keywords.push(keyword); + } + } + + $scope.removeKeyword = function(keyword) { + var position = $scope.document.keywords.indexOf(keyword); + if (keyword && position != -1) { + $scope.document.keywords.splice(position, 1); + } + } $scope.create = function (isValidForm) { 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 033de8f..db19e3f 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-questions-services.js @@ -64,17 +64,17 @@ function Question(resource, config){ this.getQuestions = function(successFunction, failFunction) { var questionResource = resource(baseURL); questionResource.query().$promise.then(successFunction, failFunction); - } + }; this.deleteQuestion = function(questionId, successFunction, failFunction) { 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); - } + }; this.addNewDocuments = function(questionId, documents, successFunction, failFunction) { @@ -93,5 +93,16 @@ function Question(resource, config){ } }); questionResource.save(null, formData, successFunction, failFunction); - } + }; + + this.findAllTypes = function(successFunction){ + var questionResource = resource(baseURL + '/types'); + questionResource.query(successFunction); + }; + + this.findAllThemes = function(successFunction){ + var questionResource = resource(baseURL + '/themes'); + questionResource.query(successFunction); + }; + }; \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/js/coselmar-services.js b/coselmar-ui/src/main/webapp/js/coselmar-services.js index 709b9e9..55a30fb 100644 --- a/coselmar-ui/src/main/webapp/js/coselmar-services.js +++ b/coselmar-ui/src/main/webapp/js/coselmar-services.js @@ -52,7 +52,7 @@ function Document(resource, config){ } }); docResource.upload(null, formData, successFunction, failFunction); - } + }; this.getDocument = function(id, scope){ // Load the document @@ -61,14 +61,14 @@ function Document(resource, config){ console.log(document); scope.document = document; }); - } + }; this.deleteDocument = function(id, scope, successFunction){ // Load the document var docResource = resource(baseURL + '/:documentId', {documentId:'@id'}); docResource.delete({documentId:id}, successFunction); - } + }; this.getDocumentFile = function(id, scope){ // Load the document @@ -76,7 +76,7 @@ function Document(resource, config){ docResource.get({documentId:id}, function(file){ // redirect to document page ? }); - } + }; this.getDocuments = function(scope){ // Load all documents @@ -92,5 +92,16 @@ function Document(resource, config){ console.log("WTF ?!?"); } }); - } + }; + + this.findAllTypes = function(successFunction){ + var docResource = resource(baseURL + '/types'); + docResource.query(successFunction); + }; + + this.findAllKeywords = function(successFunction){ + var docResource = resource(baseURL + '/keywords'); + docResource.query(successFunction); + }; + }; \ No newline at end of file diff --git a/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html index 7db4b43..478cdcc 100644 --- a/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html +++ b/coselmar-ui/src/main/webapp/views/documents/modalDocumentEdit.html @@ -55,11 +55,14 @@ <div class="col-md-4"> <input type="text" class="form-control" name="type" - ng-model="document.type" required/> + data-ng-model="document.type" list="modalExistingTypes" required/> <p ng-show="documentForm.type.$invalid && !documentForm.type.$pristine" class="help-block">Document type is required.</p> </div> + <datalist id="modalExistingTypes"> + <option data-ng-repeat="type in modalExisting.types" value="{{type}}"> + </datalist> </div> </div> <!-- End Line with name and Type --> @@ -94,45 +97,49 @@ <!-- End Line with file and externalURL --> - <!-- Line with keywords and privacy --> + <!-- Line with keywords --> <div class="form-group"> - <div - ng-class="{'has-error' : documentForm.keywords.$invalid && !documentForm.keywords.$pristine}"> + <div ng-class="{'has-error' : documentForm.keywords.$invalid && + !documentForm.keywords.$pristine}"> + <label class="col-md-2 control-label">keywords *</label> - <div class="col-md-4"> + <div class="col-md-2"> <input type="text" class="form-control" name="keywords" - ng-model="document.keywords" ng-list required - placeholder="keyword 1, keyword2"/> + data-ng-model="modalToAddKeyword" list="modalExistingKeywords"/> - <p ng-show="documentForm.keywords.$invalid && !documentForm.keywords.$pristine" + <p ng-show="document.keywords.length < 1 && !documentForm.keywords.$pristine" class="help-block">At least one keyword is required.</p> </div> + <datalist id="modalExistingKeywords"> + <option data-ng-repeat="keyword in modalExisting.keywords" value="{{keyword}}"> + </datalist> </div> - <div class=""> - <label class="col-md-2 control-label">Privacy</label> + <div class="col-md-1"> + <button class="btn btn-primary" value="add" + ng-click="addKeyword(modalToAddKeyword); modalToAddKeyword=''">add</button> + </div> - <div class="col-md-4"> - <select class="form-control" name="privacy" - ng-model="document.privacy"> - <option value="PRIVATE">Private</option> - <option value="PUBLIC">Public</option> - <option value="RESTRICTED">Restricted to current Question</option> - </select> - </div> + <div class="col-md-7"> + <span ng-repeat="keyword in document.keywords" class="" aria-hidden="true"> + {{keyword}} + <button type="button" class="close" title="remove" ng-click="removeKeyword(keyword);"> + × + </button> + </span> </div> </div> - <!-- End Line with keywords and privacy --> + <!-- End Line with keywords --> - <!-- Line with publication Date, Authors and Language --> + <!-- Line with Publication Date and privacy --> <div class="form-group"> <div class=""> <label class="col-md-2 control-label">Publication date</label> - <div class="col-md-2"> + <div class="col-md-4"> <div class="input-group"> <input type="text" class="form-control" name="publicationDate" ng-model="document.publicationDate" @@ -145,11 +152,31 @@ </div> </div> + <div class=""> + <label class="col-md-2 control-label">Privacy</label> + + <div class="col-md-4"> + <select class="form-control" name="privacy" + ng-model="document.privacy"> + <option value="PRIVATE">Private</option> + <option value="PUBLIC">Public</option> + <option value="RESTRICTED">Restricted to current Question</option> + </select> + + </div> + + </div> + </div> + <!-- End Line with publication Date and privacy --> + + <!-- Line with Authors and Language --> + <div class="form-group"> + <div class="" ng-class="{'has-error' : documentForm.authors.$invalid && !documentForm.authors.$pristine}"> - <label class="col-md-1 control-label">Authors *</label> + <label class="col-md-2 control-label">Authors *</label> - <div class="col-md-4"> + <div class="col-md-5"> <input type="text" class="form-control" name="authors" ng-model="document.authors" required/> @@ -159,9 +186,9 @@ </div> <div class=""> - <label class="col-md-1 control-label">Language</label> + <label class="col-md-2 control-label">Language</label> - <div class="col-md-2"> + <div class="col-md-3"> <input type="text" class="form-control" name="language" ng-model="document.language"/> </div> diff --git a/coselmar-ui/src/main/webapp/views/documents/newdocument.html b/coselmar-ui/src/main/webapp/views/documents/newdocument.html index 39a4f0d..3996a14 100644 --- a/coselmar-ui/src/main/webapp/views/documents/newdocument.html +++ b/coselmar-ui/src/main/webapp/views/documents/newdocument.html @@ -64,11 +64,14 @@ <div class="col-md-5"> <input type="text" class="form-control" name="type" - ng-model="document.type" required/> + data-ng-model="document.type" list="existingTypes" required/> <p ng-show="documentForm.type.$invalid && !documentForm.type.$pristine" class="help-block">Document type is required.</p> </div> + <datalist id="existingTypes"> + <option data-ng-repeat="type in existing.types" value="{{type}}"> + </datalist> </div> <div class="form-group"> diff --git a/coselmar-ui/src/main/webapp/views/questions/editquestion.html b/coselmar-ui/src/main/webapp/views/questions/editquestion.html index e46b0fe..1ea349b 100644 --- a/coselmar-ui/src/main/webapp/views/questions/editquestion.html +++ b/coselmar-ui/src/main/webapp/views/questions/editquestion.html @@ -45,9 +45,12 @@ <div class="col-md-2 "> <input type="text" class="form-control" name="type" - ng-model="question.type" required/> + data-ng-model="question.type" list="existingTypes" required/> <p ng-show="questionForm.type.$invalid && !questionForm.type.$pristine" class="help-block">Type is required.</p> </div> + <datalist id="existingTypes"> + <option data-ng-repeat="type in existing.types" value="{{type}}"> + </datalist> </div> @@ -106,11 +109,14 @@ <div class="col-md-2"> <input type="text" class="form-control" name="themes" - ng-model="toAddTheme"/> + data-ng-model="toAddTheme" list="existingThemes" /> <p ng-show="question.themes.length < 1 && !questionForm.themes.$pristine" class="help-block">At least one theme is required.</p> </div> + <datalist id="existingThemes"> + <option data-ng-repeat="theme in existing.themes" value="{{theme}}"> + </datalist> <div class="col-md-1"> <button class="btn btn-primary" value="add" ng-click="addTheme(toAddTheme); toAddTheme=''">add</button> -- To stop receiving notification emails like this one, please contact codelutin.com SCM administrator <admin+scm@codelutin.com>.