This is an automated email from the ASF dual-hosted git repository.

dklco pushed a commit to branch insights-feature
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git


The following commit(s) were added to refs/heads/insights-feature by this push:
     new 514e6b7  Adding working insight tools for HTML Validation and Page 
speed
514e6b7 is described below

commit 514e6b753b28ac04278d594281623100da6d40db
Author: Dan Klco <[email protected]>
AuthorDate: Thu Oct 18 13:57:02 2018 -0400

    Adding working insight tools for HTML Validation and Page speed
---
 core/pom.xml                                       |   8 +
 .../providers/HTMLValdiatorInsightProvider.java    | 166 +++++++++++++++++++++
 .../impl/providers/PageSpeedInsightProvider.java   | 152 +++++++++++++++++++
 .../providers}/ReadabilityInsightProvider.java     |  20 ++-
 .../main/resources/OSGI-INF/l10n/bundle.properties |  17 ++-
 pom.xml                                            |  14 ++
 .../resources/jcr_root/libs/sling-cms/i18n.json    |  30 ++++
 7 files changed, 400 insertions(+), 7 deletions(-)

diff --git a/core/pom.xml b/core/pom.xml
index 219306d..9f2d39b 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -202,5 +202,13 @@
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.webconsole</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-math</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
diff --git 
a/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/HTMLValdiatorInsightProvider.java
 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/HTMLValdiatorInsightProvider.java
