This is an automated email from the ASF dual-hosted git repository. dklco pushed a commit to branch forms-enhancements in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git
commit c6e02d9be734f5f2433f6e69e85b885333d5836d Author: Dan Klco <[email protected]> AuthorDate: Fri Jan 15 10:07:11 2021 -0500 Working on enhanced capabilities for the reference forms --- reference/pom.xml | 20 +++ .../sling/cms/reference/forms/FormConstants.java | 37 +++++ .../sling/cms/reference/forms/FormUtils.java | 28 ++++ .../cms/reference/forms/impl/FormHandler.java | 15 +- .../forms/impl/actions/CreateUserAction.java | 163 +++++++++++++++++++ .../impl/actions/RequestPasswordResetAction.java | 115 ++++++++++++++ .../forms/impl/actions/ResetPasswordAction.java | 126 +++++++++++++++ .../forms/impl/actions/SendEmailAction.java | 31 +++- .../forms/impl/actions/UpdateProfileAction.java | 83 +++++----- .../forms/impl/actions/UpdateResourceAction.java | 113 +++++++++++++ .../providers/RequestParametersValueProvider.java | 56 +++++++ .../providers/SuffixResourceFormValueProvider.java | 63 ++++++++ .../providers/UserProfileFormValueProvider.java | 3 +- .../main/resources/OSGI-INF/l10n/bundle.properties | 24 ++- .../components/forms/actions/createuser.json | 5 + .../forms/actions/createuser/createuser.jsp | 32 ++++ .../components/forms/actions/createuser/edit.json | 47 ++++++ .../forms/actions/requestpasswordreset/edit.json | 16 ++ .../requestpasswordreset/requestpasswordreset.jsp | 26 +++ .../components/forms/actions/resetpassword.json | 5 + .../forms/actions/resetpassword/edit.json | 5 + .../forms/actions/resetpassword/resetpassword.jsp | 22 +++ .../components/forms/actions/updateresource.json | 5 + .../forms/actions/updateresource/edit.json | 32 ++++ .../actions/updateresource/updateresource.jsp | 30 ++++ .../components/forms/fields/textarea/textarea.jsp | 4 +- .../forms/fields/textfield/textfield.jsp | 2 +- .../apps/reference/components/forms/login.json | 5 + .../reference/components/forms/login/edit.json | 46 ++++++ .../reference/components/forms/login/login.jsp | 42 +++++ .../forms/providers/requestparameters.json | 6 + .../forms/providers/requestparameters/edit.json | 21 +++ .../requestparameters/requestparameters.jsp | 26 +++ .../components/forms/providers/suffixresource.json | 6 + .../forms/providers/suffixresource/edit.json | 28 ++++ .../providers/suffixresource/suffixresource.jsp | 28 ++++ .../cms/reference/forms/impl/FormHandlerTest.java | 15 +- .../reference/forms/impl/FormRequestImplTest.java | 3 +- .../actions/RequestPasswordResetActionTest.java | 146 +++++++++++++++++ .../impl/actions/ResetPasswordActionTest.java | 175 +++++++++++++++++++++ .../forms/impl/actions/SendEmailActionTest.java | 18 ++- .../impl/actions/UpdateResourceActionTest.java | 162 +++++++++++++++++++ .../forms/impl/fields/HoneypotHandlerTest.java | 99 ++++++++++++ .../forms/impl/fields/TextfieldHandlerTest.java | 47 +++++- .../RequestParametersValueProviderTest.java | 76 +++++++++ .../SuffixResourceFormValueProviderTest.java | 156 ++++++++++++++++++ reference/src/test/resources/form.json | 42 ++++- 47 files changed, 2183 insertions(+), 72 deletions(-) diff --git a/reference/pom.xml b/reference/pom.xml index 6c4b682..12e59ac 100644 --- a/reference/pom.xml +++ b/reference/pom.xml @@ -41,6 +41,26 @@ <groupId>biz.aQute.bnd</groupId> <artifactId>bnd-maven-plugin</artifactId> </plugin> + <plugin> + <groupId>org.jacoco</groupId> + <artifactId>jacoco-maven-plugin</artifactId> + <version>0.8.2</version> + <executions> + <execution> + <goals> + <goal>prepare-agent</goal> + </goals> + </execution> + <!-- attached to Maven test phase --> + <execution> + <id>report</id> + <phase>test</phase> + <goals> + <goal>report</goal> + </goals> + </execution> + </executions> + </plugin> </plugins> </build> diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/FormConstants.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/FormConstants.java new file mode 100644 index 0000000..182dcc9 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/FormConstants.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms; + +public class FormConstants { + + public static final String PATH_PROFILE = "profile"; + + public static final String PN_EMAIL = "email"; + + public static final String PN_SERVICE_USER = "serviceUser"; + + public static final String PN_ALLOWED_PROPERTIES = "allowedProperties"; + + public static final String PN_SUBPATH = "subpath"; + + public static final String SERVICE_USER = "slingcms-reference-usermanager"; + + public static final String PN_RESETTOKEN = "resettoken"; + public static final String PN_RESETTIMEOUT = "resettimeout"; + + public static final String PN_PASSWORD = "passwordw"; +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/FormUtils.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/FormUtils.java new file mode 100644 index 0000000..3e77430 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/FormUtils.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms; + +import java.util.stream.Stream; + +import org.apache.sling.api.resource.Resource; + +public class FormUtils { + + public static final boolean handles(String[] supportedTypes, Resource resource) { + return Stream.of(supportedTypes).anyMatch(t -> t.equals(resource.getResourceType())); + } +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/FormHandler.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/FormHandler.java index b75ad13..0b8de5b 100644 --- a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/FormHandler.java +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/FormHandler.java @@ -36,6 +36,7 @@ import org.apache.sling.cms.Page; import org.apache.sling.cms.PageManager; import org.apache.sling.cms.ResourceTree; import org.apache.sling.cms.reference.forms.FormAction; +import org.apache.sling.cms.reference.forms.FormActionResult; import org.apache.sling.cms.reference.forms.FormException; import org.apache.sling.cms.reference.forms.FormRequest; import org.osgi.service.component.annotations.Activate; @@ -86,11 +87,15 @@ public class FormHandler extends SlingAllMethodsServlet { request.getSession().setAttribute(formRequest.getSessionId(), formRequest.getFormData()); for (Resource actionResource : actionResources) { log.debug("Finding action handler for: {}", actionResource); - for (FormAction action : formActions) { - if (action.handles(actionResource)) { - log.debug("Invoking handler: {}", action.getClass()); - action.handleForm(actionResource, formRequest); - break; + FormAction action = formActions.stream().filter(fa -> fa.handles(actionResource)).findFirst() + .orElse(null); + if (action != null) { + FormActionResult result = action.handleForm(actionResource, formRequest); + if (!result.isSucceeded()) { + throw new FormException( + "Failed to invoke action: " + action + " with message: " + result.getMessage()); + } else { + log.debug("Successfully invoked action: {}", result.getMessage()); } } } diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/CreateUserAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/CreateUserAction.java new file mode 100644 index 0000000..83cfc4d --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/CreateUserAction.java @@ -0,0 +1,163 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.Group; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.oak.spi.security.principal.PrincipalImpl; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.cms.reference.forms.FormAction; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.apache.sling.cms.reference.forms.FormUtils; +import org.jetbrains.annotations.NotNull; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = { FormAction.class }) +@Designate(ocd = CreateUserAction.Config.class) +public class CreateUserAction implements FormAction { + + private static final Logger log = LoggerFactory.getLogger(CreateUserAction.class); + public static final String DEFAULT_RESOURCE_TYPE = "reference/components/forms/actions/createuser"; + public static final String PROFILE_PROPERTIES = "profileProperties"; + public static final String GROUPS = "groups"; + + private final ResourceResolverFactory factory; + private final Config config; + + @Activate + public CreateUserAction(@Reference ResourceResolverFactory factory, Config config) { + this.factory = factory; + this.config = config; + } + + @Override + public FormActionResult handleForm(final Resource actionResource, final FormRequest request) throws FormException { + final StringSubstitutor sub = new StringSubstitutor(request.getFormData()); + + final ValueMap properties = actionResource.getValueMap(); + + String username = request.getFormData().get("username", String.class); + String password = request.getFormData().get(FormConstants.PN_PASSWORD, String.class); + + String intermediatePath = properties.get("intermediatePath", String.class); + + if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) { + return FormActionResult.failure("Empty username / password"); + } + + try { + try (ResourceResolver adminResolver = factory.getServiceResourceResolver( + Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, FormConstants.SERVICE_USER))) { + JackrabbitSession session = (JackrabbitSession) adminResolver.adaptTo(Session.class); + final UserManager userManager = session.getUserManager(); + + if (userManager.getAuthorizable(new PrincipalImpl(username)) == null) { + + log.debug("Creating user {}", username); + User user = userManager.createUser(username, password, new PrincipalImpl(username), + intermediatePath); + + String[] groups = properties.get(GROUPS, new String[0]); + for (String g : groups) { + String groupName = sub.replace(g); + Authorizable group = userManager.getAuthorizable(new PrincipalImpl(groupName)); + if (group == null || !group.isGroup()) { + log.error("Could not find group {}", groupName); + return FormActionResult.failure("Could not find group: " + groupName); + } else { + ((Group) group).addMember(user); + } + + } + log.debug("Updating profile for {}", username); + updateProfile(adminResolver, user, properties.get(PROFILE_PROPERTIES, new String[0]), + request.getFormData()); + + log.debug("Saving changes!"); + adminResolver.commit(); + return FormActionResult.success("User " + username + " created successfully"); + } else { + log.error("Failed to create user, {} already exists", username); + return FormActionResult.failure("User " + username + " already exists"); + } + } + } catch (LoginException le) { + log.error("Failed to get user manager service user", le); + return FormActionResult.failure("Failed to get service user"); + } catch (PersistenceException | RepositoryException e) { + log.error("Failed to create user " + username, e); + return FormActionResult.failure("Failed to create user " + username); + } + } + + private void updateProfile(ResourceResolver adminResolver, User user, String @NotNull [] toset, ValueMap formData) + throws PersistenceException, RepositoryException { + if (toset.length > 0) { + Map<String, Object> properties = new HashMap<>(); + Arrays.stream(toset).filter(k -> formData.keySet().contains(k)) + .forEach(k -> properties.put(k, formData.get(k))); + properties.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED); + ResourceUtil.getOrCreateResource(adminResolver, user.getPath() + "/profile", properties, + JcrConstants.NT_UNSTRUCTURED, false); + } + + } + + @Override + public boolean handles(Resource actionResource) { + return FormUtils.handles(config.supportedTypes(), actionResource); + } + + @ObjectClassDefinition(name = "%cms.reference.createuser.name", description = "%cms.reference.createuser.description", localization = "OSGI-INF/l10n/bundle") + public @interface Config { + + @AttributeDefinition(name = "%cms.reference.supportedTypes.name", description = "%cms.reference.supportedTypes.description", defaultValue = { + DEFAULT_RESOURCE_TYPE }) + String[] supportedTypes() default { DEFAULT_RESOURCE_TYPE }; + } + +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetAction.java new file mode 100644 index 0000000..9ab6da6 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetAction.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import java.util.Calendar; +import java.util.Collections; +import java.util.UUID; +import java.util.stream.Stream; + +import javax.jcr.Session; +import javax.jcr.ValueFactory; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.cms.reference.forms.FormAction; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = { FormAction.class }) +@Designate(ocd = RequestPasswordResetAction.Config.class) +public class RequestPasswordResetAction implements FormAction { + + public static final String DEFAULT_RESOURCE_TYPE = "reference/components/forms/actions/requestpasswordreset"; + public static final String PN_RESETTOKEN = "resettoken"; + public static final String PN_RESETTIMEOUT = "resettimeout"; + private static final Logger log = LoggerFactory.getLogger(RequestPasswordResetAction.class); + private ResourceResolverFactory factory; + private Config config; + + @Activate + public RequestPasswordResetAction(@Reference ResourceResolverFactory factory, Config config) { + this.factory = factory; + this.config = config; + } + + @Override + public FormActionResult handleForm(Resource actionResource, FormRequest request) throws FormException { + String email = request.getFormData().get(FormConstants.PN_EMAIL, String.class); + int resetTimeout = actionResource.getValueMap().get(PN_RESETTIMEOUT, Integer.class); + + try (ResourceResolver adminResolver = factory.getServiceResourceResolver( + Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, FormConstants.SERVICE_USER))) { + + JackrabbitSession session = (JackrabbitSession) adminResolver.adaptTo(Session.class); + final UserManager userManager = session.getUserManager(); + + if (userManager.getAuthorizable(email) != null) { + + User user = (User) userManager.getAuthorizable(email); + + String resetToken = UUID.randomUUID().toString(); + Calendar deadline = Calendar.getInstance(); + deadline.add(Calendar.SECOND, resetTimeout); + + ValueFactory vf = session.getValueFactory(); + + user.setProperty(PN_RESETTOKEN, vf.createValue(resetToken)); + user.setProperty(PN_RESETTIMEOUT, vf.createValue(deadline)); + + request.getFormData().put(PN_RESETTOKEN, resetToken); + + adminResolver.commit(); + + } else { + log.warn("Unable to find user {}", email); + return FormActionResult.failure("Unable to find user"); + } + } catch (Exception e) { + throw new FormException("Failed to initiate password reset", e); + } + return FormActionResult.success("Reset token created"); + } + + @Override + public boolean handles(Resource actionResource) { + return Stream.of(config.supportedTypes()).anyMatch(t -> t.equals(actionResource.getResourceType())); + } + + @ObjectClassDefinition(name = "%cms.reference.requestpasswordreset.name", description = "%cms.reference.requestpasswordreset.description", localization = "OSGI-INF/l10n/bundle") + public @interface Config { + + @AttributeDefinition(name = "%cms.reference.supportedTypes.name", description = "%cms.reference.supportedTypes.description", defaultValue = { + DEFAULT_RESOURCE_TYPE }) + String[] supportedTypes() default { DEFAULT_RESOURCE_TYPE }; + } + +} \ No newline at end of file diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordAction.java new file mode 100644 index 0000000..da70b34 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordAction.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import java.util.Calendar; +import java.util.Collections; +import java.util.stream.Stream; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.Value; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.cms.reference.forms.FormAction; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.apache.sling.cms.reference.forms.FormUtils; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = { FormAction.class }) +@Designate(ocd = ResetPasswordAction.Config.class) +public class ResetPasswordAction implements FormAction { + + public static final String DEFAULT_RESOURCE_TYPE = "reference/components/forms/actions/resetpassword"; + private static final Logger log = LoggerFactory.getLogger(ResetPasswordAction.class); + private ResourceResolverFactory factory; + private Config config; + + @Activate + public ResetPasswordAction(@Reference ResourceResolverFactory factory, Config config) { + this.factory = factory; + this.config = config; + } + + @Override + public FormActionResult handleForm(Resource actionResource, FormRequest request) throws FormException { + String email = request.getFormData().get(FormConstants.PN_EMAIL, String.class); + String resetToken = request.getFormData().get(FormConstants.PN_RESETTOKEN, String.class); + String password = request.getFormData().get("password", String.class); + + try (ResourceResolver adminResolver = factory.getServiceResourceResolver( + Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, FormConstants.SERVICE_USER))) { + + JackrabbitSession session = (JackrabbitSession) adminResolver.adaptTo(Session.class); + final UserManager userManager = session.getUserManager(); + + User user = (User) userManager.getAuthorizable(email); + + if (user == null) { + return FormActionResult.failure("No user found for " + email); + } + + String storedToken = getValue(user.getProperty(FormConstants.PN_RESETTOKEN), String.class); + Calendar resetTimeout = getValue(user.getProperty(FormConstants.PN_RESETTIMEOUT), Calendar.class); + if (storedToken == null || !storedToken.equals(resetToken)) { + return FormActionResult.failure("Failed to validate token"); + } + if (Calendar.getInstance().after(resetTimeout)) { + return FormActionResult.failure("Timeout already passed"); + } + user.changePassword(password); + + log.debug("Saving changes!"); + adminResolver.commit(); + + return FormActionResult.success("Password reset successfully!"); + } catch (Exception e) { + throw new FormException("Failed to complete password reset", e); + } + } + + private <E> E getValue(Value[] property, Class<E> clazz) throws IllegalStateException, RepositoryException { + if (property != null && property.length > 0) { + Value v = property[0]; + if (clazz.isAssignableFrom(String.class)) { + return clazz.cast(v.getString()); + } + if (clazz.isAssignableFrom(Calendar.class)) { + return clazz.cast(v.getDate()); + } + } + return null; + } + + @Override + public boolean handles(Resource actionResource) { + return FormUtils.handles(config.supportedTypes(), actionResource); + } + + @ObjectClassDefinition(name = "%cms.reference.resetpassword.name", description = "%cms.reference.resetpassword.description", localization = "OSGI-INF/l10n/bundle") + public @interface Config { + + @AttributeDefinition(name = "%cms.reference.supportedTypes.name", description = "%cms.reference.supportedTypes.description", defaultValue = { + DEFAULT_RESOURCE_TYPE }) + String[] supportedTypes() default { DEFAULT_RESOURCE_TYPE }; + } + +} \ No newline at end of file diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailAction.java index 630e129..cc20283 100644 --- a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailAction.java +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailAction.java @@ -16,6 +16,8 @@ */ package org.apache.sling.cms.reference.forms.impl.actions; +import java.util.stream.Stream; + import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; @@ -28,14 +30,20 @@ import org.apache.sling.cms.reference.forms.FormException; import org.apache.sling.cms.reference.forms.FormRequest; import org.apache.sling.commons.messaging.mail.MailService; import org.jetbrains.annotations.NotNull; +import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; +import org.osgi.service.metatype.annotations.AttributeDefinition; +import org.osgi.service.metatype.annotations.Designate; +import org.osgi.service.metatype.annotations.ObjectClassDefinition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(service = { FormAction.class }) +@Designate(ocd = SendEmailAction.Config.class) public class SendEmailAction implements FormAction { + public static final String DEFAULT_RESOURCE_TYPE = "reference/components/forms/actions/sendemail"; public static final String FROM = "from"; private static final Logger log = LoggerFactory.getLogger(SendEmailAction.class); @@ -43,7 +51,14 @@ public class SendEmailAction implements FormAction { public static final String SUBJECT = "subject"; public static final String TO = "to"; - private MailService mailService; + private final MailService mailService; + private Config config; + + @Activate + public SendEmailAction(@Reference MailService mailService, Config config) { + this.mailService = mailService; + this.config = config; + } @Override public FormActionResult handleForm(final Resource actionResource, final FormRequest request) throws FormException { @@ -71,13 +86,15 @@ public class SendEmailAction implements FormAction { } @Override - public boolean handles(final Resource actionResource) { - return "reference/components/forms/actions/sendemail".equals(actionResource.getResourceType()); + public boolean handles(Resource actionResource) { + return Stream.of(config.supportedTypes()).anyMatch(t -> t.equals(actionResource.getResourceType())); } - @Reference - public void setMailService(MailService mailService) { - this.mailService = mailService; - } + @ObjectClassDefinition(name = "%cms.reference.sendemail.name", description = "%cms.reference.sendemail.description", localization = "OSGI-INF/l10n/bundle") + public @interface Config { + @AttributeDefinition(name = "%cms.reference.supportedTypes.name", description = "%cms.reference.supportedTypes.description", defaultValue = { + DEFAULT_RESOURCE_TYPE }) + String[] supportedTypes() default { DEFAULT_RESOURCE_TYPE }; + } } diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateProfileAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateProfileAction.java index 53d24d9..4aba5cc 100644 --- a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateProfileAction.java +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateProfileAction.java @@ -34,6 +34,7 @@ import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.cms.reference.forms.FormAction; import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; import org.apache.sling.cms.reference.forms.FormException; import org.apache.sling.cms.reference.forms.FormRequest; import org.osgi.service.component.annotations.Component; @@ -51,51 +52,53 @@ public class UpdateProfileAction implements FormAction { String userId = resolver.getUserID(); JackrabbitSession session = (JackrabbitSession) resolver.adaptTo(Session.class); - if (session != null) { - try { - final UserManager userManager = session.getUserManager(); - if (userManager.getAuthorizable(userId) != null) { - - User user = (User) userManager.getAuthorizable(userId); - log.debug("Updating profile for {}", userId); - - String subpath = actionResource.getValueMap().get("subpath", "profile"); - ValueFactory valueFactory = session.getValueFactory(); - - for (Entry<String, Object> e : request.getFormData().entrySet()) { - Value value = null; - if (e.getValue() instanceof String[]) { - user.setProperty(subpath + "/" + e.getKey(), Arrays.stream((String[]) e.getValue()) - .map(valueFactory::createValue).collect(Collectors.toList()).toArray(new Value[0])); - } else { - if (e.getValue() instanceof Calendar) { - value = valueFactory.createValue((Calendar) e.getValue()); - } else if (e.getValue() instanceof Double) { - value = valueFactory.createValue((Double) e.getValue()); - } else if (e.getValue() instanceof Integer) { - value = valueFactory.createValue((Double) e.getValue()); - } else { - value = valueFactory.createValue((String) e.getValue()); - } - user.setProperty(subpath + "/" + e.getKey(), value); - } - } - log.debug("Saving changes!"); - resolver.commit(); + if (session == null) { + log.warn("Failed to get session for {}", userId); + return FormActionResult.failure("Failed to get session for " + userId); + } + try { + final UserManager userManager = session.getUserManager(); + if (userManager.getAuthorizable(userId) == null) { + + log.warn("No profile found for {}", userId); + return FormActionResult.failure("No profile found for " + userId); + } + + User user = (User) userManager.getAuthorizable(userId); + log.debug("Updating profile for {}", userId); - return FormActionResult.success("Profile Updated"); + String subpath = actionResource.getValueMap().get(FormConstants.PN_SUBPATH, FormConstants.PATH_PROFILE); + ValueFactory valueFactory = session.getValueFactory(); + + for (Entry<String, Object> e : request.getFormData().entrySet()) { + Value value = null; + if (e.getValue() instanceof String[]) { + Value[] values = Arrays.stream(((String[]) e.getValue())).map(valueFactory::createValue) + .collect(Collectors.toList()).toArray(new Value[0]); + user.setProperty(subpath + "/" + e.getKey(), values); } else { - log.warn("No profile found for {}", userId); - return FormActionResult.failure("No profile found for " + userId); + if (e.getValue() instanceof Calendar) { + value = valueFactory.createValue((Calendar) e.getValue()); + } else if (e.getValue() instanceof Double) { + value = valueFactory.createValue((Double) e.getValue()); + } else if (e.getValue() instanceof Integer) { + value = valueFactory.createValue((Double) e.getValue()); + } else { + value = valueFactory.createValue((String) e.getValue()); + } + user.setProperty(subpath + "/" + e.getKey(), value); } - } catch (RepositoryException | PersistenceException e) { - log.warn("Failed to update profile for {}", userId, e); - return FormActionResult.failure("Failed to update profile for " + userId); } - } else { - log.warn("Failed to get session for {}", userId); - return FormActionResult.failure("Failed to get session for " + userId); + log.debug("Saving changes!"); + resolver.commit(); + + return FormActionResult.success("Profile Updated"); + + } catch (RepositoryException | PersistenceException e) { + log.warn("Failed to update profile for {}", userId, e); + return FormActionResult.failure("Failed to update profile for " + userId); } + } @Override diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceAction.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceAction.java new file mode 100644 index 0000000..b6b4203 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceAction.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; +import org.apache.jackrabbit.JcrConstants; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.PersistenceException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.api.resource.ResourceUtil; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.cms.reference.forms.FormAction; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = FormAction.class, immediate = true) +public class UpdateResourceAction implements FormAction { + + private static final Logger log = LoggerFactory.getLogger(UpdateResourceAction.class); + + private ResourceResolverFactory factory; + + @Activate + public UpdateResourceAction(@Reference ResourceResolverFactory factory) { + this.factory = factory; + } + + @Override + public FormActionResult handleForm(Resource actionResource, FormRequest request) throws FormException { + log.trace("handleForm"); + + ValueMap properties = actionResource.getValueMap(); + + String[] allowedProperties = properties.get(FormConstants.PN_ALLOWED_PROPERTIES, String[].class); + ResourceResolver resolver = null; + try { + resolver = getResourceResolver(actionResource); + StringSubstitutor sub = new StringSubstitutor(request.getFormData()); + + String path = sub.replace(properties.get("path", String.class)); + log.debug("Upserting resource at path: {}", path); + Resource resource = ResourceUtil.getOrCreateResource(resolver, path, + Collections.singletonMap(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED), + JcrConstants.NT_UNSTRUCTURED, false); + + ModifiableValueMap mvm = resource.adaptTo(ModifiableValueMap.class); + Map<String, Object> formData = request.getFormData(); + if (allowedProperties != null && allowedProperties.length > 0) { + Arrays.stream(allowedProperties).filter(formData::containsKey).forEach(p -> { + log.debug("Setting property {}", p); + mvm.put(p, formData.get(p)); + }); + } else { + mvm.putAll(formData); + } + resolver.commit(); + log.debug("Successfully persisted resource"); + return FormActionResult.success("Updated resource"); + } catch (LoginException | PersistenceException e) { + throw new FormException("Failed to update resource", e); + } finally { + if (StringUtils.isNotBlank(properties.get(FormConstants.PN_SERVICE_USER, String.class)) && resolver != null) { + resolver.close(); + } + } + } + + private ResourceResolver getResourceResolver(Resource actionResource) throws LoginException { + String serviceUser = actionResource.getValueMap().get(FormConstants.PN_SERVICE_USER, String.class); + if (StringUtils.isNotBlank(serviceUser)) { + return factory.getServiceResourceResolver( + Collections.singletonMap(ResourceResolverFactory.SUBSERVICE, serviceUser)); + } else { + return actionResource.getResourceResolver(); + } + + } + + @Override + public boolean handles(Resource actionResource) { + return "reference/components/forms/actions/updateresource".equals(actionResource.getResourceType()); + } + +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProvider.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProvider.java new file mode 100644 index 0000000..ca08dee --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProvider.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.providers; + +import java.util.Arrays; +import java.util.Map; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.cms.reference.forms.FormValueProvider; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = FormValueProvider.class) +public class RequestParametersValueProvider implements FormValueProvider { + + private static final Logger log = LoggerFactory.getLogger(RequestParametersValueProvider.class); + + @Override + public void loadValues(SlingHttpServletRequest request, Resource providerResource, Map<String, Object> formData) { + log.trace("loadFormData"); + String[] parameters = providerResource.getValueMap().get("allowedParameters", String[].class); + if (parameters != null) { + Arrays.stream(parameters).forEach(p -> { + if (request.getParameter(p) != null) { + if (request.getParameterValues(p).length > 1) { + formData.put(p, request.getParameterValues(p)); + } else { + formData.put(p, request.getParameter(p)); + } + } + }); + } + + } + + @Override + public boolean handles(Resource valueProviderResource) { + return "reference/components/forms/providers/requestparameters".equals(valueProviderResource.getResourceType()); + } +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProvider.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProvider.java new file mode 100644 index 0000000..15e57c6 --- /dev/null +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProvider.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.providers; + +import java.util.Arrays; +import java.util.Map; + +import org.apache.commons.text.StringSubstitutor; +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormValueProvider; +import org.osgi.service.component.annotations.Component; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Component(service = FormValueProvider.class) +public class SuffixResourceFormValueProvider implements FormValueProvider { + + private static final Logger log = LoggerFactory.getLogger(SuffixResourceFormValueProvider.class); + + @Override + public void loadValues(SlingHttpServletRequest request, Resource providerResource, Map<String, Object> formData) { + log.trace("loadValues"); + Resource suffixResource = request.getRequestPathInfo().getSuffixResource(); + + ValueMap providerProperties = providerResource.getValueMap(); + StringSubstitutor sub = new StringSubstitutor(formData); + String basePath = sub.replace(providerProperties.get("basePath", String.class)); + String[] allowedProperties = providerProperties.get(FormConstants.PN_ALLOWED_PROPERTIES, String[].class); + if (suffixResource != null && suffixResource.getPath().startsWith(basePath)) { + ValueMap suffixProperties = suffixResource.getValueMap(); + if (allowedProperties != null && allowedProperties.length > 0) { + Arrays.stream(allowedProperties).filter(suffixProperties::containsKey) + .forEach(p -> formData.put(p, suffixProperties.get(p))); + } else { + formData.putAll(suffixProperties); + } + formData.put("suffixResource", suffixResource.getPath()); + } + + } + + @Override + public boolean handles(Resource valueProviderResource) { + return "reference/components/forms/providers/suffixresource".equals(valueProviderResource.getResourceType()); + } +} diff --git a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/UserProfileFormValueProvider.java b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/UserProfileFormValueProvider.java index 4219be9..323b51a 100644 --- a/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/UserProfileFormValueProvider.java +++ b/reference/src/main/java/org/apache/sling/cms/reference/forms/impl/providers/UserProfileFormValueProvider.java @@ -32,6 +32,7 @@ import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.api.resource.Resource; import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.cms.reference.forms.FormConstants; import org.apache.sling.cms.reference.forms.FormValueProvider; import org.osgi.service.component.annotations.Component; import org.slf4j.Logger; @@ -53,7 +54,7 @@ public class UserProfileFormValueProvider implements FormValueProvider { UserManager userManager = session.getUserManager(); User user = (User) userManager.getAuthorizable(userId); - String subpath = providerResource.getValueMap().get("subpath", "profile"); + String subpath = providerResource.getValueMap().get(FormConstants.PN_SUBPATH, FormConstants.PATH_PROFILE); log.debug("Loading profile data from: {}/{}", user.getPath(), subpath); Iterator<String> keys = user.getPropertyNames(subpath); diff --git a/reference/src/main/resources/OSGI-INF/l10n/bundle.properties b/reference/src/main/resources/OSGI-INF/l10n/bundle.properties index 0911dc9..5b263ae 100644 --- a/reference/src/main/resources/OSGI-INF/l10n/bundle.properties +++ b/reference/src/main/resources/OSGI-INF/l10n/bundle.properties @@ -23,7 +23,7 @@ # the Sling SCR plugin ## Search Service Entries -cms.reference.search.name=Apache Sling Reference Search Configuration +cms.reference.search.name=Apache Sling CMS - Reference Search Configuration cms.reference.search.description=Configuration for the Reference \ Search component @@ -32,7 +32,7 @@ searchServiceUsername.description=The name of a service user to use \ to use for searching, if not specified, the current user will be used # Send Email -cms.reference.sendemail.name=Apache Sling Reference Send Email Form Action +cms.reference.sendemail.name=Apache Sling CMS - Reference Send Email Form Action cms.reference.sendemail.description=A reference form action for sending a \ simple text email @@ -44,7 +44,6 @@ cms.reference.sendemail.smtpPort.name=SMTP Port cms.reference.sendemail.smtpPort.description=The port upon which to connect \ to the SMTP server - cms.reference.sendemail.tlsEnabled.name=TLS Enabled cms.reference.sendemail.tlsEnabled.description=Whether or not TLS security is \ enabled @@ -55,4 +54,21 @@ to the SMTP server cms.reference.sendemail.password.name=Password cms.reference.sendemail.password.description=The password to use when connecting \ -to the SMTP server \ No newline at end of file +to the SMTP server + +# Form configuration titles +cms.reference.supportedTypes.name=Supported Types +cms.reference.supportedTypes.description=The resource types supported by this action + +cms.reference.createuser.name=Apache Sling CMS - Reference Create User +cms.reference.createuser.description=Form action for creating a user + +cms.reference.requestpasswordreset.name=Apache Sling CMS - Reference Password Reset Request +cms.reference.requestpasswordreset.description=Form action for initiating a password \ +reset request + +cms.reference.resetpassword.name=Apache Sling CMS - Reference Password Reset +cms.reference.requestpasswordreset.description=Form action for resetting a user password + +cms.reference.sendemail.name=Apache Sling CMS - Reference Send Email +cms.reference.requestpasswordreset.description=Form action for sending an email \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser.json new file mode 100644 index 0000000..7e59253 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser.json @@ -0,0 +1,5 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Create User", + "componentType": "Form Action" +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/createuser.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/createuser.jsp new file mode 100644 index 0000000..b6c450f --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/createuser.jsp @@ -0,0 +1,32 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Create User</h3> + <dl> + <dt>Group Membership</dt> + <dd>${sling:encode(fn:join(properties.groups,','),'HTML')}</dd> + <dt>Intermediate Path</dt> + <dd>${sling:encode(properties.intermediatePath,'HTML')}</dd> + <dt>Service User</dt> + <dd>${sling:encode(properties.serviceUser,'HTML')}</dd> + <dt>Set Profile Properties</dt> + <dd>${sling:encode(fn:join(properties.profileProperties,','),'HTML')}</dd> + </dl> +</c:if> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/edit.json new file mode 100644 index 0000000..3af280d --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/createuser/edit.json @@ -0,0 +1,47 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/general/container", + "groups": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/repeating", + "label": "Group Membership", + "name": "groups" + }, + "groupsTypeHint": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/hidden", + "name": "groups@TypeHint", + "value": "String[]" + }, + "profileProperties": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/repeating", + "label": "Profile Properties", + "name": "profileProperties" + }, + "profilePropertiesTypeHint": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/hidden", + "name": "profileProperties@TypeHint", + "value": "String[]" + }, + "intermediatePath": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "label": "Intermediate Path", + "name": "intermediatePath", + "required": true + }, + "serviceUser": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "label": "Service User", + "name": "serviceUser", + "required": true + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/edit.json new file mode 100644 index 0000000..760ab09 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/edit.json @@ -0,0 +1,16 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/general/container", + "resettimeout": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "label": "Reset Timeout", + "name": "resettimeout", + "type": "number" + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/requestpasswordreset.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/requestpasswordreset.jsp new file mode 100644 index 0000000..becd97e --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/requestpasswordreset/requestpasswordreset.jsp @@ -0,0 +1,26 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Request Password Reset</h3> + <dl> + <dt>Reset Timeout</dt> + <dd>${sling:encode(properties.resettimeout,'HTML')}</dd> + </dl> +</c:if> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword.json new file mode 100644 index 0000000..0635a1b --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword.json @@ -0,0 +1,5 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Reset Password", + "componentType": "Form Action" +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/edit.json new file mode 100644 index 0000000..4f8a34b --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/edit.json @@ -0,0 +1,5 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "No need to edit" +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/resetpassword.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/resetpassword.jsp new file mode 100644 index 0000000..7c9b003 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/resetpassword/resetpassword.jsp @@ -0,0 +1,22 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Reset Password</h3> +</c:if> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource.json new file mode 100644 index 0000000..2ec03ee --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource.json @@ -0,0 +1,5 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Update Resource", + "componentType": "Form Action" +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/edit.json new file mode 100644 index 0000000..a67511b --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/edit.json @@ -0,0 +1,32 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "allowedProperties": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/repeating", + "label": "Allowed Properties", + "name": "allowedProperties" + }, + "allowedPropertiesTypeHint": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/hidden", + "name": "allowedProperties@TypeHint", + "value": "String[]" + }, + "path": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/path", + "label": "Path", + "name": "path", + "required": true + }, + "serviceUser": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "label": "Service User", + "name": "serviceUser" + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/updateresource.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/updateresource.jsp new file mode 100644 index 0000000..3389be7 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/actions/updateresource/updateresource.jsp @@ -0,0 +1,30 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Update Resource</h3> + <dl> + <dt>Allowed Properties</dt> + <dd>${sling:encode(fn:join(properties.allowedProperties,','),'HTML')}</dd> + <dt>Path</dt> + <dd>${sling:encode(properties.path,'HTML')}</dd> + <dt>Service User</dt> + <dd>${sling:encode(properties.serviceUser,'HTML')}</dd> + </dl> +</c:if> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textarea/textarea.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textarea/textarea.jsp index e046a87..e2ca9cf 100644 --- a/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textarea/textarea.jsp +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textarea/textarea.jsp @@ -39,9 +39,9 @@ <c:set var="fieldValue" value="${properties.value}" /> </c:when> </c:choose> - <textarea class="${formConfig.fieldClass}" id="${properties.name}" name="${properties.name}" ${properties.required ? 'required="required"' : ''} + <textarea class="${sling:encode(formConfig.fieldClass,'HTML')}" id="${sling:encode(properties.name,'HTML')}" name="${sling:encode(properties.name,'HTML')}" ${properties.required ? 'required="required"' : ''} <c:forEach var="attr" items="${properties.additionalAttributes}"> ${fn:split(attr,'\\=')[0]}="${fn:split(attr,'\\=')[1]}" </c:forEach> - >${fieldValue}</textarea> + >${sling:encode(fieldValue,'HTML')}</textarea> </div> diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textfield/textfield.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textfield/textfield.jsp index 09d5cfa..4adc29c 100644 --- a/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textfield/textfield.jsp +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/fields/textfield/textfield.jsp @@ -41,7 +41,7 @@ <c:set var="fieldValue" value="${properties.value}" /> </c:when> </c:choose> - <input type="${properties.type}" class="${formConfig.fieldClass}" id="${properties.name}" name="${properties.name}" value="${fieldValue}" ${not empty properties.pattern ? patternStr : ''} ${not empty properties.placeholder ? placeholderStr : ''} ${properties.required ? 'required="required"' : ''} + <input type="${sling:encode(properties.type,'HTML_ATTR')}" class="${sling:encode(formConfig.fieldClass,'HTML_ATTR')}" id="${sling:encode(properties.name,'HTML_ATTR')}" name="${sling:encode(properties.name,'HTML_ATTR')}" value="${sling:encode(fieldValue,'HTML_ATTR')}" ${not empty properties.pattern ? patternStr : ''} ${not empty properties.placeholder ? placeholderStr : ''} ${properties.required ? 'required="required"' : ''} <c:forEach var="attr" items="${properties.additionalAttributes}"> ${fn:split(attr,'\\=')[0]}="${fn:split(attr,'\\=')[1]}" </c:forEach> diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/login.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login.json new file mode 100644 index 0000000..39fbbdc --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login.json @@ -0,0 +1,5 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Login", + "componentType": "General" +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/edit.json new file mode 100644 index 0000000..1f19887 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/edit.json @@ -0,0 +1,46 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/general/container", + "errorMessage": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/richtext", + "label": "Error Message", + "name": "errorMessage", + "required": true + }, + "usernameLabel": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "defaultValue": "Submit", + "label": "Username Label", + "name": "usernameLabel", + "required": true + }, + "passwordLabel": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "defaultValue": "Submit", + "label": "Password Label", + "name": "passwordLabel", + "required": true + }, + "submitLabel": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/text", + "defaultValue": "Submit", + "label": "Submit Label", + "name": "submitLabel", + "required": true + }, + "successPage": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/path", + "label": "Success Page", + "name": "successPage" + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/login.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/login.jsp new file mode 100644 index 0000000..15d0957 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/login/login.jsp @@ -0,0 +1,42 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> +<%@include file="/libs/sling-cms/global.jsp"%> +<sling:adaptTo adaptable="${resource}" adaptTo="org.apache.sling.cms.PageManager" var="pageManager" /> +<sling:adaptTo adaptable="${resource}" adaptTo="org.apache.sling.cms.ComponentPolicyManager" var="componentPolicyMgr" /> +<c:set var="formConfig" value="${componentPolicyMgr.componentPolicy.componentConfigs['reference/components/forms/form'].valueMap}" /> +<form class="${formConfig.formClass}" action="${pageManager.page.path}.allowpost.html/j_security_check" method="post" data-analytics-id="Login Form"> + <c:if test="${not empty param.j_reason}"> + <div class="${formConfig.alertClass}"> + ${sling:encode(properties.errorMessage,'HTML')} + </div> + </c:if> + <div class="${formConfig.fieldGroupClass}"> + <label for="j_username" class="label">${sling:encode(properties.usernameLabel,'HTML')} <span class="${formConfig.fieldRequiredClass}">*</span></label> + <input type="text" class="${formConfig.fieldClass}" required="required" name="j_username" /> + </div> + <div class="${formConfig.fieldGroupClass}"> + <label for="j_password" class="label">${sling:encode(properties.passwordLabel,'HTML')} <span class="${formConfig.fieldRequiredClass}">*</span></label> + <input type="password" class="${formConfig.fieldClass}" required="required" name="j_password" /> + </div> + <input type="hidden" name="resource" value="${sling:encode(properties.successPage,'HTML_ATTR')}.html" /> + <input type="hidden" name="j_validate" value="true" /> + <div class="${formConfig.fieldGroupClass}"> + <button class="${formConfig.submitClass}">${sling:encode(properties.submitLabel,'HTML')}</button> + </div> +</form> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters.json new file mode 100644 index 0000000..802f8db --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters.json @@ -0,0 +1,6 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Request Parameters", + "componentType": "Form Value Provider", + "reloadPage": true +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/edit.json new file mode 100644 index 0000000..1fccbaf --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/edit.json @@ -0,0 +1,21 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/general/container", + "subpath": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/repeating", + "label": "Allowed Parameters", + "name": "allowedParameters" + }, + "subpathTypeHint": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/hidden", + "name": "allowedParameters@TypeHint", + "value": "String[]" + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/requestparameters.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/requestparameters.jsp new file mode 100644 index 0000000..aad2f64 --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/requestparameters/requestparameters.jsp @@ -0,0 +1,26 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Request Parameters</h3> + <dl> + <dt>Allowed Parameters</dt> + <dd>${sling:encode(fn:join(properties.allowedParameters,','),'HTML')}</dd> + </dl> +</c:if> \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource.json new file mode 100644 index 0000000..e497a9c --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource.json @@ -0,0 +1,6 @@ +{ + "jcr:primaryType" : "sling:Component", + "jcr:title": "Suffix Resource", + "componentType": "Form Value Provider", + "reloadPage": true +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/edit.json b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/edit.json new file mode 100644 index 0000000..1e67bbb --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/edit.json @@ -0,0 +1,28 @@ +{ + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/slingform", + "button": "Save", + "fields": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/general/container", + "allowedProperties": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/repeating", + "label": "Allowed Properties", + "name": "allowedProperties" + }, + "allowedPropertiesTypeHint": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/hidden", + "name": "allowedProperties@TypeHint", + "value": "String[]" + }, + "basePath": { + "jcr:primaryType": "nt:unstructured", + "sling:resourceType": "sling-cms/components/editor/fields/path", + "label": "Base Path", + "name": "basePath", + "required": true + } + } +} \ No newline at end of file diff --git a/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/suffixresource.jsp b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/suffixresource.jsp new file mode 100644 index 0000000..c702d5f --- /dev/null +++ b/reference/src/main/resources/jcr_root/apps/reference/components/forms/providers/suffixresource/suffixresource.jsp @@ -0,0 +1,28 @@ +<%-- /* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ --%> + <%@include file="/libs/sling-cms/global.jsp"%> +<c:if test="${cmsEditEnabled == 'true'}"> + <h3>Suffix Resource</h3> + <dl> + <dt>Allowed Properties</dt> + <dd>${sling:encode(fn:join(properties.allowedProperties,','),'HTML')}</dd> + <dt>Base Path</dt> + <dd>${sling:encode(properties.basePath,'HTML')}</dd> + </dl> +</c:if> \ No newline at end of file diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormHandlerTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormHandlerTest.java index 048d08d..f8b80ce 100644 --- a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormHandlerTest.java +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormHandlerTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.never; import java.io.IOException; +import java.lang.annotation.Annotation; import java.util.Arrays; import javax.servlet.ServletException; @@ -64,11 +65,21 @@ public class FormHandlerTest { formRequest = new FormRequestImpl(context.request(), null, Arrays.asList(new SelectionHandler(), new TextareaHandler(), new TextfieldHandler())); - final SendEmailAction sendEmailAction = new SendEmailAction(); mailService = Mockito.mock(MailService.class); Mockito.when(mailService.getMessageBuilder()).thenReturn(new MockMessageBuilder()); - sendEmailAction.setMailService(mailService); + final SendEmailAction sendEmailAction = new SendEmailAction(mailService, new SendEmailAction.Config() { + @Override + public Class<? extends Annotation> annotationType() { + return null; + } + + @Override + public String[] supportedTypes() { + return new String[] { SendEmailAction.DEFAULT_RESOURCE_TYPE }; + } + + }); formHandler = new FormHandler(Arrays.asList(sendEmailAction)) { private static final long serialVersionUID = 1L; diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormRequestImplTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormRequestImplTest.java index 824056d..e90933f 100644 --- a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormRequestImplTest.java +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/FormRequestImplTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import java.io.ByteArrayInputStream; import java.util.Arrays; import com.google.common.collect.ImmutableMap; @@ -49,7 +50,7 @@ public class FormRequestImplTest { .setParameterMap(ImmutableMap.<String, Object>builder().put("requiredtextarea", "Hello World!") .put("singleselect", "Hello World!").put("anotherkey", "Hello World!").put("money", "123") .put("patternfield", "123").put("double", "2.7").put("integer", "2") - .put("datefield", "2019-02-02").build()); + .put("file", new ByteArrayInputStream(new byte[0])).put("datefield", "2019-02-02").build()); formRequest = new FormRequestImpl(context.request(), null, Arrays.asList(new SelectionHandler(), new TextareaHandler(), new TextfieldHandler())); diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetActionTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetActionTest.java new file mode 100644 index 0000000..78ee756 --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/RequestPasswordResetActionTest.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.Annotation; +import java.util.Collections; + +import javax.jcr.AccessDeniedException; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.ValueFactory; +import javax.jcr.ValueFormatException; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.value.AbstractValueFactory; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.apache.sling.cms.reference.forms.impl.FormRequestImpl; +import org.apache.sling.cms.reference.forms.impl.actions.RequestPasswordResetAction.Config; +import org.apache.sling.servlethelpers.MockSlingHttpServletRequest; +import org.apache.sling.testing.resourceresolver.MockResource; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class RequestPasswordResetActionTest { + + private ResourceResolverFactory factory; + private ResourceResolver resolver; + + @Before + public void init() throws FormException, LoginException, AccessDeniedException, + UnsupportedRepositoryOperationException, RepositoryException { + + factory = Mockito.mock(ResourceResolverFactory.class); + + resolver = Mockito.mock(ResourceResolver.class); + Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap())).thenReturn(resolver); + + JackrabbitSession session = Mockito.mock(JackrabbitSession.class); + Mockito.when(resolver.adaptTo(Mockito.any())).thenReturn(session); + + ValueFactory vf = new AbstractValueFactory() { + + @Override + protected void checkPathFormat(String pathValue) throws ValueFormatException { + // TODO Auto-generated method stub + + } + + @Override + protected void checkNameFormat(String nameValue) throws ValueFormatException { + // TODO Auto-generated method stub + + } + + }; + Mockito.when(session.getValueFactory()).thenReturn(vf); + + UserManager userManager = Mockito.mock(UserManager.class); + Mockito.when(session.getUserManager()).thenReturn(userManager); + + Mockito.when(userManager.getAuthorizable(Mockito.eq("[email protected]"))).thenReturn(Mockito.mock(User.class)); + + } + + @Test + public void testHandleForm() throws FormException { + + RequestPasswordResetAction action = new RequestPasswordResetAction(factory, null); + + FormRequest request = new FormRequestImpl(new MockSlingHttpServletRequest(resolver), null, null); + request.getFormData().put("email", "[email protected]"); + + Resource actionResource = new MockResource("/content", + Collections.singletonMap(RequestPasswordResetAction.PN_RESETTIMEOUT, 2), null); + + FormActionResult result = action.handleForm(actionResource, request); + assertTrue(result.isSucceeded()); + + } + + @Test + public void testNoUser() throws FormException { + + RequestPasswordResetAction action = new RequestPasswordResetAction(factory, null); + + FormRequest request = new FormRequestImpl(new MockSlingHttpServletRequest(resolver), null, null); + request.getFormData().put("email", "[email protected]"); + + Resource actionResource = new MockResource("/content", + Collections.singletonMap(RequestPasswordResetAction.PN_RESETTIMEOUT, 2), null); + + FormActionResult result = action.handleForm(actionResource, request); + assertFalse(result.isSucceeded()); + + } + + @Test + public void testHandles() throws FormException { + Config config = new Config() { + @Override + public Class<? extends Annotation> annotationType() { + return null; + } + + @Override + public String[] supportedTypes() { + return new String[] { RequestPasswordResetAction.DEFAULT_RESOURCE_TYPE }; + } + }; + RequestPasswordResetAction action = new RequestPasswordResetAction(null, config); + Resource validResource = Mockito.mock(Resource.class); + Mockito.when(validResource.getResourceType()).thenReturn(RequestPasswordResetAction.DEFAULT_RESOURCE_TYPE); + assertTrue(action.handles(validResource)); + + Resource inValidResource = Mockito.mock(Resource.class); + Mockito.when(inValidResource.getResourceType()).thenReturn("something/else"); + assertFalse(action.handles(inValidResource)); + } + +} diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordActionTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordActionTest.java new file mode 100644 index 0000000..3f86f86 --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/ResetPasswordActionTest.java @@ -0,0 +1,175 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.Annotation; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.jcr.AccessDeniedException; +import javax.jcr.RepositoryException; +import javax.jcr.UnsupportedRepositoryOperationException; +import javax.jcr.Value; +import javax.jcr.ValueFactory; +import javax.jcr.ValueFormatException; + +import org.apache.jackrabbit.api.JackrabbitSession; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; +import org.apache.jackrabbit.value.AbstractValueFactory; +import org.apache.jackrabbit.value.DateValue; +import org.apache.jackrabbit.value.StringValue; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.cms.reference.forms.FormActionResult; +import org.apache.sling.cms.reference.forms.FormConstants; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.apache.sling.cms.reference.forms.impl.FormRequestImpl; +import org.apache.sling.cms.reference.forms.impl.actions.RequestPasswordResetAction.Config; +import org.apache.sling.servlethelpers.MockSlingHttpServletRequest; +import org.apache.sling.testing.resourceresolver.MockResource; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class ResetPasswordActionTest { + + private ResourceResolverFactory factory; + private ResourceResolver resolver; + + @Before + public void init() throws FormException, LoginException, AccessDeniedException, + UnsupportedRepositoryOperationException, RepositoryException { + + factory = Mockito.mock(ResourceResolverFactory.class); + + resolver = Mockito.mock(ResourceResolver.class); + Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap())).thenReturn(resolver); + + JackrabbitSession session = Mockito.mock(JackrabbitSession.class); + Mockito.when(resolver.adaptTo(Mockito.any())).thenReturn(session); + + ValueFactory vf = new AbstractValueFactory() { + + @Override + protected void checkPathFormat(String pathValue) throws ValueFormatException { + // TODO Auto-generated method stub + + } + + @Override + protected void checkNameFormat(String nameValue) throws ValueFormatException { + // TODO Auto-generated method stub + + } + + }; + Mockito.when(session.getValueFactory()).thenReturn(vf); + + UserManager userManager = Mockito.mock(UserManager.class); + Mockito.when(session.getUserManager()).thenReturn(userManager); + + User validUser = Mockito.mock(User.class); + Mockito.when(validUser.getProperty(FormConstants.PN_RESETTOKEN)) + .thenReturn(new Value[] { new StringValue("123") }); + Calendar cal = Calendar.getInstance(); + cal.add(Calendar.HOUR, 24); + Mockito.when(validUser.getProperty(FormConstants.PN_RESETTIMEOUT)) + .thenReturn(new Value[] { new DateValue(cal) }); + Mockito.when(userManager.getAuthorizable(Mockito.eq("[email protected]"))).thenReturn(validUser); + + User invalidUser = Mockito.mock(User.class); + Mockito.when(invalidUser.getProperty(FormConstants.PN_RESETTOKEN)) + .thenReturn(new Value[] { new StringValue("456") }); + Mockito.when(userManager.getAuthorizable(Mockito.eq("[email protected]"))).thenReturn(invalidUser); + + User expiredUser = Mockito.mock(User.class); + Mockito.when(expiredUser.getProperty(FormConstants.PN_RESETTOKEN)) + .thenReturn(new Value[] { new StringValue("456") }); + cal = Calendar.getInstance(); + cal.add(Calendar.HOUR, -24); + Mockito.when(expiredUser.getProperty(FormConstants.PN_RESETTIMEOUT)) + .thenReturn(new Value[] { new DateValue(cal) }); + Mockito.when(userManager.getAuthorizable(Mockito.eq("[email protected]"))).thenReturn(expiredUser); + + } + + @Test + public void testHandleForm() throws FormException { + + RequestPasswordResetAction action = new RequestPasswordResetAction(factory, null); + + FormRequest request = new FormRequestImpl(new MockSlingHttpServletRequest(resolver), null, null); + request.getFormData().put("email", "[email protected]"); + request.getFormData().put(FormConstants.PN_RESETTOKEN, "123"); + request.getFormData().put(FormConstants.PN_PASSWORD, "password1"); + + Resource actionResource = new MockResource("/content", Collections.emptyMap(), null); + + FormActionResult result = action.handleForm(actionResource, request); + assertTrue(result.isSucceeded()); + + } + + @Test + public void testNoUser() throws FormException { + + ResetPasswordAction action = new ResetPasswordAction(factory, null); + + FormRequest request = new FormRequestImpl(new MockSlingHttpServletRequest(resolver), null, null); + request.getFormData().put("email", "[email protected]"); + + Resource actionResource = new MockResource("/content", + Collections.singletonMap(RequestPasswordResetAction.PN_RESETTIMEOUT, 2), null); + + FormActionResult result = action.handleForm(actionResource, request); + assertFalse(result.isSucceeded()); + + } + + @Test + public void testHandles() throws FormException { + Config config = new Config() { + @Override + public Class<? extends Annotation> annotationType() { + return null; + } + + @Override + public String[] supportedTypes() { + return new String[] { RequestPasswordResetAction.DEFAULT_RESOURCE_TYPE }; + } + }; + RequestPasswordResetAction action = new RequestPasswordResetAction(null, config); + Resource validResource = Mockito.mock(Resource.class); + Mockito.when(validResource.getResourceType()).thenReturn(RequestPasswordResetAction.DEFAULT_RESOURCE_TYPE); + assertTrue(action.handles(validResource)); + + Resource inValidResource = Mockito.mock(Resource.class); + Mockito.when(inValidResource.getResourceType()).thenReturn("something/else"); + assertFalse(action.handles(inValidResource)); + } + +} diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailActionTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailActionTest.java index eb4aaf7..815dd5e 100644 --- a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailActionTest.java +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/SendEmailActionTest.java @@ -19,6 +19,8 @@ package org.apache.sling.cms.reference.forms.impl.actions; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.lang.annotation.Annotation; + import org.apache.sling.api.resource.ResourceResolver; import org.apache.sling.cms.reference.forms.FormActionResult; import org.apache.sling.cms.reference.forms.FormException; @@ -47,12 +49,20 @@ public class SendEmailActionTest { context.request().setResource(context.resourceResolver().getResource("/form/jcr:content/container/form")); resolver = context.resourceResolver(); - action = new SendEmailAction(); - mailService = Mockito.mock(MailService.class); Mockito.when(mailService.getMessageBuilder()).thenReturn(new MockMessageBuilder()); - - action.setMailService(mailService); + action = new SendEmailAction(mailService, new SendEmailAction.Config() { + + @Override + public Class<? extends Annotation> annotationType() { + return null; + } + + @Override + public String[] supportedTypes() { + return new String[] { SendEmailAction.DEFAULT_RESOURCE_TYPE }; + } + }); } @Test diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceActionTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceActionTest.java new file mode 100644 index 0000000..49315cb --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/actions/UpdateResourceActionTest.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.actions; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.HashMap; + +import org.apache.jackrabbit.JcrConstants; +import org.apache.sling.api.resource.LoginException; +import org.apache.sling.api.resource.ModifiableValueMap; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.api.resource.ResourceResolverFactory; +import org.apache.sling.api.resource.ValueMap; +import org.apache.sling.api.wrappers.ModifiableValueMapDecorator; +import org.apache.sling.api.wrappers.ValueMapDecorator; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.FormRequest; +import org.junit.Test; +import org.mockito.Mockito; + +public class UpdateResourceActionTest { + + @Test + public void testHandles() { + + UpdateResourceAction ur = new UpdateResourceAction(null); + + Resource validResource = Mockito.mock(Resource.class); + Mockito.when(validResource.getResourceType()).thenReturn("reference/components/forms/actions/updateresource"); + assertTrue(ur.handles(validResource)); + + Resource invalidResource = Mockito.mock(Resource.class); + Mockito.when(invalidResource.getResourceType()) + .thenReturn("reference/components/forms/actions/someotheraction"); + assertFalse(ur.handles(invalidResource)); + } + + @Test + public void testActionResolver() throws FormException { + + UpdateResourceAction ur = new UpdateResourceAction(null); + + ModifiableValueMap contentData = new ModifiableValueMapDecorator(new HashMap<String, Object>()); + contentData.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED); + Resource contentResource = Mockito.mock(Resource.class); + Mockito.when(contentResource.getValueMap()).thenReturn(contentData); + Mockito.when(contentResource.adaptTo(Mockito.any())).thenReturn(contentData); + + ResourceResolver resolver = Mockito.mock(ResourceResolver.class); + Mockito.when(resolver.getResource(Mockito.eq("/content/test"))).thenReturn(contentResource); + + ValueMap actionData = new ModifiableValueMapDecorator(new HashMap<String, Object>()); + actionData.put("allowedProperties", new String[] { "name1", "name3", "name4" }); + actionData.put("path", "/content/test"); + + Resource actionResource = Mockito.mock(Resource.class); + Mockito.when(actionResource.getValueMap()).thenReturn(actionData); + Mockito.when(actionResource.getResourceResolver()).thenReturn(resolver); + + FormRequest formRequest = Mockito.mock(FormRequest.class); + ValueMap formData = new ValueMapDecorator(new HashMap<>()); + formData.put("name2", "value2"); + formData.put("name3", "value3"); + Mockito.when(formRequest.getFormData()).thenReturn(formData); + + ur.handleForm(actionResource, formRequest); + + assertEquals(2, contentResource.getValueMap().keySet().size()); + assertEquals("value3", contentResource.getValueMap().get("name3")); + assertFalse(contentResource.getValueMap().containsKey("name2")); + + } + + @Test + public void testInvalidServiceUser() throws FormException, LoginException { + + LoginException le = new LoginException(); + ResourceResolverFactory factory = Mockito.mock(ResourceResolverFactory.class); + Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap())).thenThrow(le); + + UpdateResourceAction ur = new UpdateResourceAction(factory); + + ValueMap actionData = new ModifiableValueMapDecorator(new HashMap<String, Object>()); + actionData.put("allowedProperties", new String[] { "name1", "name3", "name4" }); + actionData.put("path", "/content/test"); + actionData.put("serviceUser", "bob"); + + Resource actionResource = Mockito.mock(Resource.class); + Mockito.when(actionResource.getValueMap()).thenReturn(actionData); + + FormRequest formRequest = Mockito.mock(FormRequest.class); + ValueMap formData = new ValueMapDecorator(new HashMap<>()); + formData.put("name2", "value2"); + formData.put("name3", "value3"); + Mockito.when(formRequest.getFormData()).thenReturn(formData); + + try { + ur.handleForm(actionResource, formRequest); + fail(); + } catch (FormException fe) { + assertEquals(le, fe.getCause()); + } + } + + @Test + public void testServiceUser() throws FormException, LoginException { + + ModifiableValueMap contentData = new ModifiableValueMapDecorator(new HashMap<String, Object>()); + contentData.put(JcrConstants.JCR_PRIMARYTYPE, JcrConstants.NT_UNSTRUCTURED); + Resource contentResource = Mockito.mock(Resource.class); + Mockito.when(contentResource.getValueMap()).thenReturn(contentData); + Mockito.when(contentResource.adaptTo(Mockito.any())).thenReturn(contentData); + + ResourceResolver resolver = Mockito.mock(ResourceResolver.class); + Mockito.when(resolver.getResource(Mockito.eq("/content/test"))).thenReturn(contentResource); + + ResourceResolverFactory factory = Mockito.mock(ResourceResolverFactory.class); + Mockito.when(factory.getServiceResourceResolver(Mockito.anyMap())).thenReturn(resolver); + + UpdateResourceAction ur = new UpdateResourceAction(factory); + + ValueMap actionData = new ModifiableValueMapDecorator(new HashMap<String, Object>()); + actionData.put("allowedProperties", new String[] { "name1", "name3", "name4" }); + actionData.put("path", "/content/test"); + actionData.put("serviceUser", "bob"); + + Resource actionResource = Mockito.mock(Resource.class); + Mockito.when(actionResource.getValueMap()).thenReturn(actionData); + + FormRequest formRequest = Mockito.mock(FormRequest.class); + ValueMap formData = new ValueMapDecorator(new HashMap<>()); + formData.put("name2", "value2"); + formData.put("name3", "value3"); + Mockito.when(formRequest.getFormData()).thenReturn(formData); + + ur.handleForm(actionResource, formRequest); + + assertEquals(2, contentResource.getValueMap().keySet().size()); + assertEquals("value3", contentResource.getValueMap().get("name3")); + assertFalse(contentResource.getValueMap().containsKey("name2")); + + } +} diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/HoneypotHandlerTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/HoneypotHandlerTest.java new file mode 100644 index 0000000..7deb692 --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/HoneypotHandlerTest.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.fields; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.api.resource.ResourceResolver; +import org.apache.sling.cms.reference.forms.FormException; +import org.apache.sling.cms.reference.forms.impl.SlingContextHelper; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +public class HoneypotHandlerTest { + + @Rule + public final SlingContext context = new SlingContext(); + private HoneypotHandler handler; + private ResourceResolver resolver; + + @Before + public void init() { + SlingContextHelper.initContext(context); + + resolver = context.resourceResolver(); + handler = new HoneypotHandler(); + } + + @Test + public void testHandles() { + + assertFalse(handler.handles(resolver.getResource("/form/jcr:content/container/form/fields/fieldset/fields"))); + + assertTrue(handler + .handles(resolver.getResource("/form/jcr:content/container/form/fields/fieldset/fields/honeypot"))); + } + + @Test + public void testValid() throws FormException { + ResourceResolver resolver = context.resourceResolver(); + + context.request().setParameterMap(Collections.singletonMap("someothervalue", "something else")); + + Map<String, Object> formData = new HashMap<>(); + Resource fieldResource = resolver + .getResource("/form/jcr:content/container/form/fields/fieldset/fields/honeypot"); + + handler.handleField(context.request(), fieldResource, formData); + + assertTrue(formData.isEmpty()); + } + + @Test + public void testInValid() throws FormException { + ResourceResolver resolver = context.resourceResolver(); + + context.request().setParameterMap(Collections.singletonMap("honeypot", "a value")); + + Map<String, Object> formData = new HashMap<>(); + Resource fieldResource = resolver + .getResource("/form/jcr:content/container/form/fields/fieldset/fields/honeypot"); + + try { + + handler.handleField(context.request(), fieldResource, formData); + fail(); + } catch (FormException e) { + // expected + } + + assertTrue(formData.isEmpty()); + } + +} diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/TextfieldHandlerTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/TextfieldHandlerTest.java index 3f1afcd..855127f 100644 --- a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/TextfieldHandlerTest.java +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/fields/TextfieldHandlerTest.java @@ -18,10 +18,13 @@ package org.apache.sling.cms.reference.forms.impl.fields; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.ByteArrayInputStream; import java.util.Calendar; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -71,12 +74,21 @@ public class TextfieldHandlerTest { handler.handleField(context.request(), fieldResource, formData); fail(); } catch (FormException pe) { + // expected + } + + context.request() + .setParameterMap(ImmutableMap.<String, Object>builder().put("invaliddate", "2019-99-99").build()); + + Resource invalidType = resolver.getResource("/form/jcr:content/container/invalidtype"); + try { + handler.handleField(context.request(), invalidType, formData); + fail(); + } catch (FormException pe) { } Resource invalidDate = resolver.getResource("/form/jcr:content/container/invaliddate"); - context.request() - .setParameterMap(ImmutableMap.<String, Object>builder().put("invalidate", "2019-02-12").build()); try { handler.handleField(context.request(), invalidDate, formData); fail(); @@ -103,9 +115,30 @@ public class TextfieldHandlerTest { handler.handleField(context.request(), fieldResource, formData); fail(); } catch (FormException pe) { + // expected + } + fieldResource = resolver + .getResource("/form/jcr:content/container/form/fields/fieldset/fields/unvalidateddouble"); + try { + handler.handleField(context.request(), fieldResource, formData); + fail(); + } catch (FormException pe) { + // expected } + } + @Test + public void testFile() throws FormException { + ResourceResolver resolver = context.resourceResolver(); + + context.request().setParameterMap(Collections.singletonMap("file", new ByteArrayInputStream(new byte[0]))); + + Map<String, Object> formData = new HashMap<>(); + Resource fieldResource = resolver.getResource("/form/jcr:content/container/form/fields/fieldset/fields/file"); + handler.handleField(context.request(), fieldResource, formData); + assertNotNull(formData.get("file")); + assertEquals(ByteArrayInputStream.class, formData.get("file").getClass()); } @Test @@ -137,9 +170,17 @@ public class TextfieldHandlerTest { handler.handleField(context.request(), fieldResource, formData); fail(); } catch (FormException pe) { - + // expected } + fieldResource = resolver + .getResource("/form/jcr:content/container/form/fields/fieldset/fields/unvalidatedinteger"); + try { + handler.handleField(context.request(), fieldResource, formData); + fail(); + } catch (FormException pe) { + + } } @Test diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProviderTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProviderTest.java new file mode 100644 index 0000000..eeb771c --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/RequestParametersValueProviderTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.apache.sling.api.resource.Resource; +import org.apache.sling.servlethelpers.MockSlingHttpServletRequest; +import org.apache.sling.testing.resourceresolver.MockResource; +import org.junit.Test; +import org.mockito.Mockito; + +public class RequestParametersValueProviderTest { + + @Test + public void testAccepts() { + RequestParametersValueProvider rpp = new RequestParametersValueProvider(); + + Resource validResource = Mockito.mock(Resource.class); + Mockito.when(validResource.getResourceType()) + .thenReturn("reference/components/forms/providers/requestparameters"); + assertTrue(rpp.handles(validResource)); + + Resource invalidResource = Mockito.mock(Resource.class); + Mockito.when(invalidResource.getResourceType()) + .thenReturn("reference/components/forms/providers/someotherprovider"); + assertFalse(rpp.handles(invalidResource)); + } + + @Test + public void testRequestParameters() { + RequestParametersValueProvider rpp = new RequestParametersValueProvider(); + + MockResource mockResource = new MockResource("/apps/rpv", + Collections.singletonMap("allowedParameters", new String[] { "name1", "name3", "name4" }), null); + + MockSlingHttpServletRequest mockRequest = new MockSlingHttpServletRequest(null); + mockRequest.addRequestParameter("name1", "value1"); + mockRequest.addRequestParameter("name2", "value2"); + mockRequest.addRequestParameter("name4", "value1"); + mockRequest.addRequestParameter("name4", "value2"); + + Map<String, Object> formData = new HashMap<>(); + rpp.loadValues(mockRequest, mockResource, formData); + + assertTrue(formData.containsKey("name1")); + assertEquals("value1", formData.get("name1")); + + assertFalse(formData.containsKey("name2")); + assertFalse(formData.containsKey("name3")); + + assertTrue(formData.containsKey("name1")); + assertTrue(Arrays.equals(new String[] { "value1", "value2" }, (String[]) formData.get("name4"))); + } +} diff --git a/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProviderTest.java b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProviderTest.java new file mode 100644 index 0000000..ab61a41 --- /dev/null +++ b/reference/src/test/java/org/apache/sling/cms/reference/forms/impl/providers/SuffixResourceFormValueProviderTest.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sling.cms.reference.forms.impl.providers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.sling.api.SlingHttpServletRequest; +import org.apache.sling.api.request.RequestPathInfo; +import org.apache.sling.api.resource.Resource; +import org.apache.sling.testing.resourceresolver.MockResource; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class SuffixResourceFormValueProviderTest { + + public Resource providerResource; + + @Before + public void init() { + Map<String, Object> properties = new HashMap<>(); + properties.put("basePath", "/content/apath"); + properties.put("allowedProperties", new String[] { "name1", "name3", "name4" }); + + providerResource = new MockResource("/apps/rpv", properties, null); + + } + + @Test + public void testAccepts() { + SuffixResourceFormValueProvider sr = new SuffixResourceFormValueProvider(); + + Resource validResource = Mockito.mock(Resource.class); + Mockito.when(validResource.getResourceType()).thenReturn("reference/components/forms/providers/suffixresource"); + assertTrue(sr.handles(validResource)); + + Resource invalidResource = Mockito.mock(Resource.class); + Mockito.when(invalidResource.getResourceType()) + .thenReturn("reference/components/forms/providers/someotherprovider"); + assertFalse(sr.handles(invalidResource)); + } + + @Test + public void testNoSuffix() { + SuffixResourceFormValueProvider sr = new SuffixResourceFormValueProvider(); + + Map<String, Object> formData = new HashMap<>(); + + SlingHttpServletRequest mockRequest = Mockito.mock(SlingHttpServletRequest.class); + RequestPathInfo rpi = Mockito.mock(RequestPathInfo.class); + Mockito.when(mockRequest.getRequestPathInfo()).thenReturn(rpi); + sr.loadValues(mockRequest, providerResource, formData); + + assertEquals(0, formData.entrySet().size()); + Mockito.verify(rpi).getSuffixResource(); + + } + + @Test + public void testValidSuffix() { + SuffixResourceFormValueProvider sr = new SuffixResourceFormValueProvider(); + + Map<String, Object> formData = new HashMap<>(); + + Map<String, Object> properties = new HashMap<>(); + properties.put("name1", "value1"); + properties.put("name2", "value2"); + + Resource suffixResource = new MockResource("/content/apath/apage", properties, null); + + SlingHttpServletRequest mockRequest = Mockito.mock(SlingHttpServletRequest.class); + RequestPathInfo rpi = Mockito.mock(RequestPathInfo.class); + Mockito.when(rpi.getSuffixResource()).thenReturn(suffixResource); + Mockito.when(mockRequest.getRequestPathInfo()).thenReturn(rpi); + sr.loadValues(mockRequest, providerResource, formData); + + assertEquals(2, formData.entrySet().size()); + assertEquals("value1", formData.get("name1")); + assertEquals("/content/apath/apage", formData.get("suffixResource")); + Mockito.verify(rpi).getSuffixResource(); + + } + + @Test + public void testAllAllowed() { + SuffixResourceFormValueProvider sr = new SuffixResourceFormValueProvider(); + + Map<String, Object> formData = new HashMap<>(); + + Map<String, Object> properties = new HashMap<>(); + properties.put("name1", "value1"); + properties.put("name2", "value2"); + + Map<String, Object> providerProperties = new HashMap<>(); + providerProperties.put("basePath", "/content/apath"); + + Resource pr2 = new MockResource("/apps/rpv", providerProperties, null); + + Resource suffixResource = new MockResource("/content/apath/apage", properties, null); + + SlingHttpServletRequest mockRequest = Mockito.mock(SlingHttpServletRequest.class); + RequestPathInfo rpi = Mockito.mock(RequestPathInfo.class); + Mockito.when(rpi.getSuffixResource()).thenReturn(suffixResource); + Mockito.when(mockRequest.getRequestPathInfo()).thenReturn(rpi); + sr.loadValues(mockRequest, pr2, formData); + + assertEquals(3, formData.entrySet().size()); + assertEquals("value1", formData.get("name1")); + assertEquals("value2", formData.get("name2")); + assertEquals("/content/apath/apage", formData.get("suffixResource")); + Mockito.verify(rpi).getSuffixResource(); + + } + + @Test + public void testInvalidSuffix() { + SuffixResourceFormValueProvider sr = new SuffixResourceFormValueProvider(); + + Map<String, Object> formData = new HashMap<>(); + + Map<String, Object> properties = new HashMap<>(); + properties.put("name1", "value1"); + properties.put("name2", "value2"); + + Resource suffixResource = new MockResource("/content/anotherpath", properties, null); + + SlingHttpServletRequest mockRequest = Mockito.mock(SlingHttpServletRequest.class); + RequestPathInfo rpi = Mockito.mock(RequestPathInfo.class); + Mockito.when(rpi.getSuffixResource()).thenReturn(suffixResource); + Mockito.when(mockRequest.getRequestPathInfo()).thenReturn(rpi); + sr.loadValues(mockRequest, providerResource, formData); + + assertEquals(0, formData.entrySet().size()); + Mockito.verify(rpi).getSuffixResource(); + + } +} diff --git a/reference/src/test/resources/form.json b/reference/src/test/resources/form.json index dbe3595..1d558c8 100644 --- a/reference/src/test/resources/form.json +++ b/reference/src/test/resources/form.json @@ -8,11 +8,18 @@ "published": true, "container": { "jcr:primaryType": "nt:unstructured", + "invalidtype": { + "jcr:primaryType": "nt:unstructured", + "required": true, + "name": "invalidtype", + "label": "Daty", + "saveAs": "date", + "sling:resourceType": "reference/components/forms/fields/textfield" + }, "invaliddate": { "jcr:primaryType": "nt:unstructured", "required": true, - "name": "datefield", - "type": "text", + "name": "invaliddate", "label": "Daty", "saveAs": "date", "sling:resourceType": "reference/components/forms/fields/textfield" @@ -101,6 +108,23 @@ "saveAs": "double", "sling:resourceType": "reference/components/forms/fields/textfield" }, + "unvalidateddouble": { + "jcr:primaryType": "nt:unstructured", + "required": true, + "name": "double", + "type": "text", + "label": "Daty", + "saveAs": "double", + "sling:resourceType": "reference/components/forms/fields/textfield" + }, + "file": { + "jcr:primaryType": "nt:unstructured", + "required": true, + "name": "file", + "type": "file", + "label": "File", + "sling:resourceType": "reference/components/forms/fields/textfield" + }, "integer": { "jcr:primaryType": "nt:unstructured", "required": true, @@ -110,6 +134,15 @@ "saveAs": "integer", "sling:resourceType": "reference/components/forms/fields/textfield" }, + "unvalidatedinteger": { + "jcr:primaryType": "nt:unstructured", + "required": true, + "name": "integer", + "type": "text", + "label": "Inty", + "saveAs": "integer", + "sling:resourceType": "reference/components/forms/fields/textfield" + }, "datefield": { "jcr:primaryType": "nt:unstructured", "required": true, @@ -130,6 +163,11 @@ "multiple": false, "sling:resourceType": "reference/components/forms/fields/selection", "display": "select" + }, + "honeypot": { + "jcr:primaryType": "nt:unstructured", + "name": "honeypot", + "sling:resourceType": "reference/components/forms/fields/honeypot" } } }
