You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ol...@apache.org on 2016/04/19 22:14:30 UTC

svn commit: r1739998 - in /sling/trunk/samples/fling: ./ src/main/java/org/apache/sling/samples/fling/form/ src/main/java/org/apache/sling/samples/fling/internal/ src/main/java/org/apache/sling/samples/fling/page/ src/main/java/org/apache/sling/samples...

Author: olli
Date: Tue Apr 19 20:14:30 2016
New Revision: 1739998

URL: http://svn.apache.org/viewvc?rev=1739998&view=rev
Log:
SLING-4464 Use Sling Validation in Fling sample

Added:
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/BaseForm.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/CommentForm.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/Form.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/FormFactory.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/internal/FormServlet.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/FormPage.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/CommentValidator.java
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/html.html
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/success.html.html
    sling/trunk/samples/fling/src/main/resources/apps/fling/validation/
    sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/
    sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/comment.json
    sling/trunk/samples/fling/src/main/resources/etc/
    sling/trunk/samples/fling/src/main/resources/etc/i18n/
    sling/trunk/samples/fling/src/main/resources/etc/i18n/org.apache.sling.samples.fling.json
    sling/trunk/samples/fling/src/main/resources/etc/messaging/
    sling/trunk/samples/fling/src/main/resources/etc/messaging/form/
    sling/trunk/samples/fling/src/main/resources/etc/messaging/form/comment.txt
Removed:
    sling/trunk/samples/fling/src/main/resources/i18n/
Modified:
    sling/trunk/samples/fling/pom.xml
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/MessagesPage.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/Page.java
    sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/UserPage.java
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/messages/html.html
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/simple/html.html
    sling/trunk/samples/fling/src/main/resources/apps/fling/page/user/html.html
    sling/trunk/samples/fling/src/main/resources/content/fling.json

Modified: sling/trunk/samples/fling/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/pom.xml?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/pom.xml (original)
+++ sling/trunk/samples/fling/pom.xml Tue Apr 19 20:14:30 2016
@@ -56,6 +56,12 @@
       <version>2.0</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+      <version>3.1.0</version>
+      <scope>provided</scope>
+    </dependency>
     <!-- OSGi -->
     <dependency>
       <groupId>org.osgi</groupId>
@@ -119,6 +125,12 @@
       <version>2.3.2</version>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.sling</groupId>
+      <artifactId>org.apache.sling.validation.api</artifactId>
+      <version>1.0.0-SNAPSHOT</version>
+      <scope>provided</scope>
+    </dependency>
     <!-- Apache Jackrabbit -->
     <dependency>
       <groupId>org.apache.jackrabbit</groupId>
@@ -126,6 +138,13 @@
       <version>2.12.1</version>
       <scope>provided</scope>
     </dependency>
+    <!-- Thymeleaf -->
+    <dependency>
+      <groupId>org.thymeleaf</groupId>
+      <artifactId>thymeleaf</artifactId>
+      <version>3.0.0.BETA02</version>
+      <scope>provided</scope>
+    </dependency>
     <!-- SubEtha SMTP -->
     <dependency>
       <groupId>org.subethamail</groupId>
@@ -169,14 +188,30 @@
             <Sling-Initial-Content>
               apps/fling;path:=/apps/fling;overwrite:=true;uninstall:=true,
               content;path:=/content;overwrite:=true;uninstall:=true,
-              i18n;path:=/etc/i18n/org.apache.sling.samples.fling;overwrite:=true;uninstall:=true
+              etc;path:=/etc;overwrite:=true;uninstall:=true
             </Sling-Initial-Content>
             <Sling-Model-Packages>
-              org.apache.sling.samples.fling
+              org.apache.sling.samples.fling.page
             </Sling-Model-Packages>
+            <_dsannotations>*</_dsannotations>
+            <_metatypeannotations>*</_metatypeannotations>
+            <_removeheaders>
+              Embed-Dependency,
+              Private-Package,
+              Include-Resource
+            </_removeheaders>
           </instructions>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.rat</groupId>
