diff --git a/keycloak-templates/themes/README.md b/keycloak-templates/themes/README.md new file mode 100644 index 0000000000000000000000000000000000000000..eeadc58a84fdf924c629b1b28ddaba37b36231b8 --- /dev/null +++ b/keycloak-templates/themes/README.md @@ -0,0 +1,24 @@ +Creating Themes +=============== + +Themes are used to configure the look and feel of login pages and the account management console. + +Custom themes packaged in a JAR file should be deployed to the `${kc.home.dir}/providers` directory. After that, run +the `build` command to install them before starting the server. + +You are also able to create your custom themes in this directory, directly. Themes within this directory do not require +the `build` command to be installed. + +When running the server in development mode using `start-dev`, themes are not cached so that you can easily work on them without a need to restart +the server when making changes. + +See the theme section in the [Server Developer Guide](https://www.keycloak.org/docs/latest/server_development/#_themes) for more details about how to create custom themes. + +Overriding the built-in templates +--------------------------------- + +While creating custom themes, especially when overriding templates, it may be useful to use the built-in templates as +a reference. These can be found within the theme directory of `../lib/lib/main/org.keycloak.keycloak-themes-24.0.5.jar`, which can be opened using any +standard ZIP archive tool. + +**Built-in themes should not be modified directly, instead a custom theme should be created.** \ No newline at end of file diff --git a/keycloak-templates/themes/ozg-by/login/footer-links.ftl b/keycloak-templates/themes/ozg-by/login/footer-links.ftl new file mode 100644 index 0000000000000000000000000000000000000000..16572fcda5000f4874b3e73c1098eeba6939c3b5 --- /dev/null +++ b/keycloak-templates/themes/ozg-by/login/footer-links.ftl @@ -0,0 +1,3 @@ +<#macro content> + <li class="ozg-login-footer__item"><a href="${properties.staticUrlBarrierefreiheit}/barrierefreiheit" target="_blank" rel="noopener noreferrer">Barrierefreiheit</a></li> +</#macro> \ No newline at end of file diff --git a/keycloak-templates/themes/ozg-by/login/theme.properties b/keycloak-templates/themes/ozg-by/login/theme.properties new file mode 100644 index 0000000000000000000000000000000000000000..8a1b5d6b0e63ea054d7089791aabcc6907e16c5b --- /dev/null +++ b/keycloak-templates/themes/ozg-by/login/theme.properties @@ -0,0 +1,2 @@ +parent=ozg +staticUrlBarrierefreiheit=https://by-static.dev.by.ozg-cloud.de/barrierefreiheit \ No newline at end of file diff --git a/keycloak-templates/themes/ozg-sh/login/footer-links.ftl b/keycloak-templates/themes/ozg-sh/login/footer-links.ftl new file mode 100644 index 0000000000000000000000000000000000000000..16572fcda5000f4874b3e73c1098eeba6939c3b5 --- /dev/null +++ b/keycloak-templates/themes/ozg-sh/login/footer-links.ftl @@ -0,0 +1,3 @@ +<#macro content> + <li class="ozg-login-footer__item"><a href="${properties.staticUrlBarrierefreiheit}/barrierefreiheit" target="_blank" rel="noopener noreferrer">Barrierefreiheit</a></li> +</#macro> \ No newline at end of file diff --git a/keycloak-templates/themes/ozg-sh/login/theme.properties b/keycloak-templates/themes/ozg-sh/login/theme.properties new file mode 100644 index 0000000000000000000000000000000000000000..ded1d12072958e9cbfe0c2538bc8faf342eaf81b --- /dev/null +++ b/keycloak-templates/themes/ozg-sh/login/theme.properties @@ -0,0 +1,2 @@ +parent=ozg +staticUrlBarrierefreiheit=https://sh-static.dev.by.ozg-cloud.de/barrierefreiheit \ No newline at end of file diff --git a/keycloak-templates/themes/ozg/login/footer-links.ftl b/keycloak-templates/themes/ozg/login/footer-links.ftl new file mode 100644 index 0000000000000000000000000000000000000000..f200a87c1549019f8dceb93ce73059e8d8a73a18 --- /dev/null +++ b/keycloak-templates/themes/ozg/login/footer-links.ftl @@ -0,0 +1,5 @@ +<#macro content> + <li class="ozg-login-footer__item"><a href="${properties.staticUrlBarrierefreiheit}/barrierefreiheit" target="_blank" rel="noopener noreferrer">Barrierefreiheit</a></li> + <!-- li class="ozg-login-footer__item" target="_blank"><a href="${properties.staticUrlDatenschutz}/datenschutz" rel="noopener noreferrer">Datenschutz</a></li> + <li class="ozg-login-footer__item" target="_blank"><a href="${properties.staticUrlImpressum}/impressum" rel="noopener noreferrer">Impressum</a></li> --> +</#macro> \ No newline at end of file diff --git a/keycloak-templates/themes/ozg/login/resources/css/styles.css b/keycloak-templates/themes/ozg/login/resources/css/styles.css new file mode 100644 index 0000000000000000000000000000000000000000..d9ef288f4596dc05a21eb09b199b59ae7c0b8014 --- /dev/null +++ b/keycloak-templates/themes/ozg/login/resources/css/styles.css @@ -0,0 +1,155 @@ +a { + color: #1565c0; + text-decoration: none; +} + +a:hover { + color: #1565c0; + text-decoration: underline; +} + +a:focus { + color: #1565c0; + outline: 2px solid #1565c0; + outline-offset: 4px; + border-radius: 6px; + text-decoration: none; +} + +.login-pf a:hover { + color: #1565c0; +} + +.login-pf body { + background-image: none; + background-color: #f9f9f9; + font-size: 16px; + font-family: Roboto, "Helvetica Neue", sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.ozg-login-h1 { + font-size: 24px; + font-weight: 500; + color: #1565c0; + padding: 32px 0 0 0; + margin-top: 0; + margin-bottom: 0; + text-align: center; +} + +.ozg-login-h2 { + font-size: 20px; + font-weight: 500; + color: #1565c0; + padding: 12px 0; + margin-top: 0; + margin-bottom: 0; + text-align: center; +} + +.ozg-login-logo { + background-image: url(../img/OZG_Cloud_logo.png); + background-repeat: no-repeat; + background-size: contain; + aspect-ratio: 1.3; + height: 110px; + margin: 0 auto; +} + +.login-pf-page .card-pf { + border-radius: 6px; +} + +.pf-c-form__label-text { + font-weight: 500; + font-size: 16px; + margin-bottom: 8px; +} + +.pf-c-form-control { + border-radius: 6px; + border-color: #4678c2; + padding: 8px 12px; +} + +.pf-c-form-control:focus { + padding: 8px 12px; + border-color: #e0e0e0; + outline: 2px solid #4678c2; + outline-offset: 2px; +} + +.pf-c-form-control[aria-invalid="true"] { + border-color: #d62424; + border-color: #e0e0e0; + border-bottom-width: 1px; +} + +.pf-c-form-control[aria-invalid="true"]:focus { + border-color: #e0e0e0; + outline: 2px solid #d62424; + outline-offset: 2px; +} + +.pf-c-button.pf-m-control { + border-color: #4678c2; + margin-left: 0; +} + +.pf-c-form__helper-text { + font-size: 14px; +} + +.required { + color: #d62424; +} + +.pf-c-button:after { + border: none; +} + +.pf-c-alert { + background-color: white; + border-top: none; + padding: 0 0 16px 0; + color: #d62424; +} + +.pf-c-alert__title { + font-size: 14px; + color: #d62424; +} + +.btn-lg { + border-radius: 6px; + font-size: 14px; + color: white; + font-weight: 500; + padding: 8px 32px; + min-width: 126px; + width: auto; +} + +.btn-lg:focus-visible { + outline: 2px solid #4678c2; + outline-offset: 2px; +} + +.ozg-login-footer { + display: flex; + flex-direction: row; + justify-content: center; + flex-wrap: wrap; + padding: 60px 0; +} + +.ozg-login-footer__item { + padding: 0 12px; + font-size: 16px; +} + +#kc-info-wrapper { + font-size: 16px; +} diff --git a/keycloak-templates/themes/ozg/login/resources/img/OZG_Cloud_Logo.png b/keycloak-templates/themes/ozg/login/resources/img/OZG_Cloud_Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..124440050006f888cd60276dbb9a6b8d779475bd Binary files /dev/null and b/keycloak-templates/themes/ozg/login/resources/img/OZG_Cloud_Logo.png differ diff --git a/keycloak-templates/themes/ozg/login/template.ftl b/keycloak-templates/themes/ozg/login/template.ftl new file mode 100644 index 0000000000000000000000000000000000000000..50129607f2fcc973cbc9a5f441c502f2300710ef --- /dev/null +++ b/keycloak-templates/themes/ozg/login/template.ftl @@ -0,0 +1,174 @@ +<#macro registrationLayout bodyClass="" displayInfo=false displayMessage=true displayRequiredFields=false> +<!DOCTYPE html> +<html class="${properties.kcHtmlClass!}"<#if realm.internationalizationEnabled> lang="${locale.currentLanguageTag}"</#if>> + +<head> + <meta charset="utf-8"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + <meta name="robots" content="noindex, nofollow"> + + <#if properties.meta?has_content> + <#list properties.meta?split(' ') as meta> + <meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}"/> + </#list> + </#if> + <title>${msg("loginTitle",(realm.displayName!''))}</title> + <link rel="icon" href="${url.resourcesPath}/img/favicon.ico" /> + <#if properties.stylesCommon?has_content> + <#list properties.stylesCommon?split(' ') as style> + <link href="${url.resourcesCommonPath}/${style}" rel="stylesheet" /> + </#list> + </#if> + <#if properties.styles?has_content> + <#list properties.styles?split(' ') as style> + <link href="${url.resourcesPath}/${style}" rel="stylesheet" /> + </#list> + </#if> + <#if properties.scripts?has_content> + <#list properties.scripts?split(' ') as script> + <script src="${url.resourcesPath}/${script}" type="text/javascript"></script> + </#list> + </#if> + <script src="${url.resourcesPath}/js/menu-button-links.js" type="module"></script> + <#if scripts??> + <#list scripts as script> + <script src="${script}" type="text/javascript"></script> + </#list> + </#if> + <#if authenticationSession??> + <script type="module"> + import { checkCookiesAndSetTimer } from "${url.resourcesPath}/js/authChecker.js"; + + checkCookiesAndSetTimer( + "${authenticationSession.authSessionId}", + "${authenticationSession.tabId}", + "${url.ssoLoginInOtherTabsUrl?no_esc}" + ); + </script> + </#if> +</head> + +<body class="${properties.kcBodyClass!}"> +<div class="${properties.kcLoginClass!}"> + <header id="kc-header" class="${properties.kcHeaderClass!}"> + <div class="ozg-login-logo" role="img" aria-label="OZG-Cloud Logo"></div> + <h1 class="ozg-login-h1">${kcSanitize(msg("loginTitleHtml",(realm.displayNameHtml!'')))?no_esc}</h1> + </header> + <main class="${properties.kcFormCardClass!}"> + <div class="${properties.kcFormHeaderClass!}"> + <#if realm.internationalizationEnabled && locale.supported?size gt 1> + <div class="${properties.kcLocaleMainClass!}" id="kc-locale"> + <div id="kc-locale-wrapper" class="${properties.kcLocaleWrapperClass!}"> + <div id="kc-locale-dropdown" class="menu-button-links ${properties.kcLocaleDropDownClass!}"> + <button tabindex="1" id="kc-current-locale-link" aria-label="${msg("languages")}" aria-haspopup="true" aria-expanded="false" aria-controls="language-switch1">${locale.current}</button> + <ul role="menu" tabindex="-1" aria-labelledby="kc-current-locale-link" aria-activedescendant="" id="language-switch1" class="${properties.kcLocaleListClass!}"> + <#assign i = 1> + <#list locale.supported as l> + <li class="${properties.kcLocaleListItemClass!}" role="none"> + <a role="menuitem" id="language-${i}" class="${properties.kcLocaleItemClass!}" href="${l.url}">${l.label}</a> + </li> + <#assign i++> + </#list> + </ul> + </div> + </div> + </div> + </#if> + <#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())> + <#if displayRequiredFields> + <div class="${properties.kcContentWrapperClass!}"> + <div class="${properties.kcLabelWrapperClass!} subtitle"> + <span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span> + </div> + <div class="col-md-10"> + <h2 class="ozg-login-h2" id="kc-page-title"><#nested "header"></h2> + </div> + </div> + <#else> + <h2 class="ozg-login-h2" id="kc-page-title"><#nested "header"></h2> + </#if> + <#else> + <#if displayRequiredFields> + <div class="${properties.kcContentWrapperClass!}"> + <div class="${properties.kcLabelWrapperClass!} subtitle"> + <span class="subtitle"><span class="required">*</span> ${msg("requiredFields")}</span> + </div> + <div class="col-md-10"> + <#nested "show-username"> + <div id="kc-username" class="${properties.kcFormGroupClass!}"> + <label id="kc-attempted-username">${auth.attemptedUsername}</label> + <a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}"> + <div class="kc-login-tooltip"> + <i class="${properties.kcResetFlowIcon!}"></i> + <span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span> + </div> + </a> + </div> + </div> + </div> + <#else> + <#nested "show-username"> + <div id="kc-username" class="${properties.kcFormGroupClass!}"> + <label id="kc-attempted-username">${auth.attemptedUsername}</label> + <a id="reset-login" href="${url.loginRestartFlowUrl}" aria-label="${msg("restartLoginTooltip")}"> + <div class="kc-login-tooltip"> + <i class="${properties.kcResetFlowIcon!}"></i> + <span class="kc-tooltip-text">${msg("restartLoginTooltip")}</span> + </div> + </a> + </div> + </#if> + </#if> + </div> + <div id="kc-content"> + <div id="kc-content-wrapper"> + <#-- App-initiated actions should not see warning messages about the need to complete the action --> + <#-- during login. --> + <#if displayMessage && message?has_content && (message.type != 'warning' || !isAppInitiatedAction??)> + <div class="alert-${message.type} ${properties.kcAlertClass!} pf-m-<#if message.type = 'error'>danger<#else>${message.type}</#if>"> + <div class="pf-c-alert__icon"> + <#if message.type = 'success'><span class="${properties.kcFeedbackSuccessIcon!}"></span></#if> + <#if message.type = 'warning'><span class="${properties.kcFeedbackWarningIcon!}"></span></#if> + <#if message.type = 'error'><span class="${properties.kcFeedbackErrorIcon!}"></span></#if> + <#if message.type = 'info'><span class="${properties.kcFeedbackInfoIcon!}"></span></#if> + </div> + <span class="${properties.kcAlertTitleClass!}">${kcSanitize(message.summary)?no_esc}</span> + </div> + </#if> + + <#nested "form"> + + <#if auth?has_content && auth.showTryAnotherWayLink()> + <form id="kc-select-try-another-way-form" action="${url.loginAction}" method="post"> + <div class="${properties.kcFormGroupClass!}"> + <input type="hidden" name="tryAnotherWay" value="on"/> + <a href="#" id="try-another-way" + onclick="document.forms['kc-select-try-another-way-form'].submit();return false;">${msg("doTryAnotherWay")}</a> + </div> + </form> + </#if> + + <#nested "socialProviders"> + + <#if displayInfo> + <div id="kc-info" class="${properties.kcSignUpClass!}"> + <div id="kc-info-wrapper" class="${properties.kcInfoAreaWrapperClass!}"> + <#nested "info"> + </div> + </div> + </#if> + </div> + </div> + </main> + <footer> + <nav> + <ul class="ozg-login-footer"> + <#import "footer-links.ftl" as footer> + <@footer.content/> + </ul> + </nav> + </footer> + </div> +</body> +</html> +</#macro> diff --git a/keycloak-templates/themes/ozg/login/theme.properties b/keycloak-templates/themes/ozg/login/theme.properties new file mode 100644 index 0000000000000000000000000000000000000000..44cd95cd68f56a254e3dceae3b2094124599f701 --- /dev/null +++ b/keycloak-templates/themes/ozg/login/theme.properties @@ -0,0 +1,3 @@ +parent=keycloak +styles=css/login.css css/styles.css +staticUrlBarrierefreiheit=https://static.dev.by.ozg-cloud.de/barrierefreiheit \ No newline at end of file