new file mode 100644
index 0000000..e849772
--- /dev/null
+++ 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/HTMLValdiatorInsightProvider.java
@@ -0,0 +1,166 @@
+/*
+ * 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.core.insights.impl.providers;
+
+import java.io.StringReader;
+import java.net.URLEncoder;
+
+import javax.json.Json;
+import javax.json.JsonArray;
+import javax.json.JsonObject;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.sling.cms.core.insights.impl.BaseInsightProvider;
+import 
org.apache.sling.cms.core.insights.impl.providers.HTMLValdiatorInsightProvider.Config;
+import org.apache.sling.cms.i18n.I18NDictionary;
+import org.apache.sling.cms.i18n.I18NProvider;
+import org.apache.sling.cms.insights.Insight;
+import org.apache.sling.cms.insights.InsightProvider;
+import org.apache.sling.cms.insights.InsightRequest;
+import org.apache.sling.cms.insights.Message;
+import org.apache.sling.cms.insights.PageInsightRequest;
+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;
+
+import com.google.common.base.Charsets;
+
+@Component(service = InsightProvider.class)
+@Designate(ocd = Config.class)
+public class HTMLValdiatorInsightProvider extends BaseInsightProvider {
+
+    public static final String I18N_KEY_HTMLVALIDATOR_DANGER = 
"slingcms.htmlvalidator.danger";
+    public static final String I18N_KEY_HTMLVALIDATOR_WARN = 
"slingcms.htmlvalidator.warn";
+    public static final String I18N_KEY_HTMLVALIDATOR_SUCCESS = 
"slingcms.htmlvalidator.success";
+
+    @ObjectClassDefinition(name = "%htmlvalidator.config.name", description = 
"%htmlvalidator.config.description", localization = "OSGI-INF/l10n/bundle")
+    public @interface Config {
+        @AttributeDefinition(name = "%htmlvalidator.param.enabled.name", 
description = "%htmlvalidator.param.enabled.description")
+        boolean enabled() default true;
+    }
+
+    @Reference
+    private I18NProvider i18nProvider;
+
+    private static final Logger log = 
LoggerFactory.getLogger(HTMLValdiatorInsightProvider.class);
+
+    private Config config;
+
+    @Activate
+    public void activate(Config config) {
+        this.config = config;
+    }
+
+    @Override
+    protected Insight doEvaluateRequest(InsightRequest request) throws 
Exception {
+        Insight insight = new Insight(this, request);
+        insight.setScored(true);
+        PageInsightRequest pageRequest = (PageInsightRequest) request;
+
+        String html = pageRequest.getPageHtml();
+
+        HttpPost httpPost = new 
HttpPost("http://validator.w3.org/nu/?out=json&showsource=no&level=all";);
+        httpPost.addHeader("Content-type", "text/html; charset=utf-8");
+        HttpEntity htmlEntity = new ByteArrayEntity(html.getBytes("UTF-8"));
+        httpPost.setEntity(htmlEntity);
+
+        I18NDictionary dictionary = 
i18nProvider.getDictionary(request.getResource().getResourceResolver());
+
+        CloseableHttpResponse response = null;
+        try (CloseableHttpClient client = HttpClients.createDefault()) {
+            response = client.execute(httpPost);
+            HttpEntity entity = response.getEntity();
+            JsonObject json = Json.createReader(new 
StringReader(EntityUtils.toString(entity))).readObject();
+            log.debug("Loaded response: {}", json.toString(), 2);
+            JsonArray messages = json.getJsonArray("messages");
+            int errors = 0;
+            int warnings = 0;
+            for (int i = 0; i < messages.size(); i++) {
+                JsonObject message = messages.getJsonObject(i);
+                if ("error".equals(message.getString("type"))) {
+                    errors++;
+                } else if ("info".equals(message.getString("type")) && 
message.containsKey("subtype")
+                        && "warning".equals(message.getString("subtype"))) {
+                    warnings++;
+                }
+            }
+            double score = 0.0;
+            if (errors > 5) {
+                insight.setPrimaryMessage(Message
+                        .danger(dictionary.get(I18N_KEY_HTMLVALIDATOR_DANGER, 
new Object[] { errors, warnings })));
+                score = 0.2;
+            } else if (errors > 0) {
+                insight.setPrimaryMessage(Message
+                        .danger(dictionary.get(I18N_KEY_HTMLVALIDATOR_DANGER, 
new Object[] { errors, warnings })));
+                score = 0.4;
+            } else if (warnings > 5) {
+                insight.setPrimaryMessage(
+                        
Message.danger(dictionary.get(I18N_KEY_HTMLVALIDATOR_WARN, new Object[] { 
warnings })));
+                score = 0.6;
+            } else if (warnings > 0) {
+                insight.setPrimaryMessage(
+                        
Message.danger(dictionary.get(I18N_KEY_HTMLVALIDATOR_WARN, new Object[] { 
warnings })));
+                score = 0.8;
+            } else {
+                
insight.setPrimaryMessage(Message.danger(dictionary.get(I18N_KEY_HTMLVALIDATOR_SUCCESS)));
+                score = 1.0;
+            }
+            insight.setScore(score);
+            
insight.addMessage(Message.defaultMsg("https://validator.w3.org/nu/?doc=";
+                    + 
URLEncoder.encode(pageRequest.getPage().getPublishedUrl(), 
Charsets.UTF_8.toString())));
+        }
+
+        return insight;
+    }
+
+    @Override
+    public String getId() {
+        return "htmlvalidator";
+    }
+
+    @Override
+    public String getTitle() {
+        return "HTML Validator";
+    }
+
+    @Override
+    public boolean isEnabled(InsightRequest request) {
+        if (!config.enabled()) {
+            log.debug("HTML Validator is not enabled");
+            return false;
+        }
+        if (request.getType() != InsightRequest.TYPE.PAGE) {
+            log.debug("Request {} is not a page", request);
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git 
a/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/PageSpeedInsightProvider.java
 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/PageSpeedInsightProvider.java
new file mode 100644
index 0000000..6914cbd
--- /dev/null
+++ 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/PageSpeedInsightProvider.java
@@ -0,0 +1,152 @@
+/*
+ * 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.core.insights.impl.providers;
+
+import java.io.StringReader;
+import java.net.URLEncoder;
+
+import javax.json.Json;
+import javax.json.JsonObject;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.apache.sling.cms.core.insights.impl.BaseInsightProvider;
+import 
org.apache.sling.cms.core.insights.impl.providers.PageSpeedInsightProvider.Config;
+import org.apache.sling.cms.i18n.I18NDictionary;
+import org.apache.sling.cms.i18n.I18NProvider;
+import org.apache.sling.cms.insights.Insight;
+import org.apache.sling.cms.insights.InsightProvider;
+import org.apache.sling.cms.insights.InsightRequest;
+import org.apache.sling.cms.insights.Message;
+import org.apache.sling.cms.insights.PageInsightRequest;
+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 = InsightProvider.class)
+@Designate(ocd = Config.class)
+public class PageSpeedInsightProvider extends BaseInsightProvider {
+
+    @ObjectClassDefinition(name = "%pagespeed.config.name", description = 
"%pagespeed.config.description", localization = "OSGI-INF/l10n/bundle")
+    public @interface Config {
+        @AttributeDefinition(name = "%pagespeed.param.enabled.name", 
description = "%pagespeed.param.enabled.description")
+        boolean enabled();
+
+        @AttributeDefinition(name = "%pagespeed.param.apikey.name", 
description = "%pagespeed.param.apikey.description")
+        String apikey();
+    }
+
+    @Reference
+    private I18NProvider i18nProvider;
+
+    public static final String I18N_KEY_READABILITY_RESULT_DANGER = 
"slingcms.pagespeed.danger";
+    public static final String I18N_KEY_READABILITY_RESULT_WARN = 
"slingcms.pagespeed.warn";
+    public static final String I18N_KEY_READABILITY_RESULT_SUCCESS = 
"slingcms.pagespeed.success";
+    private static final String REQUEST_FORMAT = 
"https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=%s&fields=%s&key=%s";;
+    private static final String PARAMETERS = 
"id%2CinvalidRules%2CresponseCode%2CruleGroups";
+    private static final String PAGESPEED_FORMAT = 
"https://developers.google.com/speed/pagespeed/insights/?url=%s";;
+    private static final Logger log = 
LoggerFactory.getLogger(PageSpeedInsightProvider.class);
+
+    private Config config;
+
+    @Activate
+    public void activate(Config config) {
+        this.config = config;
+    }
+
+    @Override
+    protected Insight doEvaluateRequest(InsightRequest request) throws 
Exception {
+        Insight insight = new Insight(this, request);
+        PageInsightRequest pageRequest = (PageInsightRequest) request;
+        String publishedUrl = pageRequest.getPage().getPublishedUrl();
+        String checkUrl = String.format(REQUEST_FORMAT, 
URLEncoder.encode(publishedUrl, "UTF-8"), PARAMETERS,
+                config.apikey());
+
+        HttpGet httpGet = new HttpGet(checkUrl);
+
+        CloseableHttpResponse response = null;
+        try (CloseableHttpClient client = HttpClients.createDefault()) {
+
+            I18NDictionary dictionary = 
i18nProvider.getDictionary(request.getResource().getResourceResolver());
+
+            log.debug("Requesting page speed via: {}", checkUrl);
+            response = client.execute(httpGet);
+            HttpEntity entity = response.getEntity();
+            JsonObject resp = Json.createReader(new 
StringReader(EntityUtils.toString(entity))).readObject();
+
+            log.debug("Retrieved response: {}", resp.toString());
+
+            insight.setScored(true);
+            double score = 
resp.getJsonObject("ruleGroups").getJsonObject("SPEED").getJsonNumber("score").doubleValue()
+                    / 100.0;
+            insight.setScore(score);
+            log.debug("Parsed pagespeed score {}", score);
+
+            if (score < 0.65) {
+                
insight.setPrimaryMessage(Message.danger(dictionary.get(I18N_KEY_READABILITY_RESULT_DANGER)));
+            } else if (score < 0.8) {
+                
insight.setPrimaryMessage(Message.warn(dictionary.get(I18N_KEY_READABILITY_RESULT_WARN)));
+            } else {
+                
insight.setPrimaryMessage(Message.success(dictionary.get(I18N_KEY_READABILITY_RESULT_SUCCESS)));
+            }
+            
insight.addMessage(Message.defaultMsg(String.format(PAGESPEED_FORMAT, 
URLEncoder.encode(publishedUrl, "UTF-8"))));
+
+            log.debug("Response parsed successfully");
+
+        }
+        return insight;
+    }
+
+    @Override
+    public String getId() {
+        return "pagespeed";
+    }
+
+    @Override
+    public String getTitle() {
+        return "Page Speed";
+    }
+
+    @Override
+    public boolean isEnabled(InsightRequest request) {
+        if (!config.enabled()) {
+            log.debug("Page Speed is not enabled");
+            return false;
+        }
+        if (request.getType() != InsightRequest.TYPE.PAGE) {
+            log.debug("Request {} is not a page", request);
+            return false;
+        }
+        if (!((PageInsightRequest) request).getPage().isPublished()) {
+            log.debug("The page for {} is not published", request);
+            return false;
+        }
+        return true;
+    }
+
+}
diff --git 
a/core/src/main/java/org/apache/sling/cms/core/readability/impl/ReadabilityInsightProvider.java
 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/ReadabilityInsightProvider.java
similarity index 90%
rename from 
core/src/main/java/org/apache/sling/cms/core/readability/impl/ReadabilityInsightProvider.java
rename to 
core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/ReadabilityInsightProvider.java
index 47d94e2..9a5d0af 100644
--- 
a/core/src/main/java/org/apache/sling/cms/core/readability/impl/ReadabilityInsightProvider.java
+++ 
b/core/src/main/java/org/apache/sling/cms/core/insights/impl/providers/ReadabilityInsightProvider.java
@@ -14,10 +14,11 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.cms.core.readability.impl;
+package org.apache.sling.cms.core.insights.impl.providers;
 
 import java.text.DecimalFormat;
 
+import org.apache.commons.math.stat.descriptive.moment.StandardDeviation;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.caconfig.resource.ConfigurationResourceResolver;
 import org.apache.sling.cms.CMSConstants;
@@ -44,7 +45,6 @@ import org.slf4j.LoggerFactory;
 public class ReadabilityInsightProvider extends BaseInsightProvider {
 
     public static final String I18N_KEY_READABILITY_DETAIL = 
"slingcms.readability.detail";
-
     public static final String I18N_KEY_READABILITY_RESULT_DANGER = 
"slingcms.readability.danger";
     public static final String I18N_KEY_READABILITY_RESULT_SUCCESS = 
"slingcms.readability.success";
     public static final String I18N_KEY_READABILITY_RESULT_WARN = 
"slingcms.readability.warn";
@@ -109,7 +109,21 @@ public class ReadabilityInsightProvider extends 
BaseInsightProvider {
             if (score > config.getMaxGradeLevel() || score < 
config.getMinGradeLevel()) {
                 log.debug("Retrieved out of bounds readability {} based on 
range {}-{}", score,
                         config.getMinGradeLevel(), config.getMaxGradeLevel());
-                insight.setScore(0.5);
+
+                StandardDeviation sd = new StandardDeviation(false);
+                double stddev = sd.evaluate(new double[] { 
config.getMinGradeLevel(), config.getMaxGradeLevel() });
+                double dev = 0.0;
+                if (score > config.getMaxGradeLevel()) {
+                    dev = score - config.getMaxGradeLevel();
+                } else {
+                    dev = config.getMinGradeLevel() - score;
+                }
+                double calcScore = 1 - (dev / stddev) * .5;
+                if (calcScore > 0) {
+                    insight.setScore(calcScore);
+                } else {
+                    insight.setScore(0.0);
+                }
                 
insight.setPrimaryMessage(Message.warn(dictionary.get(I18N_KEY_READABILITY_RESULT_WARN,
                         new Object[] { config.getMinGradeLevel(), 
config.getMaxGradeLevel(), scoreStr })));
             } else {
diff --git a/core/src/main/resources/OSGI-INF/l10n/bundle.properties 
b/core/src/main/resources/OSGI-INF/l10n/bundle.properties
index 168dc90..c1d3250 100644
--- a/core/src/main/resources/OSGI-INF/l10n/bundle.properties
+++ b/core/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -65,7 +65,7 @@ replacement.char.description=A character with which to 
replace any non-allowed \
 characters in the name 
 
 # User Generated Content
-ugc.name=User Generated Content
+ugc.name=Apache Sling CMS User Generated Content
 ugc.description=Service for creating buckets of User Generated Content
 
 ugcRoot.name=UGC Root
@@ -79,7 +79,7 @@ a bucket of bob and a path depth of 1, this would yield a 
path like: bob/1/123.
 This can be overridden by the path depth in the UGCBucketConfig.
 
 # Path Suggestion Servlet
-pathsuggestionservlet.name=Path Suggestion Servlet
+pathsuggestionservlet.name=Apache Sling CMS Path Suggestion Servlet
 pathsuggestionservlet.description=A servlet for providing suggested paths 
based \
 on a stemmed path
 
@@ -89,7 +89,7 @@ suggestion servlet. Each filter is provided in the format 
[name]=[type1],[type2]
 Each child node of the parent resource to the provided path will be checked to 
see \
 if the node is one of the provided types (or a sub-type) for the filter. 
 
-readability.config.name=Readability Service
+readability.config.name=Apache Sling CMS - Insights Readability Service
 readability.config.description=A service for calculating the readability of 
text using a number of different readability \
 algorithms
 
@@ -109,4 +109,13 @@ readability.param.complexitymin.name=Minimum Complexity
 readability.param.complexitymin.description=The minimum number of syllables 
for a word to be considered complex
 
 readability.param.iswordexp.name=Is Word Expression
-readability.param.iswordexp.description=A regular expression for detecting if 
a string is a word
\ No newline at end of file
+readability.param.iswordexp.description=A regular expression for detecting if 
a string is a word
+
+pagespeed.config.name=Apache Sling CMS - Insights Page Speed
+pagespeed.config.description=Uses Google Page Speed Insights to estimate the 
perceived speed of a particular page
+
+pagespeed.param.enabled.name=Enabled
+pagespeed.param.enabled.description=Whether or not the Page Speed Insight 
service is enabled
+
+pagespeed.param.apikey.name=API Key
+pagespeed.param.apikey.description=The API key Google Cloud, see: 
https://developers.google.com/speed/docs/insights/
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2003a1a..8b75a63 100644
--- a/pom.xml
+++ b/pom.xml
@@ -189,6 +189,20 @@
                 <version>4.2.0</version>
                 <scope>provided</scope>
             </dependency>
+            <dependency>
+                <groupId>org.apache.commons</groupId>
+                <artifactId>commons-math</artifactId>
+                <version>2.2</version>
+                <scope>provided</scope>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.httpcomponents</groupId>
+                <artifactId>httpclient</artifactId>
+                <version>4.5.5</version>
+                <scope>provided</scope>
+            </dependency>
+
+
         </dependencies>
     </dependencyManagement>
 
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json 
b/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
index 48f47f4..b0edf2a 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/i18n.json
@@ -10,6 +10,36 @@
         ],
         "jcr:language": "en",
         "sling:resourceType": "sling-cms/components/cms/blank",
+        "slingcms-htmlvalidator-danger":{
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "There were {0} validation errors and {1} 
warnings",
+            "sling:key": "slingcms.htmlvalidator.danger"
+        },
+        "slingcms-htmlvalidator-success":{
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "HTML Validation successful!",
+            "sling:key": "slingcms.htmlvalidator.success"
+        },
+        "slingcms-htmlvalidator-warn":{
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "There were {0} validation warnings",
+            "sling:key": "slingcms.htmlvalidator.warn"
+        },
+        "slingcms-pagespeed-danger": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "This website is much slower than average, page 
performance must be optimized",
+            "sling:key": "slingcms.pagespeed.danger"
+        },
+        "slingcms-pagespeed-warn": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "This website is slower than average, page 
performance should be optimized",
+            "sling:key": "slingcms.pagespeed.warn"
+        },
+        "slingcms-pagespeed-success": {
+            "jcr:primaryType": "sling:MessageEntry",
+            "sling:message": "This website has acceptable performance",
+            "sling:key": "slingcms.pagespeed.success"
+        },
         "slingcms-readability-danger": {
             "jcr:primaryType": "sling:MessageEntry",
             "sling:message": "Failed to calculate readability for {1}",

Reply via email to