+        <artifactId>apache-rat-plugin</artifactId>
+        <configuration>
+          <excludes combine.children="append">
+            <exclude>**/etc/messaging/form/comment.txt</exclude>
+          </excludes>
+        </configuration>
+      </plugin>
     </plugins>
   </build>
 

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/BaseForm.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/BaseForm.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/BaseForm.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/BaseForm.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,153 @@
+/*
+ * 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.samples.fling.form;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+
+import org.apache.sling.validation.ValidationFailure;
+import org.apache.sling.validation.ValidationResult;
+
+public class BaseForm implements Form {
+
+    private ValidationResult validationResult;
+
+    protected final Map<String, Object> fields = new HashMap<>();
+
+    public static final String RESOURCE_TYPE = "fling/form";
+
+    public BaseForm() {
+    }
+
+    @Override
+    public String getResourceType() {
+        return RESOURCE_TYPE;
+    }
+
+    @Override
+    public void setValidationResult(final ValidationResult validationResult) {
+        this.validationResult = validationResult;
+    }
+
+    @Override
+    public ValidationResult getValidationResult() {
+        return validationResult;
+    }
+
+    @Override
+    public boolean hasFailure(final String name) {
+        if (validationResult == null) {
+            return false;
+        }
+        if (validationResult.isValid()) {
+            return false;
+        }
+        for (final ValidationFailure failure : validationResult.getFailures()) {
+            if (name.equals(failure.getLocation())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public List<ValidationFailure> getFailures(final String name) {
+        if (validationResult == null) {
+            return Collections.emptyList();
+        }
+        if (validationResult.isValid()) {
+            return Collections.emptyList();
+        }
+        return validationResult.getFailures()
+            .stream()
+            .filter(failure -> name.equals(failure.getLocation()))
+            .collect(Collectors.toList());
+    }
+
+    @Override
+    public int size() {
+        return fields.size();
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return fields.isEmpty();
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+        return fields.containsKey(key);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+        return fields.containsValue(value);
+    }
+
+    @Override
+    public Object get(Object key) {
+        return fields.get(key);
+    }
+
+    @Override
+    public Object put(String key, Object value) {
+        return fields.put(key, value);
+    }
+
+    @Override
+    public Object remove(Object key) {
+        return fields.remove(key);
+    }
+
+    @Override
+    public void putAll(@Nonnull Map<? extends String, ?> m) {
+        fields.putAll(m);
+    }
+
+    @Override
+    public void clear() {
+        fields.clear();
+    }
+
+    @Override
+    @Nonnull
+    public Set<String> keySet() {
+        return fields.keySet();
+    }
+
+    @Override
+    @Nonnull
+    public Collection<Object> values() {
+        return fields.values();
+    }
+
+    @Override
+    @Nonnull
+    public Set<Entry<String, Object>> entrySet() {
+        return fields.entrySet();
+    }
+
+}

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/CommentForm.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/CommentForm.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/CommentForm.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/CommentForm.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,46 @@
+/*
+ * 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.samples.fling.form;
+
+import org.apache.sling.api.resource.ValueMap;
+
+public class CommentForm extends BaseForm {
+
+    public static final String RESOURCE_TYPE = "fling/form/comment";
+
+    public CommentForm(final ValueMap parameters) {
+        populate(parameters);
+    }
+
+    private void populate(final ValueMap parameters) {
+        put("name", parameters.get("name", String.class));
+        put("comment", parameters.get("comment", String.class));
+    }
+
+    @Override
+    public String getResourceType() {
+        return RESOURCE_TYPE;
+    }
+
+    @Override
+    public String toString() {
+        return fields.toString();
+    }
+
+}

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/Form.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/Form.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/Form.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/Form.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,39 @@
+/*
+ * 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.samples.fling.form;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.validation.ValidationFailure;
+import org.apache.sling.validation.ValidationResult;
+
+public interface Form extends Map<String, Object> {
+
+    String getResourceType();
+
+    void setValidationResult(final ValidationResult validationResult);
+
+    ValidationResult getValidationResult();
+
+    boolean hasFailure(final String name);
+
+    List<ValidationFailure> getFailures(final String name);
+
+}

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/FormFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/FormFactory.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/FormFactory.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/form/FormFactory.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,35 @@
+/*
+ * 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.samples.fling.form;
+
+import org.apache.sling.api.resource.ValueMap;
+
+public class FormFactory {
+
+    public static Form build(final String formType, final ValueMap parameters) {
+        final String resourceType = String.format("fling/form/%s", formType);
+        switch (resourceType) {
+            case CommentForm.RESOURCE_TYPE:
+                return new CommentForm(parameters);
+            default:
+                return null;
+        }
+    }
+
+}

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/internal/FormServlet.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/internal/FormServlet.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/internal/FormServlet.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/internal/FormServlet.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,185 @@
+/*
+ * 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.samples.fling.internal;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+import javax.annotation.Nonnull;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.servlets.HttpConstants;
+import org.apache.sling.api.servlets.SlingAllMethodsServlet;
+import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
+import org.apache.sling.commons.messaging.MessageService;
+import org.apache.sling.commons.messaging.Result;
+import org.apache.sling.samples.fling.form.Form;
+import org.apache.sling.samples.fling.form.FormFactory;
+import org.apache.sling.validation.ValidationResult;
+import org.apache.sling.validation.ValidationService;
+import org.apache.sling.validation.model.ValidationModel;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.thymeleaf.ITemplateEngine;
+import org.thymeleaf.context.Context;
+import org.thymeleaf.context.IContext;
+
+@Component(
+    service = Servlet.class,
+    property = {
+        Constants.SERVICE_DESCRIPTION + "=Apache Sling Fling Sample “Form Servlet”",
+        Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+        "sling.servlet.resourceTypes=fling/page/form",
+        "sling.servlet.methods=POST"
+    }
+)
+public class FormServlet extends SlingAllMethodsServlet {
+
+    @Reference(
+        policy = ReferencePolicy.DYNAMIC,
+        policyOption = ReferencePolicyOption.GREEDY
+    )
+    private volatile ValidationService validationService;
+
+    @Reference(
+        policy = ReferencePolicy.DYNAMIC,
+        policyOption = ReferencePolicyOption.GREEDY
+    )
+    private volatile ITemplateEngine templateEngine;
+
+    @Reference(
+        policy = ReferencePolicy.DYNAMIC,
+        policyOption = ReferencePolicyOption.GREEDY
+    )
+    private volatile MessageService messageService;
+
+    private final String recipient = "recipient@example.net"; // TODO
+
+    // see FormPage.form
+    private static final String FORM_REQUEST_ATTRIBUTE_NAME = "form";
+
+    private static final String SUCCESS_SELECTOR = "success";
+
+    private final Logger logger = LoggerFactory.getLogger(FormServlet.class);
+
+    public FormServlet() {
+    }
+
+    @Override
+    protected void doPost(@Nonnull SlingHttpServletRequest request, @Nonnull SlingHttpServletResponse response) throws ServletException, IOException {
+        final ValueMap parameters = request.adaptTo(ValueMap.class);
+        logger.debug("parameters: {}", parameters);
+
+        final String formType = parameters.get("formType", String.class);
+        logger.debug("form type is '{}'", formType);
+
+        final Form form = FormFactory.build(formType, parameters);
+        if (form == null) {
+            fail(null, 400, request, response);
+            return;
+        }
+
+        final String resourcePath = request.getRequestPathInfo().getResourcePath();
+        final ValidationModel validationModel = validationService.getValidationModel(form.getResourceType(), resourcePath, false);
+        if (validationModel == null) {
+            logger.error("no validation model found");
+            fail(form, 500, request, response);
+            return;
+        }
+
+        final ValidationResult validationResult = validationService.validate(parameters, validationModel);
+        form.setValidationResult(validationResult);
+
+        if (!validationResult.isValid()) {
+            logger.debug("validation result not valid");
+            fail(form, 400, request, response);
+            return;
+        }
+
+        // render form with message template
+        final String template = "/etc/messaging/form/comment.txt"; // TODO
+        final Map<String, Object> variables = new HashMap<>();
+        variables.put(SlingBindings.RESOLVER, request.getResourceResolver()); // TODO service resource resolver?
+        variables.put("form", form);
+        final IContext context = new Context(Locale.ENGLISH, variables);
+        logger.debug("rendering message template '{}' with variables: {}", template, variables);
+        final String message = templateEngine.process(template, context);
+        logger.debug("message: '{}'", message);
+
+        try {
+            final CompletableFuture<Result> future = messageService.send(message, recipient);
+            future.get(1, TimeUnit.SECONDS);
+            logger.debug("comment [{}] form sent to {}", message, recipient);
+        } catch (Exception e) {
+            logger.error("sending message failed: {}", e.getMessage(), e);
+            fail(form, 500, request, response);
+            return;
+        }
+
+        succeed(form, request, response);
+    }
+
+    private void fail(final Form form, final int status, final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException {
+        response.setStatus(status);
+        forward(form, request, response, null);
+    }
+
+    private void succeed(final Form form, final SlingHttpServletRequest request, final SlingHttpServletResponse response) throws ServletException, IOException {
+        final RequestDispatcherOptions options = new RequestDispatcherOptions();
+        options.setAddSelectors(SUCCESS_SELECTOR);
+        forward(form, request, response, options);
+    }
+
+    private void forward(final Form form, final SlingHttpServletRequest request, final SlingHttpServletResponse response, final RequestDispatcherOptions options) throws ServletException, IOException {
+        final FormValidationHttpServletRequestWrapper requestWrapper = new FormValidationHttpServletRequestWrapper(request, form);
+        final RequestDispatcher dispatcher = request.getRequestDispatcher(request.getResource(), options);
+        dispatcher.forward(requestWrapper, response);
+    }
+
+    private class FormValidationHttpServletRequestWrapper extends SlingHttpServletRequestWrapper {
+
+        FormValidationHttpServletRequestWrapper(final SlingHttpServletRequest request, final Form form) {
+            super(request);
+            setAttribute(FORM_REQUEST_ATTRIBUTE_NAME, form);
+        }
+
+        @Override
+        public String getMethod() {
+            return HttpConstants.METHOD_GET;
+        }
+
+    }
+
+}

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/FormPage.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/FormPage.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/FormPage.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/FormPage.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,47 @@
+/*
+ * 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.samples.fling.page;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.models.annotations.Model;
+import org.apache.sling.models.annotations.injectorspecific.RequestAttribute;
+import org.apache.sling.samples.fling.form.BaseForm;
+import org.apache.sling.samples.fling.form.Form;
+
+import static org.apache.sling.models.annotations.injectorspecific.InjectionStrategy.OPTIONAL;
+
+@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
+public class FormPage extends Page {
+
+    @RequestAttribute(injectionStrategy = OPTIONAL)
+    private Form form = new BaseForm();
+
+    public FormPage() {
+    }
+
+    public Form getForm() {
+        return form;
+    }
+
+    public String getFormAction() {
+        return request.getRequestURI();
+    }
+
+}

Modified: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/MessagesPage.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/MessagesPage.java?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/MessagesPage.java (original)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/MessagesPage.java Tue Apr 19 20:14:30 2016
@@ -22,12 +22,13 @@ import java.util.List;
 
 import javax.mail.internet.MimeMessage;
 
+import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.models.annotations.Model;
 import org.apache.sling.models.annotations.injectorspecific.OSGiService;
 import org.apache.sling.samples.fling.SmtpService;
 
-@Model(adaptables = Resource.class)
+@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
 public class MessagesPage extends Page {
 
     @OSGiService

Modified: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/Page.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/Page.java?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/Page.java (original)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/Page.java Tue Apr 19 20:14:30 2016
@@ -18,27 +18,22 @@
  */
 package org.apache.sling.samples.fling.page;
 
