2 * Copyright (c) 2010-2021 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.mielecloud.internal.config.servlet;
15 import java.io.IOException;
16 import java.util.Locale;
17 import java.util.Optional;
19 import java.util.stream.Collectors;
21 import javax.servlet.http.HttpServletRequest;
22 import javax.servlet.http.HttpServletResponse;
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;
33 * Servlet showing the success page.
35 * @author Björn Lange - Initial Contribution
38 public class SuccessServlet extends AbstractShowPageServlet {
39 private static final long serialVersionUID = 7013060161686096950L;
41 public static final String BRIDGE_UID_PARAMETER_NAME = "bridgeUid";
42 public static final String EMAIL_PARAMETER_NAME = "email";
44 public static final String BRIDGE_CREATION_FAILED_PARAMETER_NAME = "bridgeCreationFailed";
45 public static final String BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME = "bridgeReconfigurationFailed";
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 -->";
52 private static final String LOCALE_OPTIONS_PLACEHOLDER = "<!-- LOCALE OPTIONS -->";
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");
57 private final Logger logger = LoggerFactory.getLogger(SuccessServlet.class);
59 private final LanguageProvider languageProvider;
60 private final ThingsTemplateGenerator templateGenerator;
63 * Creates a new {@link SuccessServlet}.
65 * @param resourceLoader Loader for resources.
66 * @param languageProvider Provider for the language to use as default selection.
68 public SuccessServlet(ResourceLoader resourceLoader, LanguageProvider languageProvider) {
69 super(resourceLoader);
70 this.languageProvider = languageProvider;
71 this.templateGenerator = new ThingsTemplateGenerator();
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.");
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.");
91 ThingUID bridgeUid = null;
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.");
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.");
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);
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>");
123 return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, "");
127 private String renderBridgeUid(String skeleton, ThingUID bridgeUid) {
128 return skeleton.replace(BRIDGE_UID_PLACEHOLDER, bridgeUid.getAsString());
131 private String renderEmail(String skeleton, String email) {
132 return skeleton.replace(EMAIL_PLACEHOLDER, email);
135 private String renderLocaleSelection(String skeleton) {
136 String preSelectedLanguage = languageProvider.getLanguage().filter(SUPPORTED_LANGUAGES::contains)
137 .orElse(DEFAULT_LANGUAGE);
139 return skeleton.replace(LOCALE_OPTIONS_PLACEHOLDER,
140 SUPPORTED_LANGUAGES.stream().map(Language::fromCode).filter(Optional::isPresent).map(Optional::get)
142 .map(language -> createOptionTag(language, preSelectedLanguage.equals(language.getCode())))
143 .collect(Collectors.joining("\n")));
146 private String createOptionTag(Language language, boolean selected) {
147 String firstPart = " <option value=\"" + language.getCode() + "\"";
148 String secondPart = ">" + language.format() + "</option>";
150 return firstPart + " selected=\"selected\"" + secondPart;
152 return firstPart + secondPart;
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);
163 * A language representation for user display.
165 * @author Björn Lange - Initial contribution
167 private static final class Language implements Comparable<Language> {
168 private final String code;
169 private final String name;
171 private Language(String code, String name) {
177 * Gets the 2-letter language code for accessing the Miele Cloud service.
179 public String getCode() {
184 * Formats the language for displaying.
186 public String format() {
187 return name + " - " + code;
191 public int compareTo(Language other) {
192 return name.toUpperCase().compareTo(other.name.toUpperCase());
196 * Constructs a {@link Language} from a 2-letter language code.
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.
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();
208 return Optional.of(new Language(code, name));