securis/src/main/java/net/curisit/securis/db/License.java
.. .. @@ -15,6 +15,7 @@ 15 15 16 16 import org.codehaus.jackson.annotate.JsonAutoDetect; 17 17 import org.codehaus.jackson.annotate.JsonIgnore; 18 +import org.codehaus.jackson.annotate.JsonIgnoreProperties;18 19 import org.codehaus.jackson.annotate.JsonProperty; 19 20 import org.codehaus.jackson.map.annotate.JsonSerialize; 20 21 .. .. @@ -26,8 +27,9 @@ 26 27 @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) 27 28 @Entity 28 29 @Table(name = "license") 30 +@JsonIgnoreProperties(ignoreUnknown = true)29 31 @NamedQueries( 30 - { @NamedQuery(name = "list-licenses", query = "SELECT l FROM License l") })32 + { @NamedQuery(name = "list-licenses-by-pack", query = "SELECT l FROM License l where l.pack.id = :packId") })31 33 public class License implements Serializable { 32 34 33 35 private static final long serialVersionUID = 1L; .. .. @@ -56,9 +58,18 @@ 56 58 private int status; 57 59 58 60 @Column(name = "full_name") 61 + @JsonProperty("full_name")59 62 private String fullName; 60 63 61 64 private String email; 65 +66 + @Column(name = "request_data")67 + @JsonProperty("request_data")68 + private String requestData;69 +70 + @Column(name = "license_data")71 + @JsonProperty("license_data")72 + private String licenseData;62 73 63 74 @Column(name = "creation_timestamp") 64 75 private Date creationTimestamp; .. .. @@ -77,6 +88,8 @@ 77 88 78 89 @Column(name = "last_access_timestamp") 79 90 private Date lastAccessTimestamp; 91 +92 + private String comments;80 93 81 94 public int getId() { 82 95 return id; .. .. @@ -240,6 +253,30 @@ 240 253 this.lastAccessTimestamp = lastAccessTimestamp; 241 254 } 242 255 256 + public String getRequestData() {257 + return requestData;258 + }259 +260 + public void setRequestData(String requestData) {261 + this.requestData = requestData;262 + }263 +264 + public String getLicenseData() {265 + return licenseData;266 + }267 +268 + public void setLicenseData(String licenseData) {269 + this.licenseData = licenseData;270 + }271 +272 + public String getComments() {273 + return comments;274 + }275 +276 + public void setComments(String comments) {277 + this.comments = comments;278 + }279 +243 280 public static class Status { 244 281 public static final int CREATED = 0; 245 282 public static final int SENT = 1; securis/src/main/java/net/curisit/securis/services/LicenseResource.java
.. .. @@ -7,16 +7,15 @@ 7 7 import javax.inject.Provider; 8 8 import javax.persistence.EntityManager; 9 9 import javax.persistence.TypedQuery; 10 -import javax.servlet.http.HttpServletRequest;11 10 import javax.ws.rs.Consumes; 12 11 import javax.ws.rs.DELETE; 13 12 import javax.ws.rs.GET; 14 -import javax.ws.rs.HeaderParam;15 13 import javax.ws.rs.POST; 16 14 import javax.ws.rs.PUT; 17 15 import javax.ws.rs.Path; 18 16 import javax.ws.rs.PathParam; 19 17 import javax.ws.rs.Produces; 18 +import javax.ws.rs.QueryParam;20 19 import javax.ws.rs.core.Context; 21 20 import javax.ws.rs.core.MediaType; 22 21 import javax.ws.rs.core.Response; .. .. @@ -28,6 +27,8 @@ 28 27 import net.curisit.securis.db.License; 29 28 import net.curisit.securis.db.Pack; 30 29 import net.curisit.securis.db.User; 30 +import net.curisit.securis.security.BasicSecurityContext;31 +import net.curisit.securis.security.Securable;31 32 import net.curisit.securis.utils.TokenHelper; 32 33 33 34 import org.slf4j.Logger; .. .. @@ -40,7 +41,7 @@ 40 41 * 41 42 * @author roberto <roberto.sanchez@curisit.net> 42 43 */ 43 -@Path("/organization")44 +@Path("/license")44 45 public class LicenseResource { 45 46 46 47 private static final Logger log = LoggerFactory.getLogger(LicenseResource.class); .. .. @@ -60,14 +61,25 @@ 60 61 */ 61 62 @GET 62 63 @Path("/") 64 + @Securable63 65 @Produces( 64 66 { MediaType.APPLICATION_JSON }) 65 - public Response index() {67 + public Response index(@QueryParam("packId") Integer packId, @Context BasicSecurityContext bsc) {66 68 log.info("Getting licenses list "); 67 69 68 70 EntityManager em = emProvider.get(); 69 - TypedQuery<License> q = em.createNamedQuery("list-licenses-by-pack", License.class);70 71 72 + if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {73 + Pack pack = em.find(Pack.class, packId);74 + if (pack == null)75 + return Response.ok().build();76 + if (!bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {77 + log.error("Pack with id {} not accesible by user {}", pack, bsc.getUserPrincipal());78 + return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to pack licenses").build();79 + }80 + }81 + TypedQuery<License> q = em.createNamedQuery("list-licenses-by-pack", License.class);82 + q.setParameter("packId", packId);71 83 List<License> list = q.getResultList(); 72 84 73 85 return Response.ok(list).build(); .. .. @@ -79,9 +91,10 @@ 79 91 */ 80 92 @GET 81 93 @Path("/{licId}") 94 + @Securable82 95 @Produces( 83 96 { MediaType.APPLICATION_JSON }) 84 - public Response get(@PathParam("licId") String licId, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {97 + public Response get(@PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) {85 98 log.info("Getting organization data for id: {}: ", licId); 86 99 if (licId == null || licId.equals("")) { 87 100 log.error("License ID is mandatory"); .. .. @@ -89,21 +102,28 @@ 89 102 } 90 103 91 104 EntityManager em = emProvider.get(); 92 - License lt = em.find(License.class, Integer.parseInt(licId));93 - if (lt == null) {105 + License lic = em.find(License.class, licId);106 + if (lic == null) {94 107 log.error("License with id {} not found in DB", licId); 95 - return Response.status(Status.NOT_FOUND).build();108 + return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License not found for ID: " + licId).build();96 109 } 97 - return Response.ok(lt).build();110 + if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {111 + if (!bsc.getOrganizationsIds().contains(lic.getPack().getOrganization().getId())) {112 + log.error("License with id {} is not accesible by user {}", licId, bsc.getUserPrincipal());113 + return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to license data").build();114 + }115 + }116 + return Response.ok(lic).build();98 117 } 99 118 100 119 @POST 101 120 @Path("/") 102 121 @Consumes(MediaType.APPLICATION_JSON) 122 + @Securable103 123 @Produces( 104 124 { MediaType.APPLICATION_JSON }) 105 125 @Transactional 106 - public Response create(License lic, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {126 + public Response create(License lic, @Context BasicSecurityContext bsc) {107 127 log.info("Creating new organization"); 108 128 EntityManager em = emProvider.get(); 109 129 Pack pack = null; .. .. @@ -112,6 +132,13 @@ 112 132 if (pack == null) { 113 133 log.error("License pack with id {} not found in DB", lic.getPackId()); 114 134 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build(); 135 + } else {136 + if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {137 + if (!bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {138 + log.error("License for pack with id {} can not be created by user {}", pack.getId(), bsc.getUserPrincipal());139 + return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized action on pack license").build();140 + }141 + }115 142 } 116 143 } 117 144 .. .. @@ -124,16 +151,11 @@ 124 151 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's created by user not found with ID: " + createdByUsername).build(); 125 152 } 126 153 127 - try {128 - User canceledBy = getUser(lic.getCanceledById(), em);129 - lic.setCanceledBy(canceledBy);130 - } catch (CurisException ex) {131 - String canceledByUsername = lic.getCreatedById();132 - log.error("License canceled by user with id {} not found in DB", canceledByUsername);133 - return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's canceled by user not found with ID: " + canceledByUsername).build();134 - }135 -154 + // ODO: Manage status if request data is set155 + lic.setCanceledBy(null);156 + lic.setStatus(License.Status.CREATED);136 157 lic.setCreationTimestamp(new Date()); 158 + lic.setModificationTimestamp(lic.getCreationTimestamp());137 159 em.persist(lic); 138 160 139 161 return Response.ok(lic).build(); .. .. @@ -153,22 +175,30 @@ 153 175 @PUT 154 176 @POST 155 177 @Path("/{licId}") 178 + @Securable156 179 @Transactional 157 180 @Consumes(MediaType.APPLICATION_JSON) 158 181 @Produces( 159 182 { MediaType.APPLICATION_JSON }) 160 - public Response modify(License lic, @PathParam("licId") String licId, @HeaderParam(TokenHelper.TOKEN_HEADER_PÀRAM) String token) {183 + public Response modify(License lic, @PathParam("licId") Integer licId, @Context BasicSecurityContext bsc) {161 184 log.info("Modifying organization with id: {}", licId); 162 185 EntityManager em = emProvider.get(); 163 186 164 - Pack pack = null;165 - if (lic.getPackId() != null) {166 - pack = em.find(Pack.class, lic.getPackId());167 - if (pack == null) {168 - log.error("License pack with id {} not found in DB", lic.getPackId());169 - return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build();170 - }171 - }187 + // Pack pack = null;188 + // if (lic.getPackId() != null) {189 + // pack = em.find(Pack.class, lic.getPackId());190 + // if (pack == null) {191 + // log.error("License pack with id {} not found in DB", lic.getPackId());192 + // return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's pack not found with ID: " + lic.getPackId()).build();193 + // } else {194 + // if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {195 + // if (!bsc.getOrganizationsIds().contains(pack.getOrganization().getId())) {196 + // log.error("License for pack with id {} can not be modified by user {}", pack.getId(), bsc.getUserPrincipal());197 + // return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized action on pack license").build();198 + // }199 + // }200 + // }201 + // }172 202 User createdBy = null; 173 203 try { 174 204 createdBy = getUser(lic.getCreatedById(), em); .. .. @@ -186,30 +216,59 @@ 186 216 log.error("License canceled by user with id {} not found in DB", canceledByUsername); 187 217 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License's canceled by user not found with ID: " + canceledByUsername).build(); 188 218 } 219 + License currentLicense = em.find(License.class, lic.getId());220 + if (currentLicense == null) {221 + log.error("License with id {} not found in DB", licId);222 + return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License not found for ID: " + licId).build();223 + }224 + if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {225 + if (!bsc.getOrganizationsIds().contains(lic.getPack().getOrganization().getId())) {226 + log.error("License with id {} is not accesible by user {}", licId, bsc.getUserPrincipal());227 + return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to license data").build();228 + }229 + }230 + // TODO: set status based in current one and dates ? use custom actions ?231 + currentLicense.setCreatedBy(createdBy);232 + currentLicense.setCanceledBy(canceledBy);233 + // currentLicense.setPack(pack);234 + currentLicense.setCode(lic.getCode());235 + currentLicense.setFullName(lic.getFullName());236 + currentLicense.setEmail(lic.getEmail());237 + currentLicense.setRequestData(lic.getRequestData());238 + currentLicense.setModificationTimestamp(new Date());239 + em.persist(currentLicense);189 240 190 - lic.setCreatedBy(createdBy);191 - lic.setCanceledBy(canceledBy);192 - lic.setPack(pack);193 - em.persist(lic);194 -195 - return Response.ok(lic).build();241 + return Response.ok(currentLicense).build();196 242 } 197 243 198 244 @DELETE 199 245 @Path("/{licId}") 200 246 @Transactional 247 + @Securable201 248 @Produces( 202 249 { MediaType.APPLICATION_JSON }) 203 - public Response delete(@PathParam("licId") String licId, @Context HttpServletRequest request) {250 + public Response delete(@PathParam("licId") String licId, @Context BasicSecurityContext bsc) {204 251 log.info("Deleting license with id: {}", licId); 205 252 EntityManager em = emProvider.get(); 206 - License org = em.find(License.class, Integer.parseInt(licId));207 - if (org == null) {253 + License lic = em.find(License.class, Integer.parseInt(licId));254 + if (lic == null) {208 255 log.error("License with id {} can not be deleted, It was not found in DB", licId); 209 256 return Response.status(Status.NOT_FOUND).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License was not found, ID: " + licId).build(); 210 257 } 211 258 212 - em.remove(org);259 + if (!bsc.isUserInRole(BasicSecurityContext.ROL_ADMIN)) {260 + if (!bsc.getOrganizationsIds().contains(lic.getPack().getOrganization().getId())) {261 + log.error("License with id {} is not accesible by user {}", licId, bsc.getUserPrincipal());262 + return Response.status(Status.UNAUTHORIZED).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "Unathorized access to license data").build();263 + }264 + }265 +266 + if (lic.getStatus() != License.Status.CANCELED || lic.getStatus() != License.Status.CREATED) {267 + log.error("License {} can not be deleted with status {}", lic.getCode(), lic.getStatus());268 + return Response.status(Status.FORBIDDEN).header(DefaultExceptionHandler.ERROR_MESSAGE_HEADER, "License can not be deleted in current status").build();269 + }270 +271 + em.remove(lic);213 272 return Response.ok(Utils.createMap("success", true, "id", licId)).build(); 214 273 } 215 274 securis/src/main/java/net/curisit/securis/services/PackResource.java
.. .. @@ -101,7 +101,7 @@ 101 101 @Securable 102 102 @Produces( 103 103 { MediaType.APPLICATION_JSON }) 104 - public Response get(@PathParam("packId") String packId, @Context BasicSecurityContext bsc) {104 + public Response get(@PathParam("packId") Integer packId, @Context BasicSecurityContext bsc) {105 105 log.info("Getting pack data for id: {}: ", packId); 106 106 if (packId == null || packId.equals("")) { 107 107 log.error("Pack ID is mandatory"); .. .. @@ -109,7 +109,7 @@ 109 109 } 110 110 111 111 EntityManager em = emProvider.get(); 112 - Pack pack = em.find(Pack.class, Integer.parseInt(packId));112 + Pack pack = em.find(Pack.class, packId);113 113 if (pack == null) { 114 114 log.error("Pack with id {} not found in DB", packId); 115 115 return Response.status(Status.NOT_FOUND).build(); .. .. @@ -171,10 +171,10 @@ 171 171 @Consumes(MediaType.APPLICATION_JSON) 172 172 @Produces( 173 173 { MediaType.APPLICATION_JSON }) 174 - public Response modify(Pack pack, @PathParam("packId") String packId) {174 + public Response modify(Pack pack, @PathParam("packId") Integer packId) {175 175 log.info("Modifying pack with id: {}", packId); 176 176 EntityManager em = emProvider.get(); 177 - Pack currentPack = em.find(Pack.class, Integer.parseInt(packId));177 + Pack currentPack = em.find(Pack.class, packId);178 178 179 179 Organization org = null; 180 180 if (pack.getOrgId() != null) { securis/src/main/resources/static/css/securis.css
.. .. @@ -15,6 +15,7 @@ 15 15 } 16 16 } 17 17 18 +18 19 a { 19 20 cursor: default !important; 20 21 } securis/src/main/resources/static/js/licenses.js
.. .. @@ -60,7 +60,7 @@ 60 60 if (!txt || txt.length <= len) return txt; 61 61 return txt.substring(0, len) + '...'; 62 62 } 63 - $scope.currentPackId = $store.get('currentPackId');63 + $scope.currentPack = $store.get('currentPack');64 64 65 65 }]); 66 66 .. .. @@ -153,11 +153,131 @@ 153 153 $scope.showForm = false; 154 154 } 155 155 156 - $scope.selectPack = function(packId) {157 - $scope.$parent.currentPackId = packId;158 - $store.put('currentPackId', packId);156 + $scope.selectPack = function(pack) {157 + $scope.$parent.currentPack = pack;158 + $store.put('currentPack', pack);159 + $scope.$parent.$broadcast('pack_changed', pack);159 160 } 160 161 161 162 } ]); 162 163 164 + app.controller('LicensesCtrl', [165 + '$scope',166 + '$http',167 + '$resource',168 + 'toaster',169 + '$store',170 + '$L',171 + function($scope, $http, $resource, toaster, $store, $L) {172 + $scope.$on('pack_changed', function(evt, message) {173 + $scope.licenses = licenseResource.query({packId: $scope.currentPack.id});174 + console.log('on pack_changed');175 + if ($scope.showForm) {176 + if ($scope.isNew) {177 + $scope.license.pack_id = $scope.currentPack.id178 + } else {179 + $scope.showForm = false;180 + }181 + }182 + })183 +184 + var licenseResource = $resource('/license/:licenseId', {185 + licenseId : '@id'186 + });187 + $scope.mandatory = {188 + code: true189 + }190 + $scope.maxlength = {191 + code: 50,192 + comments: 1024193 + }194 + $scope.refs = {};195 +196 + // Used to create the form with the appropriate data197 + $scope.isNew = undefined;198 +199 + // Selected license from listing200 + // license is the edited license, in creation contains the data for the new license201 + $scope.license = null;202 + if ($scope.currentPack)203 + $scope.licenses = licenseResource.query({packId: $scope.currentPack.id});204 +205 + $scope.save = function() {206 + var _success = function() {207 + if (!$scope.isNew) $scope.showForm = false;208 + $scope.licenses = licenseResource.query({packId: $scope.currentPack.id});209 + }210 + licenseResource.save($scope.license, _success)211 + }212 +213 + $scope.newLicense = function() {214 + if (!$scope.currentPack) {215 + BootstrapDialog.show({216 + title: $L.get('New license'),217 + type: BootstrapDialog.TYPE_WARNING,218 + message: $L.get('Please, select a pack before to create a new license'),219 + buttons: [{220 + label: 'OK',221 + action: function(dialog) {222 + dialog.close();223 + }224 + }]225 + });226 + return;227 + }228 +229 + $scope.isNew = true;230 + $scope.showForm = true;231 + $scope.license = {232 + pack_id: $scope.currentPack.id233 + }234 + setTimeout(function() {235 + $('#licenseForm * #code').focus();236 + }, 0);237 + }238 +239 + $scope.editLicense = function(selectedlicense) {240 + $scope.isNew = false;241 + $scope.showForm = true;242 + $scope.license = selectedlicense;243 + setTimeout(function() {244 + $('#licenseForm * #code').focus();245 + }, 0);246 + }247 +248 + $scope.deletelicense = function(selectedlicense) {249 + $scope.showForm = false;250 + BootstrapDialog.confirm($L.get("The license '{0}' will be deleted, are you sure?", selectedlicense.code), function(result){251 + if(result) {252 + var promise = licenseResource.remove({}, {id: selectedlicense.id}).$promise;253 + promise.then(function(data) {254 + $scope.selectlicense(null);255 + $scope.licenses = licenseResource.query({packId: $scope.currentPack.id});256 + toaster.pop('success', Catalogs.getName(), $L.get("License '{0}' deleted successfully", selectedlicense.code));257 + },function(error) {258 + console.log(error);259 + toaster.pop('error', Catalogs.getName(), $L.get("Error deleting license, reason: {0}. Details: {1}", $L.get(HTTP_ERRORS[error.status]), error.headers('X-SECURIS-ERROR')), 10000);260 + });261 + }262 + });263 + $scope.isNew = false;264 + }265 +266 +267 + $scope.cancel = function() {268 + $scope.showForm = false;269 + }270 +271 + $scope.showStatus = function(lic) {272 +273 + }274 + $scope.showStatusComplete = function(license) {275 +276 + }277 + $scope.isActionVisible = function(actionMask, lic) {278 +279 + }280 +281 + } ]);282 +163 283 })(); securis/src/main/resources/static/js/main.js
.. .. @@ -21,26 +21,27 @@ 21 21 } 22 22 }); 23 23 24 - m.factory('securisHttpInterceptor', function($q, $location, $store) {24 + m.factory('securisHttpInterceptor', function($q, $location, $store, toaster) {25 25 var isUnauthorizedAccess = function(rejection) { 26 - console.log('rejection -----------------------');27 - console.log(rejection);28 26 return rejection.status === 401 /* Unauthorized */; 29 27 } 30 28 return { 31 29 'request': function(config) { 32 - var la = $store.get('last_access');33 - var now = new Date().getTime();34 - if (la !== null) {35 - if (now > (la + 1800000)) { // Session timeout is 1/2 hour36 - $store.clear();37 - $location.path('/login');38 - BootstrapDialog.alert('Session has expired');39 - } else {40 - console.log('Last access recent');41 - }42 - }43 - $store.set('last_access', now);30 + var token = $store.get('token');31 + if (token) {32 + var la = $store.get('last_access');33 + var now = new Date().getTime();34 + if (la !== null) {35 + if (now > (la + 1800000)) { // Session timeout is 1/2 hour36 + $store.clear();37 + $location.path('/login');38 + toaster.pop('warning', 'Session has expired', null, 4000);39 + } else {40 + console.log('Last access recent');41 + }42 + }43 + $store.set('last_access', now);44 + }44 45 return config || $q.when(config); 45 46 }, 46 47 'responseError': function(rejection) { securis/src/main/resources/static/licenses.html
.. .. @@ -145,7 +145,7 @@ 145 145 </tr> 146 146 </thead> 147 147 <tbody> 148 - <tr ng-repeat="p in packs | filter:searchText" ng-dblclick="editPack(p)" ng-class="{success: currentPackId === p.id}" ng-click="selectPack(p.id)">148 + <tr ng-repeat="p in packs | filter:searchText" ng-dblclick="editPack(p)" ng-class="{success: currentPack.id === p.id}" ng-click="selectPack(p)">149 149 <td style="white-space: nowrap;" ng-bind="p.code"></td> 150 150 <td ng-bind="ellipsis(p.organization_name, 20)" title="{{pack.organization_name}}" ></td> 151 151 <td ng-bind="p.application_name"></td> .. .. @@ -164,8 +164,8 @@ 164 164 165 165 </div> 166 166 167 - <div id="licenses_section" class="col-md-6" >168 - <nav class="navbar navbar-default navbar-static-top" ng-disabled="currentPackId === null">167 + <div id="licenses_section" class="col-md-6" ng-controller="LicensesCtrl">168 + <nav class="navbar navbar-default navbar-static-top" ng-disabled="!currentPack">169 169 <!-- Brand and toggle get grouped for better mobile display --> 170 170 <div class="navbar-header success"> 171 171 <a class="navbar-brand" i18n>Licenses</a> .. .. @@ -175,9 +175,9 @@ 175 175 <div class="collapse navbar-collapse" 176 176 id="bs-example-navbar-collapse-1"> 177 177 <ul class="nav navbar-nav"> 178 - <li><a i18n ng-click="editNewLicense()"><span class="glyphicon glyphicon-plus"></span>178 + <li><a i18n ng-click="newLicense()"><span class="glyphicon glyphicon-plus"></span>179 179 New</a></li> 180 - <li><a i18n ng-click="cancelEditionLicense()"> <span180 + <li><a i18n ng-click="cancel()"> <span181 181 class="glyphicon glyphicon-ban-circle"></span> Cancel 182 182 </a></li> 183 183 </ul> .. .. @@ -191,12 +191,179 @@ 191 191 </div> 192 192 </nav> 193 193 194 - <div ng-if="currentPackId === null" class="well well-lg">194 + <div ng-if="!currentPack" class="well well-lg">195 195 <h4 i18n>No pack selected</h4> 196 196 <p i18n>Please, select a pack to manage its licenses</p> 197 197 </div> 198 198 199 - <div class="panel panel-default" ng-if="currentPackId !== null">199 + <div ng-if="currentPack" class="panel panel-default animate-show ng-hide" ng-show="showForm">200 + <form role="form" class="form-horizontal " name="licenseForm" id="licenseForm" ng-submit="save()" >201 + <div class="form-group" ng-if="!isNew">202 + <label class="col-md-3 control-label" >ID</label>203 + <div class="col-md-8">204 + <p class="form-control-static" ng-bind="license.id"></p>205 + </div>206 + </div>207 + <div class="form-group" >208 + <label class="col-md-3 control-label" for="pack_id" i18n>Pack</label>209 + <div class="col-md-8">210 + <p class="form-control-static" ng-bind="currentPack.code"></p>211 + <input type="hidden" id="pack_id" name="pack_id" ng-model="license.pack_id" />212 + </div>213 + </div>214 + <div class="form-group" >215 + <label class="col-md-3 control-label" for="code" i18n>Code</label>216 + <div class="col-md-8">217 + <input type="string" id="code" name="code" placeholder="" class="form-control" ng-model="license.code" ng-required="mandatory.code" ng-maxlength="{{maxlength.code}}" />218 + <div class="alert inline-alert alert-warning" ng-show="licenseForm.code.$invalid">219 + <span class="glyphicon glyphicon-warning-sign"></span>220 + <span ng-show="licenseForm.code.$error.maxlength" ng-bind="maxlengthErrorMsg('Code', maxlength.code)"></span>221 + <span ng-show="licenseForm.code.$error.required" ng-bind="mandatoryFieldErrorMsg('Code')"></span>222 + </div>223 + </div>224 + </div>225 + <div class="form-group" ng-if="!isNew">226 + <label class="col-md-3 control-label" i18n>Status</label>227 + <div class="col-md-8">228 + <p class="form-control-static" ng-bind="showStatusComplete(license)"></p>229 + </div>230 + </div>231 +232 + <div class="form-group" >233 + <label class="col-md-3 control-label" for="full_name" i18n>User full name</label>234 + <div class="col-md-8">235 + <input type="string" id="full_name" name="full_name" placeholder="" class="form-control" ng-model="license.full_name" ng-required="mandatory.full_name" />236 + <div class="alert inline-alert alert-warning" ng-show="licenseForm.full_name.$invalid">237 + <span class="glyphicon glyphicon-warning-sign"></span>238 + <span ng-show="licenseForm.full_name.$error.maxlength" ng-bind="maxlengthErrorMsg('User full name', maxlength.full_name)"></span>239 + <span ng-show="licenseForm.full_name.$error.required" ng-bind="mandatoryFieldErrorMsg('User full name')"></span>240 + </div>241 + </div>242 + </div>243 +244 + <div class="form-group" >245 + <label class="col-md-3 control-label" for="email" i18n>User email</label>246 + <div class="col-md-8">247 + <input type="email" id="email" name="email" placeholder="" class="form-control" ng-model="license.email" ng-required="mandatory.email" />248 + <div class="alert inline-alert alert-warning" ng-show="licenseForm.email.$invalid">249 + <span class="glyphicon glyphicon-warning-sign"></span>250 + <span ng-show="licenseForm.email.$error.email" ng-bind="'Please, write a valid email address'"></span>251 + <span ng-show="licenseForm.email.$error.maxlength" ng-bind="maxlengthErrorMsg('User email', maxlength.email)"></span>252 + <span ng-show="licenseForm.email.$error.required" ng-bind="mandatoryFieldErrorMsg('User email')"></span>253 + </div>254 + </div>255 + </div>256 +257 + <div class="form-group" ng-if="isNew || !license.request_data" >258 + <label class="col-md-3 control-label" for="request_data" i18n>Request data</label>259 + <div class="col-md-8">260 + <textarea type="string" id="request_data" name="request_data" placeholder=""261 + class="form-control" ng-model="license.request_data" rows="2" ng-required="mandatory.request_data" ng-maxlength="{{maxlength.request_data}}"></textarea>262 + <div class="alert inline-alert alert-warning" ng-show="licenseForm.request_data.$invalid">263 + <span class="glyphicon glyphicon-warning-sign"></span>264 + <span ng-show="licenseForm.request_data.$error.maxlength" ng-bind="maxlengthErrorMsg('Request data', maxlength.request_data)"></span>265 + <span ng-show="licenseForm.request_data.$error.required" ng-bind="mandatoryFieldErrorMsg('Request data')"></span>266 + </div>267 + </div>268 + </div>269 +270 + <div class="form-group" >271 + <label class="col-md-3 control-label" for="comments" i18n>Comments</label>272 + <div class="col-md-8">273 + <textarea type="string" id="comments" name="comments" placeholder=""274 + class="form-control" ng-model="license.comments" rows="2" ng-required="mandatory.comments" ng-maxlength="{{maxlength.comments}}"></textarea>275 + <div class="alert inline-alert alert-warning" ng-show="licenseForm.comments.$invalid">276 + <span class="glyphicon glyphicon-warning-sign"></span>277 + <span ng-show="licenseForm.comments.$error.maxlength" ng-bind="maxlengthErrorMsg('Comments', maxlength.comments)"></span>278 + <span ng-show="licenseForm.comments.$error.required" ng-bind="mandatoryFieldErrorMsg('comments')"></span>279 + </div>280 + </div>281 + </div>282 +283 + <div class="form-group" ng-if="!isNew && license.request_data">284 + <label class="col-md-3 control-label" i18n>Request data</label>285 + <div class="col-md-8">286 + <pre class="form-control-static" ng-bind="license.request_data | json"></pre>287 + </div>288 + </div>289 +290 + <div class="form-group" ng-if="!isNew && license.license_data">291 + <label class="col-md-3 control-label" i18n >License file</label>292 + <div class="col-md-8">293 + <p class="form-control-static" ng-bind="license.license_data"></p>294 + <button id="downloadLicense" class="btn btn-xs btn-link" ng-click="downloadLicense(license)">295 + <span i18n class="glyphicon glyphicon-download"></span>296 + </button>297 + </div>298 + </div>299 +300 + <div class="form-group" ng-if="!isNew">301 + <label class="col-md-3 control-label" i18n>Created by</label>302 + <div class="col-md-8">303 + <p class="form-control-static" ng-bind="license.created_by_name"></p>304 + </div>305 + </div>306 +307 + <div class="form-group" ng-if="!isNew && license.canceled_by_name">308 + <label class="col-md-3 control-label" >Canceled by</label>309 + <div class="col-md-8">310 + <p class="form-control-static" ng-bind="license.canceled_by_name"></p>311 + </div>312 + </div>313 +314 + <div class="form-group" ng-if="!isNew">315 + <label class="col-md-3 control-label" i18n>Creation date</label>316 + <div class="col-md-8">317 + <p class="form-control-static" ng-bind="license.creationTimestamp | date:'medium'"></p>318 + </div>319 + </div>320 +321 + <div class="form-group" ng-if="!isNew">322 + <label class="col-md-3 control-label" i18n >Modification date</label>323 + <div class="col-md-8">324 + <p class="form-control-static" ng-bind="license.modificationTimestamp | date:'medium'"></p>325 + </div>326 + </div>327 +328 + <div class="form-group" ng-if="!isNew && license.activationTimestamp">329 + <label class="col-md-3 control-label" i18n >Activation date</label>330 + <div class="col-md-8">331 + <p class="form-control-static" ng-bind="license.activationTimestamp | date:'medium'"></p>332 + </div>333 + </div>334 +335 + <div class="form-group" ng-if="!isNew && license.sendTimestamp">336 + <label class="col-md-3 control-label" i18n >Send date</label>337 + <div class="col-md-8">338 + <p class="form-control-static" ng-bind="license.sendTimestamp | date:'medium'"></p>339 + </div>340 + </div>341 +342 + <div class="form-group" ng-if="!isNew && license.cancelationTimestamp">343 + <label class="col-md-3 control-label" i18n >Cancelation date</label>344 + <div class="col-md-8">345 + <p class="form-control-static" ng-bind="license.cancelationTimestamp | date:'medium'"></p>346 + </div>347 + </div>348 +349 + <div class="form-group" ng-if="!isNew && license.lastAccessTimestamp">350 + <label class="col-md-3 control-label" i18n>Last access date</label>351 + <div class="col-md-8">352 + <p class="form-control-static" ng-bind="license.lastAccessTimestamp | date:'medium'"></p>353 + </div>354 + </div>355 +356 + <div class="form-group">357 + <div class="col-md-offset-3 col-md-10" id="saveContainer">358 + <button id="save" type="submit" class="btn btn-primary" >359 + <span i18n class="glyphicon glyphicon-floppy-disk"></span> Save360 + </button>361 + </div>362 + </div>363 + </form>364 + </div>365 +366 + <div class="panel panel-default" ng-if="currentPack">200 367 <div class="panel-heading"> 201 368 <span i18n>Licenses for pack: </span>{{currentPack.code}} 202 369 <span style="color: lightgreen;" class="badge pull-right" ng-bind="currentPack.lic_available || 0"></span> .. .. @@ -217,19 +384,20 @@ 217 384 <tbody> 218 385 <tr ng-repeat="lic in licenses | filter:searchLicenseText" ng-dblclick="editLicense(lic)" > 219 386 <td style="white-space: nowrap;" ng-bind="lic.code"></td> 220 - <td ng-bind="ellipsis(lic.user_fullname, 20)" title="{{lic.user_fullname}}" ></td>221 - <td ng-bind="ellipsis(lic.user_email, 30)" title="{{lic.user_email}}" ></td>222 - <td ng-bind="lic.status"></td>387 + <td ng-bind="ellipsis(lic.full_name, 20)" title="{{lic.full_name}}" ></td>388 + <td ng-bind="ellipsis(lic.email, 30)" title="{{lic.email}}" ></td>389 + <td ng-bind="showStatus(lic.status)"></td>223 390 <td> 224 391 <div class="dropdown"> 225 392 <a class="dropdown-toggle" data-toggle="dropdown" > 226 393 <span class="glyphicon glyphicon-align-justify"></span> <span class="caret"></span> 227 394 </a> 228 395 <ul class="dropdown-menu"> 229 - <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-pencil"></span> <span i18n>Edit</span></a></li>230 - <li><a ng-click="activateLicense(lic)"><span class="glyphicon glyphicon-check"></span> <span i18n>Activate</span></a></li>231 - <li><a ng-click="sendEmail(lic)"><span class="glyphicon glyphicon-send"></span> <span i18n>Send email</span></a></li>232 - <li><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-remove"></span> <span i18n>Remove</span></a></li>396 + <li ng-if="isActionVisible(1, lic)"><a ng-click="downloadLicense(lic)"><span class="glyphicon glyphicon-download"></span> <span i18n>Download</span></a></li>397 + <li ng-if="isActionVisible(2, lic)"><a ng-click="editLicense(lic)"><span class="glyphicon glyphicon-pencil"></span> <span i18n>Edit</span></a></li>398 + <li ng-if="isActionVisible(4, lic)"><a ng-click="activateLicense(lic)"><span class="glyphicon glyphicon-check"></span> <span i18n>Activate</span></a></li>399 + <li ng-if="isActionVisible(8, lic)"><a ng-click="sendEmail(lic)"><span class="glyphicon glyphicon-send"></span> <span i18n>Send email</span></a></li>400 + <li ng-if="isActionVisible(16, lic)"><a ng-click="deleteLicense(lic)"><span class="glyphicon glyphicon-remove"></span> <span i18n>Remove</span></a></li>233 401 </ul> 234 402 </div> 235 403 </td>