-import javax.inject.Inject;
-
+import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.models.annotations.Model;
-import org.apache.sling.models.annotations.Optional;
-import org.apache.sling.models.annotations.injectorspecific.Self;
+import org.apache.sling.models.annotations.injectorspecific.SlingObject;
 
+import static org.apache.sling.models.annotations.injectorspecific.InjectionStrategy.OPTIONAL;
 import static org.apache.sling.query.SlingQuery.$;
 
-@Model(adaptables = Resource.class)
+@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
 public class Page {
 
-    @Self
+    @SlingObject
     protected Resource resource;
 
-    @Inject
-    private String title;
-
-    @Inject
-    @Optional
-    private String content;
+    @SlingObject(injectionStrategy = OPTIONAL)
+    protected SlingHttpServletRequest request;
 
     public Page() {
     }
@@ -52,11 +47,11 @@ public class Page {
     }
 
     public String getTitle() {
-        return title;
+        return resource.getValueMap().get("title", String.class);
     }
 
     public String getContent() {
-        return content;
+        return resource.getValueMap().get("content", String.class);
     }
 
     public Iterable<Page> getParents() {

Modified: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/UserPage.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/UserPage.java?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/UserPage.java (original)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/page/UserPage.java Tue Apr 19 20:14:30 2016
@@ -30,11 +30,12 @@ import javax.jcr.Value;
 
 import org.apache.jackrabbit.api.security.user.User;
 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.jcr.base.util.AccessControlUtil;
 import org.apache.sling.models.annotations.Model;
 
-@Model(adaptables = Resource.class)
+@Model(adaptables = {Resource.class, SlingHttpServletRequest.class})
 public class UserPage extends Page {
 
     private User user;

Added: sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/CommentValidator.java
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/CommentValidator.java?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/CommentValidator.java (added)
+++ sling/trunk/samples/fling/src/main/java/org/apache/sling/samples/fling/validation/CommentValidator.java Tue Apr 19 20:14:30 2016
@@ -0,0 +1,64 @@
+/*
+ * 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.samples.fling.validation;
+
+import javax.annotation.Nonnull;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.validation.SlingValidationException;
+import org.apache.sling.validation.ValidationFailure;
+import org.apache.sling.validation.ValidationResult;
+import org.apache.sling.validation.spi.DefaultValidationFailure;
+import org.apache.sling.validation.spi.DefaultValidationResult;
+import org.apache.sling.validation.spi.ValidationContext;
+import org.apache.sling.validation.spi.Validator;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.annotations.Component;
+
+@Component(
+    service = Validator.class,
+    property = {
+        Constants.SERVICE_DESCRIPTION + "=Apache Sling Fling Sample “Comment Validator”",
+        Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
+    },
+    immediate = true
+)
+public class CommentValidator implements Validator<String> {
+
+    private static final String COMMENT_PARAMETER = "comment";
+
+    private static final String I18N_MESSAGE_KEY = "fling.validator.comment.invalid";
+
+    @Override
+    @Nonnull
+    public ValidationResult validate(@Nonnull String data, @Nonnull ValidationContext validationContext, @Nonnull ValueMap arguments) throws SlingValidationException {
+        final String comment = arguments.get(COMMENT_PARAMETER, String.class);
+        if (comment == null) {
+            throw new SlingValidationException("Valid comment is missing.");
+        }
+        if (comment.equals(data)) {
+            return DefaultValidationResult.VALID;
+
+        } else {
+            final ValidationFailure failure = new DefaultValidationFailure(validationContext.getLocation(), 0, I18N_MESSAGE_KEY, comment);
+            return new DefaultValidationResult(failure);
+        }
+    }
+
+}

Added: sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/html.html
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/html.html?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/html.html (added)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/html.html Tue Apr 19 20:14:30 2016
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html data-th-with="page=${request.adaptTo(@org.apache.sling.samples.fling.page.FormPage@class)}">
+<head data-th-replace="/apps/fling/page/fragments/head.html::default"></head>
+<body>
+<div class="container">
+  <nav class="nav-main">
+    <div class="pull-left">
+      <ul class="breadcrumb" style="direction: rtl" data-th-replace="/apps/fling/page/fragments/navigation.html::breadcrumb"></ul>
+    </div>
+    <ul class="nav nav-pills" role="tablist" data-th-replace="/apps/fling/page/fragments/navigation.html::children"></ul>
+  </nav>
+  <div class="panel panel-primary">
+    <div class="panel-heading">
+      <span data-th-text="${page.title}">FORM PAGE</span>
+    </div>
+    <div class="panel-body" data-th-with="i18n=${request.getResourceBundle(request.getLocale())}">
+      <form data-th-action="${page.formAction}" data-th-with="form=${page.form}" method="post">
+        <input type="hidden" name="formType" value="comment">
+        <div class="form-group" data-th-classappend="${form.hasFailure('name')} ? 'has-error' : ''">
+          <label for="name">Your Name</label>
+          <input type="text" class="form-control" id="name" name="name" placeholder="Name" data-th-value="${form.name}">
+          <ul data-th-each="failure : ${form.getFailures('name')}">
+            <span class="help-block" data-th-text="${failure.getMessage(i18n)}"></span>
+          </ul>
+        </div>
+        <div class="form-group" data-th-classappend="${form.hasFailure('comment')} ? 'has-error' : ''">
+          <label for="comment">Your Comment</label>
+          <textarea class="form-control" rows="3" id="comment" name="comment" placeholder="Comment" data-th-text="${form.comment}"></textarea>
+          <ul data-th-each="failure : ${form.getFailures('comment')}">
+            <span class="help-block" data-th-text="${failure.getMessage(i18n)}"></span>
+          </ul>
+        </div>
+        <button type="submit" class="btn btn-default">Submit</button>
+      </form>
+    </div>
+    <div class="panel-footer" data-th-replace="/apps/fling/page/fragments/panel.html::footer"></div>
+  </div>
+</div>
+</body>
+</html>

Added: sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/success.html.html
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/success.html.html?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/success.html.html (added)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/page/form/success.html.html Tue Apr 19 20:14:30 2016
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<!--
+    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.
+-->
+<html data-th-with="page=${request.adaptTo(@org.apache.sling.samples.fling.page.FormPage@class)}">
+<head data-th-replace="/apps/fling/page/fragments/head.html::default"></head>
+<body>
+<div class="container">
+  <nav class="nav-main">
+    <div class="pull-left">
+      <ul class="breadcrumb" style="direction: rtl" data-th-replace="/apps/fling/page/fragments/navigation.html::breadcrumb"></ul>
+    </div>
+    <ul class="nav nav-pills" role="tablist" data-th-replace="/apps/fling/page/fragments/navigation.html::children"></ul>
+  </nav>
+  <div class="panel panel-primary">
+    <div class="panel-heading">
+      <span data-th-text="${page.title}">FORM PAGE</span>
+    </div>
+    <div class="panel-body" data-th-with="form=${page.form}">
+      <p>Your form submission was successful!</p>
+      <span data-th-text="${form.name}"></span><span> says: </span><span data-th-text="${form.comment}"></span>
+    </div>
+    <div class="panel-footer" data-th-replace="/apps/fling/page/fragments/panel.html::footer"></div>
+  </div>
+</div>
+</body>
+</html>

Modified: sling/trunk/samples/fling/src/main/resources/apps/fling/page/messages/html.html
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/page/messages/html.html?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/page/messages/html.html (original)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/page/messages/html.html Tue Apr 19 20:14:30 2016
@@ -17,7 +17,7 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<html data-th-with="page=${resource.adaptTo(@org.apache.sling.samples.fling.page.MessagesPage@class)}">
+<html data-th-with="page=${request.adaptTo(@org.apache.sling.samples.fling.page.MessagesPage@class)}">
 <head data-th-replace="/apps/fling/page/fragments/head.html::default"></head>
 <body>
 <div class="container">

Modified: sling/trunk/samples/fling/src/main/resources/apps/fling/page/simple/html.html
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/page/simple/html.html?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/page/simple/html.html (original)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/page/simple/html.html Tue Apr 19 20:14:30 2016
@@ -17,7 +17,7 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<html data-th-with="page=${resource.adaptTo(@org.apache.sling.samples.fling.page.Page@class)}">
+<html data-th-with="page=${request.adaptTo(@org.apache.sling.samples.fling.page.Page@class)}">
 <head data-th-replace="/apps/fling/page/fragments/head.html::default">
 </head>
 <body>

Modified: sling/trunk/samples/fling/src/main/resources/apps/fling/page/user/html.html
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/page/user/html.html?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/page/user/html.html (original)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/page/user/html.html Tue Apr 19 20:14:30 2016
@@ -17,7 +17,7 @@
     specific language governing permissions and limitations
     under the License.
 -->
-<html data-th-with="page=${resource.adaptTo(@org.apache.sling.samples.fling.page.UserPage@class)}">
+<html data-th-with="page=${request.adaptTo(@org.apache.sling.samples.fling.page.UserPage@class)}">
 <head data-th-replace="/apps/fling/page/fragments/head.html::default">
 </head>
 <body>

Added: sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/comment.json
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/comment.json?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/comment.json (added)
+++ sling/trunk/samples/fling/src/main/resources/apps/fling/validation/form/comment.json Tue Apr 19 20:14:30 2016
@@ -0,0 +1,37 @@
+{
+  "jcr:primaryType": "nt:unstructured",
+  "sling:resourceType": "sling/validation/model",
+  "applicablePaths": [
+    "/content/fling/"
+  ],
+  "validatedResourceType": "fling/form/comment",
+  "properties": {
+    "jcr:primaryType": "nt:unstructured",
+    "name": {
+      "jcr:primaryType": "nt:unstructured",
+      "propertyType": "string",
+      "validators": {
+        "jcr:primaryType": "nt:unstructured",
+        "org.apache.sling.validation.impl.validators.RegexValidator": {
+          "jcr:primaryType": "nt:unstructured",
+          "validatorArguments": ["regex=^\\p{L}+$"]
+        }
+      }
+    },
+    "comment": {
+      "jcr:primaryType": "nt:unstructured",
+      "propertyType": "string",
+      "validators": {
+        "jcr:primaryType": "nt:unstructured",
+        "org.apache.sling.samples.fling.validation.CommentValidator": {
+          "jcr:primaryType": "nt:unstructured",
+          "validatorArguments": ["comment=I love Apache Sling!"]
+        },
+        "org.apache.sling.validation.impl.validators.RegexValidator": {
+          "jcr:primaryType": "nt:unstructured",
+          "validatorArguments": ["regex=^\\D*$"]
+        }
+      }
+    }
+  }
+}

Modified: sling/trunk/samples/fling/src/main/resources/content/fling.json
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/content/fling.json?rev=1739998&r1=1739997&r2=1739998&view=diff
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/content/fling.json (original)
+++ sling/trunk/samples/fling/src/main/resources/content/fling.json Tue Apr 19 20:14:30 2016
@@ -25,6 +25,12 @@
         "title": "Sling Scripting Thymeleaf",
         "content": "<p><em>Sling Scripting Thymeleaf</em> with <em>Sling Models</em> and <code>adaptTo()</code> to render resources:</p><pre>&lt;html data-th-with=\"page=${resource.adaptTo(@org.apache.sling.samples.fling.Page@class)}\"&gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"/&gt;\n  &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"/&gt;\n  &lt;title data-th-text=\"${page.title}\"&gt;Page Title&lt;/title&gt;\n[...]</pre><p><em>Sling Scripting Thymeleaf</em> with <em>Sling i18n</em> for localized messages:</p><pre>&lt;a href=\"/system/sling/logout\" data-th-text=\"#{action.logout}\"&gt;logout&lt;/a&gt;</pre><pre>{\n    \"en\": {\n        \"jcr:mixinTypes\": [\n            \"mix:language\"\n        ],\n        \"jcr:language\": \"en\",\n        \"sling:basename\": \"org.apache.sling.samples.fling\",\n        \"action.logout\": {\n            \"jcr:primaryType\": \"sling:MessageEntry\",\n            \"sling:key\": \"action.logout\",\n            \"slin
 g:message\": \"logout\"\n        },\n        [...]\n    }\n}\n</pre>"
     },
+    "validation": {
+        "jcr:primaryType": "nt:unstructured",
+        "sling:resourceType": "fling/page/form",
+        "sling:resourceSuperType": "fling/page",
+        "title": "Sling Validation"
+    },
     "messaging": {
         "jcr:primaryType": "nt:unstructured",
         "sling:resourceType": "fling/page/messages",

Added: sling/trunk/samples/fling/src/main/resources/etc/i18n/org.apache.sling.samples.fling.json
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/etc/i18n/org.apache.sling.samples.fling.json?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/etc/i18n/org.apache.sling.samples.fling.json (added)
+++ sling/trunk/samples/fling/src/main/resources/etc/i18n/org.apache.sling.samples.fling.json Tue Apr 19 20:14:30 2016
@@ -0,0 +1,21 @@
+{
+    "en": {
+        "jcr:mixinTypes": [
+            "mix:language"
+        ],
+        "jcr:language": "en",
+        "sling:basename": "org.apache.sling.samples.fling",
+        "action.logout": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "logout"
+        },
+        "no_auth": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "no auth"
+        },
+        "fling.validator.comment.invalid": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "Invalid comment, try with: {0}"
+        }
+    }
+}

Added: sling/trunk/samples/fling/src/main/resources/etc/messaging/form/comment.txt
URL: http://svn.apache.org/viewvc/sling/trunk/samples/fling/src/main/resources/etc/messaging/form/comment.txt?rev=1739998&view=auto
==============================================================================
--- sling/trunk/samples/fling/src/main/resources/etc/messaging/form/comment.txt (added)
+++ sling/trunk/samples/fling/src/main/resources/etc/messaging/form/comment.txt Tue Apr 19 20:14:30 2016
@@ -0,0 +1,5 @@
+A visitor of Fling left a comment.
+
+[[${form.name}]] says:
+
+[[${form.comment}]]