]> git.basschouten.com Git - openhab-addons.git/blob
d240f215ee738181442b38e099a2a4e4a8936f19
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2021 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.mielecloud.internal.config.servlet;
14
15 import java.io.IOException;
16 import java.util.Locale;
17 import java.util.Optional;
18 import java.util.Set;
19 import java.util.stream.Collectors;
20
21 import javax.servlet.http.HttpServletRequest;
22 import javax.servlet.http.HttpServletResponse;
23
24 import org.eclipse.jdt.annotation.NonNullByDefault;
25 import org.openhab.binding.mielecloud.internal.config.ThingsTemplateGenerator;
26 import org.openhab.binding.mielecloud.internal.util.EmailValidator;
27 import org.openhab.binding.mielecloud.internal.webservice.language.LanguageProvider;
28 import org.openhab.core.thing.ThingUID;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 /**
33  * Servlet showing the success page.
34  *
35  * @author Björn Lange - Initial Contribution
36  */
37 @NonNullByDefault
38 public class SuccessServlet extends AbstractShowPageServlet {
39     private static final long serialVersionUID = 7013060161686096950L;
40
41     public static final String BRIDGE_UID_PARAMETER_NAME = "bridgeUid";
42     public static final String EMAIL_PARAMETER_NAME = "email";
43
44     public static final String BRIDGE_CREATION_FAILED_PARAMETER_NAME = "bridgeCreationFailed";
45     public static final String BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME = "bridgeReconfigurationFailed";
46
47     private static final String ERROR_MESSAGE_TEXT_PLACEHOLDER = "<!-- ERROR MESSAGE TEXT -->";
48     private static final String BRIDGE_UID_PLACEHOLDER = "<!-- BRIDGE UID -->";
49     private static final String EMAIL_PLACEHOLDER = "<!-- EMAIL -->";
50     private static final String THINGS_TEMPLATE_CODE_PLACEHOLDER = "<!-- THINGS TEMPLATE CODE -->";
51
52     private static final String LOCALE_OPTIONS_PLACEHOLDER = "<!-- LOCALE OPTIONS -->";
53
54     private static final String DEFAULT_LANGUAGE = "en";
55     private static final Set<String> SUPPORTED_LANGUAGES = Set.of("da", "nl", "en", "fr", "de", "it", "nb", "es");
56
57     private final Logger logger = LoggerFactory.getLogger(SuccessServlet.class);
58
59     private final LanguageProvider languageProvider;
60     private final ThingsTemplateGenerator templateGenerator;
61
62     /**
63      * Creates a new {@link SuccessServlet}.
64      *
65      * @param resourceLoader Loader for resources.
66      * @param languageProvider Provider for the language to use as default selection.
67      */
68     public SuccessServlet(ResourceLoader resourceLoader, LanguageProvider languageProvider) {
69         super(resourceLoader);
70         this.languageProvider = languageProvider;
71         this.templateGenerator = new ThingsTemplateGenerator();
72     }
73
74     @Override
75     protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response)
76             throws MieleHttpException, IOException {
77         String bridgeUidString = request.getParameter(BRIDGE_UID_PARAMETER_NAME);
78         if (bridgeUidString == null || bridgeUidString.isEmpty()) {
79             logger.warn("Success page is missing bridge UID.");
80             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
81                     "Missing bridge UID.");
82         }
83
84         String email = request.getParameter(EMAIL_PARAMETER_NAME);
85         if (email == null || email.isEmpty()) {
86             logger.warn("Success page is missing e-mail address.");
87             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
88                     "Missing e-mail address.");
89         }
90
91         ThingUID bridgeUid = null;
92         try {
93             bridgeUid = new ThingUID(bridgeUidString);
94         } catch (IllegalArgumentException e) {
95             logger.warn("Success page received malformed bridge UID '{}'.", bridgeUidString);
96             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
97                     "Malformed bridge UID.");
98         }
99
100         if (!EmailValidator.isValid(email)) {
101             logger.warn("Success page received malformed e-mail address '{}'.", email);
102             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
103                     "Malformed e-mail address.");
104         }
105
106         String skeleton = getResourceLoader().loadResourceAsString("success.html");
107         skeleton = renderErrorMessage(request, skeleton);
108         skeleton = renderBridgeUid(skeleton, bridgeUid);
109         skeleton = renderEmail(skeleton, email);
110         skeleton = renderLocaleSelection(skeleton);
111         skeleton = renderBridgeConfigurationTemplate(skeleton, bridgeUid, email);
112         return skeleton;
113     }
114
115     private String renderErrorMessage(HttpServletRequest request, String skeleton) {
116         if (ServletUtil.isParameterEnabled(request, BRIDGE_CREATION_FAILED_PARAMETER_NAME)) {
117             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
118                     "<div class=\"alert alert-danger\" role=\"alert\">Could not auto configure the bridge. Failed to approve the bridge from the inbox. Please try the configuration flow again.</div>");
119         } else if (ServletUtil.isParameterEnabled(request, BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME)) {
120             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
121                     "<div class=\"alert alert-danger\" role=\"alert\">Could not auto reconfigure the bridge. Bridge thing or thing handler is not available. Please try the configuration flow again.</div>");
122         } else {
123             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, "");
124         }
125     }
126
127     private String renderBridgeUid(String skeleton, ThingUID bridgeUid) {
128         return skeleton.replace(BRIDGE_UID_PLACEHOLDER, bridgeUid.getAsString());
129     }
130
131     private String renderEmail(String skeleton, String email) {
132         return skeleton.replace(EMAIL_PLACEHOLDER, email);
133     }
134
135     private String renderLocaleSelection(String skeleton) {
136         String preSelectedLanguage = languageProvider.getLanguage().filter(SUPPORTED_LANGUAGES::contains)
137                 .orElse(DEFAULT_LANGUAGE);
138
139         return skeleton.replace(LOCALE_OPTIONS_PLACEHOLDER,
140                 SUPPORTED_LANGUAGES.stream().map(Language::fromCode).filter(Optional::isPresent).map(Optional::get)
141                         .sorted()
142                         .map(language -> createOptionTag(language, preSelectedLanguage.equals(language.getCode())))
143                         .collect(Collectors.joining("\n")));
144     }
145
146     private String createOptionTag(Language language, boolean selected) {
147         String firstPart = "                                    <option value=\"" + language.getCode() + "\"";
148         String secondPart = ">" + language.format() + "</option>";
149         if (selected) {
150             return firstPart + " selected=\"selected\"" + secondPart;
151         } else {
152             return firstPart + secondPart;
153         }
154     }
155
156     private String renderBridgeConfigurationTemplate(String skeleton, ThingUID bridgeUid, String email) {
157         String bridgeTemplate = templateGenerator.createBridgeConfigurationTemplate(bridgeUid.getId(), email,
158                 languageProvider.getLanguage().orElse("en"));
159         return skeleton.replace(THINGS_TEMPLATE_CODE_PLACEHOLDER, bridgeTemplate);
160     }
161
162     /**
163      * A language representation for user display.
164      *
165      * @author Björn Lange - Initial contribution
166      */
167     private static final class Language implements Comparable<Language> {
168         private final String code;
169         private final String name;
170
171         private Language(String code, String name) {
172             this.code = code;
173             this.name = name;
174         }
175
176         /**
177          * Gets the 2-letter language code for accessing the Miele Cloud service.
178          */
179         public String getCode() {
180             return code;
181         }
182
183         /**
184          * Formats the language for displaying.
185          */
186         public String format() {
187             return name + " - " + code;
188         }
189
190         @Override
191         public int compareTo(Language other) {
192             return name.toUpperCase().compareTo(other.name.toUpperCase());
193         }
194
195         /**
196          * Constructs a {@link Language} from a 2-letter language code.
197          *
198          * @param code 2-letter language code.
199          * @return An {@link Optional} wrapping the {@link Language} or an empty {@link Optional} if there is no
200          *         representation for the given language code.
201          */
202         public static Optional<Language> fromCode(String code) {
203             Locale locale = new Locale(code);
204             String name = locale.getDisplayLanguage(locale);
205             if (name.isEmpty()) {
206                 return Optional.empty();
207             } else {
208                 return Optional.of(new Language(code, name));
209             }
210         }
211     }
212 }