From f8831580018fb0495201e94537703a43f05280d3 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Fri, 10 Apr 2015 23:57:36 -0700 Subject: [PATCH] cleaning up MIFOSX-1947 --- api-docs/apiLive.htm | 307 +++--------------- .../integrationtests/StaffImageApiTest.java | 5 + .../integrationtests/common/ImageHelper.java | 44 ++- .../api/ImagesApiResource.java | 92 ++++-- ...EntityTypeForImageManagementException.java | 18 + .../exception/InvalidImageTypeException.java | 13 - .../service/ImageReadPlatformService.java | 2 +- .../service/ImageReadPlatformServiceImpl.java | 48 ++- .../service/ImageWritePlatformService.java | 12 +- ...WritePlatformServiceJpaRepositoryImpl.java | 88 ++--- ...pdate script for General_Ledger_report.sql | 8 +- .../core_db/V257__staff_image_association.sql | 15 +- 12 files changed, 228 insertions(+), 424 deletions(-) create mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidEntityTypeForImageManagementException.java delete mode 100644 mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidImageTypeException.java diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 5ee65369c..deaf1870e 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -1796,6 +1796,26 @@ Update a Staff Member + + Staff Images + staff/{staffId}/images + Upload an Image for a Staff Member (as DATA URI) + Get Staff Image (DATA URI) + Update Staff Image (DATA URI) + Delete Staff Image + + + + + + Upload an Image for a Staff Member (Multi-part form data) + + Get Staff Image (Binary file) + + Update Staff Image (Multi-part form data) + + + Charges charges @@ -4709,12 +4729,12 @@ No Request Body:  
-

Client Images

+

Images

The current API provides support for the addition of a single - image for a Client
+ image for entities like Client (URL pattern /clients) and Staff (URL pattern /staff)
Allowed formats: JPEG (.jpg or .jpeg), GIF (.gif) and PNG (.png)

-

The API supports two different Approaches for manipulating Client Images +

