]> git.basschouten.com Git - openhab-addons.git/blob
847a64d3a386bbb8b86e178e5dba54fdb996c0b7
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 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.webservice.language.LanguageProvider;
27 import org.openhab.core.thing.ThingUID;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 /**
32  * Servlet showing the success page.
33  *
34  * @author Björn Lange - Initial Contribution
35  */
36 @NonNullByDefault
37 public class SuccessServlet extends AbstractShowPageServlet {
38     private static final long serialVersionUID = 7013060161686096950L;
39
40     public static final String BRIDGE_UID_PARAMETER_NAME = "bridgeUid";
41     public static final String EMAIL_PARAMETER_NAME = "email";
42
43     public static final String BRIDGE_CREATION_FAILED_PARAMETER_NAME = "bridgeCreationFailed";
44     public static final String BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME = "bridgeReconfigurationFailed";
45
46     private static final String ERROR_MESSAGE_TEXT_PLACEHOLDER = "<!-- ERROR MESSAGE TEXT -->";
47     private static final String BRIDGE_UID_PLACEHOLDER = "<!-- BRIDGE UID -->";
48     private static final String EMAIL_PLACEHOLDER = "<!-- EMAIL -->";
49     private static final String THINGS_TEMPLATE_CODE_PLACEHOLDER = "<!-- THINGS TEMPLATE CODE -->";
50
51     private static final String LOCALE_OPTIONS_PLACEHOLDER = "<!-- LOCALE OPTIONS -->";
52
53     private static final String DEFAULT_LANGUAGE = "en";
54     private static final Set<String> SUPPORTED_LANGUAGES = Set.of("da", "nl", "en", "fr", "de", "it", "nb", "es");
55
56     private final Logger logger = LoggerFactory.getLogger(SuccessServlet.class);
57
58     private final LanguageProvider languageProvider;
59     private final ThingsTemplateGenerator templateGenerator;
60
61     /**
62      * Creates a new {@link SuccessServlet}.
63      *
64      * @param resourceLoader Loader for resources.
65      * @param languageProvider Provider for the language to use as default selection.
66      */
67     public SuccessServlet(ResourceLoader resourceLoader, LanguageProvider languageProvider) {
68         super(resourceLoader);
69         this.languageProvider = languageProvider;
70         this.templateGenerator = new ThingsTemplateGenerator();
71     }
72
73     @Override
74     protected String handleGetRequest(HttpServletRequest request, HttpServletResponse response)
75             throws MieleHttpException, IOException {
76         String bridgeUidString = request.getParameter(BRIDGE_UID_PARAMETER_NAME);
77         if (bridgeUidString == null || bridgeUidString.isEmpty()) {
78             logger.warn("Success page is missing bridge UID.");
79             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
80                     "Missing bridge UID.");
81         }
82
83         String email = request.getParameter(EMAIL_PARAMETER_NAME);
84         if (email == null || email.isEmpty()) {
85             logger.warn("Success page is missing e-mail address.");
86             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
87                     "Missing e-mail address.");
88         }
89
90         ThingUID bridgeUid = null;
91         try {
92             bridgeUid = new ThingUID(bridgeUidString);
93         } catch (IllegalArgumentException e) {
94             logger.warn("Success page received malformed bridge UID '{}'.", bridgeUidString);
95             return getResourceLoader().loadResourceAsString("failure.html").replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
96                     "Malformed bridge UID.");
97         }
98
99         String skeleton = getResourceLoader().loadResourceAsString("success.html");
100         skeleton = renderErrorMessage(request, skeleton);
101         skeleton = renderBridgeUid(skeleton, bridgeUid);
102         skeleton = renderEmail(skeleton, email);
103         skeleton = renderLocaleSelection(skeleton);
104         skeleton = renderBridgeConfigurationTemplate(skeleton, bridgeUid, email);
105         return skeleton;
106     }
107
108     private String renderErrorMessage(HttpServletRequest request, String skeleton) {
109         if (ServletUtil.isParameterEnabled(request, BRIDGE_CREATION_FAILED_PARAMETER_NAME)) {
110             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
111                     "<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>");
112         } else if (ServletUtil.isParameterEnabled(request, BRIDGE_RECONFIGURATION_FAILED_PARAMETER_NAME)) {
113             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER,
114                     "<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>");
115         } else {
116             return skeleton.replace(ERROR_MESSAGE_TEXT_PLACEHOLDER, "");
117         }
118     }
119
120     private String renderBridgeUid(String skeleton, ThingUID bridgeUid) {
121         return skeleton.replace(BRIDGE_UID_PLACEHOLDER, bridgeUid.getAsString());
122     }
123
124     private String renderEmail(String skeleton, String email) {
125         return skeleton.replace(EMAIL_PLACEHOLDER, email);
126     }
127
128     private String renderLocaleSelection(String skeleton) {
129         String preSelectedLanguage = languageProvider.getLanguage().filter(SUPPORTED_LANGUAGES::contains)
130                 .orElse(DEFAULT_LANGUAGE);
131
132         return skeleton.replace(LOCALE_OPTIONS_PLACEHOLDER,
133                 SUPPORTED_LANGUAGES.stream().map(Language::fromCode).filter(Optional::isPresent).map(Optional::get)
134                         .sorted()
135                         .map(language -> createOptionTag(language, preSelectedLanguage.equals(language.getCode())))
136                         .collect(Collectors.joining("\n")));
137     }
138
139     private String createOptionTag(Language language, boolean selected) {
140         String firstPart = "                                    <option value=\"" + language.getCode() + "\"";
141         String secondPart = ">" + language.format() + "</option>";
142         if (selected) {
143             return firstPart + " selected=\"selected\"" + secondPart;
144         } else {
145             return firstPart + secondPart;
146         }
147     }
148
149     private String renderBridgeConfigurationTemplate(String skeleton, ThingUID bridgeUid, String email) {
150         String bridgeTemplate = templateGenerator.createBridgeConfigurationTemplate(bridgeUid.getId(), email,
151                 languageProvider.getLanguage().orElse("en"));
152         return skeleton.replace(THINGS_TEMPLATE_CODE_PLACEHOLDER, bridgeTemplate);
153     }
154
155     /**
156      * A language representation for user display.
157      *
158      * @author Björn Lange - Initial contribution
159      */
160     private static final class Language implements Comparable<Language> {
161         private final String code;
162         private final String name;
163
164         private Language(String code, String name) {
165             this.code = code;
166             this.name = name;
167         }
168
169         /**
170          * Gets the 2-letter language code for accessing the Miele Cloud service.
171          */
172         public String getCode() {
173             return code;
174         }
175
176         /**
177          * Formats the language for displaying.
178          */
179         public String format() {
180             return name + " - " + code;
181         }
182
183         @Override
184         public int compareTo(Language other) {
185             return name.toUpperCase().compareTo(other.name.toUpperCase());
186         }
187
188         /**
189          * Constructs a {@link Language} from a 2-letter language code.
190          *
191          * @param code 2-letter language code.
192          * @return An {@link Optional} wrapping the {@link Language} or an empty {@link Optional} if there is no
193          *         representation for the given language code.
194          */
195         public static Optional<Language> fromCode(String code) {
196             Locale locale = new Locale(code);
197             String name = locale.getDisplayLanguage(locale);
198             if (name.isEmpty()) {
199                 return Optional.empty();
200             } else {
201                 return Optional.of(new Language(code, name));
202             }
203         }
204     }
205 }