001/* 002 * (C) Copyright 2013-2015 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Nuxeo - initial API and implementation 018 */ 019package org.nuxeo.ecm.platform.ui.web.auth.service; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.HashMap; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import javax.ws.rs.core.UriBuilder; 030 031import org.apache.commons.lang.StringUtils; 032import org.nuxeo.common.Environment; 033import org.nuxeo.common.xmap.XMap; 034import org.nuxeo.common.xmap.annotation.XNode; 035import org.nuxeo.common.xmap.annotation.XNodeList; 036import org.nuxeo.common.xmap.annotation.XNodeMap; 037import org.nuxeo.common.xmap.annotation.XObject; 038import org.nuxeo.runtime.api.Framework; 039 040/** 041 * {@link XMap} object to manage configuration of the login screen (login.jsp) 042 * 043 * @author <a href="mailto:tdelprat@nuxeo.com">Tiry</a> 044 * @since 5.7 045 */ 046@XObject("loginScreenConfig") 047public class LoginScreenConfig implements Serializable { 048 049 private static final long serialVersionUID = 1L; 050 051 /** 052 * @since 8.4 053 */ 054 @XNodeMap(value = "startupPages/startupPage", key = "@id", type = HashMap.class, componentType = LoginStartupPage.class) 055 protected Map<String, LoginStartupPage> startupPages = new HashMap<>(); 056 057 @XNodeList(value = "loginProviders/loginProvider", type = ArrayList.class, componentType = LoginProviderLink.class) 058 protected List<LoginProviderLink> providers; 059 060 /** 061 * @since 7.10 062 */ 063 @XNodeList(value = "videos/video", type = ArrayList.class, componentType = LoginVideo.class, nullByDefault = true) 064 protected List<LoginVideo> videos; 065 066 /** 067 * @since 7.10 068 */ 069 @XNode("videos@muted") 070 protected Boolean muted; 071 072 /** 073 * @since 7.10 074 */ 075 @XNode("videos@loop") 076 protected Boolean loop; 077 078 /** 079 * @since 7.10 080 */ 081 protected String backgroundImage; 082 083 @XNode("removeNews") 084 protected Boolean removeNews = false; 085 086 protected String headerStyle; 087 088 protected String footerStyle; 089 090 protected String newsIframeUrl = "//www.nuxeo.com/login-page-embedded/"; 091 092 protected String newsIframeFullUrl = null; 093 094 protected String bodyBackgroundStyle; 095 096 protected String loginBoxBackgroundStyle; 097 098 @XNode("loginBoxWidth") 099 protected String loginBoxWidth; 100 101 protected String logoUrl; 102 103 @XNode("logoAlt") 104 protected String logoAlt; 105 106 @XNode("logoWidth") 107 protected String logoWidth; 108 109 @XNode("logoHeight") 110 protected String logoHeight; 111 112 /** 113 * @since 7.10 114 */ 115 @XNode("fieldAutocomplete") 116 protected Boolean fieldAutocomplete; 117 118 /** 119 * Boolean to disable background-cover CSS behavior on login page background, as it may not be compliant with all 120 * browsers (see NXP-12972/NXP-12978). 121 * 122 * @since 5.8 123 */ 124 @XNode("disableBackgroundSizeCover") 125 protected Boolean disableBackgroundSizeCover; 126 127 /** 128 * @since 7.10 129 */ 130 @XNode("loginButtonBackgroundColor") 131 protected String loginButtonBackgroundColor; 132 133 /** 134 * @since 8.4 135 */ 136 @XNode("defaultLocale") 137 protected String defaultLocale; 138 139 /** 140 * @since 8.4 141 */ 142 @XNode("supportedLocales@append") 143 boolean appendSupportedLocales; 144 145 /** 146 * @since 8.4 147 */ 148 @XNodeList(value = "supportedLocales/locale", type = ArrayList.class, componentType = String.class) 149 List<String> supportedLocales; 150 151 public LoginScreenConfig() { 152 } 153 154 public List<LoginProviderLink> getProviders() { 155 return providers; 156 } 157 158 public void setProviders(List<LoginProviderLink> providers) { 159 this.providers = providers; 160 } 161 162 public LoginProviderLink getProvider(String name) { 163 if (getProviders() == null) { 164 return null; 165 } 166 for (LoginProviderLink provider : getProviders()) { 167 if (name.equals(provider.getName())) { 168 return provider; 169 } 170 } 171 return null; 172 } 173 174 public void registerLoginProvider(String name, String iconUrl, String link, String label, String description, 175 LoginProviderLinkComputer computer) { 176 LoginProviderLink newProvider = new LoginProviderLink(); 177 newProvider.name = name; 178 newProvider.iconPath = iconUrl; 179 newProvider.link = link; 180 newProvider.label = label; 181 newProvider.description = description; 182 if (computer != null) { 183 newProvider.urlComputer = computer; 184 } 185 186 LoginProviderLink existingProvider = getProvider(name); 187 if (existingProvider != null) { 188 existingProvider.merge(newProvider); 189 } else { 190 if (providers == null) { 191 providers = new ArrayList<>(); 192 } 193 providers.add(newProvider); 194 } 195 } 196 197 /** 198 * @since 8.4 199 */ 200 public Map<String, LoginStartupPage> getStartupPages() { 201 return startupPages; 202 } 203 204 public String getHeaderStyle() { 205 return headerStyle; 206 } 207 208 public String getFooterStyle() { 209 return footerStyle; 210 } 211 212 public String getBodyBackgroundStyle() { 213 return bodyBackgroundStyle; 214 } 215 216 public String getLoginBoxBackgroundStyle() { 217 return loginBoxBackgroundStyle; 218 } 219 220 public String getLoginBoxWidth() { 221 return loginBoxWidth; 222 } 223 224 public String getLogoUrl() { 225 return logoUrl; 226 } 227 228 public String getLogoAlt() { 229 return logoAlt; 230 } 231 232 public String getLogoWidth() { 233 return logoWidth; 234 } 235 236 public String getLogoHeight() { 237 return logoHeight; 238 } 239 240 public List<LoginVideo> getVideos() { 241 return videos; 242 } 243 244 public Boolean getVideoMuted() { 245 return muted == null ? false : muted; 246 } 247 248 public Boolean getVideoLoop() { 249 return loop == null ? true : loop; 250 } 251 252 public boolean hasVideos() { 253 return videos != null && !videos.isEmpty(); 254 } 255 256 public boolean getDisplayNews() { 257 return !(removeNews || StringUtils.isBlank(newsIframeUrl)); 258 } 259 260 public Boolean getFieldAutocomplete() { 261 return fieldAutocomplete == null ? true : fieldAutocomplete; 262 } 263 264 @XNode("headerStyle") 265 public void setHeaderStyle(String headerStyle) { 266 this.headerStyle = Framework.expandVars(headerStyle); 267 } 268 269 @XNode("footerStyle") 270 public void setFooterStyle(String footerStyle) { 271 this.footerStyle = Framework.expandVars(footerStyle); 272 } 273 274 @XNode("bodyBackgroundStyle") 275 public void setBodyBackgroundStyle(String bodyBackgroundStyle) { 276 this.bodyBackgroundStyle = Framework.expandVars(bodyBackgroundStyle); 277 } 278 279 @XNode("backgroundImage") 280 public void setBackgroundImage(String backgroundImage) { 281 this.backgroundImage = Framework.expandVars(backgroundImage); 282 } 283 284 public String getBackgroundImage() { 285 return this.backgroundImage; 286 } 287 288 public String getLoginButtonBackgroundColor() { 289 return loginButtonBackgroundColor; 290 } 291 292 @XNode("loginBoxBackgroundStyle") 293 public void setLoginBoxBackgroundStyle(String loginBoxBackgroundStyle) { 294 this.loginBoxBackgroundStyle = Framework.expandVars(loginBoxBackgroundStyle); 295 } 296 297 @XNode("logoUrl") 298 public void setLogoUrl(String logoUrl) { 299 this.logoUrl = Framework.expandVars(logoUrl); 300 } 301 302 /** 303 * @since 7.10 304 */ 305 @XNode("newsIframeUrl") 306 public void setNewsIframeUrl(String newsIframeUrl) { 307 this.newsIframeUrl = newsIframeUrl; 308 newsIframeFullUrl = null; 309 } 310 311 public String getNewsIframeUrl() { 312 if (newsIframeFullUrl == null) { 313 newsIframeFullUrl = UriBuilder.fromPath(newsIframeUrl) 314 .queryParam(Environment.PRODUCT_VERSION, 315 Framework.getProperty(Environment.PRODUCT_VERSION)) 316 .queryParam(Environment.DISTRIBUTION_VERSION, 317 Framework.getProperty(Environment.DISTRIBUTION_VERSION)) 318 .queryParam(Environment.DISTRIBUTION_PACKAGE, 319 Framework.getProperty(Environment.DISTRIBUTION_PACKAGE)) 320 .build() 321 .toString(); 322 } 323 return newsIframeFullUrl; 324 } 325 326 /** 327 * @since 5.8 328 * @see #disableBackgroundSizeCover 329 */ 330 public Boolean getDisableBackgroundSizeCover() { 331 return disableBackgroundSizeCover; 332 } 333 334 /** 335 * @since 8.4 336 */ 337 public String getDefaultLocale() { 338 return defaultLocale; 339 } 340 341 /** 342 * @since 8.4 343 */ 344 public boolean isAppendSupportedLocales() { 345 return appendSupportedLocales; 346 } 347 348 /** 349 * @since 8.4 350 */ 351 public List<String> getSupportedLocales() { 352 List<String> res = new ArrayList<>(); 353 if (supportedLocales != null) { 354 res.addAll(supportedLocales); 355 } 356 if (!res.contains(getDefaultLocale())) { 357 res.add(getDefaultLocale()); 358 } 359 return res; 360 } 361 362 protected void merge(LoginScreenConfig newConfig) { 363 if (newConfig.newsIframeUrl != null) { 364 setNewsIframeUrl(newConfig.newsIframeUrl); 365 } 366 if (newConfig.headerStyle != null) { 367 headerStyle = newConfig.headerStyle; 368 } 369 if (newConfig.footerStyle != null) { 370 footerStyle = newConfig.footerStyle; 371 } 372 if (newConfig.bodyBackgroundStyle != null) { 373 bodyBackgroundStyle = newConfig.bodyBackgroundStyle; 374 } 375 if (newConfig.loginBoxBackgroundStyle != null) { 376 loginBoxBackgroundStyle = newConfig.loginBoxBackgroundStyle; 377 } 378 if (newConfig.loginBoxWidth != null) { 379 loginBoxWidth = newConfig.loginBoxWidth; 380 } 381 if (newConfig.disableBackgroundSizeCover != null) { 382 disableBackgroundSizeCover = newConfig.disableBackgroundSizeCover; 383 } 384 if (newConfig.logoAlt != null) { 385 logoAlt = newConfig.logoAlt; 386 } 387 if (newConfig.logoHeight != null) { 388 logoHeight = newConfig.logoHeight; 389 } 390 if (newConfig.logoUrl != null) { 391 logoUrl = newConfig.logoUrl; 392 } 393 if (newConfig.logoWidth != null) { 394 logoWidth = newConfig.logoWidth; 395 } 396 if (newConfig.fieldAutocomplete != null) { 397 fieldAutocomplete = newConfig.fieldAutocomplete; 398 } 399 if (newConfig.videos != null) { 400 videos = newConfig.videos; 401 } 402 if (newConfig.loop != null) { 403 loop = newConfig.loop; 404 } 405 if (newConfig.removeNews) { 406 removeNews = newConfig.removeNews; 407 } 408 if (newConfig.muted != null) { 409 muted = newConfig.muted; 410 } 411 if (newConfig.loginButtonBackgroundColor != null) { 412 loginButtonBackgroundColor = newConfig.loginButtonBackgroundColor; 413 } 414 if (newConfig.backgroundImage != null) { 415 backgroundImage = newConfig.backgroundImage; 416 } 417 418 if (providers == null) { 419 providers = newConfig.providers; 420 } else if (newConfig.providers != null && newConfig.providers.size() > 0) { 421 for (LoginProviderLink link : newConfig.providers) { 422 423 int idx = providers.indexOf(link); 424 if (idx >= 0) { 425 if (link.remove) { 426 providers.remove(idx); 427 } else { 428 providers.get(idx).merge(link); 429 } 430 } else { 431 providers.add(link); 432 } 433 } 434 } 435 436 if (startupPages == null) { 437 startupPages = newConfig.startupPages; 438 } else if (newConfig.startupPages != null && !newConfig.startupPages.isEmpty()) { 439 for (Map.Entry<String, LoginStartupPage> startupPage : newConfig.startupPages.entrySet()) { 440 if (startupPages.containsKey(startupPage.getKey())) { 441 startupPages.get(startupPage.getKey()).merge(startupPage.getValue()); 442 } else { 443 startupPages.put(startupPage.getKey(), startupPage.getValue()); 444 } 445 } 446 } 447 448 if (newConfig.defaultLocale != null) { 449 defaultLocale = newConfig.defaultLocale; 450 } 451 452 boolean append = newConfig.isAppendSupportedLocales(); 453 List<String> newLocales = newConfig.getSupportedLocales(); 454 Set<String> mergedLocales = new HashSet<String>(); 455 if (append && supportedLocales != null) { 456 mergedLocales.addAll(supportedLocales); 457 } 458 if (newLocales != null) { 459 mergedLocales.addAll(newLocales); 460 } 461 supportedLocales = new ArrayList<>(mergedLocales); 462 } 463 464 /** 465 * @since 7.10 466 */ 467 @Override 468 public LoginScreenConfig clone() { 469 LoginScreenConfig clone = new LoginScreenConfig(); 470 clone.bodyBackgroundStyle = bodyBackgroundStyle; 471 clone.disableBackgroundSizeCover = disableBackgroundSizeCover; 472 clone.fieldAutocomplete = fieldAutocomplete; 473 clone.footerStyle = footerStyle; 474 clone.headerStyle = headerStyle; 475 clone.loginBoxBackgroundStyle = loginBoxBackgroundStyle; 476 clone.loginBoxWidth = loginBoxWidth; 477 clone.loginButtonBackgroundColor = loginButtonBackgroundColor; 478 clone.logoAlt = logoAlt; 479 clone.logoHeight = logoHeight; 480 clone.logoUrl = logoUrl; 481 clone.logoWidth = logoWidth; 482 clone.loop = loop; 483 clone.muted = muted; 484 clone.newsIframeUrl = newsIframeUrl; 485 if (providers != null) { 486 clone.providers = new ArrayList<LoginProviderLink>(); 487 for (LoginProviderLink l : providers) { 488 clone.providers.add(l.clone()); 489 } 490 } 491 if (startupPages != null) { 492 clone.startupPages = new HashMap<String, LoginStartupPage>(); 493 for (Map.Entry<String, LoginStartupPage> startupPage : startupPages.entrySet()) { 494 clone.startupPages.put(startupPage.getKey(), startupPage.getValue().clone()); 495 } 496 } 497 clone.removeNews = removeNews; 498 if (videos != null) { 499 clone.videos = new ArrayList<LoginVideo>(); 500 for (LoginVideo v : videos) { 501 clone.videos.add(v.clone()); 502 } 503 } 504 clone.defaultLocale = defaultLocale; 505 clone.appendSupportedLocales = appendSupportedLocales; 506 if (supportedLocales != null) { 507 clone.supportedLocales = new ArrayList<>(supportedLocales); 508 } 509 return clone; 510 } 511 512}