├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.gradle ├── cloudinary-core ├── build.gradle └── src │ ├── main │ └── java │ │ ├── com │ │ └── cloudinary │ │ │ ├── AccessControlRule.java │ │ │ ├── Api.java │ │ │ ├── ArchiveParams.java │ │ │ ├── AuthToken.java │ │ │ ├── BaseParam.java │ │ │ ├── Cloudinary.java │ │ │ ├── Configuration.java │ │ │ ├── Coordinates.java │ │ │ ├── CustomFunction.java │ │ │ ├── EagerTransformation.java │ │ │ ├── ProgressCallback.java │ │ │ ├── ResponsiveBreakpoint.java │ │ │ ├── Search.java │ │ │ ├── SearchFolders.java │ │ │ ├── SignatureAlgorithm.java │ │ │ ├── SmartUrlEncoder.java │ │ │ ├── StoredFile.java │ │ │ ├── Transformation.java │ │ │ ├── Uploader.java │ │ │ ├── Url.java │ │ │ ├── Util.java │ │ │ ├── api │ │ │ ├── ApiResponse.java │ │ │ ├── AuthorizationRequired.java │ │ │ ├── RateLimit.java │ │ │ ├── exceptions │ │ │ │ ├── AlreadyExists.java │ │ │ │ ├── ApiException.java │ │ │ │ ├── BadRequest.java │ │ │ │ ├── GeneralError.java │ │ │ │ ├── NotAllowed.java │ │ │ │ ├── NotFound.java │ │ │ │ └── RateLimited.java │ │ │ └── signing │ │ │ │ ├── ApiResponseSignatureVerifier.java │ │ │ │ ├── NotificationRequestSignatureVerifier.java │ │ │ │ ├── SignedPayloadValidator.java │ │ │ │ └── package-info.java │ │ │ ├── metadata │ │ │ ├── DateMetadataField.java │ │ │ ├── EnumMetadataField.java │ │ │ ├── IntMetadataField.java │ │ │ ├── MetadataDataSource.java │ │ │ ├── MetadataField.java │ │ │ ├── MetadataFieldType.java │ │ │ ├── MetadataRule.java │ │ │ ├── MetadataRuleCondition.java │ │ │ ├── MetadataRuleResult.java │ │ │ ├── MetadataValidation.java │ │ │ ├── Restrictions.java │ │ │ ├── SetMetadataField.java │ │ │ └── StringMetadataField.java │ │ │ ├── provisioning │ │ │ ├── Account.java │ │ │ └── AccountConfiguration.java │ │ │ ├── strategies │ │ │ ├── AbstractApiStrategy.java │ │ │ ├── AbstractUploaderStrategy.java │ │ │ └── StrategyLoader.java │ │ │ ├── transformation │ │ │ ├── AbstractLayer.java │ │ │ ├── BaseExpression.java │ │ │ ├── Condition.java │ │ │ ├── Expression.java │ │ │ ├── FetchLayer.java │ │ │ ├── Layer.java │ │ │ ├── SubtitlesLayer.java │ │ │ └── TextLayer.java │ │ │ └── utils │ │ │ ├── Analytics.java │ │ │ ├── Base64Coder.java │ │ │ ├── Base64Map.java │ │ │ ├── HtmlEscape.java │ │ │ ├── ObjectUtils.java │ │ │ ├── Rectangle.java │ │ │ └── StringUtils.java │ │ └── org │ │ └── cloudinary │ │ └── json │ │ ├── JSONArray.java │ │ ├── JSONException.java │ │ ├── JSONObject.java │ │ ├── JSONString.java │ │ └── JSONTokener.java │ └── test │ └── java │ └── com │ └── cloudinary │ ├── AuthTokenTest.java │ ├── TransformationTest.java │ ├── UtilTest.java │ ├── analytics │ └── AnalyticsTest.java │ ├── api │ └── signing │ │ ├── ApiResponseSignatureVerifierTest.java │ │ └── NotificationRequestSignatureVerifierTest.java │ ├── test │ └── CloudinaryTest.java │ └── transformation │ ├── ExpressionTest.java │ └── LayerTest.java ├── cloudinary-http5 ├── build.gradle └── src │ ├── main │ └── java │ │ └── com │ │ └── cloudinary │ │ └── http5 │ │ ├── ApiStrategy.java │ │ ├── ApiUtils.java │ │ ├── UploaderStrategy.java │ │ └── api │ │ └── Response.java │ └── test │ └── java │ └── com │ └── cloudinary │ └── test │ ├── AccountApiTest.java │ ├── ApiTest.java │ ├── ContextTest.java │ ├── FoldersApiTest.java │ ├── SearchTest.java │ ├── StreamingProfilesApiTest.java │ ├── StructuredMetadataTest.java │ └── UploaderTest.java ├── cloudinary-taglib ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── cloudinary │ │ ├── Singleton.java │ │ ├── SingletonManager.java │ │ └── taglib │ │ ├── CloudinaryImageTag.java │ │ ├── CloudinaryJsConfigTag.java │ │ ├── CloudinaryJsIncludeTag.java │ │ ├── CloudinaryTransformationTag.java │ │ ├── CloudinaryUnsignedUploadTag.java │ │ ├── CloudinaryUploadTag.java │ │ ├── CloudinaryUrl.java │ │ └── CloudinaryVideoTag.java │ └── resources │ └── META-INF │ └── cloudinary.tld ├── cloudinary-test-common ├── build.gradle └── src │ └── main │ ├── java │ └── com │ │ └── cloudinary │ │ └── test │ │ ├── AbstractAccountApiTest.java │ │ ├── AbstractApiTest.java │ │ ├── AbstractContextTest.java │ │ ├── AbstractFoldersApiTest.java │ │ ├── AbstractSearchTest.java │ │ ├── AbstractStreamingProfilesApiTest.java │ │ ├── AbstractStructuredMetadataTest.java │ │ ├── AbstractUploaderTest.java │ │ ├── MetadataTestHelper.java │ │ ├── MockableTest.java │ │ ├── TimeoutTest.java │ │ ├── helpers │ │ └── Feature.java │ │ └── rules │ │ └── RetryRule.java │ └── resources │ ├── docx.docx │ ├── favicon.ico │ ├── old_logo.png │ └── אבג.docx ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java_shared.gradle ├── samples ├── photo_album │ ├── META-INF │ │ └── persistence.xml │ ├── README.md │ ├── pom.xml │ └── src │ │ └── main │ │ ├── java │ │ └── cloudinary │ │ │ ├── controllers │ │ │ └── PhotoController.java │ │ │ ├── lib │ │ │ └── PhotoUploadValidator.java │ │ │ ├── models │ │ │ ├── Photo.java │ │ │ └── PhotoUpload.java │ │ │ └── repositories │ │ │ └── PhotoRepository.java │ │ ├── resources │ │ └── META-INF │ │ │ └── persistence.xml │ │ └── webapp │ │ ├── WEB-INF │ │ ├── messages.properties │ │ ├── mvc-dispatcher-servlet.xml │ │ ├── pages │ │ │ ├── direct_upload_form.jsp │ │ │ ├── photos.jsp │ │ │ ├── post.jsp │ │ │ ├── pre.jsp │ │ │ ├── upload.jsp │ │ │ └── upload_form.jsp │ │ └── web.xml │ │ └── assets │ │ ├── cloudinary_cors.html │ │ ├── javascripts │ │ └── cloudinary │ │ │ ├── canvas-to-blob.min.js │ │ │ ├── jquery.cloudinary.js │ │ │ ├── jquery.fileupload-image.js │ │ │ ├── jquery.fileupload-process.js │ │ │ ├── jquery.fileupload-validate.js │ │ │ ├── jquery.fileupload.js │ │ │ ├── jquery.iframe-transport.js │ │ │ ├── jquery.ui.widget.js │ │ │ └── load-image.min.js │ │ └── stylesheets │ │ └── application.css └── photo_album_gae │ ├── META-INF │ └── persistence.xml │ ├── README.md │ ├── nbactions.xml │ ├── pom.xml │ └── src │ └── main │ ├── java │ ├── cloudinary │ │ ├── controllers │ │ │ └── PhotoController.java │ │ ├── lib │ │ │ └── PhotoUploadValidator.java │ │ └── models │ │ │ └── PhotoUpload.java │ └── org │ │ └── esxx │ │ └── js │ │ └── protocol │ │ ├── GAEClientConnection.java │ │ └── GAEConnectionManager.java │ └── webapp │ ├── WEB-INF │ ├── appengine-web.xml.sample │ ├── logging.properties │ ├── messages.properties │ ├── mvc-dispatcher-servlet.xml │ ├── pages │ │ ├── direct_upload_form.jsp │ │ ├── photos.jsp │ │ ├── post.jsp │ │ ├── pre.jsp │ │ ├── upload.jsp │ │ └── upload_form.jsp │ └── web.xml │ └── assets │ ├── cloudinary_cors.html │ ├── javascripts │ └── cloudinary │ │ ├── canvas-to-blob.min.js │ │ ├── jquery.cloudinary.js │ │ ├── jquery.fileupload-image.js │ │ ├── jquery.fileupload-process.js │ │ ├── jquery.fileupload-validate.js │ │ ├── jquery.fileupload.js │ │ ├── jquery.iframe-transport.js │ │ ├── jquery.ui.widget.js │ │ └── load-image.min.js │ └── stylesheets │ └── application.css ├── settings.gradle └── tools └── update_version.sh /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug report for Cloudinary Java SDK 11 | Before proceeding, please update to latest version and test if the issue persists 12 | 13 | ## Describe the bug in a sentence or two. 14 | … 15 | 16 | ## Issue Type (Can be multiple) 17 | [ ] Build - Can’t install or import the SDK 18 | [ ] Performance - Performance issues 19 | [ ] Behaviour - Functions aren’t working as expected (Such as generate URL) 20 | [ ] Documentation - Inconsistency between the docs and behaviour 21 | [ ] Other (Specify) 22 | 23 | ## Steps to reproduce 24 | … if applicable 25 | 26 | ## Error screenshots or Stack Trace (if applicable) 27 | … 28 | 29 | ## Build System 30 | [ ] Maven 31 | [ ] Gradle 32 | [ ] Other (Specify) 33 | 34 | ## OS (Please specify version) 35 | [ ] Windows 36 | [ ] Linux 37 | [ ] Mac 38 | [ ] Other (specify) 39 | 40 | ## Versions and Libraries (fill in the version numbers) 41 | Cloudinary Java SDK version - 0.0.0 42 | JVM (dev environment) - 0.0.0 43 | JVM (production environment) - 0.0.0 44 | Maven - 0.0.0 / N/A 45 | Gradle - 0.0.0 / N/A 46 | 47 | ## Repository 48 | If possible, please provide a link to a reproducible repository that showcases the problem 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Feature request for Cloudinary Java SDK 11 | …(If your feature is for other SDKs, please request them there) 12 | 13 | 14 | ## Explain your use case 15 | … (A high level explanation of why you need this feature) 16 | 17 | ## Describe the problem you’re trying to solve 18 | … (A more technical view of what you’d like to accomplish, and how this feature will help you achieve it) 19 | 20 | ## Do you have a proposed solution? 21 | … (yes, no? Please elaborate if needed) 22 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Brief Summary of Changes 2 | 3 | 4 | #### What does this PR address? 5 | - [ ] GitHub issue (Add reference - #XX) 6 | - [ ] Refactoring 7 | - [ ] New feature 8 | - [ ] Bug fix 9 | - [ ] Adds more tests 10 | 11 | #### Are tests included? 12 | - [ ] Yes 13 | - [ ] No 14 | 15 | #### Reviewer, please note: 16 | 23 | 24 | #### Checklist: 25 | 26 | 27 | - [ ] My code follows the code style of this project. 28 | - [ ] My change requires a change to the documentation. 29 | - [ ] I ran the full test suite before pushing the changes and all the tests pass. 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Apple storage files 2 | *.DS_Store 3 | 4 | ## Android default ignore 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | 19 | # Gradle files 20 | .gradle/ 21 | build/ 22 | /*/build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | target/ 34 | test-output/ 35 | .settings 36 | .classpath 37 | .project 38 | 39 | # intellij 40 | .idea/ 41 | *.iml 42 | 43 | appengine-web.xml 44 | cloudinary-android/src/androidTest/AndroidManifest.xml 45 | 46 | ##Tools 47 | /tools/cloudinary_url.txt 48 | /tools/History.md 49 | 50 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | dist: trusty 3 | 4 | before_cache: 5 | - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock 6 | - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ 7 | cache: 8 | directories: 9 | - $HOME/.gradle/caches/ 10 | - $HOME/.gradle/wrapper/ 11 | 12 | jdk: 13 | - oraclejdk8 14 | - oraclejdk9 15 | - oraclejdk11 16 | - openjdk8 17 | - openjdk10 18 | 19 | env: 20 | - MODULE=core 21 | - MODULE=http5 22 | 23 | branches: 24 | except: 25 | - staging-test 26 | 27 | before_script: ./gradlew createTestSubAccount -PmoduleName=${MODULE} 28 | 29 | # ciTest is configured to skip the various timeout tests that don't work in travis 30 | script: source tools/cloudinary_url.txt && ./gradlew -DCLOUDINARY_URL=$CLOUDINARY_URL ciTest -p cloudinary-${MODULE} -i 31 | 32 | 33 | notifications: 34 | email: 35 | recipients: 36 | - sdk_developers@cloudinary.com 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Cloudinary 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import groovy.json.JsonSlurper 2 | 3 | plugins { 4 | id 'io.github.gradle-nexus.publish-plugin' version '1.0.0' 5 | } 6 | 7 | allprojects { 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | project.ext.set("publishGroupId", group) 14 | } 15 | 16 | nexusPublishing { 17 | transitionCheckOptions { 18 | maxRetries.set(150) 19 | delayBetween.set(Duration.ofSeconds(5)) 20 | } 21 | repositories { 22 | sonatype { 23 | username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" 24 | password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" 25 | } 26 | } 27 | } 28 | 29 | tasks.create('createTestSubAccount') { 30 | doFirst { 31 | println("Task createTestSubAccount called with module $moduleName") 32 | 33 | def cloudinaryUrl = "" 34 | 35 | // core does not use test clouds, skip (keep empty file for a more readable generic travis test script) 36 | if (moduleName != "core") { 37 | println "Creating test cloud..." 38 | def baseUrl = new URL('https://sub-account-testing.cloudinary.com/create_sub_account') 39 | def connection = baseUrl.openConnection() 40 | connection.with { 41 | doOutput = true 42 | requestMethod = 'POST' 43 | def json = new JsonSlurper().parseText(content.text) 44 | def cloud = json["payload"]["cloudName"] 45 | def key = json["payload"]["cloudApiKey"] 46 | def secret = json["payload"]["cloudApiSecret"] 47 | cloudinaryUrl = "CLOUDINARY_URL=cloudinary://$key:$secret@$cloud" 48 | } 49 | 50 | } 51 | 52 | def dir = new File("${projectDir.path}${File.separator}tools") 53 | dir.mkdir() 54 | def file = new File(dir, "cloudinary_url.txt") 55 | file.createNewFile() 56 | file.text = cloudinaryUrl 57 | 58 | println("Test sub-account created succesfully!") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cloudinary-core/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'signing' 4 | id 'maven-publish' 5 | id 'io.codearte.nexus-staging' version '0.21.1' 6 | } 7 | 8 | task ciTest( type: Test ) 9 | 10 | dependencies { 11 | testImplementation "org.hamcrest:java-hamcrest:2.0.0.0" 12 | testImplementation group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' 13 | testImplementation group: 'junit', name: 'junit', version: '4.12' 14 | } 15 | 16 | apply from: "../java_shared.gradle" 17 | 18 | if (hasProperty("ossrhPassword")) { 19 | signing { 20 | sign configurations.archives 21 | } 22 | 23 | 24 | nexusStaging { 25 | packageGroup = group 26 | username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" 27 | password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" 28 | } 29 | 30 | publishing { 31 | publications { 32 | mavenJava(MavenPublication) { 33 | from components.java 34 | artifact sourcesJar 35 | artifact javadocJar 36 | pom { 37 | name = 'Cloudinary Core Library' 38 | packaging = 'jar' 39 | groupId = publishGroupId 40 | artifactId = 'cloudinary-core' 41 | description = publishDescription 42 | url = githubUrl 43 | licenses { 44 | license { 45 | name = licenseName 46 | url = licenseUrl 47 | } 48 | } 49 | 50 | developers { 51 | developer { 52 | id = developerId 53 | name = developerName 54 | email = developerEmail 55 | } 56 | } 57 | scm { 58 | connection = scmConnection 59 | developerConnection = scmDeveloperConnection 60 | url = scmUrl 61 | } 62 | } 63 | 64 | pom.withXml { 65 | def pomFile = file("${project.buildDir}/generated-pom.xml") 66 | writeTo(pomFile) 67 | def pomAscFile = signing.sign(pomFile).signatureFiles[0] 68 | artifact(pomAscFile) { 69 | classifier = null 70 | extension = 'pom.asc' 71 | } 72 | } 73 | 74 | // create the signed artifacts 75 | project.tasks.signArchives.signatureFiles.each { 76 | artifact(it) { 77 | def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ 78 | if (matcher.find()) { 79 | classifier = matcher.group(1) 80 | } else { 81 | classifier = null 82 | } 83 | extension = 'jar.asc' 84 | } 85 | } 86 | } 87 | } 88 | 89 | model { 90 | tasks.generatePomFileForMavenJavaPublication { 91 | destination = file("$buildDir/generated-pom.xml") 92 | } 93 | tasks.publishMavenJavaPublicationToMavenLocal { 94 | dependsOn project.tasks.signArchives 95 | } 96 | tasks.publishMavenJavaPublicationToSonatypeRepository { 97 | dependsOn project.tasks.signArchives 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/AccessControlRule.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import com.cloudinary.utils.ObjectUtils; 4 | import org.cloudinary.json.JSONObject; 5 | 6 | import java.util.Date; 7 | 8 | /** 9 | * A class representing a single access control rule for a resource. Used as a parameter for {@link Api#update} and {@link Uploader#upload} 10 | */ 11 | public class AccessControlRule extends JSONObject { 12 | 13 | /** 14 | * Construct a new token access rule 15 | * @return The access rule instance 16 | */ 17 | public static AccessControlRule token(){ 18 | return new AccessControlRule(AccessType.token, null, null); 19 | } 20 | 21 | /** 22 | * Construct a new anonymous access rule 23 | * @param start The start date for the rule 24 | * @return The access rule instance 25 | */ 26 | public static AccessControlRule anonymousFrom(Date start){ 27 | return new AccessControlRule(AccessType.anonymous, start, null); 28 | } 29 | 30 | /** 31 | * Construct a new anonymous access rule 32 | * @param end The end date for the rule 33 | * @return The access rule instance 34 | */ 35 | public static AccessControlRule anonymousUntil(Date end){ 36 | return new AccessControlRule(AccessType.anonymous, null, end); 37 | } 38 | 39 | /** 40 | * Construct a new anonymous access rule 41 | * @param start The start date for the rule 42 | * @param end The end date for the rule 43 | * @return The access rule instance 44 | */ 45 | public static AccessControlRule anonymous(Date start, Date end){ 46 | return new AccessControlRule(AccessType.anonymous, start, end); 47 | } 48 | 49 | private AccessControlRule(AccessType accessType, Date start, Date end) { 50 | put("access_type", accessType.name()); 51 | if (start != null) { 52 | put("start", ObjectUtils.toISO8601(start)); 53 | } 54 | 55 | if (end != null) { 56 | put("end", ObjectUtils.toISO8601(end)); 57 | } 58 | } 59 | 60 | /** 61 | * Access type for an access rule 62 | */ 63 | public enum AccessType { 64 | anonymous, token 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/BaseParam.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import com.cloudinary.utils.StringUtils; 4 | 5 | import java.util.List; 6 | 7 | public class BaseParam { 8 | private String param; 9 | 10 | protected BaseParam(List components) { 11 | this.param = StringUtils.join(components, ":"); 12 | } 13 | 14 | protected BaseParam(String... components) { 15 | this.param = StringUtils.join(components, ":"); 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return param; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/Coordinates.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | 7 | import com.cloudinary.utils.Rectangle; 8 | import com.cloudinary.utils.StringUtils; 9 | 10 | public class Coordinates implements Serializable{ 11 | 12 | Collection coordinates = new ArrayList(); 13 | 14 | public Coordinates() { 15 | } 16 | 17 | public Coordinates(Collection coordinates) { 18 | this.coordinates = coordinates; 19 | } 20 | 21 | public Coordinates(int[] rect) { 22 | Collection coordinates = new ArrayList(); 23 | if (rect.length != 4) { 24 | throw new IllegalArgumentException("Must supply exactly 4 values for coordinates (x,y,width,height)"); 25 | } 26 | coordinates.add(new Rectangle(rect[0], rect[1], rect[2], rect[3])); 27 | this.coordinates = coordinates; 28 | } 29 | 30 | public Coordinates(Rectangle rect) { 31 | Collection coordinates = new ArrayList(); 32 | coordinates.add(rect); 33 | this.coordinates = coordinates; 34 | } 35 | 36 | public Coordinates(String stringCoords) throws IllegalArgumentException { 37 | Collection coordinates = new ArrayList(); 38 | for (String stringRect : stringCoords.split("\\|")) { 39 | if (StringUtils.isEmpty(stringRect)) 40 | continue; 41 | String[] elements = stringRect.split(","); 42 | if (elements.length != 4) { 43 | throw new IllegalArgumentException(String.format("Must supply exactly 4 values for coordinates (x,y,width,height) %d supplied: %s", 44 | elements.length, stringRect)); 45 | } 46 | coordinates.add(new Rectangle(Integer.parseInt(elements[0]), Integer.parseInt(elements[1]), Integer.parseInt(elements[2]), Integer 47 | .parseInt(elements[3]))); 48 | } 49 | this.coordinates = coordinates; 50 | } 51 | 52 | public static Coordinates parseCoordinates(Object coordinates) throws IllegalArgumentException { 53 | if (coordinates instanceof Coordinates) { 54 | return (Coordinates) coordinates; 55 | } else if (coordinates instanceof int[]) { 56 | return new Coordinates((int[]) coordinates); 57 | } else if (coordinates instanceof Rectangle) { 58 | return new Coordinates((Rectangle) coordinates); 59 | } else { 60 | return new Coordinates(coordinates.toString()); 61 | } 62 | } 63 | 64 | public void addRect(Rectangle rect) { 65 | this.coordinates.add(rect); 66 | } 67 | 68 | public Collection underlaying() { 69 | return this.coordinates; 70 | } 71 | 72 | @Override 73 | public String toString() { 74 | ArrayList rects = new ArrayList(); 75 | for (Rectangle rect : this.coordinates) { 76 | rects.add(rect.x + "," + rect.y + "," + rect.width + "," + rect.height); 77 | } 78 | return StringUtils.join(rects, "|"); 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/CustomFunction.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import com.cloudinary.utils.Base64Coder; 4 | 5 | /** 6 | * Helper class to generate a custom function params to be used in {@link Transformation#customFunction(CustomFunction)}. 7 | */ 8 | public class CustomFunction extends BaseParam{ 9 | 10 | private CustomFunction(String... components) { 11 | super(components); 12 | } 13 | 14 | /** 15 | * Generate a web-assembly custom action param to send to {@link Transformation#customFunction(CustomFunction)} 16 | * @param publicId The public id of the web-assembly file 17 | * @return A new instance of custom action param 18 | */ 19 | public static CustomFunction wasm(String publicId){ 20 | return new CustomFunction("wasm", publicId); 21 | } 22 | 23 | /** 24 | * Generate a remote lambda custom action param to send to {@link Transformation#customFunction(CustomFunction)} 25 | * @param url The public url of the aws lambda function 26 | * @return A new instance of custom action param 27 | */ 28 | public static CustomFunction remote(String url){ 29 | return new CustomFunction("remote", Base64Coder.encodeURLSafeString(url)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/EagerTransformation.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import com.cloudinary.utils.StringUtils; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class EagerTransformation extends Transformation { 10 | protected String format; 11 | 12 | @SuppressWarnings("rawtypes") 13 | public EagerTransformation(List transformations) { 14 | super(transformations); 15 | } 16 | 17 | public EagerTransformation() { 18 | super(); 19 | } 20 | 21 | public EagerTransformation format(String format) { 22 | this.format = format; 23 | return this; 24 | } 25 | 26 | public String getFormat() { 27 | return format; 28 | } 29 | 30 | @Override 31 | public String generate(Iterable optionsList) { 32 | List components = new ArrayList(); 33 | for (Map options : optionsList) { 34 | if (options.size() > 0) { 35 | components.add(super.generate(options)); 36 | } 37 | } 38 | 39 | if (format != null){ 40 | components.add(format); 41 | } 42 | 43 | return StringUtils.join(components, "/"); 44 | } 45 | 46 | @Override 47 | public String generate(Map options) { 48 | List eager = new ArrayList(); 49 | eager.add(super.generate(options)); 50 | 51 | if (format != null){ 52 | eager.add(format); 53 | } 54 | 55 | return StringUtils.join(eager, "/"); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/ProgressCallback.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | /** 4 | * Defines a callback for network operations. 5 | */ 6 | public interface ProgressCallback { 7 | /** 8 | * Invoked during network operation. 9 | * @param bytesUploaded the number of bytes uploaded so far 10 | * @param totalBytes the total number of byte to upload - if known 11 | */ 12 | void onProgress(long bytesUploaded, long totalBytes); 13 | } 14 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/ResponsiveBreakpoint.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import org.cloudinary.json.JSONObject; 4 | 5 | public class ResponsiveBreakpoint extends JSONObject { 6 | public ResponsiveBreakpoint() { 7 | put("create_derived", true); 8 | } 9 | 10 | public boolean isCreateDerived() { 11 | return optBoolean("create_derived"); 12 | } 13 | 14 | public ResponsiveBreakpoint createDerived(boolean createDerived) { 15 | put("create_derived", createDerived); 16 | return this; 17 | } 18 | 19 | public Transformation transformation() { 20 | return (Transformation) opt("transformation"); 21 | } 22 | 23 | public ResponsiveBreakpoint transformation(Transformation transformation) { 24 | put("transformation", transformation); 25 | return this; 26 | } 27 | 28 | public ResponsiveBreakpoint format(String format) { 29 | put("format", format); 30 | return this; 31 | } 32 | 33 | public String format() { 34 | return optString("format"); 35 | } 36 | 37 | public int maxWidth() { 38 | return optInt("max_width"); 39 | } 40 | 41 | public ResponsiveBreakpoint maxWidth(int maxWidth) { 42 | put("max_width", maxWidth); 43 | return this; 44 | } 45 | 46 | public int minWidth() { 47 | return optInt("min_width"); 48 | } 49 | 50 | public ResponsiveBreakpoint minWidth(Integer minWidth) { 51 | put("min_width", minWidth); 52 | return this; 53 | } 54 | 55 | public int bytesStep() { 56 | return optInt("bytes_step"); 57 | } 58 | 59 | public ResponsiveBreakpoint bytesStep(Integer bytesStep) { 60 | put("bytes_step", bytesStep); 61 | return this; 62 | } 63 | 64 | public int maxImages() { 65 | return optInt("max_images"); 66 | } 67 | 68 | public ResponsiveBreakpoint maxImages(Integer maxImages) { 69 | put("max_images", maxImages); 70 | return this; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/SearchFolders.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import com.cloudinary.api.ApiResponse; 4 | import com.cloudinary.utils.ObjectUtils; 5 | 6 | import java.util.Arrays; 7 | import java.util.Map; 8 | 9 | public class SearchFolders extends Search { 10 | 11 | public SearchFolders(Cloudinary cloudinary) { 12 | super(cloudinary); 13 | } 14 | 15 | public ApiResponse execute() throws Exception { 16 | Map options = ObjectUtils.asMap("content_type", "json"); 17 | return this.api.callApi(Api.HttpMethod.POST, Arrays.asList("folders", "search"), this.toQuery(), options); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/SignatureAlgorithm.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | /** 4 | * Defines supported algorithms for generating/verifying hashed message authentication codes (HMAC). 5 | */ 6 | public enum SignatureAlgorithm { 7 | SHA1("SHA-1"), 8 | SHA256("SHA-256"); 9 | 10 | private final String algorithmId; 11 | 12 | SignatureAlgorithm(String algorithmId) { 13 | this.algorithmId = algorithmId; 14 | } 15 | 16 | public String getAlgorithmId() { 17 | return algorithmId; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/SmartUrlEncoder.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | 6 | public final class SmartUrlEncoder { 7 | private SmartUrlEncoder() {} 8 | 9 | public static String encode(String input) { 10 | try { 11 | return URLEncoder.encode(input, "UTF-8").replace("%2F", "/").replace("%3A", ":").replace("+", "%20"); 12 | } catch (UnsupportedEncodingException e) { 13 | throw new RuntimeException(e); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/StoredFile.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.regex.Matcher; 6 | import java.util.regex.Pattern; 7 | 8 | public class StoredFile { 9 | protected Long version; 10 | 11 | protected String publicId; 12 | 13 | protected String format; 14 | 15 | protected String signature; 16 | 17 | protected String type = "upload"; 18 | 19 | protected String resourceType = "image"; 20 | 21 | private static final String IMAGE_RESOURCE_TYPE = "image"; 22 | 23 | private static final String VIDEO_RESOURCE_TYPE = "video"; 24 | 25 | private static final String AUTO_RESOURCE_TYPE = "auto"; 26 | 27 | private static final Pattern PRELOADED_PATTERN = Pattern.compile("^([^\\/]+)\\/([^\\/]+)\\/v(\\d+)\\/([^#]+)#?([^\\/]+)?$"); 28 | 29 | public Long getVersion() { 30 | return version; 31 | } 32 | 33 | public void setVersion(Long version) { 34 | this.version = version; 35 | } 36 | 37 | public String getPublicId() { 38 | return publicId; 39 | } 40 | 41 | public void setPublicId(String publicId) { 42 | this.publicId = publicId; 43 | } 44 | 45 | protected String getPublicIdForSigning() { 46 | return publicId + ((format != null && !format.isEmpty() && resourceType.equals("raw")) ? "." + format : ""); 47 | } 48 | 49 | public String getFormat() { 50 | return format; 51 | } 52 | 53 | public void setFormat(String format) { 54 | this.format = format; 55 | } 56 | 57 | public String getSignature() { 58 | return signature; 59 | } 60 | 61 | public void setSignature(String signature) { 62 | this.signature = signature; 63 | } 64 | 65 | public String getResourceType() { 66 | return resourceType; 67 | } 68 | 69 | public void setResourceType(String resourceType) { 70 | this.resourceType = resourceType; 71 | } 72 | 73 | public String getType() { 74 | return type; 75 | } 76 | 77 | public void setType(String type) { 78 | this.type = type; 79 | } 80 | 81 | public String getPreloadedFile() { 82 | StringBuilder sb = new StringBuilder(); 83 | sb.append(resourceType).append("/").append(type).append("/v").append(version).append("/").append(publicId); 84 | if (format != null && !format.isEmpty()) { 85 | sb.append(".").append(format); 86 | } 87 | if (signature != null && !signature.isEmpty()) { 88 | sb.append("#").append(signature); 89 | } 90 | return sb.toString(); 91 | } 92 | 93 | public void setPreloadedFile(String uri) { 94 | if (uri.matches(PRELOADED_PATTERN.pattern())) { 95 | Matcher match = PRELOADED_PATTERN.matcher(uri); 96 | match.find(); 97 | resourceType = match.group(1); 98 | type = match.group(2); 99 | version = Long.parseLong(match.group(3)); 100 | String filename = match.group(4); 101 | if (match.groupCount() == 5) 102 | signature = match.group(5); 103 | int lastDotIndex = filename.lastIndexOf('.'); 104 | if (lastDotIndex == -1) { 105 | publicId = filename; 106 | } else { 107 | publicId = filename.substring(0, lastDotIndex); 108 | format = filename.substring(lastDotIndex + 1); 109 | } 110 | } 111 | } 112 | 113 | public String getComputedSignature(Cloudinary cloudinary) { 114 | Map params = new HashMap(); 115 | params.put("version", getVersion().toString()); 116 | params.put("public_id", getPublicIdForSigning()); 117 | cloudinary.signRequest(params, new HashMap()); 118 | return params.get("signature").toString(); 119 | } 120 | 121 | public boolean getIsImage() { 122 | return IMAGE_RESOURCE_TYPE.equals(resourceType) || AUTO_RESOURCE_TYPE.equals(resourceType); 123 | } 124 | 125 | public boolean getIsVideo() { 126 | return VIDEO_RESOURCE_TYPE.equals(resourceType); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/ApiResponse.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api; 2 | 3 | import java.text.ParseException; 4 | import java.util.Map; 5 | 6 | @SuppressWarnings("rawtypes") 7 | public interface ApiResponse extends Map { 8 | Map rateLimits() throws ParseException; 9 | 10 | RateLimit apiRateLimit() throws ParseException; 11 | } 12 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/AuthorizationRequired.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api; 2 | 3 | import com.cloudinary.api.exceptions.ApiException; 4 | 5 | public class AuthorizationRequired extends ApiException { 6 | private static final long serialVersionUID = 7160740370855761014L; 7 | 8 | public AuthorizationRequired(String message) { 9 | super(message); 10 | } 11 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/RateLimit.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api; 2 | 3 | import java.util.Date; 4 | 5 | public class RateLimit { 6 | private long limit = 0L; 7 | private long remaining = 0L; 8 | private Date reset = null; 9 | 10 | public RateLimit() { 11 | super(); 12 | } 13 | 14 | public long getLimit() { 15 | return limit; 16 | } 17 | 18 | public void setLimit(long limit) { 19 | this.limit = limit; 20 | } 21 | 22 | public long getRemaining() { 23 | return remaining; 24 | } 25 | 26 | public void setRemaining(long remaining) { 27 | this.remaining = remaining; 28 | } 29 | 30 | public Date getReset() { 31 | return reset; 32 | } 33 | 34 | public void setReset(Date reset) { 35 | this.reset = reset; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/AlreadyExists.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | public class AlreadyExists extends ApiException { 4 | private static final long serialVersionUID = 999568182896607322L; 5 | 6 | public AlreadyExists(String message) { 7 | super(message); 8 | } 9 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/ApiException.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | public class ApiException extends Exception { 4 | private static final long serialVersionUID = 4416861825144420038L; 5 | 6 | public ApiException(String message) { 7 | super(message); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/BadRequest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | 4 | public class BadRequest extends ApiException { 5 | private static final long serialVersionUID = 1410136354253339531L; 6 | 7 | public BadRequest(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/GeneralError.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | public class GeneralError extends ApiException { 4 | private static final long serialVersionUID = 4553362706625067182L; 5 | 6 | public GeneralError(String message) { 7 | super(message); 8 | } 9 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotAllowed.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | 4 | public class NotAllowed extends ApiException { 5 | private static final long serialVersionUID = 4371365822491647653L; 6 | 7 | public NotAllowed(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/NotFound.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | 4 | public class NotFound extends ApiException { 5 | private static final long serialVersionUID = -2072640462778940357L; 6 | 7 | public NotFound(String message) { 8 | super(message); 9 | } 10 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/exceptions/RateLimited.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.exceptions; 2 | 3 | 4 | public class RateLimited extends ApiException { 5 | private static final long serialVersionUID = -8298038106172355219L; 6 | 7 | public RateLimited(String message) { 8 | super(message); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/signing/ApiResponseSignatureVerifier.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.signing; 2 | 3 | import com.cloudinary.SignatureAlgorithm; 4 | import com.cloudinary.Util; 5 | import com.cloudinary.utils.ObjectUtils; 6 | import com.cloudinary.utils.StringUtils; 7 | 8 | import static com.cloudinary.utils.StringUtils.emptyIfNull; 9 | 10 | /** 11 | * The {@code ApiResponseSignatureVerifier} class is responsible for verifying Cloudinary Upload API response signatures. 12 | */ 13 | public class ApiResponseSignatureVerifier { 14 | private final String secretKey; 15 | private final SignatureAlgorithm signatureAlgorithm; 16 | 17 | /** 18 | * Initializes new instance of {@code ApiResponseSignatureVerifier} class with a secret key required to perform 19 | * API response signatures verification. 20 | * 21 | * @param secretKey shared secret key string which is used to sign and verify authenticity of API responses 22 | */ 23 | public ApiResponseSignatureVerifier(String secretKey) { 24 | if (StringUtils.isBlank(secretKey)) { 25 | throw new IllegalArgumentException("Secret key is required"); 26 | } 27 | 28 | this.secretKey = secretKey; 29 | this.signatureAlgorithm = SignatureAlgorithm.SHA1; 30 | } 31 | 32 | /** 33 | * Initializes new instance of {@code ApiResponseSignatureVerifier} class with a secret key required to perform 34 | * API response signatures verification. 35 | * 36 | * @param secretKey shared secret key string which is used to sign and verify authenticity of API responses 37 | * @param signatureAlgorithm type of hashing algorithm to use for calculation of HMACs 38 | */ 39 | public ApiResponseSignatureVerifier(String secretKey, SignatureAlgorithm signatureAlgorithm) { 40 | if (StringUtils.isBlank(secretKey)) { 41 | throw new IllegalArgumentException("Secret key is required"); 42 | } 43 | 44 | this.secretKey = secretKey; 45 | this.signatureAlgorithm = signatureAlgorithm; 46 | } 47 | 48 | /** 49 | * Checks whether particular Cloudinary Upload API response signature matches expected signature. 50 | * 51 | * The task is performed by generating signature using same hashing algorithm as used on Cloudinary servers and 52 | * comparing the result with provided actual signature. 53 | * 54 | * @param publicId public id of uploaded resource as stated in upload API response 55 | * @param version version of uploaded resource as stated in upload API response 56 | * @param signature signature of upload API response, usually passed via X-Cld-Signature custom HTTP response header 57 | * 58 | * @return true if response signature passed verification procedure 59 | */ 60 | public boolean verifySignature(String publicId, String version, String signature) { 61 | return Util.produceSignature(ObjectUtils.asMap( 62 | "public_id", emptyIfNull(publicId), 63 | "version", emptyIfNull(version)), secretKey, signatureAlgorithm).equals(signature); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifier.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.signing; 2 | 3 | import com.cloudinary.SignatureAlgorithm; 4 | 5 | import static com.cloudinary.utils.StringUtils.emptyIfNull; 6 | 7 | /** 8 | * The {@code NotificationRequestSignatureVerifier} class is responsible for verifying authenticity and integrity 9 | * of Cloudinary Upload notifications. 10 | */ 11 | public class NotificationRequestSignatureVerifier { 12 | private final SignedPayloadValidator signedPayloadValidator; 13 | 14 | /** 15 | * Initializes new instance of {@code NotificationRequestSignatureVerifier} with secret key value. 16 | * 17 | * @param secretKey shared secret key string which is used to sign and verify authenticity of notifications 18 | */ 19 | public NotificationRequestSignatureVerifier(String secretKey) { 20 | this.signedPayloadValidator = new SignedPayloadValidator(secretKey, SignatureAlgorithm.SHA1); 21 | } 22 | 23 | /** 24 | * Initializes new instance of {@code NotificationRequestSignatureVerifier} with secret key value. 25 | * 26 | * @param secretKey shared secret key string which is used to sign and verify authenticity of notifications 27 | * @param signatureAlgorithm type of hashing algorithm to use for calculation of HMACs 28 | */ 29 | public NotificationRequestSignatureVerifier(String secretKey, SignatureAlgorithm signatureAlgorithm) { 30 | this.signedPayloadValidator = new SignedPayloadValidator(secretKey, signatureAlgorithm); 31 | } 32 | 33 | /** 34 | * Verifies signature of Cloudinary Upload notification. 35 | * 36 | * @param body notification message body, represented as string 37 | * @param timestamp value of X-Cld-Timestamp custom HTTP header of notification message, representing notification 38 | * issue timestamp 39 | * @param signature actual signature value, usually passed via X-Cld-Signature custom HTTP header of notification 40 | * message 41 | * @return true if notification passed verification procedure 42 | */ 43 | public boolean verifySignature(String body, String timestamp, String signature) { 44 | return signedPayloadValidator.validateSignedPayload( 45 | emptyIfNull(body) + emptyIfNull(timestamp), 46 | signature); 47 | } 48 | 49 | /** 50 | * Verifies signature of Cloudinary Upload notification. 51 | *