The API supports two different Approaches for manipulating Images

  • Data URI's: For easier manipulation by Javascript clients etc in supported Browsers. @@ -4731,10 +4751,11 @@ No Request Body: class="old-syle-anchor"> 
    -

    Get Client Image (DATA URI)

    -

    Optional arguments are identical to those of Get Client Image (Binary file)

    +

    Get Entity Image (DATA URI)

    +

    Optional arguments are identical to those of Get Image associated with an Entity (Binary file)

    Example Requests:

    clients/1/images
    +
    staff/1/images

    @@ -4757,7 +4778,7 @@ gAEAOg13B6R/SAgAAAAASUVORK5CYII= class="old-syle-anchor"> 
    -

    Get Client Image (Binary file)

    +

    Get Image associated with an Entity (Binary file)

    Optional Arguments
    output
    @@ -4791,7 +4812,7 @@ GET https://DomainName/api/v1/clients/{clientId}/images Accept: application/octet-stream -POST clients/1/images +GET clients/1/images Accept: application/octet-stream @@ -4803,7 +4824,7 @@ Accept: application/octet-stream  
    -

    Upload an Image for a Client (Data URI)

    +

    Upload an Image for an Entity (Data URI)

    @@ -4831,7 +4852,7 @@ P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC  
    -

    Upload an Image for a Client (Multi-part Form data)

    +

    Upload an Image for an Entity (Multi-part Form data)

    @@ -4871,7 +4892,7 @@ Request Body: Not shown 
    -

    Update a Client Image (Data URI)

    +

    Update a Image associated with an Entity (Data URI)

    @@ -4899,7 +4920,7 @@ P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC  
    -

    Update a Client Image (Multi-part Form data)

    +

    Update an Entity's Image (Multi-part Form data)

    Mandatory Fields
    @@ -4932,7 +4953,7 @@ Request Body: Not shown  
    -

    Delete a Client Image

    +

    Delete an Entity's Image

    @@ -16767,252 +16788,7 @@ Request Body:
    -   -
    -
    -

    Staff Images

    -

    The current API provides support for the addition of a single - image for a Staff Member
    - Allowed formats: JPEG (.jpg or .jpeg), GIF (.gif) and PNG (.png) -

    -

    The API supports two different Approaches for manipulating Staff Images -

      -
    • Data URI's: - For easier manipulation by Javascript clients etc in supported Browsers. -
    • -
    • - Multi-part form data: - Images can be uploaded using Multi part forms and downloaded as regular binary files -
    • -
      -
    -
    -   -
    -
    -

    Upload an Image for a Staff (Data URI)

    -
    -
    - -POST https://DomainName/api/v1/staff/{staffId}/images - - -POST staff/1/images -Content-Type: text/plain -Request Body: -data:image/png;base64,iVBORw0KGgoAA -AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l -EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6 -P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC - - -{ - "resourceId": 1, - "changes": {}, - "resourceIdentifier": "1" -} - -
    -
    -   -
    -
    -

    Upload an Image for a Staff Member (Multi-part Form data)

    -
    Mandatory Fields
    - - - - - - -
    Mandatory Fields
    file -
    -

    - The form should contain a required named body part with the - name "file".

    - If you are using a HTML form, a snippet like - <input type="file" name="file"></input> - can be used for uploading the image file -

    -
    -
    - -POST https://DomainName/api/v1/staff/{staffId}/images - - -POST staff/1/images -Content-Type: multipart/form-data -Request Body: Not shown - - -{ - "resourceId": 1, - "changes": {}, - "resourceIdentifier": "1" -} - -
    -
    - -  -
    -
    -

    Get Staff Image (DATA URI)

    -

    Optional arguments are identical to those of Get Staff Image (Binary file)

    -

    Example Requests:

    -
    staff/1/images
    -
    -
    -
    - -GET https://DomainName/api/v1/staff/{staffId}/images -Accept: text/plain - - - -data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJ -bWFnZVJlYWR5ccllPAAAAJ1JREFUeNpi+P//PwMIA4E9EG8E4idQDGLbw+WhiiqA+D8OXAFVAzbp -DxBvB2JLIGaGYkuoGEjOhhFIHAbij0BdPgxYACMj42ogJQpifwBiXSDeC8JIbt4LxSC5DyxQjTeB -+BeaYb+Q5EBOAVutCzMJHUNNPADzzDokiYdAfAmJvwLkGeTgWQfyKZICS6hYBTwc0QL8ORSjBDhA -gAEAOg13B6R/SAgAAAAASUVORK5CYII= - -
    -
    - -  -
    -
    -

    Get Staff Image (Binary file)

    -
    Optional Arguments
    -
    -
    output
    -
    - String optional one of octet or inline_octet -
    -
    The query parameter overrides the "Accept" Header and sets the Media Type to "application/octet-stream"
    - octet : Returns the image binary file as an attachment. The Content-Disposition header is set to attachment;filename=somefile.ext
    - inline_octet :The Content-Disposition header is set to inline;filename=somefile.ext -
    -
    maxWidth
    -
    - Integer optional -
    -
    Triggers resizing of the image to the defined width
    - -
    maxHeight
    -
    -
    - Integer optional -
    - -
    Triggers resizing of the image to the defined height
    - - -
    -
    -
    - -GET https://DomainName/api/v1/staff/{staffId}/images -Accept: application/octet-stream - - -POST staff/1/images -Accept: application/octet-stream - - -Not shown: The corresponding binary (image) file - -
    -
    - -   -
    -
    -

    Update a Staff Image (Data URI)

    -
    -
    - -PUT https://DomainName/api/v1/staff/{staffId}/images - - -PUT staff/1/images -Content-Type: text/plain -Request Body: -data:image/png;base64,iVBORw0KGgoAA -AANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAM0l -EQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4Ug9C9zwz3gVLMDA/A6 -P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC - - -{ - "resourceId": 1, - "changes": {}, - "resourceIdentifier": "1" -} - -
    -
    - -   -
    -
    -

    Update a Staff Member Image (Multi-part Form data)

    - - - - - - - -
    Mandatory Fields
    file -
    -

    - The form should contain a required named body part with the - name "file".

    - If you are using a HTML form, a snippet like - <input type="file" name="file"></input> - can be used for uploading the image file -

    -
    -
    - -PUT https://DomainName/api/v1/staff/{staffId}/images - - -PUT staff/1/images -Content-Type: multipart/form-data -Request Body: Not shown - -
    -
    - -   -
    -
    -

    Delete a Staff Image

    -
    -
    - -DELETE https://DomainName/api/v1/staff/{staffId}/images - - -DELETE staff/1/images -Content-Type: application/json -No Request Body: - - -{ - "resourceId": 1, - "changes": {}, - "resourceIdentifier": "1" -} - -
    -
    -  
    @@ -19705,7 +19481,6 @@ Content-Type: application/json Optional: If provided, used along with lockinPeriodFrequencyType to indicate the length of time that the recurring deposit account is 'locked in' and premature closure is not allowed. e.g. 2 Months lockinPeriodFrequencyType - Optional: If provided, used along with lockinPeriodFrequency to indicate the length of time that the recurring deposit account is 'locked in' and premature closure is not allowed. 0=Days, 1=Weeks, 2=Months, 3=Years e.g. 2 Months recurringDepositFrequency @@ -36498,21 +36273,21 @@ No Request Body: - Staff Images + Staff Images staff/{staffId}/images - Upload an Image for a Staff Member (as DATA URI) - Get Staff Image (DATA URI) - Update Staff Image (DATA URI) - Delete Staff Image + Upload an Image for a Staff Member (as DATA URI) + Get Staff Image (DATA URI) + Update Staff Image (DATA URI) + Delete Staff Image - + Upload an Image for a Staff Member (Multi-part form data) - Get Staff Image (Binary file) - + Get Staff Image (Binary file) + Update Staff Image (Multi-part form data) diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/StaffImageApiTest.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/StaffImageApiTest.java index ee428c037..84f0db56c 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/StaffImageApiTest.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/StaffImageApiTest.java @@ -1,3 +1,8 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ package org.mifosplatform.integrationtests; import com.sun.jersey.core.util.Base64; diff --git a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ImageHelper.java b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ImageHelper.java index 3bc149a89..13fa34519 100644 --- a/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ImageHelper.java +++ b/mifosng-provider/src/integrationTest/java/org/mifosplatform/integrationtests/common/ImageHelper.java @@ -1,15 +1,14 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ package org.mifosplatform.integrationtests.common; -import com.jayway.restassured.http.ContentType; +import org.apache.http.HttpHeaders; + import com.jayway.restassured.specification.RequestSpecification; import com.jayway.restassured.specification.ResponseSpecification; -import com.sun.jersey.core.util.Base64; -import org.apache.http.HttpHeaders; -import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; - -import java.io.IOException; -import java.util.HashMap; -import java.util.List; public class ImageHelper { @@ -17,40 +16,38 @@ public class ImageHelper { private static final String IMAGES_URI = "/images"; public static Integer createImageForStaff(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - Integer staffId) { + Integer staffId) { System.out.println("---------------------------------CREATING AN IMAGE FOR STAFF---------------------------------------------"); - String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI+ "?" + Utils.TENANT_IDENTIFIER; + String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI + "?" + Utils.TENANT_IDENTIFIER; return Utils.performServerPost(requestSpec, responseSpec, URL, generateImageAsText(), "resourceId"); } public static Integer updateImageForStaff(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - Integer staffId) { + Integer staffId) { System.out.println("---------------------------------UPDATING AN IMAGE FOR STAFF---------------------------------------------"); - String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI+ "?" + Utils.TENANT_IDENTIFIER; - requestSpec.header(HttpHeaders.CONTENT_TYPE,"mutlipart/form-data"); + String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI + "?" + Utils.TENANT_IDENTIFIER; return Utils.performServerPut(requestSpec, responseSpec, URL, generateImageAsText(), "resourceId"); } public static String getStaffImageAsText(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - Integer staffId){ + Integer staffId) { System.out.println("---------------------------------RETRIEVING STAFF IMAGE---------------------------------------------"); - String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI+ "?" + Utils.TENANT_IDENTIFIER; - requestSpec.header(HttpHeaders.ACCEPT,"text/plain"); + String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI + "?" + Utils.TENANT_IDENTIFIER; + requestSpec.header(HttpHeaders.ACCEPT, "text/plain"); return Utils.performGetTextResponse(requestSpec, responseSpec, URL); } public static byte[] getStaffImageAsBinary(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - Integer staffId){ + Integer staffId) { System.out.println("---------------------------------RETRIEVING STAFF IMAGE---------------------------------------------"); - String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI+ "?" + Utils.TENANT_IDENTIFIER; - requestSpec.header(HttpHeaders.ACCEPT,"application/octet-stream"); + String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI + "?" + Utils.TENANT_IDENTIFIER; + requestSpec.header(HttpHeaders.ACCEPT, "application/octet-stream"); return Utils.performGetBinaryResponse(requestSpec, responseSpec, URL); } - public static Integer deleteStaffImage(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, - Integer staffId){ + public static Integer deleteStaffImage(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, Integer staffId) { System.out.println("---------------------------------RETRIEVING STAFF IMAGE---------------------------------------------"); - String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI+ "?" + Utils.TENANT_IDENTIFIER; + String URL = STAFF_IMAGE_URL + staffId + IMAGES_URI + "?" + Utils.TENANT_IDENTIFIER; return Utils.performServerDelete(requestSpec, responseSpec, URL, "resourceId"); } @@ -60,7 +57,4 @@ public class ImageHelper { + "DxBvB2JLIGaGYkuoGEjOhhFIHAbij0BdPgxYACMj42ogJQpifwBiXSDeC8JIbt4LxSC5DyxQjTeB\n" + "+BeaYb+Q5EBOAVutCzMJHUNNPADzzDokiYdAfAmJvwLkGeTgWQfyKZICS6hYBTwc0QL8ORSjBDhA\n" + "gAEAOg13B6R/SAgAAAAASUVORK5CYII="; } - private static byte[] generateImageAsBinary(){ - return Base64.decode(generateImageAsText().split("base64,")[1]); - } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java index 2f0b6c813..4d9f81e2f 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/api/ImagesApiResource.java @@ -7,7 +7,16 @@ package org.mifosplatform.infrastructure.documentmanagement.api; import java.io.InputStream; -import javax.ws.rs.*; +import javax.ws.rs.Consumes; +import javax.ws.rs.DELETE; +import javax.ws.rs.GET; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; @@ -19,6 +28,7 @@ import org.mifosplatform.infrastructure.core.serialization.DefaultToApiJsonSeria import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils; import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryUtils.IMAGE_FILE_EXTENSION; import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; +import org.mifosplatform.infrastructure.documentmanagement.exception.InvalidEntityTypeForImageManagementException; import org.mifosplatform.infrastructure.documentmanagement.service.ImageReadPlatformService; import org.mifosplatform.infrastructure.documentmanagement.service.ImageWritePlatformService; import org.mifosplatform.infrastructure.security.service.PlatformSecurityContext; @@ -32,11 +42,7 @@ import com.sun.jersey.core.header.FormDataContentDisposition; import com.sun.jersey.multipart.FormDataBodyPart; import com.sun.jersey.multipart.FormDataParam; -/** - * TODO Vishwas need to change the url to entity/entityId/images to make the - * image management API's generic for other entities like staff etc - **/ -@Path("{clients}/{clientId}/images") +@Path("{entity}/{entityId}/images") @Component @Scope("singleton") public class ImagesApiResource { @@ -61,17 +67,17 @@ public class ImagesApiResource { @POST @Consumes({ MediaType.MULTIPART_FORM_DATA }) @Produces({ MediaType.APPLICATION_JSON }) - public String addNewClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public String addNewClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, @HeaderParam("Content-Length") final Long fileSize, @FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart) { - + validateEntityTypeforImage(entityName); // TODO: vishwas might need more advances validation (like reading magic // number) for handling malicious clients // and clients not setting mime type ContentRepositoryUtils.validateClientImageNotEmpty(fileDetails.getFileName()); ContentRepositoryUtils.validateImageMimeType(bodyPart.getMediaType().toString()); - final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateImage(clientName, clientId, + final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateImage(entityName, entityId, fileDetails.getFileName(), inputStream, fileSize); return this.toApiJsonSerializer.serialize(result); @@ -83,12 +89,12 @@ public class ImagesApiResource { @POST @Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String addNewClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public String addNewClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, final String jsonRequestBody) { - + validateEntityTypeforImage(entityName); final Base64EncodedImage base64EncodedImage = ContentRepositoryUtils.extractImageFromDataURL(jsonRequestBody); - final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateImage(clientName, clientId, base64EncodedImage); + final CommandProcessingResult result = this.imageWritePlatformService.saveOrUpdateImage(entityName, entityId, base64EncodedImage); return this.toApiJsonSerializer.serialize(result); } @@ -99,15 +105,20 @@ public class ImagesApiResource { @GET @Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.TEXT_PLAIN }) - public Response retrieveImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public Response retrieveImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, @QueryParam("maxWidth") final Integer maxWidth, @QueryParam("maxHeight") final Integer maxHeight, @QueryParam("output") final String output) { - if (output != null && (output.equals("octet") || output.equals("inline_octet"))) { return downloadClientImage(clientName, clientId, + validateEntityTypeforImage(entityName); + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equalsIgnoreCase(entityName)) { + this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equalsIgnoreCase(entityName)) { + this.context.authenticatedUser().validateHasReadPermission("STAFFIMAGE"); + } + + if (output != null && (output.equals("octet") || output.equals("inline_octet"))) { return downloadClientImage(entityName, entityId, maxWidth, maxHeight, output); } - this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); - - final ImageData imageData = this.imageReadPlatformService.retrieveImage(clientName, clientId); + final ImageData imageData = this.imageReadPlatformService.retrieveImage(entityName, entityId); // TODO: Need a better way of determining image type String imageDataURISuffix = ContentRepositoryUtils.IMAGE_DATA_URI_SUFFIX.JPEG.getValue(); @@ -124,12 +135,17 @@ public class ImagesApiResource { @GET @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_OCTET_STREAM }) - public Response downloadClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public Response downloadClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, @QueryParam("maxWidth") final Integer maxWidth, @QueryParam("maxHeight") final Integer maxHeight, @QueryParam("output") String output) { + validateEntityTypeforImage(entityName); + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equalsIgnoreCase(entityName)) { + this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equalsIgnoreCase(entityName)) { + this.context.authenticatedUser().validateHasReadPermission("STAFFIMAGE"); + } - this.context.authenticatedUser().validateHasReadPermission("CLIENTIMAGE"); - final ImageData imageData = this.imageReadPlatformService.retrieveImage(clientName, clientId); + final ImageData imageData = this.imageReadPlatformService.retrieveImage(entityName, entityId); final ResponseBuilder response = Response.ok(imageData.getContentOfSize(maxWidth, maxHeight)); String dispositionType = "inline_octet".equals(output) ? "inline" : "attachment"; @@ -149,10 +165,10 @@ public class ImagesApiResource { @PUT @Consumes({ MediaType.MULTIPART_FORM_DATA }) @Produces({ MediaType.APPLICATION_JSON }) - public String updateClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public String updateClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, @HeaderParam("Content-Length") final Long fileSize, @FormDataParam("file") final InputStream inputStream, @FormDataParam("file") final FormDataContentDisposition fileDetails, @FormDataParam("file") final FormDataBodyPart bodyPart) { - return addNewClientImage(clientName, clientId, fileSize, inputStream, fileDetails, bodyPart); + return addNewClientImage(entityName, entityId, fileSize, inputStream, fileDetails, bodyPart); } /** @@ -164,17 +180,39 @@ public class ImagesApiResource { @PUT @Consumes({ MediaType.TEXT_PLAIN, MediaType.TEXT_HTML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String updateClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId, + public String updateClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId, final String jsonRequestBody) { - return addNewClientImage(clientName, clientId, jsonRequestBody); + return addNewClientImage(entityName, entityId, jsonRequestBody); } @DELETE @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - public String deleteClientImage(@PathParam("clients") final String clientName, @PathParam("clientId") final Long clientId) { - this.imageWritePlatformService.deleteImage(clientName, clientId); - return this.toApiJsonSerializer.serialize(new CommandProcessingResult(clientId)); + public String deleteClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId) { + validateEntityTypeforImage(entityName); + this.imageWritePlatformService.deleteImage(entityName, entityId); + return this.toApiJsonSerializer.serialize(new CommandProcessingResult(entityId)); + } + + /*** Entities for document Management **/ + public static enum ENTITY_TYPE_FOR_IMAGES { + STAFF, CLIENTS; + + @Override + public String toString() { + return name().toString().toLowerCase(); + } + } + + private void validateEntityTypeforImage(final String entityName) { + if (!checkValidEntityType(entityName)) { throw new InvalidEntityTypeForImageManagementException(entityName); } + } + + private static boolean checkValidEntityType(final String entityType) { + for (final ENTITY_TYPE_FOR_IMAGES entities : ENTITY_TYPE_FOR_IMAGES.values()) { + if (entities.name().equalsIgnoreCase(entityType)) { return true; } + } + return false; } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidEntityTypeForImageManagementException.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidEntityTypeForImageManagementException.java new file mode 100644 index 000000000..65fb5c0f0 --- /dev/null +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidEntityTypeForImageManagementException.java @@ -0,0 +1,18 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + */ +package org.mifosplatform.infrastructure.documentmanagement.exception; + +import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; + +/** + * Runtime exception for invalid image types + */ +public class InvalidEntityTypeForImageManagementException extends AbstractPlatformResourceNotFoundException { + + public InvalidEntityTypeForImageManagementException(String imageType) { + super("error.imagemanagement.entitytype.invalid", "Image Management is not support for the Entity Type: " + imageType); + } +} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidImageTypeException.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidImageTypeException.java deleted file mode 100644 index e4356f686..000000000 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/exception/InvalidImageTypeException.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.mifosplatform.infrastructure.documentmanagement.exception; - -import org.mifosplatform.infrastructure.core.exception.AbstractPlatformResourceNotFoundException; - -/** - * Runtime exception for invalid image types - */ -public class InvalidImageTypeException extends AbstractPlatformResourceNotFoundException { - - public InvalidImageTypeException(String imageType) { - super("error.documentmanagement.imagetype.invalid", "Image type not supported: " + imageType, imageType); - } -} diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java index 940563645..95d883e43 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformService.java @@ -9,6 +9,6 @@ import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; public interface ImageReadPlatformService { - ImageData retrieveImage(String clientName, Long clientId); + ImageData retrieveImage(String entityType, Long entityId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java index edc6f68f9..d8452c0aa 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageReadPlatformServiceImpl.java @@ -10,6 +10,7 @@ import java.sql.SQLException; import org.mifosplatform.infrastructure.core.domain.JdbcSupport; import org.mifosplatform.infrastructure.core.service.RoutingDataSource; +import org.mifosplatform.infrastructure.documentmanagement.api.ImagesApiResource.ENTITY_TYPE_FOR_IMAGES; import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; import org.mifosplatform.infrastructure.documentmanagement.data.ImageData; @@ -30,13 +31,12 @@ public class ImageReadPlatformServiceImpl implements ImageReadPlatformService { private final JdbcTemplate jdbcTemplate; private final ContentRepositoryFactory contentRepositoryFactory; private final ClientRepositoryWrapper clientRepositoryWrapper; - private final StaffRepositoryWrapper staffRepositoryWrapper; + private final StaffRepositoryWrapper staffRepositoryWrapper; @Autowired public ImageReadPlatformServiceImpl(final RoutingDataSource dataSource, final ContentRepositoryFactory documentStoreFactory, - final ClientRepositoryWrapper clientRepositoryWrapper, - StaffRepositoryWrapper staffRepositoryWrapper) { - this.staffRepositoryWrapper = staffRepositoryWrapper; + final ClientRepositoryWrapper clientRepositoryWrapper, StaffRepositoryWrapper staffRepositoryWrapper) { + this.staffRepositoryWrapper = staffRepositoryWrapper; this.jdbcTemplate = new JdbcTemplate(dataSource); this.contentRepositoryFactory = documentStoreFactory; this.clientRepositoryWrapper = clientRepositoryWrapper; @@ -50,11 +50,11 @@ public class ImageReadPlatformServiceImpl implements ImageReadPlatformService { this.entityDisplayName = entityDisplayName; } - public String schema(String clientName) { + public String schema(String entityType) { StringBuilder builder = new StringBuilder("image.id as id, image.location as location, image.storage_type_enum as storageType "); - if (ImageWritePlatformServiceJpaRepositoryImpl.IMAGE_TYPE.CLIENTS.toString().equals(clientName)) { + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equalsIgnoreCase(entityType)) { builder.append(" from m_image image , m_client client " + " where client.image_id = image.id and client.id=?"); - } else if (ImageWritePlatformServiceJpaRepositoryImpl.IMAGE_TYPE.STAFF.toString().equals(clientName)) { + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equalsIgnoreCase(entityType)) { builder.append("from m_image image , m_staff staff " + " where staff.image_id = image.id and staff.id=?"); } return builder.toString(); @@ -71,34 +71,30 @@ public class ImageReadPlatformServiceImpl implements ImageReadPlatformService { } @Override - public ImageData retrieveImage(String clientName, final Long clientId) { + public ImageData retrieveImage(String entityType, final Long entityId) { try { - Object owner; - String displayName = null; - if (ImageWritePlatformServiceJpaRepositoryImpl.IMAGE_TYPE.CLIENTS - .toString().equals(clientName)) { - owner = this.clientRepositoryWrapper - .findOneWithNotFoundDetection(clientId); - displayName = ((Client) owner).getDisplayName(); - } else if (ImageWritePlatformServiceJpaRepositoryImpl.IMAGE_TYPE.STAFF - .toString().equals(clientName)) { - owner = this.staffRepositoryWrapper - .findOneWithNotFoundDetection(clientId); - displayName = ((Staff) owner).displayName(); - } - final ImageMapper imageMapper = new ImageMapper(displayName); + Object owner; + String displayName = null; + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equalsIgnoreCase(entityType)) { + owner = this.clientRepositoryWrapper.findOneWithNotFoundDetection(entityId); + displayName = ((Client) owner).getDisplayName(); + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equalsIgnoreCase(entityType)) { + owner = this.staffRepositoryWrapper.findOneWithNotFoundDetection(entityId); + displayName = ((Staff) owner).displayName(); + } + final ImageMapper imageMapper = new ImageMapper(displayName); - final String sql = "select " + imageMapper.schema(clientName); + final String sql = "select " + imageMapper.schema(entityType); - final ImageData imageData = this.jdbcTemplate.queryForObject(sql, imageMapper, new Object[] { clientId }); + final ImageData imageData = this.jdbcTemplate.queryForObject(sql, imageMapper, new Object[] { entityId }); final ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(imageData.storageType()); final ImageData result = contentRepository.fetchImage(imageData); - if (result.getContent() == null) { throw new ImageNotFoundException("clients", clientId); } + if (result.getContent() == null) { throw new ImageNotFoundException(entityType, entityId); } return result; } catch (final EmptyResultDataAccessException e) { - throw new ImageNotFoundException("clients", clientId); + throw new ImageNotFoundException("clients", entityId); } } } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java index d45c2017b..456272a01 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformService.java @@ -13,12 +13,12 @@ import org.springframework.security.access.prepost.PreAuthorize; public interface ImageWritePlatformService { - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") - CommandProcessingResult saveOrUpdateImage(String clientName, Long clientId, String imageName, InputStream inputStream, Long fileSize); + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE','CREATE_STAFFIMAGE')") + CommandProcessingResult saveOrUpdateImage(String entityName, Long entityId, String imageName, InputStream inputStream, Long fileSize); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE')") - CommandProcessingResult saveOrUpdateImage(String clientName, Long clientId, Base64EncodedImage encodedImage); + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'CREATE_CLIENTIMAGE','CREATE_STAFFIMAGE')") + CommandProcessingResult saveOrUpdateImage(String entityName, Long entityId, Base64EncodedImage encodedImage); - @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE')") - CommandProcessingResult deleteImage(String clientName, Long clientId); + @PreAuthorize(value = "hasAnyRole('ALL_FUNCTIONS', 'DELETE_CLIENTIMAGE','DELETE_STAFFIMAGE')") + CommandProcessingResult deleteImage(String entityName, Long entityId); } \ No newline at end of file diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java index 0dbcc9455..3259d2ed7 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/documentmanagement/service/ImageWritePlatformServiceJpaRepositoryImpl.java @@ -9,12 +9,12 @@ import java.io.InputStream; import org.mifosplatform.infrastructure.core.data.CommandProcessingResult; import org.mifosplatform.infrastructure.core.domain.Base64EncodedImage; +import org.mifosplatform.infrastructure.documentmanagement.api.ImagesApiResource.ENTITY_TYPE_FOR_IMAGES; import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepository; import org.mifosplatform.infrastructure.documentmanagement.contentrepository.ContentRepositoryFactory; import org.mifosplatform.infrastructure.documentmanagement.domain.Image; import org.mifosplatform.infrastructure.documentmanagement.domain.ImageRepository; import org.mifosplatform.infrastructure.documentmanagement.domain.StorageType; -import org.mifosplatform.infrastructure.documentmanagement.exception.InvalidImageTypeException; import org.mifosplatform.organisation.staff.domain.Staff; import org.mifosplatform.organisation.staff.domain.StaffRepositoryWrapper; import org.mifosplatform.portfolio.client.domain.Client; @@ -33,7 +33,8 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla @Autowired public ImageWritePlatformServiceJpaRepositoryImpl(final ContentRepositoryFactory documentStoreFactory, - final ClientRepositoryWrapper clientRepositoryWrapper, final ImageRepository imageRepository, StaffRepositoryWrapper staffRepositoryWrapper) { + final ClientRepositoryWrapper clientRepositoryWrapper, final ImageRepository imageRepository, + StaffRepositoryWrapper staffRepositoryWrapper) { this.contentRepositoryFactory = documentStoreFactory; this.clientRepositoryWrapper = clientRepositoryWrapper; this.imageRepository = imageRepository; @@ -42,15 +43,9 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla @Transactional @Override - public CommandProcessingResult saveOrUpdateImage(String clientName, final Long clientId, final String imageName, final InputStream inputStream, - final Long fileSize) { - Object owner = null; - if (IMAGE_TYPE.CLIENTS.toString().equals(clientName)){ - owner =this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); - } else if (IMAGE_TYPE.STAFF.toString().equals(clientName)) { - owner = this.staffRepositoryWrapper.findOneWithNotFoundDetection(clientId); - } - deletePreviousImage(owner); + public CommandProcessingResult saveOrUpdateImage(String entityName, final Long clientId, final String imageName, + final InputStream inputStream, final Long fileSize) { + Object owner = deletePreviousImage(entityName, clientId); final ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(); final String imageLocation = contentRepository.saveImage(inputStream, clientId, imageName, fileSize); @@ -59,15 +54,8 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla @Transactional @Override - public CommandProcessingResult saveOrUpdateImage(String clientName, final Long clientId, final Base64EncodedImage encodedImage) { - Object owner = null; - if (IMAGE_TYPE.CLIENTS.toString().equals(clientName)){ - owner =this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); - } else if (IMAGE_TYPE.STAFF.toString().equals(clientName)) { - owner = this.staffRepositoryWrapper.findOneWithNotFoundDetection(clientId); - } - deletePreviousImage(owner); - validateImageType(clientName); + public CommandProcessingResult saveOrUpdateImage(String entityName, final Long clientId, final Base64EncodedImage encodedImage) { + Object owner = deletePreviousImage(entityName, clientId); final ContentRepository contenRepository = this.contentRepositoryFactory.getRepository(); final String imageLocation = contenRepository.saveImage(encodedImage, clientId, "image"); @@ -77,18 +65,17 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla @Transactional @Override - public CommandProcessingResult deleteImage(String clientName, final Long clientId) { - + public CommandProcessingResult deleteImage(String entityName, final Long clientId) { Object owner = null; Image image = null; - if (IMAGE_TYPE.CLIENTS.toString().equals(clientName)){ - owner =this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equals(entityName)) { + owner = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); Client client = (Client) owner; image = client.getImage(); client.setImage(null); this.clientRepositoryWrapper.save(client); - } else if (IMAGE_TYPE.STAFF.toString().equals(clientName)) { + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equals(entityName)) { owner = this.staffRepositoryWrapper.findOneWithNotFoundDetection(clientId); Staff staff = (Staff) owner; image = staff.getImage(); @@ -107,36 +94,42 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla return new CommandProcessingResult(clientId); } - private void deletePreviousImage(final Object owner) { + /** + * @param entityName + * @param entityId + * @return + */ + private Object deletePreviousImage(String entityName, final Long entityId) { + Object owner = null; Image image = null; - Long clientId=null; - if (owner instanceof Client){ - Client client = (Client) owner; + if (ENTITY_TYPE_FOR_IMAGES.CLIENTS.toString().equals(entityName)) { + Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(entityId); image = client.getImage(); - clientId = client.getId(); - } else if (owner instanceof Staff){ - Staff staff = (Staff) owner; + owner = client; + } else if (ENTITY_TYPE_FOR_IMAGES.STAFF.toString().equals(entityName)) { + Staff staff = this.staffRepositoryWrapper.findOneWithNotFoundDetection(entityId); image = staff.getImage(); - clientId = staff.getId(); + owner = staff; } if (image != null) { final ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(StorageType.fromInt(image .getStorageType())); - contentRepository.deleteImage(clientId, image.getLocation()); + contentRepository.deleteImage(entityId, image.getLocation()); } + return owner; } private CommandProcessingResult updateImage(final Object owner, final String imageLocation, final StorageType storageType) { Image image = null; - Long clientId=null; - if (owner instanceof Client){ + Long clientId = null; + if (owner instanceof Client) { Client client = (Client) owner; image = client.getImage(); clientId = client.getId(); image = createImage(image, imageLocation, storageType); client.setImage(image); this.clientRepositoryWrapper.save(client); - } else if (owner instanceof Staff){ + } else if (owner instanceof Staff) { Staff staff = (Staff) owner; image = staff.getImage(); clientId = staff.getId(); @@ -159,25 +152,4 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla return image; } - private void validateImageType(final String clientName) { - if (!checkValidEntityType(clientName)) { throw new InvalidImageTypeException( - clientName); } - } - - private static boolean checkValidEntityType(final String entityType) { - for (final IMAGE_TYPE entities : IMAGE_TYPE.values()) { - if (entities.name().equalsIgnoreCase(entityType)) { return true; } - } - return false; - } - - /*** Entities for document Management **/ - public static enum IMAGE_TYPE { - STAFF,CLIENTS; - - @Override - public String toString() { - return name().toString().toLowerCase(); - }} - } \ No newline at end of file diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V256__Update script for General_Ledger_report.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V256__Update script for General_Ledger_report.sql index c75791dee..55d7a4ee1 100644 --- a/mifosng-provider/src/main/resources/sql/migrations/core_db/V256__Update script for General_Ledger_report.sql +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V256__Update script for General_Ledger_report.sql @@ -19,4 +19,10 @@ set report_parameter_name='branch' where report_id=(select stretchy_report.id from stretchy_report where report_name='GeneralLedgerReport') and parameter_id=(select p.id from stretchy_parameter p where parameter_name='OfficeIdSelectOne'); -INSERT INTO `m_permission` VALUES ('report', 'READ_GeneralLedgerReport', 'GeneralLedgerReport', 'READ', 0); \ No newline at end of file +INSERT INTO `m_permission` ( +`grouping` , +`code` , +`entity_name` , +`action_name` , +`can_maker_checker` +) VALUES ('report', 'READ_General Ledger Report', 'General Ledger Report', 'READ', 0); \ No newline at end of file diff --git a/mifosng-provider/src/main/resources/sql/migrations/core_db/V257__staff_image_association.sql b/mifosng-provider/src/main/resources/sql/migrations/core_db/V257__staff_image_association.sql index 3a7af2f2f..9af582baf 100644 --- a/mifosng-provider/src/main/resources/sql/migrations/core_db/V257__staff_image_association.sql +++ b/mifosng-provider/src/main/resources/sql/migrations/core_db/V257__staff_image_association.sql @@ -1,3 +1,16 @@ ALTER TABLE `m_staff` ADD COLUMN `image_id` BIGINT(20) NULL, - ADD CONSTRAINT `FK_m_staff_m_image` FOREIGN KEY (`image_id`) REFERENCES `m_image` (`id`); \ No newline at end of file + ADD CONSTRAINT `FK_m_staff_m_image` FOREIGN KEY (`image_id`) REFERENCES `m_image` (`id`); + +INSERT INTO m_permission ( +grouping , +code , +entity_name , +action_name , +can_maker_checker +) VALUES +('portfolio', 'READ_STAFFIMAGE', 'STAFFIMAGE', 'READ', '0'), +('portfolio', 'CREATE_STAFFIMAGE', 'STAFFIMAGE', 'CREATE', '1'), +('portfolio', 'CREATE_STAFFIMAGE_CHECKER', 'STAFFIMAGE', 'CREATE', '0'), +('portfolio', 'DELETE_STAFFIMAGE', 'STAFFIMAGE', 'DELETE', '1'), +('portfolio', 'DELETE_STAFFIMAGE_CHECKER', 'STAFFIMAGE', 'DELETE', '0'); \ No newline at end of file