+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to read the Software only. Permission is hereby NOT GRANTED to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.selfxdsd.selfweb.api.input; + +import com.selfxdsd.api.BillingInfo; +import com.selfxdsd.selfweb.api.input.validators.NoSpecialChars; +import com.selfxdsd.selfweb.api.input.validators.ValidCountry; + +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; + +/** + * Input for a new Stripe wallet (project billing info). + * @author Mihai Andronache (amihaiemil@gmail.com) + * @version $Id$ + * @since 0.0.1 + * @checkstyle HiddenField (200 lines) + * @checkstyle JavadocVariable (200 lines) + * @checkstyle JavadocMethod (200 lines) + */ +public final class StripeWalletInput { + + @NotBlank(message = "Legal name is mandatory!") + @NoSpecialChars + private String legalName; + + @NotBlank(message = "Country is mandatory!") + @NoSpecialChars + @ValidCountry + private String country; + + @NotBlank(message = "Address is mandatory!") + @NoSpecialChars + private String address; + + @NotBlank(message = "City is mandatory!") + @NoSpecialChars + private String city; + + @NotBlank(message = "Zipcode is mandatory!") + @NoSpecialChars + private String zipcode; + + @NotBlank(message = "E-Mail is mandatory!") + @Email(message = "Please provide a valid e-mail address.") + @NoSpecialChars + private String email; + + @NoSpecialChars + private String other; + + public String getLegalName() { + return this.legalName; + } + + public void setLegalName(final String legalName) { + this.legalName = legalName; + } + + public String getCountry() { + return this.country; + } + + public void setCountry(final String country) { + this.country = country; + } + + public String getAddress() { + return this.address; + } + + public void setAddress(final String address) { + this.address = address; + } + + public String getCity() { + return this.city; + } + + public void setCity(final String city) { + this.city = city; + } + + public String getZipcode() { + return this.zipcode; + } + + public void setZipcode(final String zipcode) { + this.zipcode = zipcode; + } + + public String getEmail() { + return this.email; + } + + public void setEmail(final String email) { + this.email = email; + } + + public String getOther() { + return this.other; + } + + public void setOther(final String other) { + this.other = other; + } + + /** + * Billing info from Stripe input form. + */ + public static final class StripeBillingInfo implements BillingInfo { + + /** + * Input from the Stripe wallet formular. + */ + private StripeWalletInput input; + + /** + * Ctor. + * @param input Input from the Stripe wallet form. + */ + public StripeBillingInfo(final StripeWalletInput input) { + this.input = input; + } + + @Override + public String legalName() { + return this.input.getLegalName(); + } + + @Override + public String country() { + return this.input.getCountry(); + } + + @Override + public String address() { + return this.input.getAddress(); + } + + @Override + public String city() { + return this.input.getCity(); + } + + @Override + public String zipcode() { + return this.input.getZipcode(); + } + + @Override + public String email() { + return this.input.getEmail(); + } + + @Override + public String other() { + return this.input.getOther(); + } + } +} diff --git a/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialChars.java b/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialChars.java new file mode 100644 index 00000000..263d8d6c --- /dev/null +++ b/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialChars.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2020, Self XDSD Contributors + * All rights reserved. + *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to read the Software only. Permission is hereby NOT GRANTED to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT + * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package com.selfxdsd.selfweb.api.input.validators; + +import javax.validation.Constraint; +import javax.validation.Payload; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; + +/** + * Annotation for String inputs. Forbids the mentioned special chars. + * @author Mihai Andronache (amihaiemil@gmail.com + * @version $Id$ + * @since 0.0.1 + * @checkstyle JavadocMethod (200 lines) + */ +@Documented +@Target({ FIELD }) +@Constraint(validatedBy = NoSpecialCharsValidator.class) +@Retention(RetentionPolicy.RUNTIME) +public @interface NoSpecialChars { + + String message() default "No special chars allowed."; + + /** + * The forbidden chars. + * By default, the forbidden chars are: "[](){};<>/\`!%^*$? + * @return String containing the forbidden chars. + */ + String forbidden() default "\"[](){};<>/\\`!%^*$?"; + + Class>[] groups() default {}; + Class extends Payload>[] payload() default {}; +} diff --git a/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialCharsValidator.java b/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialCharsValidator.java new file mode 100644 index 00000000..7b9bd041 --- /dev/null +++ b/src/main/java/com/selfxdsd/selfweb/api/input/validators/NoSpecialCharsValidator.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2020, Self XDSD Contributors + * All rights reserved. + *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), + * to read the Software only. Permission is hereby NOT GRANTED to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software. + *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.selfxdsd.selfweb.api.input.validators;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+
+/**
+ * Validator for the annotation {@link NoSpecialChars}.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 0.0.1
+ */
+public final class NoSpecialCharsValidator
+ implements ConstraintValidator
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to read the Software only. Permission is hereby NOT GRANTED to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.selfxdsd.selfweb.api.input.validators;
+
+import javax.validation.Constraint;
+import javax.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import static java.lang.annotation.ElementType.FIELD;
+
+/**
+ * Annotation for String inputs. Validates the country code.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 0.0.1
+ * @checkstyle JavadocMethod (200 lines)
+ */
+@Documented
+@Target({ FIELD })
+@Constraint(validatedBy = ValidCountryValidator.class)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValidCountry {
+
+ String message() default "Invalid country.";
+
+ Class>[] groups() default {};
+ Class extends Payload>[] payload() default {};
+
+}
diff --git a/src/main/java/com/selfxdsd/selfweb/api/input/validators/ValidCountryValidator.java b/src/main/java/com/selfxdsd/selfweb/api/input/validators/ValidCountryValidator.java
new file mode 100644
index 00000000..c09f7c7f
--- /dev/null
+++ b/src/main/java/com/selfxdsd/selfweb/api/input/validators/ValidCountryValidator.java
@@ -0,0 +1,62 @@
+/**
+ * Copyright (c) 2020, Self XDSD Contributors
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"),
+ * to read the Software only. Permission is hereby NOT GRANTED to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.selfxdsd.selfweb.api.input.validators;
+
+import javax.validation.ConstraintValidator;
+import javax.validation.ConstraintValidatorContext;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Validator for {@link ValidCountry}.
+ * @author Mihai Andronache (amihaiemil@gmail.com)
+ * @version $Id$
+ * @since 0.0.1
+ * @checkstyle ReturnCount (200 lines)
+ */
+public final class ValidCountryValidator
+ implements ConstraintValidator"
+ + " "
+ )
+ $('.pmToggle').bootstrapToggle({
+ on: 'Active',
+ off: 'Inactive',
+ width: '45%'
+ });
+ $($('input.pmToggle')[$('input.pmToggle').length - 1]).on(
+ 'change',
+ function() {
+ if($(this).prop('checked')) {
+ $('input.pmToggle').not(this).prop('checked', false);
+ let parent = $('input.pmToggle').not(this).parent();
+ parent.removeClass('btn-primary');
+ parent.addClass('btn-default');
+ parent.addClass('off');
+ activatePaymentMethod(owner, name, paymentMethod);
+ $("#activateStripeWalletButton").removeClass("disabled");
+ } else { //we don't allow manual inactivation of a PaymentMethod
+ $(this).bootstrapToggle('on');
+ }
+ }
+ );
+ $("#noRealPaymentMethods").hide();
+ $("#realPaymentMethods").show();
+ },
+ error: function(jqXHR, error, errorThrown) {
+ $("#addStripePaymentMethodButton").removeClass("disabled");
+ $("#loadingStripePaymentForm").hide();
+ if(jqXHR.status && jqXHR.status == 400){
+ console.error("Bad Request: " + jqXHR.responseText);
+ $("#stripePaymentMethodFormError").show();
+ } else {
+ console.log("Server error status: " + jqXHR.status);
+ console.log("Server error: " + jqXHR.responseText);
+ alert(
+ "Something went wrong (" + jqXHR.status + ")." +
+ "Please, try again later."
+ );
+ }
+ }
+ }
+ );
+
+
+ $("#cancelNewCardButton").trigger("click");
+ }
+ $("#addNewCardButton").removeClass("disabled");
+ $("#cancelNewCardButton").removeClass("disabled");
+ $("#loadingAddNewCard").hide();
+ });
+ }
+ )
+
+ },
+ error: function(jqXHR, error, errorThrown) {
+ $("#addStripePaymentMethodButton").removeClass("disabled");
+ $("#loadingStripePaymentForm").hide();
+ if(jqXHR.status && jqXHR.status == 400){
+ console.error("Bad Request: " + jqXHR.responseText);
+ $("#stripePaymentMethodFormError").show();
+ } else {
+ console.log("Server error status: " + jqXHR.status);
+ console.log("Server error: " + jqXHR.responseText);
+ alert(
+ "Something went wrong (" + jqXHR.status + ")." +
+ "Please, try again later."
+ );
+ }
+ }
+ }
+ );
+ }
+ );
+
+ $("#cancelNewCardButton").on(
+ "click",
+ function(event) {
+ event.preventDefault();
+ $("#payment-method-element").html('');
+ $("#addNewCardErrorMessage").html("");
+ $("#addNewCardError").hide();
+ $("#addStripePaymentMethodButton").show();
+ $("#payment-method-element-card").hide();
+ }
+ )
+ $("#activateFakeWalletButton").on(
+ "click",
+ function(event) {
+ event.preventDefault();
+ activateWallet(
+ $("#owner").text(),
+ $("#name").text(),
+ "fake"
+ )
+ }
+ )
+ $("#activateStripeWalletButton").on(
+ "click",
+ function(event) {
+ if(!$(this).hasClass("disabled")) {
+ event.preventDefault();
+ activateWallet(
+ $("#owner").text(),
+ $("#name").text(),
+ "stripe"
+ )
+ }
+ }
+ )
+
+ // $("#addContractForm").submit(
+ // function(e) {
+ // e.preventDefault();
+ // var valid = true;
+ // $.each($("#addContractForm .required"), function(index, element) {
+ // if($(element).val() == '') {
+ // $(element).addClass("is-invalid");
+ // valid = valid && false;
+ // } else {
+ // $(element).removeClass("is-invalid");
+ // valid = valid && true;
+ // }
+ // });
+ // if(valid) {
+ // var formData = $(this).serialize();
+ // //check if username exists before submit
+ // usersService.exists(
+ // $("#username").val(),
+ // "github",
+ // function(){
+ // $("#addContractLoading").show();
+ // clearFormErrors();
+ // disableForm(true);
+ // }
+ // ).then(
+ // function(){
+ // return contractsService.add(project, formData)
+ // }
+ // ).then(
+ // function(contract){
+ // $("#addContractForm input").val('');
+ // $('#addContractForm option:first').prop('selected',true);
+ // //we check the current page (0 based) displayed in table.
+ // //if is last page, we're adding the contract to table.
+ // //since it's the latest contract created.
+ // loadContracts();
+ // }
+ // ).catch(handleError)
+ // .finally(
+ // function(){
+ // disableForm(false);
+ // $("#addContractLoading").hide();
+ // });
+ // return false;
+ // }
+ // }
+ // );
+ }
+)
\ No newline at end of file
diff --git a/src/main/resources/templates/head.html b/src/main/resources/templates/head.html
index f4fd28e6..10feed6b 100644
--- a/src/main/resources/templates/head.html
+++ b/src/main/resources/templates/head.html
@@ -85,6 +85,15 @@
function formatEuro(amount) {
return new Intl.NumberFormat('de-DE', { style: 'currency', currency: 'EUR' }).format(amount);
}
+ function hasSpecialChars(text) {
+ var chars="\"[](){};<>/\\`!%^*$?"
+ for (var i = 0; i < chars.length; i++) {
+ if(text.includes(chars.charAt(i))) {
+ return true;
+ };
+ }
+ return false;
+ }
"
+ + issuer
+ + " "
+ + ""
+ + "****** " + paymentMethod.stripe.card.last4
+ + " "
+ + ""
+ + paymentMethod.stripe.card.exp_month + "/" + paymentMethod.stripe.card.exp_year
+ + " "
+ + ""
+ + active
+ + " "
+ + "