52 | * Differs from {@link #verifySignature(String, String, String)} in additional validation which consists of making 53 | * sure the notification being verified is still not expired based on timestamp parameter value. 54 | * 55 | * @param body notification message body, represented as string 56 | * @param timestamp value of X-Cld-Timestamp custom HTTP header of notification message, representing notification 57 | * issue timestamp in seconds 58 | * @param signature actual signature value, usually passed via X-Cld-Signature custom HTTP header of notification 59 | * message 60 | * @param secondsValidFor the amount of time, in seconds, the notification message is considered valid by client 61 | * @return true if notification passed verification procedure 62 | */ 63 | public boolean verifySignature(String body, String timestamp, String signature, long secondsValidFor) { 64 | long parsedTimestamp; 65 | try { 66 | parsedTimestamp = Long.parseLong(timestamp); 67 | } catch (NumberFormatException e) { 68 | throw new IllegalArgumentException("Provided timestamp is not a valid number", e); 69 | } 70 | 71 | return verifySignature(body, timestamp, signature) && 72 | (System.currentTimeMillis() / 1000L - parsedTimestamp <= secondsValidFor); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/signing/SignedPayloadValidator.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.signing; 2 | 3 | import com.cloudinary.SignatureAlgorithm; 4 | import com.cloudinary.Util; 5 | import com.cloudinary.utils.StringUtils; 6 | 7 | import static com.cloudinary.utils.StringUtils.emptyIfNull; 8 | 9 | class SignedPayloadValidator { 10 | private final String secretKey; 11 | private final SignatureAlgorithm signatureAlgorithm; 12 | 13 | SignedPayloadValidator(String secretKey, SignatureAlgorithm signatureAlgorithm) { 14 | if (StringUtils.isBlank(secretKey)) { 15 | throw new IllegalArgumentException("Secret key is required"); 16 | } 17 | 18 | this.secretKey = secretKey; 19 | this.signatureAlgorithm = signatureAlgorithm; 20 | } 21 | 22 | boolean validateSignedPayload(String signedPayload, String signature) { 23 | String expectedSignature = 24 | StringUtils.encodeHexString(Util.hash(emptyIfNull(signedPayload) + secretKey, 25 | signatureAlgorithm)); 26 | 27 | return expectedSignature.equals(signature); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/api/signing/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The package holds classes used internally to implement verification procedures of authenticity and integrity of 3 | * client communication with Cloudinary servers. Verification is in most cases based on calculating and comparing so called 4 | * signatures, or hashed message authentication codes (HMAC) - string values calculated based on message payload, some 5 | * secret key value shared between communicating parties and agreed hashing function. 6 | */ 7 | package com.cloudinary.api.signing; -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/DateMetadataField.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import com.cloudinary.utils.ObjectUtils; 4 | 5 | import java.text.ParseException; 6 | import java.util.Date; 7 | 8 | /** 9 | * Represents a metadata field with type 'date' 10 | */ 11 | public class DateMetadataField extends MetadataField { 12 | 13 | public DateMetadataField() { 14 | super(MetadataFieldType.DATE); 15 | } 16 | 17 | /** 18 | * Sets the default date used for this field. 19 | * @param defaultValue The date to set. Date only without a time component, UTC assumed. 20 | */ 21 | @Override 22 | public void setDefaultValue(Date defaultValue) { 23 | put(DEFAULT_VALUE, ObjectUtils.toISO8601DateOnly(defaultValue)); 24 | } 25 | 26 | /** 27 | * Get the default value of this date field. 28 | * @return The date only without a time component, UTC. 29 | * @throws ParseException When the underlying value is malformed. 30 | */ 31 | @Override 32 | public Date getDefaultValue() throws ParseException { 33 | Object value = get(DEFAULT_VALUE); 34 | if (value == null) { 35 | return null; 36 | } 37 | 38 | return ObjectUtils.fromISO8601DateOnly(value.toString()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/EnumMetadataField.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | /** 4 | * Represents a metadata field with 'Enum' type. 5 | */ 6 | public class EnumMetadataField extends MetadataField { 7 | EnumMetadataField() { 8 | super(MetadataFieldType.ENUM); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/IntMetadataField.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | /** 4 | * Represents a metadata field with 'Int' type. 5 | */ 6 | public class IntMetadataField extends MetadataField { 7 | public IntMetadataField() { 8 | super(MetadataFieldType.INTEGER); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataDataSource.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import org.cloudinary.json.JSONArray; 4 | import org.cloudinary.json.JSONObject; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Represent a data source for a given field. This is used in both 'Set' and 'Enum' field types. 10 | * The datasource holds a list of the valid values to be used with the corresponding metadata field. 11 | */ 12 | public class MetadataDataSource extends JSONObject { 13 | /** 14 | * Creates a new instance of data source with the given list of entries. 15 | * @param entries 16 | */ 17 | public MetadataDataSource(List entries) { 18 | put("values", new JSONArray(entries.toArray())); 19 | } 20 | 21 | /** 22 | * Represents a single entry in a datasource definition for a field. 23 | */ 24 | public static class Entry extends JSONObject { 25 | public Entry(String externalId, String value){ 26 | setExternalId(externalId); 27 | setValue(value); 28 | } 29 | 30 | /** 31 | * Create a new entry with a string value. 32 | * @param value The value to use in the entry. 33 | */ 34 | public Entry(String value){ 35 | this(null, value); 36 | } 37 | 38 | /** 39 | * Set the id of the entry. Will be auto-generated if left blank. 40 | * @param externalId 41 | */ 42 | public void setExternalId(String externalId) { 43 | put("external_id", externalId); 44 | } 45 | 46 | /** 47 | * Get the id of the entry. 48 | * @return 49 | */ 50 | public String getExternalId() { 51 | return optString("external_id"); 52 | } 53 | 54 | /** 55 | * Set the value of the entry. 56 | * @param value The value to set. 57 | */ 58 | public void setValue(String value) { 59 | put("value", value); 60 | } 61 | 62 | /** 63 | * Get the value of the entry. 64 | * @return The value. 65 | */ 66 | public String getValue() { 67 | return optString("value"); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataFieldType.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | /** 4 | * Enum represneting all the valid field types. 5 | */ 6 | public enum MetadataFieldType { 7 | STRING, 8 | INTEGER, 9 | DATE, 10 | ENUM, 11 | SET; 12 | 13 | @Override 14 | public String toString() { 15 | return super.toString().toLowerCase(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRule.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import com.cloudinary.utils.ObjectUtils; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class MetadataRule { 9 | String metadataFieldId; 10 | String name; 11 | MetadataRuleCondition condition; 12 | MetadataRuleResult result; 13 | 14 | public MetadataRule(String metadataFieldId, String name, MetadataRuleCondition condition, MetadataRuleResult result) { 15 | this.metadataFieldId = metadataFieldId; 16 | this.name = name; 17 | this.condition = condition; 18 | this.result = result; 19 | } 20 | 21 | public String getMetadataFieldId() { 22 | return metadataFieldId; 23 | } 24 | 25 | public void setMetadataFieldId(String metadataFieldId) { 26 | this.metadataFieldId = metadataFieldId; 27 | } 28 | 29 | public String getName() { 30 | return name; 31 | } 32 | 33 | public void setName(String name) { 34 | this.name = name; 35 | } 36 | 37 | public MetadataRuleCondition getCondition() { 38 | return condition; 39 | } 40 | 41 | public void setCondition(MetadataRuleCondition condition) { 42 | this.condition = condition; 43 | } 44 | 45 | public MetadataRuleResult getResult() { 46 | return result; 47 | } 48 | 49 | public void setResult(MetadataRuleResult result) { 50 | this.result = result; 51 | } 52 | 53 | public Map asMap() { 54 | Map map = new HashMap(); 55 | map.put("metadata_field_id", getMetadataFieldId()); 56 | map.put("name", getName()); 57 | if (getCondition() != null) { 58 | map.put("condition", ObjectUtils.toJSON(getCondition().asMap())); 59 | } 60 | if(getResult() != null) { 61 | map.put("result", ObjectUtils.toJSON(getResult().asMap())); 62 | } 63 | return map; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleCondition.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | import java.util.HashMap; 3 | import java.util.Map; 4 | 5 | public class MetadataRuleCondition { 6 | String metadata_field_id; 7 | Boolean populated; 8 | Map includes; 9 | String equals; 10 | 11 | public MetadataRuleCondition(String metadata_field_id, Boolean populated, Map includes, String equals) { 12 | this.metadata_field_id = metadata_field_id; 13 | this.populated = populated; 14 | this.includes = includes; 15 | this.equals = equals; 16 | } 17 | 18 | public String getMetadata_field_id() { 19 | return metadata_field_id; 20 | } 21 | 22 | public void setMetadata_field_id(String metadata_field_id) { 23 | this.metadata_field_id = metadata_field_id; 24 | } 25 | 26 | public Boolean getPopulated() { 27 | return populated; 28 | } 29 | 30 | public void setPopulated(Boolean populated) { 31 | this.populated = populated; 32 | } 33 | 34 | public Map getIncludes() { 35 | return includes; 36 | } 37 | 38 | public void setIncludes(Map includes) { 39 | this.includes = includes; 40 | } 41 | 42 | public String getEquals() { 43 | return equals; 44 | } 45 | 46 | public void setEquals(String equals) { 47 | this.equals = equals; 48 | } 49 | 50 | public Map asMap() { 51 | Map result = new HashMap(4); 52 | result.put("metadata_field_id", metadata_field_id); 53 | result.put("populated", populated); 54 | result.put("includes", includes); 55 | result.put("equals", equals); 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/MetadataRuleResult.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public class MetadataRuleResult { 7 | Boolean enabled; 8 | String activateValues; 9 | String applyValues; 10 | Boolean setMandatory; 11 | 12 | public MetadataRuleResult(Boolean enabled, String activateValues, String applyValues, Boolean setMandatory) { 13 | this.enabled = enabled; 14 | this.activateValues = activateValues; 15 | this.applyValues = applyValues; 16 | this.setMandatory = setMandatory; 17 | } 18 | 19 | public Boolean getEnabled() { 20 | return enabled; 21 | } 22 | 23 | public void setEnabled(Boolean enabled) { 24 | this.enabled = enabled; 25 | } 26 | 27 | public String getActivateValues() { 28 | return activateValues; 29 | } 30 | 31 | public void setActivateValues(String activateValues) { 32 | this.activateValues = activateValues; 33 | } 34 | 35 | public String getApplyValues() { 36 | return applyValues; 37 | } 38 | 39 | public void setApplyValues(String applyValues) { 40 | this.applyValues = applyValues; 41 | } 42 | 43 | public Boolean getSetMandatory() { 44 | return setMandatory; 45 | } 46 | 47 | public void setSetMandatory(Boolean setMandatory) { 48 | this.setMandatory = setMandatory; 49 | } 50 | public Map asMap() { 51 | Map result = new HashMap(4); 52 | result.put("enable", enabled); 53 | result.put("activate_values", activateValues); 54 | result.put("apply_values", applyValues); 55 | result.put("mandatory", setMandatory); 56 | return result; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/Restrictions.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * Represents the restrictions metadata field. 7 | */ 8 | public class Restrictions { 9 | 10 | private final HashMap restrictions = new HashMap(); 11 | 12 | /** 13 | * Set the custom field into restrictions. 14 | * @param key The key of the field. 15 | * @param value The value of the field. 16 | */ 17 | public Restrictions setRestriction(String key, Object value) { 18 | restrictions.put(key, value); 19 | return this; 20 | } 21 | 22 | /** 23 | * Set the read only ui field. 24 | * @param value The read only ui value. 25 | */ 26 | public Restrictions setReadOnlyUI(Boolean value) { 27 | return setRestriction("readonly_ui", value); 28 | } 29 | 30 | /** 31 | * Set the read only ui field to true. 32 | */ 33 | public Restrictions setReadOnlyUI() { 34 | return this.setReadOnlyUI(true); 35 | } 36 | 37 | public HashMap toHash() { 38 | return restrictions; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/SetMetadataField.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Represents a metadata field with 'Set' type. 7 | */ 8 | public class SetMetadataField extends MetadataField> { 9 | public SetMetadataField() { 10 | super(MetadataFieldType.SET); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/metadata/StringMetadataField.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.metadata; 2 | 3 | /** 4 | * Represents a metadata field with 'String' type. 5 | */ 6 | public class StringMetadataField extends MetadataField { 7 | public StringMetadataField() { 8 | super(MetadataFieldType.STRING); 9 | } 10 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/provisioning/AccountConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.provisioning; 2 | 3 | import com.cloudinary.utils.StringUtils; 4 | 5 | import java.net.URI; 6 | 7 | public class AccountConfiguration { 8 | private static final String SEPARATOR = ":"; 9 | String accountId; 10 | String provisioningApiKey; 11 | String provisioningApiSecret; 12 | 13 | public AccountConfiguration(String accountId, String provisioningApiKey, String provisioningApiSecret) { 14 | this.accountId = accountId; 15 | this.provisioningApiKey = provisioningApiKey; 16 | this.provisioningApiSecret = provisioningApiSecret; 17 | } 18 | 19 | public static AccountConfiguration from(String accountUrl) { 20 | URI uri = URI.create(accountUrl); 21 | 22 | String accountId = uri.getHost(); 23 | if (StringUtils.isBlank(accountId)) throw new IllegalArgumentException("Account id must be provided in account url"); 24 | 25 | if (uri.getUserInfo() == null) throw new IllegalArgumentException("Full credentials (key+secret) must be provided in account url"); 26 | String[] credentials = uri.getUserInfo().split(":"); 27 | if (credentials.length < 2 || 28 | StringUtils.isBlank(credentials[0]) || 29 | StringUtils.isBlank(credentials[1])) { 30 | throw new IllegalArgumentException("Full credentials (key+secret) must be provided in account url"); 31 | } 32 | 33 | return new AccountConfiguration(accountId, credentials[0], credentials[1]); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractApiStrategy.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.strategies; 2 | 3 | import com.cloudinary.Api; 4 | import com.cloudinary.Api.HttpMethod; 5 | import com.cloudinary.api.ApiResponse; 6 | import java.util.Map; 7 | 8 | 9 | public abstract class AbstractApiStrategy { 10 | protected Api api; 11 | 12 | public void init(Api api) { 13 | this.api = api; 14 | } 15 | 16 | @SuppressWarnings("rawtypes") 17 | public abstract ApiResponse callApi(HttpMethod method, String apiUrl, Map params, Map options, String authorizationHeader) throws Exception; 18 | 19 | public abstract ApiResponse callAccountApi(HttpMethod method, String apiUrl, Map params, Map options, String authorizationHeader) throws Exception; 20 | } 21 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/strategies/AbstractUploaderStrategy.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.strategies; 2 | 3 | import com.cloudinary.Cloudinary; 4 | import com.cloudinary.ProgressCallback; 5 | import com.cloudinary.Uploader; 6 | import com.cloudinary.utils.ObjectUtils; 7 | import com.cloudinary.utils.StringUtils; 8 | import org.cloudinary.json.JSONException; 9 | import org.cloudinary.json.JSONObject; 10 | 11 | import java.io.IOException; 12 | import java.util.Arrays; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public abstract class AbstractUploaderStrategy { 17 | private final static int[] ERROR_CODES = new int[]{400, 401, 403, 404, 420, 500}; 18 | protected Uploader uploader; 19 | 20 | public void init(Uploader uploader) { 21 | this.uploader = uploader; 22 | } 23 | 24 | public Cloudinary cloudinary() { 25 | return this.uploader.cloudinary(); 26 | } 27 | 28 | @SuppressWarnings("rawtypes") 29 | public Map callApi(String action, Map params, Map options, Object file) throws IOException { 30 | return callApi(action, params, options, file, null); 31 | } 32 | 33 | public abstract Map callApi(String action, Map params, Map options, Object file, ProgressCallback progressCallback) throws IOException; 34 | 35 | protected String buildUploadUrl(String action, Map options) { 36 | String cloudinary = ObjectUtils.asString(options.get("upload_prefix"), 37 | ObjectUtils.asString(uploader.cloudinary().config.uploadPrefix, "https://api.cloudinary.com")); 38 | String cloud_name = ObjectUtils.asString(options.get("cloud_name"), ObjectUtils.asString(uploader.cloudinary().config.cloudName)); 39 | if (cloud_name == null) 40 | throw new IllegalArgumentException("Must supply cloud_name in tag or in configuration"); 41 | 42 | if (action.equals("delete_by_token")) { 43 | // delete_by_token doesn't need resource_type 44 | return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, action}, "/"); 45 | } else { 46 | String resource_type = ObjectUtils.asString(options.get("resource_type"), "image"); 47 | return StringUtils.join(new String[]{cloudinary, "v1_1", cloud_name, resource_type, action}, "/"); 48 | } 49 | } 50 | 51 | protected Map processResponse(boolean returnError, int code, String responseData) { 52 | String errorMessage = null; 53 | Map result = null; 54 | if (code == 200 || includesServerResponse(code)) { 55 | try { 56 | JSONObject responseJSON = new JSONObject(responseData); 57 | result = ObjectUtils.toMap(responseJSON); 58 | 59 | if (result.containsKey("error")) { 60 | Map error = (Map) result.get("error"); 61 | error.put("http_code", code); 62 | errorMessage = (String) error.get("message"); 63 | } 64 | } catch (JSONException e) { 65 | errorMessage = "Invalid JSON response from server " + e.getMessage(); 66 | } 67 | } else { 68 | errorMessage = "Server returned unexpected status code - " + code; 69 | if (StringUtils.isNotBlank(responseData)) { 70 | errorMessage += (" - " + responseData); 71 | } 72 | } 73 | 74 | if (StringUtils.isNotBlank(errorMessage)) { 75 | if (returnError) { 76 | // return a result containing the error instead of throwing an exception: 77 | if (result == null) { 78 | Map error = new HashMap(); 79 | error.put("http_code", code); 80 | error.put("message", errorMessage); 81 | result = new HashMap(); 82 | result.put("error", error); 83 | } // else - Result is already built, with the error inside. Nothing to do. 84 | } else { 85 | throw new RuntimeException(errorMessage); 86 | } 87 | } 88 | 89 | return result; 90 | } 91 | 92 | private boolean includesServerResponse(int code) { 93 | return Arrays.binarySearch(ERROR_CODES, code) >= 0; 94 | } 95 | 96 | protected boolean requiresSigning(String action, Map options) { 97 | boolean unsigned = Boolean.TRUE.equals(options.get("unsigned")); 98 | boolean deleteByToken = "delete_by_token".equals(action); 99 | 100 | return !unsigned && !deleteByToken; 101 | } 102 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/strategies/StrategyLoader.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.strategies; 2 | 3 | import java.util.List; 4 | 5 | public class StrategyLoader { 6 | 7 | @SuppressWarnings("unchecked") 8 | public static T load(String className) { 9 | T result = null; 10 | try { 11 | Class clazz = Class.forName(className); 12 | result = (T) clazz.newInstance(); 13 | } catch (Exception e) { 14 | } 15 | return result; 16 | } 17 | 18 | public static T find(List strategies) { 19 | for (int i = 0; i < strategies.size(); i++) { 20 | T strategy = load(strategies.get(i)); 21 | if (strategy != null) { 22 | return strategy; 23 | } 24 | } 25 | return null; 26 | 27 | } 28 | 29 | public boolean exists(List strategies) { 30 | return find(strategies) != null; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/AbstractLayer.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | import java.io.Serializable; 4 | import java.util.ArrayList; 5 | 6 | import com.cloudinary.utils.StringUtils; 7 | 8 | public abstract class AbstractLayer> implements Serializable{ 9 | abstract T getThis(); 10 | 11 | protected String resourceType = null; 12 | protected String type = null; 13 | protected String publicId = null; 14 | protected String format = null; 15 | 16 | public T resourceType(String resourceType) { 17 | this.resourceType = resourceType; 18 | return getThis(); 19 | } 20 | 21 | public T type(String type) { 22 | this.type = type; 23 | return getThis(); 24 | } 25 | 26 | public T publicId(String publicId) { 27 | this.publicId = publicId.replace('/', ':'); 28 | return getThis(); 29 | } 30 | 31 | public T format(String format) { 32 | this.format = format; 33 | return getThis(); 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | ArrayList components = new ArrayList(); 39 | 40 | if (this.resourceType != null && !this.resourceType.equals("image")) { 41 | components.add(this.resourceType); 42 | } 43 | 44 | if (this.type != null && !this.type.equals("upload")) { 45 | components.add(this.type); 46 | } 47 | 48 | if (this.publicId == null) { 49 | throw new IllegalArgumentException("Must supply publicId"); 50 | } 51 | 52 | components.add(formattedPublicId()); 53 | 54 | return StringUtils.join(components, ":"); 55 | } 56 | 57 | protected String formattedPublicId() { 58 | String transientPublicId = this.publicId; 59 | 60 | if (this.format != null) { 61 | transientPublicId = transientPublicId + "." + this.format; 62 | } 63 | 64 | return transientPublicId; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/Condition.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | import com.cloudinary.Transformation; 4 | 5 | /** 6 | * Represents a condition for {@link Transformation#ifCondition()} 7 | */ 8 | public class Condition extends BaseExpression { 9 | 10 | public Condition() { 11 | super(); 12 | 13 | } 14 | 15 | /** 16 | * Create a Condition Object. The conditionStr string will be translated to a serialized condition. 17 | *
18 | * For example, new Condition("fc > 3") 19 | * @param conditionStr condition in string format 20 | */ 21 | public Condition(String conditionStr) { 22 | this(); 23 | if (conditionStr != null) { 24 | expressions.add(normalize(conditionStr)); 25 | } 26 | } 27 | 28 | @Override 29 | protected Condition newInstance() { 30 | return new Condition(); 31 | } 32 | 33 | protected Condition predicate(String name, String operator, Object value) { 34 | if (OPERATORS.containsKey(operator)) { 35 | operator = (String) OPERATORS.get(operator); 36 | } 37 | expressions.add(String.format("%s_%s_%s", name, operator, value)); 38 | return this; 39 | } 40 | 41 | /** 42 | * Terminates the definition of the condition and continue with Transformation definition. 43 | * @return the Transformation object this Condition is attached to. 44 | */ 45 | public Transformation then() { 46 | getParent().ifCondition(serialize()); 47 | return getParent(); 48 | } 49 | 50 | public Condition width(String operator, Object value) { 51 | return predicate("w", operator, value); 52 | } 53 | 54 | public Condition height(String operator, Object value) { 55 | return predicate("h", operator, value); 56 | } 57 | 58 | public Condition aspectRatio(String operator, Object value) { 59 | return predicate("ar", operator, value); 60 | } 61 | public Condition duration(String operator, Object value) { 62 | return predicate("du", operator, value); 63 | } 64 | public Condition initialDuration(String operator, Object value) { 65 | return predicate("idu", operator, value); 66 | } 67 | 68 | public Condition faceCount(String operator, Object value) { 69 | return predicate("fc", operator, value); 70 | } 71 | 72 | public Condition pageCount(String operator, Object value) { 73 | return predicate("pc", operator, value); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/Expression.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | /** 4 | * Represents a transformation parameter expression. 5 | */ 6 | public class Expression extends BaseExpression { 7 | 8 | private boolean predefined = false; 9 | 10 | public Expression(){ 11 | super(); 12 | } 13 | 14 | public Expression(String name){ 15 | super(); 16 | expressions.add(name); 17 | } 18 | 19 | public static Expression variable(String name, Object value){ 20 | Expression var = new Expression(name); 21 | var.expressions.add(value.toString()); 22 | return var; 23 | } 24 | 25 | public static Expression faceCount() { 26 | return new Expression("fc"); 27 | } 28 | 29 | @Override 30 | protected Expression newInstance() { 31 | return new Expression(); 32 | } 33 | /* 34 | * @returns a new expression with the predefined variable "width" 35 | */ 36 | public static Expression width() { 37 | return new Expression("width"); 38 | } 39 | /* 40 | * @returns a new expression with the predefined variable "height" 41 | */ 42 | public static Expression height() { 43 | return new Expression("height"); 44 | } 45 | /* 46 | * @returns a new expression with the predefined variable "initialWidth" 47 | */ 48 | public static Expression initialWidth() { 49 | return new Expression("initialWidth"); 50 | } 51 | /* 52 | * @returns a new expression with the predefined variable "initialHeight" 53 | */ 54 | public static Expression initialHeight() { 55 | return new Expression("initialHeight"); 56 | } 57 | /* 58 | * @returns a new expression with the predefined variable "aspectRatio" 59 | */ 60 | public static Expression aspectRatio() { 61 | return new Expression("aspectRatio"); 62 | } 63 | /* 64 | * @returns a new expression with the predefined variable "initialAspectRatio" 65 | */ 66 | public static Expression initialAspectRatio() { 67 | return new Expression("initialAspectRatio"); 68 | } 69 | /* 70 | * @returns a new expression with the predefined variable "pageCount" 71 | */ 72 | public static Expression pageCount() { 73 | return new Expression("pageCount"); 74 | } 75 | /* 76 | * @returns a new expression with the predefined variable "currentPage" 77 | */ 78 | public static Expression currentPage() { 79 | return new Expression("currentPage"); 80 | } 81 | /* 82 | * @returns a new expression with the predefined variable "tags" 83 | */ 84 | public static Expression tags() { 85 | return new Expression("tags"); 86 | } 87 | /* 88 | * @returns a new expression with the predefined variable "pageX" 89 | */ 90 | public static Expression pageX() { 91 | return new Expression("pageX"); 92 | } 93 | /* 94 | * @returns a new expression with the predefined variable "pageY" 95 | */ 96 | public static Expression pageY() { 97 | return new Expression("pageY"); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/FetchLayer.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | import com.cloudinary.utils.Base64Coder; 4 | 5 | public class FetchLayer extends AbstractLayer { 6 | 7 | public FetchLayer() { 8 | this.type = "fetch"; 9 | } 10 | 11 | public FetchLayer url(String remoteUrl) { 12 | this.publicId = Base64Coder.encodeURLSafeString(remoteUrl);; 13 | return this; 14 | } 15 | 16 | @Override 17 | public FetchLayer type(String type) { 18 | throw new UnsupportedOperationException("Cannot modify type for fetch layers"); 19 | } 20 | 21 | @Override 22 | FetchLayer getThis() { 23 | return this; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/Layer.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | public class Layer extends AbstractLayer { 4 | @Override 5 | Layer getThis() { 6 | return this; 7 | } 8 | } -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/transformation/SubtitlesLayer.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.transformation; 2 | 3 | public class SubtitlesLayer extends TextLayer { 4 | public SubtitlesLayer() { 5 | this.resourceType = "subtitles"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/utils/Base64Map.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.utils; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public final class Base64Map { 7 | private Base64Map() {} 8 | 9 | public static Map values; 10 | 11 | static { 12 | values = new HashMap<>(); 13 | values.put("000000", "A"); 14 | values.put("000001", "B"); 15 | values.put("000010", "C"); 16 | values.put("000011", "D"); 17 | values.put("000100", "E"); 18 | values.put("000101", "F"); 19 | values.put("000110", "G"); 20 | values.put("000111", "H"); 21 | values.put("001000", "I"); 22 | values.put("001001", "J"); 23 | values.put("001010", "K"); 24 | values.put("001011", "L"); 25 | values.put("001100", "M"); 26 | values.put("001101", "N"); 27 | values.put("001110", "O"); 28 | values.put("001111", "P"); 29 | values.put("010000", "Q"); 30 | values.put("010001", "R"); 31 | values.put("010010", "S"); 32 | values.put("010011", "T"); 33 | values.put("010100", "U"); 34 | values.put("010101", "V"); 35 | values.put("010110", "W"); 36 | values.put("010111", "X"); 37 | values.put("011000", "Y"); 38 | values.put("011001", "Z"); 39 | values.put("011010", "a"); 40 | values.put("011011", "b"); 41 | values.put("011100", "c"); 42 | values.put("011101", "d"); 43 | values.put("011110", "e"); 44 | values.put("011111", "f"); 45 | values.put("100000","g"); 46 | values.put("100001","h"); 47 | values.put("100010","i"); 48 | values.put("100011","j"); 49 | values.put("100100","k"); 50 | values.put("100101","l"); 51 | values.put("100110","m"); 52 | values.put("100111","n"); 53 | values.put("101000","o"); 54 | values.put("101001","p"); 55 | values.put("101010","q"); 56 | values.put("101011","r"); 57 | values.put("101100","s"); 58 | values.put("101101","t"); 59 | values.put("101110","u"); 60 | values.put("101111","v"); 61 | values.put("110000","w"); 62 | values.put("110001","x"); 63 | values.put("110010","y"); 64 | values.put("110011","z"); 65 | values.put("110100","0"); 66 | values.put("110101","1"); 67 | values.put("110110","2"); 68 | values.put("110111","3"); 69 | values.put("111000","4"); 70 | values.put("111001","5"); 71 | values.put("111010","6"); 72 | values.put("111011","7"); 73 | values.put("111100","8"); 74 | values.put("111101","9"); 75 | values.put("111110","+"); 76 | values.put("111111","/"); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/com/cloudinary/utils/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.utils; 2 | 3 | import java.io.Serializable; 4 | 5 | public class Rectangle implements Serializable{ 6 | 7 | public int height; 8 | public int width; 9 | public int y; 10 | public int x; 11 | 12 | public Rectangle(int x, int y, int width, int height) { 13 | this.x = x; 14 | this.y = y; 15 | this.width = width; 16 | this.height = height; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/org/cloudinary/json/JSONException.java: -------------------------------------------------------------------------------- 1 | package org.cloudinary.json; 2 | 3 | /** 4 | * The JSONException is thrown by the JSON.org classes when things are amiss. 5 | * 6 | * @author JSON.org 7 | * @version 2014-05-03 8 | */ 9 | public class JSONException extends RuntimeException { 10 | private static final long serialVersionUID = 0; 11 | private Throwable cause; 12 | 13 | /** 14 | * Constructs a JSONException with an explanatory message. 15 | * 16 | * @param message Detail about the reason for the exception. 17 | */ 18 | public JSONException(String message) { 19 | super(message); 20 | } 21 | 22 | /** 23 | * Constructs a new JSONException with the specified cause. 24 | * 25 | * @param cause The cause. 26 | */ 27 | public JSONException(Throwable cause) { 28 | super(cause.getMessage()); 29 | this.cause = cause; 30 | } 31 | 32 | /** 33 | * Returns the cause of this exception or null if the cause is nonexistent 34 | * or unknown. 35 | * 36 | * @return the cause of this exception or null if the cause is nonexistent 37 | * or unknown. 38 | */ 39 | @Override 40 | public Throwable getCause() { 41 | return this.cause; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /cloudinary-core/src/main/java/org/cloudinary/json/JSONString.java: -------------------------------------------------------------------------------- 1 | package org.cloudinary.json; 2 | 3 | /** 4 | * The JSONString interface allows a toJSONString() 5 | * method so that a class can change the behavior of 6 | * JSONObject.toString(), JSONArray.toString(), 7 | * and JSONWriter.value(Object). The 8 | * toJSONString method will be used instead of the default behavior 9 | * of using the Object's toString() method and quoting the result. 10 | */ 11 | public interface JSONString { 12 | /** 13 | * The toJSONString method allows a class to produce its own JSON 14 | * serialization. 15 | * 16 | * @return A strictly syntactically correct JSON text. 17 | */ 18 | public String toJSONString(); 19 | } 20 | -------------------------------------------------------------------------------- /cloudinary-core/src/test/java/com/cloudinary/api/signing/ApiResponseSignatureVerifierTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.signing; 2 | 3 | import com.cloudinary.SignatureAlgorithm; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertFalse; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class ApiResponseSignatureVerifierTest { 10 | @Test 11 | public void testVerifySignature() { 12 | ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU"); 13 | 14 | boolean actual = verifier.verifySignature("tests/logo.png", "1", "08d3107a5b2ad82e7d82c0b972218fbf20b5b1e0"); 15 | 16 | assertTrue(actual); 17 | } 18 | 19 | @Test 20 | public void testVerifySignatureFail() { 21 | ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU"); 22 | 23 | boolean actual = verifier.verifySignature("tests/logo.png", "1", "doesNotMatchForSure"); 24 | 25 | assertFalse(actual); 26 | } 27 | 28 | @Test 29 | public void testVerifySignatureSHA256() { 30 | ApiResponseSignatureVerifier verifier = new ApiResponseSignatureVerifier("X7qLTrsES31MzxxkxPPA-pAGGfU", SignatureAlgorithm.SHA256); 31 | 32 | boolean actual = verifier.verifySignature("tests/logo.png", "1", "cc69ae4ed73303fbf4a55f2ae5fc7e34ad3a5c387724bfcde447a2957cacdfea"); 33 | 34 | assertTrue(actual); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cloudinary-core/src/test/java/com/cloudinary/api/signing/NotificationRequestSignatureVerifierTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.api.signing; 2 | 3 | import com.cloudinary.SignatureAlgorithm; 4 | import org.junit.Test; 5 | 6 | import static org.junit.Assert.assertFalse; 7 | import static org.junit.Assert.assertTrue; 8 | 9 | public class NotificationRequestSignatureVerifierTest { 10 | @Test 11 | public void testVerifySignature() { 12 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); 13 | 14 | boolean actual = verifier.verifySignature( 15 | "{}", 16 | "0", 17 | "f9aa4471d2a88ff244424cca2444edf7d7ac3596"); 18 | 19 | assertTrue(actual); 20 | } 21 | 22 | @Test 23 | public void testVerifySignatureFailWhenSignatureDoesntMatch() { 24 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); 25 | 26 | boolean actual = verifier.verifySignature( 27 | "{}", 28 | "0", 29 | "notMatchingForSure"); 30 | 31 | assertFalse(actual); 32 | } 33 | 34 | @Test 35 | public void testVerifySignatureFailWhenTooOld() { 36 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); 37 | 38 | boolean actual = verifier.verifySignature( 39 | "{}", 40 | "0", 41 | "f9aa4471d2a88ff244424cca2444edf7d7ac3596", 42 | 1000L); 43 | 44 | assertFalse(actual); 45 | } 46 | 47 | @Test 48 | public void testVerifySignaturePassWhenStillValid() { 49 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); 50 | 51 | boolean actual = verifier.verifySignature( 52 | "{}", 53 | "0", 54 | "f9aa4471d2a88ff244424cca2444edf7d7ac3596", 55 | Long.MAX_VALUE / 1000L); 56 | 57 | assertTrue(actual); 58 | } 59 | 60 | @Test 61 | public void testVerifySignatureFailWhenStillValidButSignatureDoesntMatch() { 62 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret"); 63 | 64 | boolean actual = verifier.verifySignature( 65 | "{}", 66 | "0", 67 | "notMatchingForSure", 68 | Long.MAX_VALUE / 1000L); 69 | 70 | assertFalse(actual); 71 | } 72 | 73 | @Test 74 | public void testVerifySignatureSHA256() { 75 | NotificationRequestSignatureVerifier verifier = new NotificationRequestSignatureVerifier("someApiSecret", SignatureAlgorithm.SHA256); 76 | 77 | boolean actual = verifier.verifySignature( 78 | "{}", 79 | "0", 80 | "d5497e1a206ad0ba29ad09a7c0c5f22e939682d15009c15ab3199f62fefbd14b"); 81 | 82 | assertTrue(actual); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cloudinary-http5/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'signing' 4 | id 'maven-publish' 5 | id 'io.codearte.nexus-staging' version '0.21.1' 6 | } 7 | 8 | apply from: "../java_shared.gradle" 9 | 10 | task ciTest( type: Test ) { 11 | useJUnit { 12 | excludeCategories 'com.cloudinary.test.TimeoutTest' 13 | if (System.getProperty("CLOUDINARY_ACCOUNT_URL") == "") { 14 | exclude '**/AccountApiTest.class' 15 | } 16 | } 17 | } 18 | 19 | dependencies { 20 | compile project(':cloudinary-core') 21 | compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.1' 22 | api group: 'org.apache.httpcomponents.client5', name: 'httpclient5', version: '5.3.1' 23 | api group: 'org.apache.httpcomponents.core5', name: 'httpcore5', version: '5.2.5' 24 | testCompile project(':cloudinary-test-common') 25 | testCompile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' 26 | testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' 27 | testCompile group: 'junit', name: 'junit', version: '4.12' 28 | } 29 | 30 | if (hasProperty("ossrhPassword")) { 31 | 32 | signing { 33 | sign configurations.archives 34 | } 35 | 36 | nexusStaging { 37 | packageGroup = group 38 | username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" 39 | password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" 40 | } 41 | 42 | publishing { 43 | publications { 44 | mavenJava(MavenPublication) { 45 | from components.java 46 | artifact sourcesJar 47 | artifact javadocJar 48 | pom { 49 | name = 'Cloudinary Apache HTTP 5 Library' 50 | packaging = 'jar' 51 | groupId = publishGroupId 52 | artifactId = 'cloudinary-http5' 53 | description = publishDescription 54 | url = githubUrl 55 | licenses { 56 | license { 57 | name = licenseName 58 | url = licenseUrl 59 | } 60 | } 61 | 62 | developers { 63 | developer { 64 | id = developerId 65 | name = developerName 66 | email = developerEmail 67 | } 68 | } 69 | scm { 70 | connection = scmConnection 71 | developerConnection = scmDeveloperConnection 72 | url = scmUrl 73 | } 74 | } 75 | 76 | pom.withXml { 77 | def pomFile = file("${project.buildDir}/generated-pom.xml") 78 | writeTo(pomFile) 79 | def pomAscFile = signing.sign(pomFile).signatureFiles[0] 80 | artifact(pomAscFile) { 81 | classifier = null 82 | extension = 'pom.asc' 83 | } 84 | } 85 | 86 | // create the signed artifacts 87 | project.tasks.signArchives.signatureFiles.each { 88 | artifact(it) { 89 | def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ 90 | if (matcher.find()) { 91 | classifier = matcher.group(1) 92 | } else { 93 | classifier = null 94 | } 95 | extension = 'jar.asc' 96 | } 97 | } 98 | } 99 | } 100 | 101 | model { 102 | tasks.generatePomFileForMavenJavaPublication { 103 | destination = file("$buildDir/generated-pom.xml") 104 | } 105 | tasks.publishMavenJavaPublicationToMavenLocal { 106 | dependsOn project.tasks.signArchives 107 | } 108 | tasks.publishMavenJavaPublicationToSonatypeRepository { 109 | dependsOn project.tasks.signArchives 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /cloudinary-http5/src/main/java/com/cloudinary/http5/ApiUtils.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.http5; 2 | 3 | import com.cloudinary.utils.ObjectUtils; 4 | import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; 5 | import org.apache.hc.client5.http.config.RequestConfig; 6 | import org.apache.hc.core5.http.NameValuePair; 7 | import org.apache.hc.core5.http.message.BasicNameValuePair; 8 | import org.apache.hc.core5.util.Timeout; 9 | import org.cloudinary.json.JSONObject; 10 | 11 | import java.util.*; 12 | 13 | public final class ApiUtils { 14 | private ApiUtils() {} 15 | 16 | public static void setTimeouts(HttpUriRequestBase request, Map options) { 17 | RequestConfig config = request.getConfig(); 18 | final RequestConfig.Builder builder; 19 | 20 | if (config != null) { 21 | builder = RequestConfig.copy(config); 22 | } else { 23 | builder = RequestConfig.custom(); 24 | } 25 | 26 | Integer timeout = (Integer) options.get("timeout"); 27 | if (timeout != null) { 28 | builder.setResponseTimeout(Timeout.ofSeconds(timeout)); 29 | } 30 | 31 | Integer connectionRequestTimeout = (Integer) options.get("connection_request_timeout"); 32 | if (connectionRequestTimeout != null) { 33 | builder.setConnectionRequestTimeout(Timeout.ofSeconds(connectionRequestTimeout)); 34 | } 35 | 36 | Integer connectTimeout = (Integer) options.get("connect_timeout"); 37 | if (connectTimeout != null) { 38 | builder.setConnectTimeout(Timeout.ofSeconds(connectTimeout)); 39 | } 40 | 41 | request.setConfig(builder.build()); 42 | } 43 | 44 | 45 | public static List prepareParams(Map params) { 46 | List requestParams = new ArrayList<>(); 47 | 48 | for (Map.Entry param : params.entrySet()) { 49 | String key = param.getKey(); 50 | Object value = param.getValue(); 51 | 52 | if (value instanceof Iterable) { 53 | // If the value is an Iterable, handle each item individually 54 | for (Object single : (Iterable) value) { 55 | requestParams.add(new BasicNameValuePair(key + "[]", ObjectUtils.asString(single))); 56 | } 57 | } else if (value instanceof Map) { 58 | // Convert Map to JSON string manually to avoid empty object issues 59 | JSONObject jsonObject = new JSONObject(); 60 | for (Map.Entry entry : ((Map) value).entrySet()) { 61 | jsonObject.put(entry.getKey().toString(), entry.getValue()); 62 | } 63 | requestParams.add(new BasicNameValuePair(key, jsonObject.toString())); 64 | } else { 65 | // Handle simple key-value pairs 66 | requestParams.add(new BasicNameValuePair(key, ObjectUtils.asString(value))); 67 | } 68 | } 69 | 70 | return requestParams; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /cloudinary-http5/src/main/java/com/cloudinary/http5/api/Response.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.http5.api; 2 | 3 | import com.cloudinary.api.ApiResponse; 4 | import com.cloudinary.api.RateLimit; 5 | import org.apache.hc.core5.http.Header; 6 | import org.apache.hc.core5.http.HttpResponse; 7 | import java.text.ParseException; 8 | 9 | import java.text.DateFormat; 10 | import java.text.SimpleDateFormat; 11 | import java.util.HashMap; 12 | import java.util.Locale; 13 | import java.util.Map; 14 | import java.util.regex.Matcher; 15 | import java.util.regex.Pattern; 16 | 17 | public class Response extends HashMap implements ApiResponse { 18 | private static final long serialVersionUID = -5458609797599845837L; 19 | private final HttpResponse response; 20 | 21 | @SuppressWarnings("unchecked") 22 | public Response(HttpResponse response, Map result) { 23 | super(result); 24 | this.response = response; 25 | } 26 | 27 | public HttpResponse getRawHttpResponse() { 28 | return this.response; 29 | } 30 | 31 | private static final Pattern RATE_LIMIT_REGEX = Pattern 32 | .compile("X-FEATURE(\\w*)RATELIMIT(-LIMIT|-RESET|-REMAINING)", Pattern.CASE_INSENSITIVE); 33 | private static final String RFC1123_PATTERN = "EEE, dd MMM yyyy HH:mm:ss z"; 34 | private static final DateFormat RFC1123 = new SimpleDateFormat(RFC1123_PATTERN, Locale.ENGLISH); 35 | 36 | public Map rateLimits() throws ParseException { 37 | Header[] headers = this.response.getHeaders(); 38 | Map limits = new HashMap<>(); 39 | for (Header header : headers) { 40 | Matcher m = RATE_LIMIT_REGEX.matcher(header.getName()); 41 | if (m.matches()) { 42 | String limitName = "Api"; 43 | RateLimit limit = limits.getOrDefault(limitName, new RateLimit()); 44 | if (!m.group(1).isEmpty()) { 45 | limitName = m.group(1); 46 | } 47 | if (m.group(2).equalsIgnoreCase("-limit")) { 48 | limit.setLimit(Long.parseLong(header.getValue())); 49 | } else if (m.group(2).equalsIgnoreCase("-remaining")) { 50 | limit.setRemaining(Long.parseLong(header.getValue())); 51 | } else if (m.group(2).equalsIgnoreCase("-reset")) { 52 | limit.setReset(RFC1123.parse(header.getValue())); 53 | } 54 | limits.put(limitName, limit); 55 | } 56 | } 57 | return limits; 58 | } 59 | 60 | public RateLimit apiRateLimit() throws ParseException { 61 | return rateLimits().get("Api"); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/AccountApiTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class AccountApiTest extends AbstractAccountApiTest { 4 | } 5 | -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/ApiTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | import com.cloudinary.Cloudinary; 4 | import com.cloudinary.api.ApiResponse; 5 | import com.cloudinary.http5.ApiStrategy; 6 | import com.cloudinary.utils.ObjectUtils; 7 | import org.apache.hc.client5.http.config.RequestConfig; 8 | import org.apache.hc.core5.http.HttpHost; 9 | import org.apache.hc.core5.util.Timeout; 10 | import org.junit.Test; 11 | import org.junit.experimental.categories.Category; 12 | 13 | import java.util.Map; 14 | import java.util.UUID; 15 | 16 | import static com.cloudinary.utils.ObjectUtils.asMap; 17 | 18 | 19 | public class ApiTest extends AbstractApiTest { 20 | 21 | @Test 22 | public void testBuildRequestConfig_withProxyAndTimeout() { 23 | Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); 24 | cloudinary.config.proxyHost = "127.0.0.1"; 25 | cloudinary.config.proxyPort = 8080; 26 | cloudinary.config.timeout = 15; 27 | 28 | RequestConfig requestConfig = ((ApiStrategy)cloudinary.api().getStrategy()).buildRequestConfig(); 29 | 30 | assert(requestConfig.getProxy() != null); 31 | HttpHost proxy = requestConfig.getProxy(); 32 | assert("127.0.0.1" == proxy.getHostName()); 33 | assert(8080 == proxy.getPort()); 34 | 35 | assert(15000 == requestConfig.getConnectionRequestTimeout().toMilliseconds()); 36 | assert(15000 == requestConfig.getResponseTimeout().toMilliseconds()); 37 | } 38 | 39 | @Test 40 | public void testBuildRequestConfig_withoutProxy() { 41 | Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); 42 | cloudinary.config.timeout = 10; 43 | 44 | RequestConfig requestConfig = ((ApiStrategy)cloudinary.api().getStrategy()).buildRequestConfig(); 45 | 46 | assert(requestConfig.getProxy() == null); 47 | assert(10000 == requestConfig.getConnectionRequestTimeout().toMilliseconds()); 48 | assert(10000 == requestConfig.getResponseTimeout().toMilliseconds()); 49 | } 50 | 51 | @Category(TimeoutTest.class) 52 | @Test(expected = Exception.class) 53 | public void testConnectTimeoutParameter() throws Exception { 54 | Map options = asMap( 55 | "max_results", 500, 56 | "connect_timeout", 0.2); 57 | 58 | try { 59 | System.out.println("Setting connect timeout to 100 ms"); 60 | ApiResponse result = cloudinary.api().resources(options); 61 | System.out.println("Request completed without timeout"); 62 | } catch (Exception e) { 63 | throw new Exception("Connection timeout", e); 64 | } 65 | } 66 | 67 | @Category(TimeoutTest.class) 68 | @Test(expected = Exception.class) 69 | public void testTimeoutParameter() throws Exception { 70 | // Set a very short request timeout to trigger a timeout exception 71 | Map options = asMap( 72 | "max_results", 500, 73 | "timeout", Timeout.ofMilliseconds(1000)); // Set the timeout to 1 second 74 | 75 | try { 76 | ApiResponse result = cloudinary.api().resources(options); 77 | } catch (Exception e) { 78 | // Convert IOException to SocketTimeoutException if appropriate 79 | throw new Exception("Socket timeout"); 80 | } 81 | } 82 | 83 | @Category(TimeoutTest.class) 84 | @Test(expected = Exception.class) 85 | public void testUploaderTimeoutParameter() throws Exception { 86 | Cloudinary cloudinary = new Cloudinary("cloudinary://test:test@test.com"); 87 | cloudinary.config.uploadPrefix = "https://10.255.255.1"; 88 | String publicId = UUID.randomUUID().toString(); 89 | // Set a very short request timeout to trigger a timeout exception 90 | Map options = asMap( 91 | "max_results", 500, 92 | "timeout", Timeout.ofMilliseconds(10)); // Set the timeout to 1 second 93 | 94 | try { 95 | Map result = cloudinary.uploader().addContext(asMap("caption", "new caption"), new String[]{publicId, "no-such-id"}, options); 96 | } catch (Exception e) { 97 | // Convert IOException to SocketTimeoutException if appropriate 98 | throw new Exception("Socket timeout"); 99 | } 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/ContextTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class ContextTest extends AbstractContextTest { 4 | 5 | } -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/FoldersApiTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class FoldersApiTest extends AbstractFoldersApiTest { 4 | } 5 | -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/SearchTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class SearchTest extends AbstractSearchTest { 4 | } 5 | -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/StreamingProfilesApiTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | /** 4 | * Created by amir on 25/10/2016. 5 | */ 6 | public class StreamingProfilesApiTest extends AbstractStreamingProfilesApiTest { 7 | } 8 | -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/StructuredMetadataTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class StructuredMetadataTest extends AbstractStructuredMetadataTest { 4 | } -------------------------------------------------------------------------------- /cloudinary-http5/src/test/java/com/cloudinary/test/UploaderTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | public class UploaderTest extends AbstractUploaderTest { 4 | 5 | } -------------------------------------------------------------------------------- /cloudinary-taglib/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'signing' 4 | id 'maven-publish' 5 | id 'io.codearte.nexus-staging' version '0.21.1' 6 | } 7 | 8 | apply from: "../java_shared.gradle" 9 | 10 | task ciTest( type: Test ) 11 | 12 | dependencies { 13 | compile project(':cloudinary-core') 14 | compile group: 'org.apache.commons', name: 'commons-lang3', version:'3.1' 15 | testCompile group: 'org.hamcrest', name: 'java-hamcrest', version:'2.0.0.0' 16 | testCompile group: 'pl.pragmatists', name: 'JUnitParams', version:'1.0.5' 17 | testCompile group: 'junit', name: 'junit', version:'4.12' 18 | compile(group: 'javax.servlet', name: 'jsp-api', version:'2.0') { 19 | /* This dependency was originally in the Maven provided scope, but the project was not of type war. 20 | This behavior is not yet supported by Gradle, so this dependency has been converted to a compile dependency. 21 | Please review and delete this closure when resolved. */ 22 | } 23 | } 24 | 25 | if (hasProperty("ossrhPassword")) { 26 | 27 | signing { 28 | sign configurations.archives 29 | } 30 | 31 | nexusStaging { 32 | packageGroup = group 33 | username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" 34 | password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" 35 | } 36 | 37 | publishing { 38 | publications { 39 | mavenJava(MavenPublication) { 40 | from components.java 41 | artifact sourcesJar 42 | artifact javadocJar 43 | pom { 44 | name = 'Cloudinary Taglib Library' 45 | packaging = 'jar' 46 | groupId = publishGroupId 47 | artifactId = 'cloudinary-taglib' 48 | description = publishDescription 49 | url = githubUrl 50 | licenses { 51 | license { 52 | name = licenseName 53 | url = licenseUrl 54 | } 55 | } 56 | 57 | developers { 58 | developer { 59 | id = developerId 60 | name = developerName 61 | email = developerEmail 62 | } 63 | } 64 | scm { 65 | connection = scmConnection 66 | developerConnection = scmDeveloperConnection 67 | url = scmUrl 68 | } 69 | } 70 | 71 | pom.withXml { 72 | def pomFile = file("${project.buildDir}/generated-pom.xml") 73 | writeTo(pomFile) 74 | def pomAscFile = signing.sign(pomFile).signatureFiles[0] 75 | artifact(pomAscFile) { 76 | classifier = null 77 | extension = 'pom.asc' 78 | } 79 | } 80 | 81 | // create the signed artifacts 82 | project.tasks.signArchives.signatureFiles.each { 83 | artifact(it) { 84 | def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ 85 | if (matcher.find()) { 86 | classifier = matcher.group(1) 87 | } else { 88 | classifier = null 89 | } 90 | extension = 'jar.asc' 91 | } 92 | } 93 | } 94 | } 95 | 96 | model { 97 | tasks.generatePomFileForMavenJavaPublication { 98 | destination = file("$buildDir/generated-pom.xml") 99 | } 100 | tasks.publishMavenJavaPublicationToMavenLocal { 101 | dependsOn project.tasks.signArchives 102 | } 103 | tasks.publishMavenJavaPublicationToSonatypeRepository { 104 | dependsOn project.tasks.signArchives 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/Singleton.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | /** 4 | * This class contains a singleton in a generic way. This class is used by the tags to 5 | * retrieve the Cloudinary configuration. 6 | *

7 | * the containing framework is responsible for registering the cloudinary configuration with the 8 | * Singleton, and then removing it on shutdown. This allows the user to use Spring or any other 9 | * framework without imposing additional dependencies on the cloudinary project. 10 | * 11 | * @author jpollak 12 | */ 13 | public final class Singleton { 14 | private Singleton() {} 15 | 16 | private static Cloudinary cloudinary; 17 | 18 | public static void registerCloudinary(Cloudinary cloudinary) { 19 | Singleton.cloudinary = cloudinary; 20 | } 21 | 22 | public static void deregisterCloudinary() { 23 | cloudinary = null; 24 | } 25 | 26 | private static class DefaultCloudinaryHolder { 27 | public static final Cloudinary INSTANCE = new Cloudinary(); 28 | } 29 | 30 | public static Cloudinary getCloudinary() { 31 | if (cloudinary == null) { 32 | return DefaultCloudinaryHolder.INSTANCE; 33 | } 34 | return cloudinary; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/SingletonManager.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary; 2 | 3 | public class SingletonManager { 4 | 5 | private Cloudinary cloudinary; 6 | 7 | public void setCloudinary(Cloudinary cloudinary) { 8 | this.cloudinary = cloudinary; 9 | } 10 | 11 | public void init() { 12 | Singleton.registerCloudinary(cloudinary); 13 | } 14 | 15 | public void destroy() { 16 | Singleton.deregisterCloudinary(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryImageTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import javax.servlet.jsp.JspException; 8 | import javax.servlet.jsp.JspWriter; 9 | 10 | import com.cloudinary.*; 11 | 12 | /** 13 | * Generates an image html tag.
14 | * For example,
15 | * {@code } 16 | *
is equivalent to:
17 | *

{@code
18 |  * Transformation transformation = new Transformation()
19 |  *      .width(100)
20 |  *      .height(101)
21 |  *      .crop("crop");
22 |  * String result = cloudinary.url()
23 |  *      .transformation(transformation)
24 |  *      .imageTag("test", Cloudinary.asMap("alt", "my image"));
25 |  * }
26 | *
27 | * Both code segments above produce the following tag:
28 | * {@code my image } 30 | *
31 | * @author jpollak 32 | * 33 | */ 34 | public class CloudinaryImageTag extends CloudinaryUrl { 35 | 36 | private String id = null; 37 | private String extraClasses = null; 38 | 39 | protected Map prepareAttributes() { 40 | Map attributes = new HashMap(); 41 | if (id != null) { 42 | attributes.put("id", id); 43 | } 44 | if (extraClasses != null) { 45 | attributes.put("class", extraClasses); 46 | } 47 | return attributes; 48 | } 49 | 50 | public void doTag() throws JspException, IOException { 51 | JspWriter out = getJspContext().getOut(); 52 | Url url = this.prepareUrl(); 53 | out.println(url.imageTag(prepareAttributes())); 54 | } 55 | 56 | public void setId(String id) { 57 | this.id = id; 58 | } 59 | 60 | public String getId() { 61 | return id; 62 | } 63 | 64 | public String getExtraClasses() { 65 | return extraClasses; 66 | } 67 | 68 | public void setExtraClasses(String extraClasses) { 69 | this.extraClasses = extraClasses; 70 | } 71 | } -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsConfigTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import java.io.IOException; 4 | 5 | import javax.servlet.jsp.JspException; 6 | import javax.servlet.jsp.JspWriter; 7 | import javax.servlet.jsp.tagext.SimpleTagSupport; 8 | 9 | import com.cloudinary.Cloudinary; 10 | import com.cloudinary.Singleton; 11 | 12 | public class CloudinaryJsConfigTag extends SimpleTagSupport { 13 | @SuppressWarnings("unused") 14 | public void doTag() throws JspException, IOException { 15 | Cloudinary cloudinary = Singleton.getCloudinary(); 16 | if (cloudinary == null) { 17 | throw new JspException("Cloudinary config could not be located"); 18 | } 19 | JspWriter out = getJspContext().getOut(); 20 | out.println(""); 28 | } 29 | 30 | private void print(JspWriter out, String key, Object value) throws IOException { 31 | if (value instanceof Boolean) { 32 | out.println(key + ": " + ((Boolean) value ? "true" : "false") + ","); 33 | } else { 34 | out.println(key + ": \"" + value + "\","); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryJsIncludeTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import com.cloudinary.Cloudinary; 4 | import com.cloudinary.Singleton; 5 | 6 | import javax.servlet.jsp.JspException; 7 | import javax.servlet.jsp.JspWriter; 8 | import javax.servlet.jsp.tagext.SimpleTagSupport; 9 | import java.io.IOException; 10 | 11 | public class CloudinaryJsIncludeTag extends SimpleTagSupport { 12 | private boolean full = false; 13 | private String base = "javascripts/cloudinary/"; 14 | 15 | public void doTag() throws JspException, IOException { 16 | Cloudinary cloudinary = Singleton.getCloudinary(); 17 | if (cloudinary == null) { 18 | throw new JspException("Cloudinary config could not be located"); 19 | } 20 | JspWriter out = getJspContext().getOut(); 21 | String[] basicFiles = {"jquery.ui.widget.js", "jquery.iframe-transport.js", "jquery.fileupload.js", "jquery.cloudinary.js"}; 22 | for (String file : basicFiles) { 23 | out.println(""); 24 | } 25 | if (full) { 26 | String[] fullFiles = {"canvas-to-blob.min.js", "load-image.min.js", "jquery.fileupload-process.js", "uery.fileupload-image.js", "jquery.fileupload-validate.js"}; 27 | for (String file : fullFiles) { 28 | out.println(""); 29 | } 30 | } 31 | 32 | } 33 | 34 | public boolean isFull() { 35 | return full; 36 | } 37 | 38 | public void setFull(boolean full) { 39 | this.full = full; 40 | } 41 | 42 | public String getBase() { 43 | return base; 44 | } 45 | 46 | public void setBase(String base) { 47 | this.base = base; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryTransformationTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import com.cloudinary.Transformation; 4 | 5 | import javax.servlet.jsp.JspException; 6 | import javax.servlet.jsp.tagext.DynamicAttributes; 7 | import javax.servlet.jsp.tagext.SimpleTagSupport; 8 | import java.io.IOException; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class CloudinaryTransformationTag extends SimpleTagSupport implements DynamicAttributes { 13 | private Map tagAttrs = new HashMap(); 14 | 15 | public void doTag() throws JspException, IOException { 16 | Transformation transformation = new Transformation().params(tagAttrs); 17 | getJspContext().getOut().print(transformation.generate()); 18 | } 19 | 20 | @Override 21 | public void setDynamicAttribute(String uri, String name, Object value) throws JspException { 22 | tagAttrs.put(name, value); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryUnsignedUploadTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import java.util.Map; 4 | 5 | import com.cloudinary.Uploader; 6 | 7 | public class CloudinaryUnsignedUploadTag extends CloudinaryUploadTag { 8 | public CloudinaryUnsignedUploadTag() { 9 | super(); 10 | this.unsigned = true; 11 | } 12 | 13 | @SuppressWarnings({ "unchecked", "rawtypes" }) 14 | protected String uploadTag(Uploader uploader, Map options, Map htmlOptions) { 15 | return uploader.unsignedImageUploadTag(fieldName, uploadPreset, options, htmlOptions); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /cloudinary-taglib/src/main/java/com/cloudinary/taglib/CloudinaryVideoTag.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.taglib; 2 | 3 | import java.io.IOException; 4 | import java.util.Map; 5 | 6 | import javax.servlet.jsp.JspException; 7 | import javax.servlet.jsp.JspWriter; 8 | 9 | import com.cloudinary.Transformation; 10 | import com.cloudinary.Url; 11 | 12 | public class CloudinaryVideoTag extends CloudinaryImageTag { 13 | private String sourceTypes; 14 | private Object poster; 15 | private Boolean autoplay; 16 | private Boolean controls; 17 | private Boolean loop; 18 | private Boolean muted; 19 | private Boolean preload; 20 | 21 | public Boolean getAutoplay() { 22 | return autoplay; 23 | } 24 | public void setAutoplay(Boolean autoplay) { 25 | this.autoplay = autoplay; 26 | } 27 | public Boolean getControls() { 28 | return controls; 29 | } 30 | public void setControls(Boolean controls) { 31 | this.controls = controls; 32 | } 33 | public Boolean getLoop() { 34 | return loop; 35 | } 36 | public void setLoop(Boolean loop) { 37 | this.loop = loop; 38 | } 39 | public Boolean getMuted() { 40 | return muted; 41 | } 42 | public void setMuted(Boolean muted) { 43 | this.muted = muted; 44 | } 45 | public Boolean getPreload() { 46 | return preload; 47 | } 48 | public void setPreload(Boolean preload) { 49 | this.preload = preload; 50 | } 51 | public Object getPoster() { 52 | return poster; 53 | } 54 | public void setPoster(Object poster) { 55 | this.poster = poster; 56 | } 57 | 58 | public String getSourceTypes() { 59 | return sourceTypes; 60 | } 61 | public void setSourceTypes(String sourceTypes) { 62 | this.sourceTypes = sourceTypes; 63 | } 64 | 65 | public void doTag() throws JspException, IOException { 66 | JspWriter out = getJspContext().getOut(); 67 | Url url = this.prepareUrl(); 68 | 69 | String sourceTypes[] = null; 70 | if (this.sourceTypes != null) { 71 | sourceTypes = this.sourceTypes.split(","); 72 | url.sourceTypes(sourceTypes); 73 | } 74 | 75 | if (this.poster != null) { 76 | if (this.poster.equals("false")) { 77 | url.poster(false); 78 | } else { 79 | url.poster(this.poster); 80 | } 81 | } 82 | 83 | Map attributes = prepareAttributes(); 84 | 85 | if (sourceTypes == null) sourceTypes = Url.DEFAULT_VIDEO_SOURCE_TYPES; 86 | for (String sourceType : sourceTypes) { 87 | String transformationAttribute = sourceType + "Transformation"; 88 | if (this.tagAttrs.containsKey(transformationAttribute)) { 89 | Transformation transformation = null; 90 | Object transformationAttrValue = tagAttrs.remove(transformationAttribute); 91 | if (transformationAttrValue instanceof Transformation) { 92 | transformation = (Transformation) transformationAttrValue; 93 | } else if (transformationAttrValue instanceof Map) { 94 | transformation = new Transformation().params((Map) transformationAttrValue); 95 | } else { 96 | transformation = new Transformation().rawTransformation((String) transformationAttrValue); 97 | } 98 | url.sourceTransformationFor(sourceType, transformation ); 99 | } 100 | } 101 | 102 | if (autoplay != null) attributes.put("autoplay", autoplay.toString()); 103 | if (controls != null) attributes.put("controls", controls.toString()); 104 | if (loop != null) attributes.put("loop", loop.toString()); 105 | if (muted != null) attributes.put("muted", muted.toString()); 106 | if (preload != null) attributes.put("preload", preload.toString()); 107 | 108 | out.println(url.videoTag(attributes)); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /cloudinary-test-common/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java-library' 3 | id 'signing' 4 | id 'maven-publish' 5 | id 'io.codearte.nexus-staging' version '0.21.1' 6 | } 7 | 8 | apply from: "../java_shared.gradle" 9 | 10 | task ciTest( type: Test ) 11 | 12 | dependencies { 13 | compile project(':cloudinary-core') 14 | compile group: 'org.hamcrest', name: 'java-hamcrest', version: '2.0.0.0' 15 | compile group: 'junit', name: 'junit', version: '4.12' 16 | testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.0.5' 17 | } 18 | 19 | if (hasProperty("ossrhPassword")) { 20 | 21 | signing { 22 | sign configurations.archives 23 | } 24 | 25 | nexusStaging { 26 | packageGroup = group 27 | username = project.hasProperty("ossrhToken") ? project.ext["ossrhToken"] : "" 28 | password = project.hasProperty("ossrhTokenPassword") ? project.ext["ossrhTokenPassword"] : "" 29 | } 30 | 31 | publishing { 32 | publications { 33 | mavenJava(MavenPublication) { 34 | from components.java 35 | artifact sourcesJar 36 | artifact javadocJar 37 | pom { 38 | name = 'Cloudinary Test Common' 39 | packaging = 'jar' 40 | groupId = publishGroupId 41 | artifactId = 'cloudinary-test-common' 42 | description = publishDescription 43 | url = githubUrl 44 | licenses { 45 | license { 46 | name = licenseName 47 | url = licenseUrl 48 | } 49 | } 50 | 51 | developers { 52 | developer { 53 | id = developerId 54 | name = developerName 55 | email = developerEmail 56 | } 57 | } 58 | scm { 59 | connection = scmConnection 60 | developerConnection = scmDeveloperConnection 61 | url = scmUrl 62 | } 63 | } 64 | 65 | pom.withXml { 66 | def pomFile = file("${project.buildDir}/generated-pom.xml") 67 | writeTo(pomFile) 68 | def pomAscFile = signing.sign(pomFile).signatureFiles[0] 69 | artifact(pomAscFile) { 70 | classifier = null 71 | extension = 'pom.asc' 72 | } 73 | } 74 | 75 | // create the signed artifacts 76 | project.tasks.signArchives.signatureFiles.each { 77 | artifact(it) { 78 | def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ 79 | if (matcher.find()) { 80 | classifier = matcher.group(1) 81 | } else { 82 | classifier = null 83 | } 84 | extension = 'jar.asc' 85 | } 86 | } 87 | } 88 | } 89 | 90 | model { 91 | tasks.generatePomFileForMavenJavaPublication { 92 | destination = file("$buildDir/generated-pom.xml") 93 | } 94 | tasks.publishMavenJavaPublicationToMavenLocal { 95 | dependsOn project.tasks.signArchives 96 | } 97 | tasks.publishMavenJavaPublicationToSonatypeRepository { 98 | dependsOn project.tasks.signArchives 99 | } 100 | } 101 | } 102 | } -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractContextTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | import com.cloudinary.Cloudinary; 4 | import com.cloudinary.Transformation; 5 | import com.cloudinary.Uploader; 6 | import com.cloudinary.utils.ObjectUtils; 7 | import org.junit.*; 8 | import org.junit.rules.TestName; 9 | 10 | import java.io.IOException; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import static com.cloudinary.utils.ObjectUtils.asMap; 16 | import static org.hamcrest.Matchers.*; 17 | import static org.junit.Assert.assertEquals; 18 | import static org.junit.Assert.assertThat; 19 | import static org.junit.Assume.assumeNotNull; 20 | 21 | @SuppressWarnings({"rawtypes", "unchecked"}) 22 | abstract public class AbstractContextTest extends MockableTest { 23 | 24 | private static final String CONTEXT_TAG = "context_tag_" + String.valueOf(System.currentTimeMillis()) + SUFFIX; 25 | public static final Map CONTEXT = asMap("caption", "some cäption", "alt", "alternativè"); 26 | private Uploader uploader; 27 | 28 | @BeforeClass 29 | public static void setUpClass() throws Exception { 30 | Cloudinary cloudinary = new Cloudinary(); 31 | if (cloudinary.config.apiSecret == null) { 32 | System.err.println("Please setup environment for Upload test to run"); 33 | } 34 | } 35 | 36 | private static Map uploadResource(String publicId) throws IOException { 37 | return new Cloudinary().uploader().upload(SRC_TEST_IMAGE, 38 | asMap( "public_id", publicId, 39 | "tags", new String[]{SDK_TEST_TAG, CONTEXT_TAG}, 40 | "context", CONTEXT, 41 | "transformation", new Transformation().crop("scale").width(10))); 42 | } 43 | 44 | @AfterClass 45 | public static void tearDownClass() { 46 | Cloudinary cloudinary = new Cloudinary(); 47 | try { 48 | cloudinary.api().deleteResourcesByTag(CONTEXT_TAG, ObjectUtils.emptyMap()); 49 | } catch (Exception ignored) { 50 | } 51 | } 52 | 53 | @Rule 54 | public TestName currentTest = new TestName(); 55 | 56 | @Before 57 | public void setUp() throws Exception { 58 | System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); 59 | cloudinary = new Cloudinary(); 60 | uploader = cloudinary.uploader(); 61 | assumeNotNull(cloudinary.config.apiSecret); 62 | 63 | } 64 | 65 | @Test 66 | public void testExplicit() throws Exception { 67 | String publicId = "explicit_id" + SUFFIX; 68 | uploadResource(publicId); 69 | //should allow sending context 70 | Map differentContext = asMap("caption", "different = caption", "alt2", "alt|alternative alternative"); 71 | Map result = uploader.explicit(publicId, asMap("type", "upload", "context", differentContext)); 72 | assertEquals("explicit API should return the new context", asMap("custom", differentContext), result.get("context")); 73 | Map resource = cloudinary.api().resource(publicId, asMap("context", true)); 74 | assertEquals("explicit API should replace the context", asMap("custom", differentContext), resource.get("context")); 75 | } 76 | 77 | @Test 78 | public void testAddContext() throws Exception { 79 | String publicId = "add_context_id" + SUFFIX; 80 | Map resource = uploadResource(publicId); 81 | Map context = new HashMap((Map)((Map)resource.get("context")).get("custom")); 82 | context.put("caption", "new caption"); 83 | Map result = uploader.addContext(asMap("caption", "new caption"), new String[]{publicId, "no-such-id"}, null); 84 | assertThat("addContext should return a list of modified public IDs", (List) result.get("public_ids"), contains(publicId)); 85 | 86 | resource = cloudinary.api().resource(publicId, asMap("context", true)); 87 | assertEquals(asMap("custom", context), resource.get("context")); 88 | } 89 | 90 | @Test 91 | public void testRemoveAllContext() throws Exception { 92 | String publicId = "remove_context_id" + SUFFIX; 93 | uploadResource(publicId); 94 | Map result = uploader.removeAllContext(new String[]{publicId, "no-such-id"}, null); 95 | assertThat((List) result.get("public_ids"), contains(publicId)); 96 | 97 | Map resource = cloudinary.api().resource(publicId, asMap("context", true)); 98 | assertThat((Map)resource, not(hasKey("context"))); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/AbstractFoldersApiTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | import com.cloudinary.Api; 4 | import com.cloudinary.Cloudinary; 5 | import com.cloudinary.api.ApiResponse; 6 | import com.cloudinary.utils.ObjectUtils; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.rules.TestName; 11 | 12 | import java.util.List; 13 | 14 | import static org.junit.Assert.*; 15 | import static org.junit.Assume.assumeNotNull; 16 | 17 | @SuppressWarnings({"rawtypes"}) 18 | abstract public class AbstractFoldersApiTest extends MockableTest { 19 | protected Api api; 20 | 21 | @Rule 22 | public TestName currentTest = new TestName(); 23 | 24 | @Before 25 | public void setUp() { 26 | System.out.println("Running " + this.getClass().getName() + "." + currentTest.getMethodName()); 27 | this.cloudinary = new Cloudinary(); 28 | assumeNotNull(cloudinary.config.apiSecret); 29 | this.api = cloudinary.api(); 30 | } 31 | 32 | @Test 33 | public void testRootFolderWithParams() throws Exception { 34 | String rootFolder1Name = "rootFolderWithParamsTest1" + SUFFIX; 35 | assertTrue((Boolean) api.createFolder(rootFolder1Name, null).get("success")); 36 | 37 | String rootFolder2Name = "rootFolderWithParamsTest2" + SUFFIX; 38 | assertTrue((Boolean) api.createFolder(rootFolder2Name, null).get("success")); 39 | 40 | Thread.sleep(2000); 41 | 42 | ApiResponse rootResponse1 = api.rootFolders(ObjectUtils.asMap("max_results", 1)); 43 | List rootFolders1 = (List) rootResponse1.get("folders"); 44 | assertNotNull(rootFolders1); 45 | assertEquals(1, rootFolders1.size()); 46 | 47 | String nextCursor = (String) rootResponse1.get("next_cursor"); 48 | assertNotNull(nextCursor); 49 | 50 | ApiResponse rootResponse2 = api.rootFolders(ObjectUtils.asMap("max_results", 1, "next_cursor", nextCursor)); 51 | List folders2 = (List) rootResponse2.get("folders"); 52 | assertNotNull(folders2); 53 | assertEquals(1, folders2.size()); 54 | 55 | assertTrue(((List) api.deleteFolder(rootFolder1Name, null).get("deleted")).contains(rootFolder1Name)); 56 | assertTrue(((List) api.deleteFolder(rootFolder2Name, null).get("deleted")).contains(rootFolder2Name)); 57 | } 58 | 59 | @Test 60 | public void testSubFolderWithParams() throws Exception { 61 | String rootFolderName = "subfolderWithParamsTest" + SUFFIX; 62 | assertTrue((Boolean) api.createFolder(rootFolderName, null).get("success")); 63 | 64 | String subFolder1Name = rootFolderName + "/subfolder1" + SUFFIX; 65 | assertTrue((Boolean) api.createFolder(subFolder1Name, null).get("success")); 66 | 67 | String subFolder2Name = rootFolderName + "/subfolder2" + SUFFIX; 68 | assertTrue((Boolean) api.createFolder(subFolder2Name, null).get("success")); 69 | 70 | Thread.sleep(2000); 71 | 72 | ApiResponse response = api.subFolders(rootFolderName, ObjectUtils.asMap("max_results", 1)); 73 | List folders = (List) response.get("folders"); 74 | assertNotNull(folders); 75 | assertEquals(1, folders.size()); 76 | 77 | String nextCursor = (String) response.get("next_cursor"); 78 | assertNotNull(nextCursor); 79 | 80 | ApiResponse response2 = api.subFolders(rootFolderName, ObjectUtils.asMap("max_results", 1, "next_cursor", nextCursor)); 81 | List folders2 = (List) response2.get("folders"); 82 | assertNotNull(folders2); 83 | assertEquals(1, folders2.size()); 84 | 85 | ApiResponse result = api.deleteFolder(rootFolderName, null); 86 | assertTrue(((List) result.get("deleted")).contains(rootFolderName)); 87 | } 88 | 89 | @Test 90 | public void testDeleteFolderWithSkipBackup() throws Exception { 91 | //Create 92 | String rootFolderName = "deleteFolderWithSkipBackup" + SUFFIX; 93 | assertTrue((Boolean) api.createFolder(rootFolderName, null).get("success")); 94 | 95 | //Delete 96 | ApiResponse result = api.deleteFolder(rootFolderName, ObjectUtils.asMap("skip_backup", "true")); 97 | assertTrue(((List) result.get("deleted")).contains(rootFolderName)); 98 | 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/MetadataTestHelper.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | import com.cloudinary.Api; 4 | import com.cloudinary.api.ApiResponse; 5 | import com.cloudinary.metadata.MetadataField; 6 | import com.cloudinary.metadata.MetadataValidation; 7 | import com.cloudinary.metadata.StringMetadataField; 8 | 9 | public final class MetadataTestHelper { 10 | private MetadataTestHelper() {} 11 | 12 | public static StringMetadataField newFieldInstance(String label, Boolean mandatory) throws Exception { 13 | StringMetadataField field = new StringMetadataField(); 14 | field.setLabel(label); 15 | field.setMandatory(mandatory); 16 | field.setValidation(new MetadataValidation.StringLength(3, 9)); 17 | field.setDefaultValue("val_test"); 18 | return field; 19 | } 20 | 21 | public static ApiResponse addFieldToAccount(Api api, MetadataField field) throws Exception { 22 | ApiResponse apiResponse = api.addMetadataField(field); 23 | return apiResponse; 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/MockableTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | import com.cloudinary.Cloudinary; 4 | import com.cloudinary.test.helpers.Feature; 5 | import com.cloudinary.utils.ObjectUtils; 6 | import com.cloudinary.utils.StringUtils; 7 | 8 | import static org.junit.Assume.assumeTrue; 9 | 10 | import java.io.IOException; 11 | import java.util.Arrays; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Random; 15 | 16 | public class MockableTest { 17 | 18 | public static final String HEBREW_PDF = "../cloudinary-test-common/src/main/resources/אבג.docx"; 19 | public static final String SRC_TEST_IMAGE = "../cloudinary-test-common/src/main/resources/old_logo.png"; 20 | public static final String SRC_TEST_VIDEO = "http://res.cloudinary.com/demo/video/upload/dog.mp4"; 21 | public static final String SRC_TEST_RAW = "../cloudinary-test-common/src/main/resources/docx.docx"; 22 | public static final String REMOTE_TEST_IMAGE = "http://cloudinary.com/images/old_logo.png"; 23 | protected static String SUFFIX = StringUtils.isNotBlank(System.getenv("TRAVIS_JOB_ID")) ? System.getenv("TRAVIS_JOB_ID") : String.valueOf(new Random().nextInt(99999)); 24 | protected static final String SDK_TEST_TAG = "cloudinary_java_test_" + SUFFIX; 25 | protected Cloudinary cloudinary; 26 | 27 | protected Object getParam(String name){ 28 | throw new UnsupportedOperationException(); 29 | } 30 | protected String getURL(){ 31 | throw new UnsupportedOperationException(); 32 | } 33 | protected String getHttpMethod(){ 34 | throw new UnsupportedOperationException(); 35 | } 36 | 37 | protected Map preloadResource(Map options) throws IOException { 38 | if (!options.containsKey("tags")){ 39 | throw new IllegalArgumentException("Must provide unique per-class tags"); 40 | } 41 | Map combinedOptions = ObjectUtils.asMap("transformation", "c_scale,w_100"); 42 | combinedOptions.putAll(options); 43 | return cloudinary.uploader().upload("http://res.cloudinary.com/demo/image/upload/sample", combinedOptions); 44 | } 45 | 46 | private static final List enabledAddons = getEnabledAddons(); 47 | 48 | protected void assumeAddonEnabled(String addon) throws Exception { 49 | boolean enabled = enabledAddons.contains(addon.toLowerCase()) 50 | || (enabledAddons.size() == 1 && enabledAddons.get(0).equalsIgnoreCase("all")); 51 | 52 | assumeTrue(String.format("Use CLD_TEST_ADDONS environment variable to enable tests for %s.", addon), enabled); 53 | } 54 | 55 | private static List getEnabledAddons() { 56 | String envAddons = System.getenv() 57 | .getOrDefault("CLD_TEST_ADDONS", "") 58 | .toLowerCase() 59 | .replaceAll("\\s", ""); 60 | 61 | return Arrays.asList(envAddons.split(",")); 62 | } 63 | 64 | protected static boolean shouldTestFeature(String feature) { 65 | String sdkFeatures = System.getenv() 66 | .getOrDefault("CLD_TEST_FEATURES", "") 67 | .toLowerCase() 68 | .replaceAll("\\s", ""); 69 | List sdkFeaturesList = Arrays.asList(sdkFeatures.split(",")); 70 | return sdkFeatures.contains(feature.toLowerCase()) || (sdkFeaturesList.size() == 1 && sdkFeaturesList.get(0).equalsIgnoreCase(Feature.ALL)); 71 | } 72 | 73 | static protected boolean assumeCloudinaryAccountURLExist() { 74 | String cloudinaryAccountUrl = System.getProperty("CLOUDINARY_ACCOUNT_URL", System.getenv("CLOUDINARY_ACCOUNT_URL")); 75 | assumeTrue(String.format("Use CLOUDINARY_ACCOUNT_URL environment variable to enable tests"), cloudinaryAccountUrl != null); 76 | return cloudinaryAccountUrl != null; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/TimeoutTest.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test; 2 | 3 | /*** 4 | * Marker interface for Junit categories. 5 | */ 6 | public interface TimeoutTest { 7 | } 8 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/helpers/Feature.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test.helpers; 2 | 3 | public final class Feature { 4 | private Feature() {} 5 | 6 | public static final String ALL = "all"; 7 | public static final String DYNAMIC_FOLDERS = "dynamic_folders"; 8 | public static final String BACKEDUP_ASSETS = "backedup_assets"; 9 | public static final String CONDITIONAL_METADATA_RULES = "conditional_metadata_rules"; 10 | } 11 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/java/com/cloudinary/test/rules/RetryRule.java: -------------------------------------------------------------------------------- 1 | package com.cloudinary.test.rules; 2 | 3 | import org.junit.rules.TestRule; 4 | import org.junit.runner.Description; 5 | import org.junit.runners.model.Statement; 6 | 7 | import java.util.Objects; 8 | 9 | public class RetryRule implements TestRule { 10 | private int retryCount; 11 | private int delay; 12 | 13 | public RetryRule(int retryCount, int delay) { 14 | this.retryCount = retryCount; 15 | this.delay = delay; 16 | } 17 | 18 | public RetryRule() { 19 | this.retryCount = 3; 20 | this.delay = 3; 21 | } 22 | 23 | public Statement apply(Statement base, Description description) { 24 | return statement(base, description); 25 | } 26 | 27 | private Statement statement(final Statement base, final Description description) { 28 | return new Statement() { 29 | @Override 30 | public void evaluate() throws Throwable { 31 | Throwable caughtThrowable = null; 32 | for (int i = 0; i < retryCount; i++) { 33 | try { 34 | base.evaluate(); 35 | return; 36 | } catch (Throwable t) { 37 | caughtThrowable = t; 38 | System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed."); 39 | Thread.sleep(delay * 1000); 40 | } 41 | } 42 | System.err.println(description.getDisplayName() + ": Giving up after " + retryCount + " failures."); 43 | throw Objects.requireNonNull(caughtThrowable); 44 | } 45 | }; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/resources/docx.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary/cloudinary_java/bae86bc9717c8ef8f09d28c58de8966c72232540/cloudinary-test-common/src/main/resources/docx.docx -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/resources/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary/cloudinary_java/bae86bc9717c8ef8f09d28c58de8966c72232540/cloudinary-test-common/src/main/resources/favicon.ico -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/resources/old_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary/cloudinary_java/bae86bc9717c8ef8f09d28c58de8966c72232540/cloudinary-test-common/src/main/resources/old_logo.png -------------------------------------------------------------------------------- /cloudinary-test-common/src/main/resources/אבג.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary/cloudinary_java/bae86bc9717c8ef8f09d28c58de8966c72232540/cloudinary-test-common/src/main/resources/אבג.docx -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | publishRepo=https://oss.sonatype.org/service/local/staging/deploy/maven2/ 2 | snapshotRepo=https://oss.sonatype.org/content/repositories/snapshots/ 3 | publishDescription=Cloudinary is a cloud service that offers a solution to a web application's entire image management pipeline. Upload images to the cloud. Automatically perform smart image resizing, cropping and conversion without installing any complex software. Integrate Facebook or Twitter profile image extraction in a snap, in any dimension and style to match your website’s graphics requirements. Images are seamlessly delivered through a fast CDN, and much much more. This Java library allows to easily integrate with Cloudinary in Java applications. 4 | githubUrl=http://github.com/cloudinary/cloudinary_java 5 | scmConnection=scm:git:git://github.com/cloudinary/cloudinary_java.git 6 | scmDeveloperConnection=scm:git:git@github.com:cloudinary/cloudinary_java.git' 7 | scmUrl=http://github.com/cloudinary/cloudinary_java 8 | licenseName=MIT 9 | licenseUrl=http://opensource.org/licenses/MIT 10 | developerId=cloudinary 11 | developerName=Cloudinary 12 | developerEmail=info@cloudinary.com 13 | 14 | # These two properties must use these exact names to be compatible with 'gradle install' plugin. 15 | group=com.cloudinary 16 | version=2.2.0 17 | 18 | gnsp.disableApplyOnlyOnRootProjectEnforcement=true 19 | 20 | # see https://github.com/gradle/gradle/issues/11308 21 | systemProp.org.gradle.internal.publish.checksums.insecure=true 22 | 23 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudinary/cloudinary_java/bae86bc9717c8ef8f09d28c58de8966c72232540/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip 6 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /java_shared.gradle: -------------------------------------------------------------------------------- 1 | sourceCompatibility = 1.8 2 | targetCompatibility = 1.8 3 | 4 | javadoc { 5 | options.encoding = 'UTF-8' 6 | } 7 | 8 | test { 9 | testLogging.showStandardStreams = true 10 | testLogging.exceptionFormat = 'full' 11 | } 12 | 13 | task sourcesJar(type: Jar, dependsOn: classes) { 14 | classifier = 'sources' 15 | from sourceSets.main.allSource 16 | } 17 | 18 | task javadocJar(type: Jar, dependsOn: javadoc) { 19 | classifier = 'javadoc' 20 | from javadoc.destinationDir 21 | } 22 | 23 | artifacts { 24 | archives javadocJar, sourcesJar 25 | } 26 | 27 | tasks.withType(GenerateModuleMetadata) { 28 | enabled = false 29 | } 30 | 31 | tasks.withType(Test) { 32 | environment 'CLOUDINARY_URL', System.getProperty('CLOUDINARY_URL') 33 | maxParallelForks = Runtime.runtime.availableProcessors() 34 | 35 | // show standard out and standard error of the test JVM(s) on the console 36 | testLogging.showStandardStreams = true 37 | } 38 | 39 | tasks.withType(JavaCompile) { 40 | options.encoding = 'UTF-8' 41 | } 42 | -------------------------------------------------------------------------------- /samples/photo_album/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/photo_album/README.md: -------------------------------------------------------------------------------- 1 | Cloudinary Java/Spring MVC Sample Project 2 | ========================================= 3 | 4 | A simple web application that allows you to uploads photos, maintain a database with references to them, list them with their metadata, and display them using various cloud-based transformations. 5 | 6 | ## Installation 7 | 8 | Run the following commands from your shell. 9 | 10 | Clone the Cloudinary Java project: 11 | 12 | git clone git://github.com/cloudinary/cloudinary_java.git 13 | 14 | Compile the sample project and create a WAR file (package): 15 | 16 | cd samples/photo_album 17 | mvn compile && mvn package 18 | 19 | A WAR file should have been created in: 20 | 21 | target/photo_album.war 22 | 23 | You need to deploy this war file into your J2EE container of choice. The following instructions assume a *nix with [Tomcat](http://tomcat.apache.org/) 7 on `$CATALINA_HOME`. 24 | 25 | mv target/photo_album.war $CATALINA_HOME/webapps/ 26 | 27 | If you would like to deploy the sample application in the root server path do this instead: 28 | 29 | mv target/photo_album.war $CATALINA_HOME/webapps/ROOT.war 30 | 31 | ## Configuration 32 | 33 | Next you need to pass your Cloudinary account's Cloud Name, API Key, and API Secret. This sample application assumes these values exists in the form 34 | of a `CLOUDINARY_URL` settings either as an environment variable or as system property. The `CLOUDINARY_URL` value is available in the [dashboard of your Cloudinary account](https://cloudinary.com/console). 35 | If you don't have a Cloudinary account yet, [click here](https://cloudinary.com/users/register/free) to create one for free. 36 | 37 | The specific method with which you pass that information to Tomcat (or any other Servlet container) is not important but we present 2 alternatives here. 38 | 39 | * Start Tomcat with `CLOUDINARY_URL` as a process bound environment variable 40 | 41 | CLOUDINARY_URL=cloudinary://:@ $CATALINA_HOME/bin/startup.sh 42 | 43 | * Set `TOMCAT_OPTS` environment variable either globally (`/etc/profile`) or for the user running Tomcat (`~/.profile`) 44 | 45 | TOMCAT_OPTS=-DCLOUDINARY_URL=cloudinary://:@ 46 | 47 | ## Running 48 | 49 | If you chose the second option you will need to now start Tomcat: 50 | 51 | $CATALINA_HOME/bin/startup.sh 52 | 53 | and point your browser to [http://localhost:8080/photo_album/](http://localhost:8080/photo_album/) or [http://localhost:8080/](http://localhost:8080/) if you opted to put the WAR file in root. *Note:* you might need to give Tomcat a while to pick the deployed WAR file explode it, and deploy it. 54 | 55 | This sample uses an in memory [HSQLDB](http://hsqldb.org/) database so you should expect all data to be lost when the servlet process ends. -------------------------------------------------------------------------------- /samples/photo_album/src/main/java/cloudinary/controllers/PhotoController.java: -------------------------------------------------------------------------------- 1 | package cloudinary.controllers; 2 | 3 | import cloudinary.lib.PhotoUploadValidator; 4 | import cloudinary.models.Photo; 5 | import cloudinary.models.PhotoUpload; 6 | import cloudinary.repositories.PhotoRepository; 7 | 8 | import com.cloudinary.Cloudinary; 9 | import com.cloudinary.utils.ObjectUtils; 10 | import com.cloudinary.Singleton; 11 | 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Controller; 14 | import org.springframework.ui.ModelMap; 15 | import org.springframework.validation.BindingResult; 16 | import org.springframework.web.bind.annotation.*; 17 | 18 | import java.io.IOException; 19 | import java.util.Map; 20 | 21 | @Controller 22 | @RequestMapping("/") 23 | public class PhotoController { 24 | @Autowired 25 | private PhotoRepository photoRepository; 26 | 27 | @RequestMapping(value = "/", method = RequestMethod.GET) 28 | public String listPhotos(ModelMap model) { 29 | model.addAttribute("photos", photoRepository.findAll()); 30 | return "photos"; 31 | } 32 | 33 | @SuppressWarnings("rawtypes") 34 | @RequestMapping(value = "/upload", method = RequestMethod.POST) 35 | public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult result, ModelMap model) throws IOException { 36 | PhotoUploadValidator validator = new PhotoUploadValidator(); 37 | validator.validate(photoUpload, result); 38 | 39 | Map uploadResult = null; 40 | if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { 41 | uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), 42 | ObjectUtils.asMap("resource_type", "auto")); 43 | photoUpload.setPublicId((String) uploadResult.get("public_id")); 44 | Object version = uploadResult.get("version"); 45 | if (version instanceof Integer) { 46 | photoUpload.setVersion(new Long((Integer) version)); 47 | } else { 48 | photoUpload.setVersion((Long) version); 49 | } 50 | 51 | photoUpload.setSignature((String) uploadResult.get("signature")); 52 | photoUpload.setFormat((String) uploadResult.get("format")); 53 | photoUpload.setResourceType((String) uploadResult.get("resource_type")); 54 | } 55 | 56 | if (result.hasErrors()){ 57 | model.addAttribute("photoUpload", photoUpload); 58 | return "upload_form"; 59 | } else { 60 | Photo photo = new Photo(); 61 | photo.setTitle(photoUpload.getTitle()); 62 | photo.setUpload(photoUpload); 63 | model.addAttribute("upload", uploadResult); 64 | photoRepository.save(photo); 65 | model.addAttribute("photo", photo); 66 | return "upload"; 67 | } 68 | } 69 | 70 | @RequestMapping(value = "/upload_form", method = RequestMethod.GET) 71 | public String uploadPhotoForm(ModelMap model) { 72 | model.addAttribute("photoUpload", new PhotoUpload()); 73 | return "upload_form"; 74 | } 75 | 76 | @RequestMapping(value = "/direct_upload_form", method = RequestMethod.GET) 77 | public String directUploadPhotoForm(ModelMap model) { 78 | model.addAttribute("photoUpload", new PhotoUpload()); 79 | model.addAttribute("unsigned", false); 80 | return "direct_upload_form"; 81 | } 82 | 83 | @SuppressWarnings("unchecked") 84 | @RequestMapping(value = "/direct_unsigned_upload_form", method = RequestMethod.GET) 85 | public String directUnsignedUploadPhotoForm(ModelMap model) throws Exception { 86 | model.addAttribute("photoUpload", new PhotoUpload()); 87 | model.addAttribute("unsigned", true); 88 | Cloudinary cld = Singleton.getCloudinary(); 89 | String preset = "sample_" + cld.apiSignRequest(ObjectUtils.asMap("api_key", cld.config.apiKey), cld.config.apiSecret).substring(0, 10); 90 | model.addAttribute("preset", preset); 91 | try { 92 | Singleton.getCloudinary().api().createUploadPreset(ObjectUtils.asMap( 93 | "name", preset, 94 | "unsigned", true, 95 | "folder", "preset_folder")); 96 | } catch (Exception e) { 97 | } 98 | return "direct_upload_form"; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/java/cloudinary/lib/PhotoUploadValidator.java: -------------------------------------------------------------------------------- 1 | package cloudinary.lib; 2 | 3 | import cloudinary.models.PhotoUpload; 4 | import org.springframework.validation.Errors; 5 | import org.springframework.validation.ValidationUtils; 6 | import org.springframework.validation.Validator; 7 | 8 | public class PhotoUploadValidator implements Validator { 9 | public boolean supports(Class clazz) { 10 | return PhotoUpload.class.equals(clazz); 11 | } 12 | 13 | public void validate(Object obj, Errors e) { 14 | ValidationUtils.rejectIfEmpty(e, "title", "title.empty"); 15 | PhotoUpload pu = (PhotoUpload) obj; 16 | if (pu.getFile() == null || pu.getFile().isEmpty()) { 17 | if (!pu.validSignature()) { 18 | e.rejectValue("signature", "signature.mismatch"); 19 | } 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/java/cloudinary/models/Photo.java: -------------------------------------------------------------------------------- 1 | package cloudinary.models; 2 | 3 | import com.cloudinary.StoredFile; 4 | 5 | import javax.persistence.*; 6 | import java.util.Date; 7 | 8 | @Entity(name = "photos") 9 | public class Photo { 10 | @Id 11 | @GeneratedValue(strategy = GenerationType.AUTO) 12 | private Long id; 13 | 14 | @Basic 15 | private String title; 16 | 17 | @Basic 18 | private String image; 19 | 20 | @Basic 21 | private Date createdAt = new Date(); 22 | 23 | public Long getId() { 24 | return id; 25 | } 26 | 27 | public void setId(Long id) { 28 | this.id = id; 29 | } 30 | 31 | public String getTitle() { 32 | return title; 33 | } 34 | 35 | public void setTitle(String title) { 36 | this.title = title; 37 | } 38 | 39 | public String getImage() { 40 | return image; 41 | } 42 | 43 | public void setImage(String image) { 44 | this.image = image; 45 | } 46 | 47 | public Date getCreatedAt() { 48 | return createdAt; 49 | } 50 | 51 | public void setCreatedAt(Date createdAt) { 52 | this.createdAt = createdAt; 53 | } 54 | 55 | public StoredFile getUpload() { 56 | StoredFile file = new StoredFile(); 57 | file.setPreloadedFile(image); 58 | return file; 59 | } 60 | 61 | public void setUpload(StoredFile file) { 62 | this.image = file.getPreloadedFile(); 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /samples/photo_album/src/main/java/cloudinary/models/PhotoUpload.java: -------------------------------------------------------------------------------- 1 | package cloudinary.models; 2 | 3 | import com.cloudinary.Singleton; 4 | import com.cloudinary.StoredFile; 5 | import com.cloudinary.Transformation; 6 | import org.springframework.web.multipart.MultipartFile; 7 | 8 | public class PhotoUpload extends StoredFile { 9 | private String title; 10 | 11 | private MultipartFile file; 12 | 13 | public String getUrl() { 14 | if (version != null && format != null && publicId != null) { 15 | return Singleton.getCloudinary().url() 16 | .resourceType(resourceType) 17 | .type(type) 18 | .format(format) 19 | .version(version) 20 | .generate(publicId); 21 | } else return null; 22 | } 23 | 24 | public String getThumbnailUrl() { 25 | if (version != null && format != null && publicId != null) { 26 | return Singleton.getCloudinary().url().format(format) 27 | .resourceType(resourceType) 28 | .type(type) 29 | .version(version).transformation(new Transformation().width(150).height(150).crop("fit")) 30 | .generate(publicId); 31 | } else return null; 32 | } 33 | 34 | public String getComputedSignature() { 35 | return getComputedSignature(Singleton.getCloudinary()); 36 | } 37 | 38 | public boolean validSignature() { 39 | return getComputedSignature().equals(signature); 40 | } 41 | 42 | public String getTitle() { 43 | return title; 44 | } 45 | 46 | public void setTitle(String title) { 47 | this.title = title; 48 | } 49 | 50 | public MultipartFile getFile() { 51 | return file; 52 | } 53 | 54 | public void setFile(MultipartFile file) { 55 | this.file = file; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/java/cloudinary/repositories/PhotoRepository.java: -------------------------------------------------------------------------------- 1 | package cloudinary.repositories; 2 | 3 | import cloudinary.models.Photo; 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | 6 | public interface PhotoRepository extends JpaRepository { 7 | } -------------------------------------------------------------------------------- /samples/photo_album/src/main/resources/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | org.hibernate.jpa.HibernatePersistenceProvider 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/messages.properties: -------------------------------------------------------------------------------- 1 | title.empty = Title cannot be empty 2 | signature.mismatch = Signature mismatch -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 32 | 33 | 34 | 35 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/pages/post.jsp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/pages/pre.jsp: -------------------------------------------------------------------------------- 1 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | <%@taglib uri="http://cloudinary.com/jsp/taglib" prefix="cl" %> 3 | 4 | 5 | Photo Album 6 | "> 7 | " /> 9 | 10 | 11 | 12 | 13 | 17 | 18 |
-------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/pages/upload.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@taglib uri="http://www.springframework.org/tags" prefix="spring" %> 3 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %> 4 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 5 | 6 | <%@include file="pre.jsp"%> 7 | 8 |

Your photo was uploaded sucessfully!

9 | 10 | 11 | 22 | 23 | 24 | " class="back_link">Back to list 25 | 26 | 27 |
28 |

Upload metadata:

29 | 30 | 31 | 32 | 33 |
${entry.key}${entry.value}
34 |
35 |
36 | 37 | <%@include file="post.jsp"%> -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/pages/upload_form.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@taglib uri="http://www.springframework.org/tags" prefix="spring" %> 3 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %> 4 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 5 | 6 | <%@include file="pre.jsp"%> 7 | 8 |
9 |

New Photo

10 |

Image file is uploaded through the server

11 | 12 |
13 | Title: 14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 | 40 | 43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 | " class="back_link">Back to list 58 | <%@include file="post.jsp"%> 59 | 60 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 1 12 | 13 | 14 | 15 | mvc-dispatcher 16 | / 17 | 18 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/assets/cloudinary_cors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/assets/javascripts/cloudinary/canvas-to-blob.min.js: -------------------------------------------------------------------------------- 1 | (function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return(new Blob([new Uint8Array(100)])).size===100}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;a.split(",")[0].indexOf("base64")>=0?b=atob(a.split(",")[1]):b=decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f);for(h=0;h options.maxNumberOfFiles) { 90 | file.error = settings.i18n('maxNumberOfFiles'); 91 | } else if (options.acceptFileTypes && 92 | !(options.acceptFileTypes.test(file.type) || 93 | options.acceptFileTypes.test(file.name))) { 94 | file.error = settings.i18n('acceptFileTypes'); 95 | } else if (options.maxFileSize && file.size > options.maxFileSize) { 96 | file.error = settings.i18n('maxFileSize'); 97 | } else if ($.type(file.size) === 'number' && 98 | file.size < options.minFileSize) { 99 | file.error = settings.i18n('minFileSize'); 100 | } else { 101 | delete file.error; 102 | } 103 | if (file.error || data.files.error) { 104 | data.files.error = true; 105 | dfd.rejectWith(this, [data]); 106 | } else { 107 | dfd.resolveWith(this, [data]); 108 | } 109 | return dfd.promise(); 110 | } 111 | 112 | } 113 | 114 | }); 115 | 116 | })); 117 | -------------------------------------------------------------------------------- /samples/photo_album/src/main/webapp/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | body { font-family: Helvetica, Arial, sans-serif; color: #333; margin: 10px; width: 960px } 2 | #posterframe { position: absolute; right: 10px; top: 10px; } 3 | h1 { color: #0e2953; font-size: 18px; } 4 | h2 { color: #666; font-size: 16px; } 5 | p { font-size: 14px; line-height: 18px; } 6 | #logo { height: 51px; width: 241px; } 7 | a { color: #0b63b6 } 8 | 9 | .actions { margin: 20px 0; } 10 | .upload_link { color: #000; border: 1px solid #aaa; background-color: #e0e0e0; 11 | font-size: 18px; padding: 5px 10px; width: 250px; margin: 10px 0 20px 0; 12 | font-weight: bold; text-align: center; text-decoration: none; margin: 5px; } 13 | 14 | .photo { margin: 10px; padding: 10px; border-top: 2px solid #ccc; } 15 | .photo .thumbnail { margin-top: 10px; display: block; max-width: 200px; border: none; } 16 | .toggle_info { margin-top: 10px; font-weight: bold; color: #e62401; display: block; } 17 | .thumbnail_holder { height: 182px; margin-bottom: 5px; margin-right: 10px; } 18 | .info td, .uploaded_info td { font-size: 12px } 19 | .note { margin: 20px 0} 20 | 21 | .more_info, .show_more_info .less_info { display: none; } 22 | .show_more_info .more_info, .less_info { display: inline-block; } 23 | .inline { display: inline-block; } 24 | td { vertical-align: top; padding-right: 5px; } 25 | 26 | #backend_upload, #direct_upload { padding: 20px 20px; margin: 20px 0; 27 | border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } 28 | 29 | #backend_upload h1, #direct_upload h1 { margin: 0 0 15px 0; } 30 | 31 | .back_link { font-weight: bold; display: block; font-size: 16px; margin: 10px 0; } 32 | 33 | form { border: 1px solid #ddd; margin: 15px 0; padding: 15px 0; border-radius: 4px; } 34 | form .form_line { margin-bottom: 20px; } 35 | form .form_controls { margin-left: 180px; } 36 | form label { float: left; width: 160px; padding-top: 3px; text-align: right; } 37 | form .error { color: #c55; margin: 0 10px; } 38 | 39 | #direct_upload { border: 4px dashed #ccc; } 40 | 41 | .upload_details { font-size: 12px; margin: 20px; border-top: 1px solid #ccc; word-wrap: break-word; } 42 | 43 | .upload_button_holder { 44 | position: relative; 45 | display: inline-block; 46 | overflow: hidden; 47 | } 48 | 49 | .upload_button_holder .upload_button { 50 | display: block; 51 | position: relative; 52 | font-weight: bold; 53 | font-size: 14px; 54 | background-color: rgb(15, 97, 172); 55 | color: #fff; 56 | padding: 5px 0; 57 | border: 1px solid #000; 58 | border-radius: 4px; 59 | width: 100px; 60 | height: 18px; 61 | text-decoration: none; 62 | text-align: center; 63 | cursor: pointer; 64 | } 65 | 66 | .upload_button_holder:hover .upload_button { 67 | background-color: rgb(17, 133, 240); 68 | } 69 | 70 | .upload_button_holder .cloudinary-fileupload { 71 | opacity: 0; 72 | filter: alpha(opacity=0); 73 | cursor: pointer; 74 | position: absolute; 75 | top: 0; 76 | left: 0; 77 | width: 100%; 78 | height: 100%; 79 | margin: 0; 80 | padding: 0; 81 | border: none; 82 | } 83 | -------------------------------------------------------------------------------- /samples/photo_album_gae/META-INF/persistence.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/photo_album_gae/README.md: -------------------------------------------------------------------------------- 1 | Cloudinary Java/Spring MVC Sample Project on Google AppEngine 2 | ============================================================= 3 | 4 | A simple web application that allows you to uploads photos, maintain a database with references to them, list them with their metadata, and display them using various cloud-based transformations. 5 | 6 | ## Installation 7 | 8 | Run the following commands from your shell. 9 | 10 | Clone the Cloudinary Java project: 11 | 12 | git clone git://github.com/cloudinary/cloudinary_java.git 13 | 14 | This sample relies on a version of the Cloudinary client not yet published so we need to install it locally: 15 | 16 | cd cloudinary_java 17 | mvn install 18 | 19 | Go to the sample project folder: 20 | 21 | cd samples/photo_album_gae 22 | 23 | ## Configuration 24 | 25 | Next you need to pass your Cloudinary account's Cloud Name, API Key, and API Secret. This sample application assumes these values exists in the form 26 | of a `CLOUDINARY_URL`. The `CLOUDINARY_URL` value is available in the [dashboard of your Cloudinary account](https://cloudinary.com/console). 27 | If you don't have a Cloudinary account yet, [click here](https://cloudinary.com/users/register/free) to create one for free. 28 | 29 | cp src/main/webapp/WEB-INF/appengine-web.xml.sample src/main/webapp/WEB-INF/appengine-web.xml 30 | 31 | Edit the new `appengine-web.xml` file and set `CLOUDINARY_URL` to the value matching your account. 32 | *Note*: you can also change the application ID to match a remote project ID you might have started. 33 | 34 | ## Running 35 | 36 | Running the application locally first clean and compile to make sure everything is working as it should: 37 | 38 | mvn clean compile 39 | 40 | Then start the application: 41 | 42 | mvn appengine:devserver_start 43 | 44 | and point your browser to [http://localhost:8080/](http://localhost:8080/). 45 | 46 | This sample configures the Cloudinary client with `GAEConnectionManager` from [here](http://esxx.blogspot.co.il/2009/06/using-apaches-httpclient-on-google-app.html). 47 | You can use any other connection manager or leave the default if you have the sockets enabled for your application. In the development environment it should work with either. 48 | Also, the way the `GAEConnectionManager` is instantiated and managed in this sample is not pretty. You should change it when building a real application. -------------------------------------------------------------------------------- /samples/photo_album_gae/nbactions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CUSTOM-appengine:devserver 5 | appengine:devserver 6 | 7 | appengine:devserver 8 | 9 | 10 | 11 | CUSTOM-appengine:update 12 | appengine:update 13 | 14 | appengine:update 15 | 16 | 17 | 18 | CUSTOM-appengine:rollback 19 | appengine:rollback 20 | 21 | appengine:rollback 22 | 23 | 24 | 25 | CUSTOM-appengine:update_cron 26 | appengine:update_cron 27 | 28 | appengine:update_cron 29 | 30 | 31 | 32 | CUSTOM-appengine:update_dos 33 | appengine:update_dos 34 | 35 | appengine:update_dos 36 | 37 | 38 | 39 | CUSTOM-appengine:update_indexes 40 | appengine:update_indexes 41 | 42 | appengine:update_indexes 43 | 44 | 45 | 46 | CUSTOM-appengine:update_queues 47 | appengine:update_queues 48 | 49 | appengine:update_queues 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/java/cloudinary/controllers/PhotoController.java: -------------------------------------------------------------------------------- 1 | package cloudinary.controllers; 2 | 3 | import cloudinary.lib.PhotoUploadValidator; 4 | import cloudinary.models.PhotoUpload; 5 | import com.cloudinary.utils.ObjectUtils; 6 | import com.cloudinary.Singleton; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.ui.ModelMap; 9 | import org.springframework.validation.BindingResult; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import com.google.appengine.api.datastore.DatastoreService; 13 | import com.google.appengine.api.datastore.DatastoreServiceFactory; 14 | import com.google.appengine.api.datastore.Entity; 15 | import com.google.appengine.api.datastore.Key; 16 | import com.google.appengine.api.datastore.KeyFactory; 17 | import com.google.appengine.api.datastore.Query; 18 | import com.google.appengine.api.datastore.FetchOptions; 19 | 20 | 21 | import java.io.IOException; 22 | import java.util.Map; 23 | import java.util.List; 24 | 25 | import org.esxx.js.protocol.GAEConnectionManager; 26 | 27 | @Controller 28 | @RequestMapping("/") 29 | public class PhotoController { 30 | 31 | private final static GAEConnectionManager connectionManager = new GAEConnectionManager(); 32 | @RequestMapping(value = "/", method = RequestMethod.GET) 33 | public String listPhotos(ModelMap model) { 34 | DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 35 | Key photoKey = KeyFactory.createKey("photos", "album"); 36 | List photoEntities = datastore.prepare(new Query("photo", photoKey)).asList(FetchOptions.Builder.withDefaults()); 37 | List photos = new java.util.ArrayList(); 38 | for(int i = 0, n = photoEntities.size(); i < n; i++) { 39 | photos.add(new PhotoUpload(photoEntities.get(i))); 40 | } 41 | model.addAttribute("photos", photos); 42 | return "photos"; 43 | } 44 | 45 | @RequestMapping(value = "/upload", method = RequestMethod.POST) 46 | public String uploadPhoto(@ModelAttribute PhotoUpload photoUpload, BindingResult result, ModelMap model) throws IOException { 47 | PhotoUploadValidator validator = new PhotoUploadValidator(); 48 | validator.validate(photoUpload, result); 49 | 50 | Map uploadResult = null; 51 | if (photoUpload.getFile() != null && !photoUpload.getFile().isEmpty()) { 52 | Singleton.getCloudinary().config.properties.put("connectionManager", connectionManager); 53 | uploadResult = Singleton.getCloudinary().uploader().upload(photoUpload.getFile().getBytes(), 54 | ObjectUtils.asMap("resource_type", "auto")); 55 | 56 | photoUpload.setPublicId((String) uploadResult.get("public_id")); 57 | photoUpload.setVersion(((Integer) uploadResult.get("version")).longValue()); 58 | photoUpload.setSignature((String) uploadResult.get("signature")); 59 | photoUpload.setFormat((String) uploadResult.get("format")); 60 | photoUpload.setResourceType((String) uploadResult.get("resource_type")); 61 | } 62 | 63 | if (result.hasErrors()){ 64 | model.addAttribute("photoUpload", photoUpload); 65 | return "upload_form"; 66 | } else { 67 | Key photoKey = KeyFactory.createKey("photos", "album"); 68 | Entity photo = new Entity("photo", photoKey); 69 | photoUpload.toEntity(photo); 70 | model.addAttribute("upload", uploadResult); 71 | DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); 72 | datastore.put(photo); 73 | model.addAttribute("photo", photoUpload); 74 | return "upload"; 75 | } 76 | } 77 | 78 | @RequestMapping(value = "/upload_form", method = RequestMethod.GET) 79 | public String uploadPhotoForm(ModelMap model) { 80 | model.addAttribute("photo", new PhotoUpload()); 81 | return "upload_form"; 82 | } 83 | 84 | @RequestMapping(value = "/direct_upload_form", method = RequestMethod.GET) 85 | public String directUploadPhotoForm(ModelMap model) { 86 | model.addAttribute("photo", new PhotoUpload()); 87 | return "direct_upload_form"; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/java/cloudinary/lib/PhotoUploadValidator.java: -------------------------------------------------------------------------------- 1 | package cloudinary.lib; 2 | 3 | import cloudinary.models.PhotoUpload; 4 | import org.springframework.validation.Errors; 5 | import org.springframework.validation.ValidationUtils; 6 | import org.springframework.validation.Validator; 7 | 8 | public class PhotoUploadValidator implements Validator { 9 | public boolean supports(Class clazz) { 10 | return PhotoUpload.class.equals(clazz); 11 | } 12 | 13 | public void validate(Object obj, Errors e) { 14 | ValidationUtils.rejectIfEmpty(e, "title", "title.empty"); 15 | PhotoUpload pu = (PhotoUpload) obj; 16 | if (pu.getFile() == null || pu.getFile().isEmpty()) { 17 | if (!pu.validSignature()) { 18 | e.rejectValue("signature", "signature.mismatch"); 19 | } 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/java/cloudinary/models/PhotoUpload.java: -------------------------------------------------------------------------------- 1 | package cloudinary.models; 2 | 3 | import com.cloudinary.Singleton; 4 | import com.cloudinary.StoredFile; 5 | import com.cloudinary.Transformation; 6 | import org.gmr.web.multipart.GMultipartFile; 7 | import com.google.appengine.api.datastore.Entity; 8 | 9 | public class PhotoUpload extends StoredFile { 10 | private String title; 11 | 12 | private GMultipartFile file; 13 | public PhotoUpload() { 14 | super(); 15 | } 16 | 17 | public PhotoUpload(Entity entity) { 18 | super(); 19 | this.publicId = entity.getProperty("public_id").toString(); 20 | this.format = entity.getProperty("format").toString(); 21 | this.version = (Long) entity.getProperty("version"); 22 | this.type = entity.getProperty("type").toString(); 23 | this.resourceType = entity.getProperty("resource_type").toString(); 24 | this.title = entity.getProperty("title").toString(); 25 | } 26 | 27 | public String getUrl() { 28 | if (version != null && format != null && publicId != null) { 29 | return Singleton.getCloudinary().url() 30 | .resourceType(resourceType) 31 | .type(type) 32 | .format(format) 33 | .version(version) 34 | .generate(publicId); 35 | } else return null; 36 | } 37 | 38 | public String getThumbnailUrl() { 39 | if (version != null && format != null && publicId != null) { 40 | return Singleton.getCloudinary().url().format(format) 41 | .resourceType(resourceType) 42 | .type(type) 43 | .version(version).transformation(new Transformation().width(150).height(150).crop("fit")) 44 | .generate(publicId); 45 | } else return null; 46 | } 47 | 48 | public String getComputedSignature() { 49 | return getComputedSignature(Singleton.getCloudinary()); 50 | } 51 | 52 | public boolean validSignature() { 53 | return getComputedSignature().equals(signature); 54 | } 55 | 56 | public String getTitle() { 57 | return title; 58 | } 59 | 60 | public void setTitle(String title) { 61 | this.title = title; 62 | } 63 | 64 | public GMultipartFile getFile() { 65 | return file; 66 | } 67 | 68 | public void setFile(GMultipartFile file) { 69 | this.file = file; 70 | } 71 | 72 | public void toEntity(Entity photo) { 73 | photo.setProperty("title", getTitle()); 74 | photo.setProperty("version", getVersion()); 75 | photo.setProperty("public_id", getPublicId()); 76 | photo.setProperty("format", getFormat()); 77 | photo.setProperty("url", getUrl()); 78 | photo.setProperty("type", getType()); 79 | photo.setProperty("resource_type", getResourceType()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/java/org/esxx/js/protocol/GAEConnectionManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | ESXX - The friendly ECMAscript/XML Application Server 3 | Copyright (C) 2007-2010 Martin Blom 4 | 5 | This program is free software: you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public License 7 | as published by the Free Software Foundation, either version 3 8 | of the License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public License 16 | along with this program. If not, see . 17 | 18 | 19 | PLEASE NOTE THAT THIS FILE'S LICENSE IS DIFFERENT FROM THE REST OF ESXX! 20 | */ 21 | 22 | package org.esxx.js.protocol; 23 | 24 | import java.net.*; 25 | import java.util.concurrent.TimeUnit; 26 | import org.apache.http.conn.*; 27 | import org.apache.http.params.*; 28 | import org.apache.http.conn.routing.HttpRoute; 29 | import org.apache.http.conn.scheme.*; 30 | 31 | public class GAEConnectionManager 32 | implements ClientConnectionManager { 33 | 34 | public GAEConnectionManager() { 35 | SocketFactory no_socket_factory = new SocketFactory() { 36 | public Socket connectSocket(Socket sock, String host, int port, 37 | InetAddress localAddress, int localPort, 38 | HttpParams params) { 39 | return null; 40 | } 41 | 42 | public Socket createSocket() { 43 | return null; 44 | } 45 | 46 | public boolean isSecure(Socket s) { 47 | return false; 48 | } 49 | }; 50 | 51 | schemeRegistry = new SchemeRegistry(); 52 | schemeRegistry.register(new Scheme("http", no_socket_factory, 80)); 53 | schemeRegistry.register(new Scheme("https", no_socket_factory, 443)); 54 | } 55 | 56 | 57 | @Override public SchemeRegistry getSchemeRegistry() { 58 | return schemeRegistry; 59 | } 60 | 61 | @Override public ClientConnectionRequest requestConnection(final HttpRoute route, 62 | final Object state) { 63 | return new ClientConnectionRequest() { 64 | public void abortRequest() { 65 | // Nothing to do 66 | } 67 | 68 | public ManagedClientConnection getConnection(long timeout, TimeUnit tunit) { 69 | return GAEConnectionManager.this.getConnection(route, state); 70 | } 71 | }; 72 | } 73 | 74 | @Override public void releaseConnection(ManagedClientConnection conn, 75 | long validDuration, TimeUnit timeUnit) { 76 | } 77 | 78 | @Override public void closeIdleConnections(long idletime, TimeUnit tunit) { 79 | } 80 | 81 | @Override public void closeExpiredConnections() { 82 | } 83 | 84 | @Override public void shutdown() { 85 | } 86 | 87 | private ManagedClientConnection getConnection(HttpRoute route, Object state) { 88 | return new GAEClientConnection(this, route, state); 89 | } 90 | 91 | private SchemeRegistry schemeRegistry; 92 | } 93 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/appengine-web.xml.sample: -------------------------------------------------------------------------------- 1 | 2 | 3 | {Your Application ID} 4 | 1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | false 18 | 19 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/logging.properties: -------------------------------------------------------------------------------- 1 | # A default java.util.logging configuration. 2 | # (All App Engine logging is through java.util.logging by default). 3 | # 4 | # To use this configuration, copy it into your application's WEB-INF 5 | # folder and add the following to your appengine-web.xml: 6 | # 7 | # 8 | # 9 | # 10 | # 11 | 12 | # Set the default logging level for all loggers to WARNING 13 | .level = WARNING 14 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/messages.properties: -------------------------------------------------------------------------------- 1 | title.empty = Title cannot be empty 2 | signature.mismatch = Signature mismatch -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/mvc-dispatcher-servlet.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/pages/post.jsp: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/pages/pre.jsp: -------------------------------------------------------------------------------- 1 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 2 | <%@taglib uri="http://cloudinary.com/jsp/taglib" prefix="cl" %> 3 | 4 | 5 | Photo Album 6 | 7 | " /> 9 | 10 | 11 | 12 | 13 | 17 | 18 |
-------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/pages/upload.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@taglib uri="http://www.springframework.org/tags" prefix="spring" %> 3 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %> 4 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 5 | 6 | <%@include file="pre.jsp"%> 7 | 8 |

Your photo was uploaded sucessfully!

9 | 10 | 11 | 22 | 23 | 24 | " class="back_link">Back to list 25 | 26 | 27 |
28 |

Upload metadata:

29 | 30 | 31 | 32 | 33 |
${entry.key}${entry.value}
34 |
35 |
36 | 37 | <%@include file="post.jsp"%> -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/pages/upload_form.jsp: -------------------------------------------------------------------------------- 1 | 2 | <%@taglib uri="http://www.springframework.org/tags" prefix="spring" %> 3 | <%@taglib uri="http://www.springframework.org/tags/form" prefix="form" %> 4 | <%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> 5 | 6 | <%@include file="pre.jsp"%> 7 | 8 |
9 |

New Photo

10 |

Image file is uploaded through the server

11 | 12 |
13 | Title: 14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 23 |
24 | 25 |
26 |
27 |
28 | 29 | 30 |
31 | 32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 | 40 | 43 |
44 |
45 |
46 |
47 |
48 | 49 |
50 |
51 | 52 | 53 |
54 | 55 |
56 | 57 | " class="back_link">Back to list 58 | <%@include file="post.jsp"%> 59 | 60 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | Spring MVC Application 7 | 8 | 9 | mvc-dispatcher 10 | org.springframework.web.servlet.DispatcherServlet 11 | 1 12 | 13 | 14 | 15 | mvc-dispatcher 16 | / 17 | 18 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/assets/cloudinary_cors.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/assets/javascripts/cloudinary/canvas-to-blob.min.js: -------------------------------------------------------------------------------- 1 | (function(a){"use strict";var b=a.HTMLCanvasElement&&a.HTMLCanvasElement.prototype,c=a.Blob&&function(){try{return Boolean(new Blob)}catch(a){return!1}}(),d=c&&a.Uint8Array&&function(){try{return(new Blob([new Uint8Array(100)])).size===100}catch(a){return!1}}(),e=a.BlobBuilder||a.WebKitBlobBuilder||a.MozBlobBuilder||a.MSBlobBuilder,f=(c||e)&&a.atob&&a.ArrayBuffer&&a.Uint8Array&&function(a){var b,f,g,h,i,j;a.split(",")[0].indexOf("base64")>=0?b=atob(a.split(",")[1]):b=decodeURIComponent(a.split(",")[1]),f=new ArrayBuffer(b.length),g=new Uint8Array(f);for(h=0;h options.maxNumberOfFiles) { 90 | file.error = settings.i18n('maxNumberOfFiles'); 91 | } else if (options.acceptFileTypes && 92 | !(options.acceptFileTypes.test(file.type) || 93 | options.acceptFileTypes.test(file.name))) { 94 | file.error = settings.i18n('acceptFileTypes'); 95 | } else if (options.maxFileSize && file.size > options.maxFileSize) { 96 | file.error = settings.i18n('maxFileSize'); 97 | } else if ($.type(file.size) === 'number' && 98 | file.size < options.minFileSize) { 99 | file.error = settings.i18n('minFileSize'); 100 | } else { 101 | delete file.error; 102 | } 103 | if (file.error || data.files.error) { 104 | data.files.error = true; 105 | dfd.rejectWith(this, [data]); 106 | } else { 107 | dfd.resolveWith(this, [data]); 108 | } 109 | return dfd.promise(); 110 | } 111 | 112 | } 113 | 114 | }); 115 | 116 | })); 117 | -------------------------------------------------------------------------------- /samples/photo_album_gae/src/main/webapp/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | body { font-family: Helvetica, Arial, sans-serif; color: #333; margin: 10px; width: 960px } 2 | #posterframe { position: absolute; right: 10px; top: 10px; } 3 | h1 { color: #0e2953; font-size: 18px; } 4 | h2 { color: #666; font-size: 16px; } 5 | p { font-size: 14px; line-height: 18px; } 6 | #logo { height: 51px; width: 241px; } 7 | a { color: #0b63b6 } 8 | 9 | .actions { margin: 20px 0; } 10 | .upload_link { color: #000; border: 1px solid #aaa; background-color: #e0e0e0; 11 | font-size: 18px; padding: 5px 10px; width: 250px; margin: 10px 0 20px 0; 12 | font-weight: bold; text-align: center; text-decoration: none; margin: 5px; } 13 | 14 | .photo { margin: 10px; padding: 10px; border-top: 2px solid #ccc; } 15 | .photo .thumbnail { margin-top: 10px; display: block; max-width: 200px; border: none; } 16 | .toggle_info { margin-top: 10px; font-weight: bold; color: #e62401; display: block; } 17 | .thumbnail_holder { height: 182px; margin-bottom: 5px; margin-right: 10px; } 18 | .info td, .uploaded_info td { font-size: 12px } 19 | .note { margin: 20px 0} 20 | 21 | .more_info, .show_more_info .less_info { display: none; } 22 | .show_more_info .more_info, .less_info { display: inline-block; } 23 | .inline { display: inline-block; } 24 | td { vertical-align: top; padding-right: 5px; } 25 | 26 | #backend_upload, #direct_upload { padding: 20px 20px; margin: 20px 0; 27 | border-top: 1px solid #ccc; border-bottom: 1px solid #ccc; } 28 | 29 | #backend_upload h1, #direct_upload h1 { margin: 0 0 15px 0; } 30 | 31 | .back_link { font-weight: bold; display: block; font-size: 16px; margin: 10px 0; } 32 | 33 | form { border: 1px solid #ddd; margin: 15px 0; padding: 15px 0; border-radius: 4px; } 34 | form .form_line { margin-bottom: 20px; } 35 | form .form_controls { margin-left: 180px; } 36 | form label { float: left; width: 160px; padding-top: 3px; text-align: right; } 37 | form .error { color: #c55; margin: 0 10px; } 38 | 39 | #direct_upload { border: 4px dashed #ccc; } 40 | 41 | .upload_details { font-size: 12px; margin: 20px; border-top: 1px solid #ccc; word-wrap: break-word; } 42 | 43 | .upload_button_holder { 44 | position: relative; 45 | display: inline-block; 46 | overflow: hidden; 47 | } 48 | 49 | .upload_button_holder .upload_button { 50 | display: block; 51 | position: relative; 52 | font-weight: bold; 53 | font-size: 14px; 54 | background-color: rgb(15, 97, 172); 55 | color: #fff; 56 | padding: 5px 0; 57 | border: 1px solid #000; 58 | border-radius: 4px; 59 | width: 100px; 60 | height: 18px; 61 | text-decoration: none; 62 | text-align: center; 63 | cursor: pointer; 64 | } 65 | 66 | .upload_button_holder:hover .upload_button { 67 | background-color: rgb(17, 133, 240); 68 | } 69 | 70 | .upload_button_holder .cloudinary-fileupload { 71 | opacity: 0; 72 | filter: alpha(opacity=0); 73 | cursor: pointer; 74 | position: absolute; 75 | top: 0; 76 | left: 0; 77 | width: 100%; 78 | height: 100%; 79 | margin: 0; 80 | padding: 0; 81 | border: none; 82 | } 83 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'cloudinary-parent' 2 | include ':cloudinary-core' 3 | include ':cloudinary-taglib' 4 | include ':cloudinary-http5' 5 | include ':cloudinary-test-common' 6 | 7 | -------------------------------------------------------------------------------- /tools/update_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | new_version=$1 3 | 4 | current_version=`grep -oP "(?<=VERSION \= \")([0-9.]+)(?=\")" cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java` 5 | current_version_re=${current_version//./\\.} 6 | echo "Current version is $current_version" 7 | if [ -n "$new_version" ]; then 8 | echo "New version will be $new_version" 9 | echo "Pattern used: $current_version_re" 10 | sed -e "s/${current_version_re}/${new_version}/g" -i "" cloudinary-core/src/main/java/com/cloudinary/Cloudinary.java 11 | sed -e "s/${current_version_re}/${new_version}/g" -i "" README.md 12 | sed -e "s/${current_version_re}/${new_version}/g" -i "" gradle.properties 13 | git changelog -t $new_version 14 | else 15 | echo "Usage: $0 " 16 | echo "For example: $0 1.9.2" 17 | fi 18 | --------------------------------------------------------------------------------