├── .gitignore
├── LICENSE
├── README.md
├── SECURITY.md
├── centeredUi
├── README.md
├── ThemeCenterBrand.css
├── ThemeCenterBrandRTL.css
├── images
│ ├── empty_user.png
│ ├── screenshot.png
│ ├── screenshot_paginated.png
│ └── screenshot_paginated2.png
└── paginatedOnload.js
├── communityCustomizations
├── CustomImagesThemeGenerator
│ ├── Digitude.Adfs.CustomImagesThemeGenerator.sln
│ ├── README.md
│ ├── images
│ │ ├── 3bbc0a40203d4ce7ba0c4d97b8b7dd93.png
│ │ ├── 9544fd2a4ad07f3956b38493ab2d3453.png
│ │ ├── b7f27c9fe4d4a175d9b243f6bb694a85.png
│ │ └── c587fa46b0cf0b5afbac869eb426db97.png
│ └── src
│ │ ├── App.config
│ │ ├── Digitude.Adfs.CustomImagesThemeGenerator.csproj
│ │ ├── Main.Designer.cs
│ │ ├── Main.cs
│ │ ├── Main.resx
│ │ ├── Program.cs
│ │ ├── Properties
│ │ ├── AssemblyInfo.cs
│ │ ├── Resources.Designer.cs
│ │ ├── Resources.resx
│ │ ├── Settings.Designer.cs
│ │ └── Settings.settings
│ │ ├── Resources
│ │ ├── mockup_YCw_icon.ico
│ │ ├── onload.js
│ │ └── style.css
│ │ ├── WebThemeInfo.Designer.cs
│ │ ├── WebThemeInfo.cs
│ │ ├── WebThemeInfo.xsc
│ │ ├── WebThemeInfo.xsd
│ │ └── WebThemeInfo.xss
├── README.md
├── RenameAndReorderADCPTrust
│ ├── ONLOAD.JS
│ └── README.md
└── ShowPasswordButton
│ ├── OnLoad.js
│ ├── README.md
│ └── images
│ ├── Customization1.png
│ └── Customization2.png
├── mfaLoadingWheel
├── README.md
├── images
│ └── screenshot_wheel.png
└── loadWheel.js
└── pageDetectionTelemetry
├── InteractiveCompletionByPlatformQuery.txt
├── InteractiveCompletionQuery.txt
├── LoginReliabilityByPlatformQuery.txt
├── README.md
├── UserPromptRateByPlatformQuery.txt
├── UserPromptRateQuery.txt
└── onload.js
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # .NET Core
46 | project.lock.json
47 | project.fragment.lock.json
48 | artifacts/
49 | **/Properties/launchSettings.json
50 |
51 | *_i.c
52 | *_p.c
53 | *_i.h
54 | *.ilk
55 | *.meta
56 | *.obj
57 | *.pch
58 | *.pdb
59 | *.pgc
60 | *.pgd
61 | *.rsp
62 | *.sbr
63 | *.tlb
64 | *.tli
65 | *.tlh
66 | *.tmp
67 | *.tmp_proj
68 | *.log
69 | *.vspscc
70 | *.vssscc
71 | .builds
72 | *.pidb
73 | *.svclog
74 | *.scc
75 |
76 | # Chutzpah Test files
77 | _Chutzpah*
78 |
79 | # Visual C++ cache files
80 | ipch/
81 | *.aps
82 | *.ncb
83 | *.opendb
84 | *.opensdf
85 | *.sdf
86 | *.cachefile
87 | *.VC.db
88 | *.VC.VC.opendb
89 |
90 | # Visual Studio profiler
91 | *.psess
92 | *.vsp
93 | *.vspx
94 | *.sap
95 |
96 | # TFS 2012 Local Workspace
97 | $tf/
98 |
99 | # Guidance Automation Toolkit
100 | *.gpState
101 |
102 | # ReSharper is a .NET coding add-in
103 | _ReSharper*/
104 | *.[Rr]e[Ss]harper
105 | *.DotSettings.user
106 |
107 | # JustCode is a .NET coding add-in
108 | .JustCode
109 |
110 | # TeamCity is a build add-in
111 | _TeamCity*
112 |
113 | # DotCover is a Code Coverage Tool
114 | *.dotCover
115 |
116 | # Visual Studio code coverage results
117 | *.coverage
118 | *.coveragexml
119 |
120 | # NCrunch
121 | _NCrunch_*
122 | .*crunch*.local.xml
123 | nCrunchTemp_*
124 |
125 | # MightyMoose
126 | *.mm.*
127 | AutoTest.Net/
128 |
129 | # Web workbench (sass)
130 | .sass-cache/
131 |
132 | # Installshield output folder
133 | [Ee]xpress/
134 |
135 | # DocProject is a documentation generator add-in
136 | DocProject/buildhelp/
137 | DocProject/Help/*.HxT
138 | DocProject/Help/*.HxC
139 | DocProject/Help/*.hhc
140 | DocProject/Help/*.hhk
141 | DocProject/Help/*.hhp
142 | DocProject/Help/Html2
143 | DocProject/Help/html
144 |
145 | # Click-Once directory
146 | publish/
147 |
148 | # Publish Web Output
149 | *.[Pp]ublish.xml
150 | *.azurePubxml
151 | # TODO: Comment the next line if you want to checkin your web deploy settings
152 | # but database connection strings (with potential passwords) will be unencrypted
153 | *.pubxml
154 | *.publishproj
155 |
156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
157 | # checkin your Azure Web App publish settings, but sensitive information contained
158 | # in these scripts will be unencrypted
159 | PublishScripts/
160 |
161 | # NuGet Packages
162 | *.nupkg
163 | # The packages folder can be ignored because of Package Restore
164 | **/packages/*
165 | # except build/, which is used as an MSBuild target.
166 | !**/packages/build/
167 | # Uncomment if necessary however generally it will be regenerated when needed
168 | #!**/packages/repositories.config
169 | # NuGet v3's project.json files produces more ignorable files
170 | *.nuget.props
171 | *.nuget.targets
172 |
173 | # Microsoft Azure Build Output
174 | csx/
175 | *.build.csdef
176 |
177 | # Microsoft Azure Emulator
178 | ecf/
179 | rcf/
180 |
181 | # Windows Store app package directories and files
182 | AppPackages/
183 | BundleArtifacts/
184 | Package.StoreAssociation.xml
185 | _pkginfo.txt
186 |
187 | # Visual Studio cache files
188 | # files ending in .cache can be ignored
189 | *.[Cc]ache
190 | # but keep track of directories ending in .cache
191 | !*.[Cc]ache/
192 |
193 | # Others
194 | ClientBin/
195 | ~$*
196 | *~
197 | *.dbmdl
198 | *.dbproj.schemaview
199 | *.jfm
200 | *.pfx
201 | *.publishsettings
202 | orleans.codegen.cs
203 |
204 | # Since there are multiple workflows, uncomment next line to ignore bower_components
205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
206 | #bower_components/
207 |
208 | # RIA/Silverlight projects
209 | Generated_Code/
210 |
211 | # Backup & report files from converting an old project file
212 | # to a newer Visual Studio version. Backup files are not needed,
213 | # because we have git ;-)
214 | _UpgradeReport_Files/
215 | Backup*/
216 | UpgradeLog*.XML
217 | UpgradeLog*.htm
218 |
219 | # SQL Server files
220 | *.mdf
221 | *.ldf
222 | *.ndf
223 |
224 | # Business Intelligence projects
225 | *.rdl.data
226 | *.bim.layout
227 | *.bim_*.settings
228 |
229 | # Microsoft Fakes
230 | FakesAssemblies/
231 |
232 | # GhostDoc plugin setting file
233 | *.GhostDoc.xml
234 |
235 | # Node.js Tools for Visual Studio
236 | .ntvs_analysis.dat
237 | node_modules/
238 |
239 | # Typescript v1 declaration files
240 | typings/
241 |
242 | # Visual Studio 6 build log
243 | *.plg
244 |
245 | # Visual Studio 6 workspace options file
246 | *.opt
247 |
248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
249 | *.vbw
250 |
251 | # Visual Studio LightSwitch build output
252 | **/*.HTMLClient/GeneratedArtifacts
253 | **/*.DesktopClient/GeneratedArtifacts
254 | **/*.DesktopClient/ModelManifest.xml
255 | **/*.Server/GeneratedArtifacts
256 | **/*.Server/ModelManifest.xml
257 | _Pvt_Extensions
258 |
259 | # Paket dependency manager
260 | .paket/paket.exe
261 | paket-files/
262 |
263 | # FAKE - F# Make
264 | .fake/
265 |
266 | # JetBrains Rider
267 | .idea/
268 | *.sln.iml
269 |
270 | # CodeRush
271 | .cr/
272 |
273 | # Python Tools for Visual Studio (PTVS)
274 | __pycache__/
275 | *.pyc
276 |
277 | # Cake - Uncomment if you are using it
278 | # tools/**
279 | # !tools/packages.config
280 |
281 | # Telerik's JustMock configuration file
282 | *.jmconfig
283 |
284 | # BizTalk build output
285 | *.btp.cs
286 | *.btm.cs
287 | *.odx.cs
288 | *.xsd.cs
289 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AD FS Web Customizations
2 |
3 | ## Overview
4 |
5 | This repository contains useful web customizations for AD FS. The following customizations are currently included:
6 |
7 | 1. __[pageDetectionTelemetry](pageDetectionTelemetry)__ - JavaScript customization to detect AD FS pages and upload telemetry
8 | to your [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) datastore.
9 |
10 | 2. __[centeredUi](centeredUi)__ - CSS customization to allow your on-prem AD FS to be consistent with the look-and-feel of the
11 | [centered Azure AD Sign-in](https://cloudblogs.microsoft.com/enterprisemobility/2017/08/02/the-new-azure-ad-signin-experience-is-now-in-public-preview/)
12 |
13 | __ Action Required__
14 |
15 | If you use the paginated onload.js web customization to create a paginated experience on your AD FS server, please update to the latest version. If you deployed the onload.js on or before __May 29, 2018__, please update your deployment.
16 |
17 | 3. __[mfaLoadingWheel](mfaLoadingWheel)__ - JavaScript customization to add a loading wheel to the AD FS authentication options page.
18 |
19 | 4. __[communityCustomizations](communityCustomizations)__ - JavaScript customizations from community members.
20 |
21 | ## Usage
22 |
23 | If you use any of our open source tools or projects, please consider subscribing to our announcements newsletter. We use this list to provide updates on security bugs, new feature announcements, and other info directly relevant to our users.
24 |
25 | [Sign up here](http://eepurl.com/dwF5gP)
26 |
27 | ## Contributing
28 |
29 | This project welcomes contributions and suggestions. We encourage you to fork this project, include any web customizations
30 | you find useful, and then do a pull request to master. If your customizations work, we'll include them so everyone can benefit.
31 |
32 | Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do,
33 | grant us the rights to use your contribution. For details, visit https://cla.microsoft.com.
34 |
35 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
36 | a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
37 | provided by the bot. You will only need to do this once across all repos using our CLA.
38 |
39 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
40 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
41 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
42 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/centeredUi/README.md:
--------------------------------------------------------------------------------
1 | # Match Azure AD Centered Login Page
2 |
3 | ## Overview
4 |
5 | This project provides an Active Directory Federation Services (AD FS) style sheet to allow your AD FS login form to be consistent with the [new Azure Active Directory centered sign-in experience](https://cloudblogs.microsoft.com/enterprisemobility/2017/08/02/the-new-azure-ad-signin-experience-is-now-in-public-preview/).
6 |
7 | Note that this customization comes in two parts. The first is a style sheet, which allows the look-and-feel of your AD FS to match the Azure AD centered UI experience. The second is a more advanced customization, using the AD FS JavaScript customization feature to create a front-end paginated sign-in experience.
8 |
9 | ##  Action Required
10 |
11 | If you use the paginated onload.js web customization to create a paginated experience on your AD FS server, please update to the latest version. If you deployed the onload.js on or before __May 29, 2018__, please update your deployment.
12 |
13 | ## Getting Started
14 |
15 | We will break the deployment of this feature into two parts. First, the style sheet to create a consistent look-and-feel. Second, the JavaScript to create a front-end paginated experience. You can choose if you wish to deploy one or both.
16 |
17 | ## Getting Started - Style Sheet Deployment
18 |
19 | 1. Download the ```ThemeCenterBrand.css``` file to your AD FS server, wherever you host your style sheets.
20 |
21 | Note: It is recommended that you minify your CSS for a production environment.
22 |
23 | 2. Create a custom web theme using the following command in PowerShell:
24 |
25 | ```New-AdfsWebTheme –Name custom -SourceName default –StyleSheet @{path="c:\style\ThemeCenterBrand.css"}```
26 |
27 | 3. Apply the new custom web theme using the following command in PowerShell:
28 |
29 | ```Set-AdfsWebConfig -ActiveThemeName custom```
30 |
31 | 4. Update the logo and background image. For details and image size recommendations, see [this post](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/azure-ux-web-theme-in-ad-fs).
32 |
33 | ## Getting Started - JavaScript Deployment
34 |
35 | 1. Download the ```paginatedOnload.js``` file to your AD FS server, wherever you host your JavaScript.
36 |
37 | Note: It is *__highly__* recommended that you minify your ```paginatedOnload.js``` before including it in a production environment. There are many popular tools online for minifying JavaScript code. Two popular choices are [minifier.org](http://www.minifier.org/) and [JSCompress](https://jscompress.com/).
38 |
39 | 2. Modify your existing custom web theme from the style sheet deployment to include the new JavaScript. In PowerShell:
40 |
41 | ```Set-AdfsWebTheme –TargetName custom -AdditionalFileResource @{Uri="/adfs/portal/script/onload.js"; path="c:\paginatedOnload.js"}```
42 |
43 | 3. Apply the modified custom web theme using the following command in PowerShell:
44 |
45 | ```Set-AdfsWebConfig -ActiveThemeName custom```
46 |
47 | 4. For more information on JavaScript customization, see [Advanced AD FS Customization](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/advanced-customization-of-ad-fs-sign-in-pages).
48 |
49 | ## Additional JavaScript Changes
50 |
51 | The JavaScript we provide out-of-the-box does not provide two key features you may want.
52 |
53 | 1. The JavaScript works for deployments in most major languages. However, if you wish to have your pages work under other languages, you will need to follow the steps below in ```Supporting Non-English Languages```.
54 |
55 | ## Supporting Non-English Languages
56 |
57 | In order to support non-English languages, you will need to add translated text for the new UI items that are created by the JavaScript.
58 |
59 | In the code, you should locate the translation table in the function ```GetLocalizedStringForElement```. You should add translations for the text in the translation table.
60 |
61 | Each translation should be mapped to the correct language code. For a reference on language codes, see the ```ISO 639-1 Code``` column in the table at [this resource](https://www.loc.gov/standards/iso639-2/php/code_list.php).
62 |
63 | ## Example
64 |
65 | 
66 |
67 | ## Contributing (Special Note)
68 |
69 | If you find any problems with the CSS, JavaScript, or docs, please fork and send us your fix. If you don't have a fix, please open an issue, and describe what you are seeing (feel free to include screenshots).
70 |
71 | For the full Contributing details, please see __[the root README](../README.md)__.
72 |
--------------------------------------------------------------------------------
/centeredUi/ThemeCenterBrand.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | }
5 |
6 | html, body {
7 | height: 100%;
8 | width: 100%;
9 | background-color: #ffffff;
10 | color: #000000;
11 | font-weight: normal;
12 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
13 | -ms-overflow-style: -ms-autohiding-scrollbar;
14 | }
15 |
16 | body {
17 | font-size: 0.9em;
18 | }
19 |
20 | #noScript {
21 | margin: 16px;
22 | color: Black;
23 | }
24 |
25 | :lang(en-GB) {
26 | quotes: '\2018' '\2019' '\201C' '\201D';
27 | }
28 |
29 | :lang(zh) {
30 | font-family: 微软雅黑;
31 | }
32 |
33 | @-ms-viewport {
34 | width: device-width;
35 | }
36 |
37 | @-moz-viewport {
38 | width: device-width;
39 | }
40 |
41 | @-o-viewport {
42 | width: device-width;
43 | }
44 |
45 | @-webkit-viewport {
46 | width: device-width;
47 | }
48 |
49 | @viewport {
50 | width: device-width;
51 | }
52 |
53 | /* Theme layout styles */
54 |
55 | #fullPage {
56 | position: absolute;
57 | bottom: 28px;
58 | top: 0px;
59 | width: 100%;
60 | }
61 |
62 | #brandingWrapper {
63 | background-color: #4488dd;
64 | height: 100%;
65 | width: 100%;
66 | }
67 |
68 | #branding {
69 | /* A background image will be added to the #branding element at run-time once the illustration image is configured in the theme.
70 | Recommended image dimensions: 1420x1200 pixels, JPG or PNG, 200 kB average, 500 kB maximum. */
71 | background-color: inherit;
72 | background-repeat: no-repeat;
73 | background-size: cover;
74 | height: 100%;
75 | width: 100%;
76 | -webkit-background-size: cover;
77 | -moz-background-size: cover;
78 | -o-background-size: cover;
79 | }
80 |
81 | #brandingTint {
82 | /* This will define a tint to be overlaid on top of the illustration background image. */
83 | background-color: rgba(0,0,0,0.4);
84 | height: 100%;
85 | width: 100%;
86 | -webkit-background-size: cover;
87 | -moz-background-size: cover;
88 | -o-background-size: cover;
89 | }
90 |
91 | #contentWrapper {
92 | background-color: transparent;
93 | height: 0px;
94 | width: 100%;
95 | }
96 |
97 | #content {
98 | /* Set content to center */
99 | position: fixed;
100 | top: 50%;
101 | left: 50%;
102 | transform: translate(-50%, -50%);
103 |
104 | background-color: #fff;
105 |
106 | /* Set size margins */
107 | margin-bottom: 28px;
108 | margin-left: auto;
109 | margin-right: auto;
110 | min-height: 235px;
111 | min-width: 320px;
112 | max-width: 412px;
113 | width: 338px; /*calc(100% - 40px); */
114 | height: auto;
115 | padding: 36px;
116 |
117 | /* Add drop shadow */
118 | box-shadow: 0 2px 3px rgba(0,0,0,0.55);
119 | border: 1px solid rgba(0,0,0,0.4);
120 |
121 | /* Allow Scrolling */
122 | overflow-y: auto;
123 | max-height: 80%;
124 | }
125 |
126 | #header {
127 | font-size: 2em;
128 | font-weight: lighter;
129 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
130 | padding: 0px 0px 0px 0px;
131 | margin: 0px;
132 | height: 36px;
133 | width: 338px;
134 | background-color: transparent;
135 | }
136 |
137 | #header img {
138 | /* Logo image recommended dimension: 108x24 or 338x24 (elongated), 4 kB average, 10 kB maximum. Transparent PNG strongly recommended. */
139 | width: auto;
140 | height: 100%;
141 | position: relative;
142 | top: -7px;
143 | }
144 |
145 | #loginMessage {
146 | box-sizing: border-box;
147 | color: rgb(38, 38, 38);
148 | direction: ltr;
149 | display: block;
150 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
151 | font-weight: 300;
152 | font-size: 1.2rem;
153 | height: auto;
154 | line-height: 28px;
155 | margin-bottom: 16px;
156 | margin-left: -2px;
157 | margin-right: -2px;
158 | margin-top: 16px;
159 | padding-bottom: 0px;
160 | padding-left: 0px;
161 | padding-right: 0px;
162 | padding-top: 0px;
163 | text-align: left;
164 | text-size-adjust: 100%;
165 | width: 342px;
166 | background-color: transparent;
167 | }
168 |
169 | #loginForm {
170 | width: 338px;
171 | }
172 |
173 | #workArea, #header {
174 | word-wrap: break-word;
175 | }
176 |
177 | #workArea {
178 | margin-bottom: 4%;
179 | margin-top: 16px;
180 | background-color: transparent;
181 | }
182 |
183 | #footerPlaceholder {
184 | height: 0px;
185 | }
186 |
187 | #footer {
188 | background-color: rgba(0, 0, 0, 0.6);
189 | bottom: 0px;
190 | box-sizing: border-box;
191 | clear: both;
192 | color: rgb(38, 38, 38);
193 | direction: ltr;
194 | display: block;
195 | filter: none;
196 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
197 | font-size: 15px;
198 | font-weight: normal;
199 | height: 28px;
200 | line-height: 20px;
201 | overflow-x: visible;
202 | overflow-y: visible;
203 | position: fixed;
204 | text-align: left;
205 | text-size-adjust: 100%;
206 | width: 100%;
207 | }
208 |
209 | #footerLinks {
210 | padding-left: 10px;
211 | }
212 |
213 | #copyright {
214 | box-sizing: border-box;
215 | color: rgb(255, 255, 255);
216 | direction: ltr;
217 | display: inline-block;
218 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
219 | font-size: 12px;
220 | font-weight: normal;
221 | height: 28px;
222 | line-height: 28px;
223 | margin-left: 8px;
224 | margin-right: 8px;
225 | text-align: left;
226 | text-size-adjust: 100%;
227 | }
228 |
229 | #userNameArea, #passwordArea {
230 | box-sizing: border-box;
231 | color: rgb(38, 38, 38);
232 | direction: ltr;
233 | display: block;
234 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
235 | font-size: 15px;
236 | font-weight: normal;
237 | height: 52px;
238 | line-height: 20px;
239 | margin-left: 0px;
240 | margin-right: -2px;
241 | text-align: left;
242 | text-size-adjust: 100%;
243 | width: 342px;
244 | }
245 |
246 | #updatePasswordForm #submitButton, #cancelButton {
247 | width: 48%;
248 | }
249 |
250 | #oldPasswordArea, #newPasswordArea {
251 | margin-bottom: 7px;
252 | }
253 |
254 | #errorMessage{
255 | margin-top: 5px;
256 | }
257 |
258 | .pageLink {
259 | padding-left: 5px;
260 | padding-right: 5px;
261 | box-sizing: border-box;
262 | color: rgb(0, 0, 0);
263 | direction: ltr;
264 | display: inline-block;
265 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
266 | font-size: 12px;
267 | font-weight: normal;
268 | height: 28px;
269 | line-height: 28px;
270 | margin-left: 0px;
271 | margin-right: 0px;
272 | text-align: left;
273 | text-size-adjust: 100%;
274 | text-decoration: underline;
275 | }
276 |
277 | /* Common content styles */
278 |
279 | .clear {
280 | clear: both;
281 | }
282 |
283 | .float {
284 | float: left;
285 | }
286 |
287 | .floatReverse {
288 | float: right;
289 | }
290 |
291 | .indent {
292 | margin-left: 16px;
293 | }
294 |
295 | .indentNonCollapsible {
296 | padding-left: 16px;
297 | }
298 |
299 | .hidden {
300 | display: none;
301 | }
302 |
303 | .notHidden {
304 | display: inherit;
305 | }
306 |
307 | .error {
308 | color: #e81123;
309 | }
310 |
311 | .actionLink {
312 | margin-top: 5px;
313 | margin-bottom: 8px;
314 | display: block;
315 | }
316 |
317 | a {
318 | color: #0067b8;
319 | text-decoration: none;
320 | background-color: transparent;
321 | word-wrap: normal;
322 | }
323 |
324 | ul {
325 | list-style-type: disc;
326 | }
327 |
328 | ul, ol, dd {
329 | padding: 0 0 0 16px;
330 | }
331 |
332 | h1, h2, h3, h4, h5, label {
333 | margin-bottom: 8px;
334 | }
335 |
336 | .submitMargin {
337 | margin-top: 18px;
338 | margin-bottom: 18px;
339 | }
340 |
341 | .topFieldMargin {
342 | margin-top: 8px;
343 | }
344 |
345 | .fieldMargin {
346 | margin-bottom: 8px;
347 | }
348 |
349 | .groupMargin {
350 | margin-bottom: 0px;
351 | }
352 |
353 | .sectionMargin {
354 | margin-bottom: 64px;
355 | }
356 |
357 | .block {
358 | display: block;
359 | }
360 |
361 | .autoWidth {
362 | width: auto;
363 | }
364 |
365 | .fullWidth {
366 | width: 342px;
367 | }
368 |
369 | .fullWidthIndent {
370 | width: 326px;
371 | }
372 |
373 | .smallTopSpacing {
374 | margin-top: 15px;
375 | }
376 |
377 | .mediumTopSpacing {
378 | margin-top: 25px;
379 | }
380 |
381 | .largeTopSpacing {
382 | margin-top: 35px;
383 | }
384 |
385 | .smallBottomSpacing {
386 | margin-bottom: 5px;
387 | }
388 |
389 | .mediumBottomSpacing {
390 | margin-bottom: 15px;
391 | }
392 |
393 | .largeBottomSpacing {
394 | margin-bottom: 25px;
395 | }
396 |
397 | #openingMessage {
398 | margin-bottom: 4px;
399 | }
400 |
401 | input {
402 | max-width: 100%;
403 | font-family: inherit;
404 | margin-top: 0px;
405 | margin-bottom: 0px;
406 | }
407 |
408 | input[type="radio"], input[type="checkbox"] {
409 | vertical-align: middle;
410 | margin-bottom: 0px;
411 | }
412 |
413 | span.submit, input[type="submit"] {
414 | align-items: flex-start;
415 | background-color: rgb(0, 103, 184);
416 | border-bottom-color: rgb(0, 103, 184);
417 | border-bottom-style: solid;
418 | border-bottom-width: 1px;
419 | border-image-outset: 0px;
420 | border-image-repeat: stretch;
421 | border-image-slice: 100%;
422 | border-image-source: none;
423 | border-image-width: 1;
424 | border-left-color: rgb(0, 103, 184);
425 | border-left-style: solid;
426 | border-left-width: 1px;
427 | border-right-color: rgb(0, 103, 184);
428 | border-right-style: solid;
429 | border-right-width: 1px;
430 | border-top-color: rgb(0, 103, 184);
431 | border-top-style: solid;
432 | border-top-width: 1px;
433 | box-sizing: border-box;
434 | color: rgb(255, 255, 255);
435 | cursor: pointer;
436 | direction: ltr;
437 | display: inline-block;
438 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
439 | font-size: 15px;
440 | font-stretch: normal;
441 | font-style: normal;
442 | font-variant-caps: normal;
443 | font-variant-ligatures: normal;
444 | font-variant-numeric: normal;
445 | font-weight: normal;
446 | height: 36px;
447 | letter-spacing: normal;
448 | line-height: 25px;
449 | margin-bottom: 0px;
450 | margin-left: 0px;
451 | margin-right: 0px;
452 | margin-top: 0px;
453 | max-width: 100%;
454 | overflow-x: hidden;
455 | overflow-y: hidden;
456 | padding-bottom: 4px;
457 | padding-left: 12px;
458 | padding-right: 12px;
459 | padding-top: 3px;
460 | position: relative;
461 | text-align: center;
462 | text-indent: 0px;
463 | text-overflow: ellipsis;
464 | text-rendering: auto;
465 | text-shadow: none;
466 | text-size-adjust: 100%;
467 | touch-action: manipulation;
468 | user-select: none;
469 | vertical-align: middle;
470 | white-space: nowrap;
471 | width: 100%;
472 | word-spacing: 0px;
473 | writing-mode: horizontal-tb;
474 | -webkit-appearance: none;
475 | -webkit-rtl-ordering: logical;
476 | -webkit-border-image: none;
477 | }
478 |
479 | input[type="submit"]:hover, span.submit:hover {
480 | background: rgb(0, 85, 152);
481 | border: 1px solid rgb(0, 85, 152);
482 | }
483 |
484 |
485 | input.text {
486 | background-color: rgba(255, 255, 255, 0.4);
487 | background-image: none;
488 | border-bottom-color: rgba(0, 0, 0, 0.6);
489 | border-bottom-style: solid;
490 | border-bottom-width: 1px;
491 | border-image-outset: 0px;
492 | border-image-repeat: stretch;
493 | border-image-slice: 100%;
494 | border-image-source: none;
495 | border-image-width: 1;
496 | border-top: none;
497 | border-left: none;
498 | border-right: none;
499 | box-sizing: border-box;
500 | color: rgb(38, 38, 38);
501 | cursor: auto;
502 | direction: ltr;
503 | display: block;
504 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
505 | font-size: 15px;
506 | font-stretch: normal;
507 | font-style: normal;
508 | font-variant-caps: normal;
509 | font-variant-ligatures: normal;
510 | font-variant-numeric: normal;
511 | font-weight: normal;
512 | height: 36px;
513 | letter-spacing: normal;
514 | line-height: 20px;
515 | margin-bottom: 0px;
516 | margin-left: 0px;
517 | margin-right: 0px;
518 | margin-top: 0px;
519 | max-width: 100%;
520 | outline-color: rgb(38, 38, 38);
521 | outline-style: none;
522 | outline-width: 0px;
523 | padding-bottom: 6px;
524 | padding-left: 0px;
525 | padding-right: 10px;
526 | padding-top: 6px;
527 | text-align: start;
528 | text-indent: 0px;
529 | text-rendering: auto;
530 | text-shadow: none;
531 | text-size-adjust: 100%;
532 | text-transform: none;
533 | user-select: text;
534 | width: 338px;
535 | word-spacing: 0px;
536 | writing-mode: horizontal-tb;
537 | -webkit-appearance: none;
538 | -webkit-locale: "en";
539 | -webkit-rtl-ordering: logical;
540 | -webkit-border-image: none;
541 | }
542 |
543 | input.text:focus {
544 | border: 1px solid #0067B8;
545 | border-top: none;
546 | border-left: none;
547 | border-right: none;
548 | }
549 |
550 | select {
551 | height: 28px;
552 | min-width: 60px;
553 | max-width: 100%;
554 | margin-bottom: 8px;
555 | white-space: nowrap;
556 | overflow: hidden;
557 | box-shadow: none;
558 | padding: 2px;
559 | font-family: inherit;
560 | }
561 |
562 | h1, .giantText {
563 | font-size: 2.0em;
564 | font-weight: lighter;
565 | }
566 |
567 | h2, .bigText {
568 | font-size: 1.33em;
569 | font-weight: lighter;
570 | }
571 |
572 | h3, .normalText {
573 | font-size: 1.0em;
574 | font-weight: normal;
575 | }
576 |
577 | h4, .smallText {
578 | font-size: 0.9em;
579 | font-weight: normal;
580 | }
581 |
582 | h4 {
583 | font-size: 0.7em;
584 | font-weight: normal;
585 | }
586 |
587 | h5, .tinyText {
588 | font-size: 0.8em;
589 | font-weight: normal;
590 | }
591 |
592 | .hint {
593 | color: #999999;
594 | }
595 |
596 | .emphasis {
597 | font-weight: 700;
598 | color: #2F2F2F;
599 | }
600 |
601 | .smallIcon {
602 | height: 20px;
603 | padding-right: 12px;
604 | vertical-align: middle;
605 | }
606 |
607 | .largeIcon {
608 | height: 48px;
609 | /* width:48px; */
610 | vertical-align: middle;
611 | }
612 |
613 | .largeTextNoWrap {
614 | height: 48px;
615 | display: table-cell; /* needed when in float*/
616 | vertical-align: middle;
617 | white-space: nowrap;
618 | font-size: 1.2em;
619 | }
620 |
621 | .idp {
622 | height: 48px;
623 | clear: both;
624 | padding: 8px;
625 | overflow: hidden;
626 | cursor: pointer;
627 | }
628 |
629 | .idp:hover {
630 | background-color: #cccccc;
631 | cursor: pointer;
632 | }
633 |
634 | .idpDescription {
635 | width: 80%;
636 | cursor: pointer;
637 | }
638 |
639 | .identityBanner{
640 | color: black;
641 | text-align: left;
642 | white-space: nowrap;
643 | line-height: 28px;
644 | height: 28px;
645 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
646 | font-size: 15px;
647 | font-weight: 300;
648 | white-space: nowrap;
649 | overflow: hidden;
650 | -o-text-overflow: ellipsis;
651 | text-overflow: ellipsis;
652 | }
653 |
654 | .submit.backButton{
655 | color: black;
656 | width: 108px;
657 | float: left;
658 | background: #CCCCCC;
659 | border-color: #CCCCCC;
660 | margin-left: -2px;
661 | height: 32px;
662 | position: relative;
663 | left:110px;
664 | bottom:17px;
665 | }
666 |
667 | input[type="submit"].backButton:hover, span.submit.backButton:hover {
668 | background: #AAA;
669 | border: 1px solid #AAA;
670 | }
671 |
672 | .submit.nextButton{
673 | margin-left: -2px;
674 | left:229px;
675 | bottom:-40px;
676 | height: 32px;
677 | width: 108px;
678 | }
679 |
680 | .submit.modifiedSignIn{
681 | display: block;
682 | width: auto;
683 | position: relative;
684 | height: 32px;
685 | width: 108px;
686 | left:229px;
687 | bottom:-15px;
688 | }
689 |
690 | #submissionArea{
691 | margin-top: 8px;
692 | }
693 |
694 | @media (max-width: 600px), (max-height: 392px){
695 | #content {
696 | /* Set content to center */
697 | position: relative;
698 | top: 0;
699 | left: 0;
700 | transform: none;
701 | background-color: #fff;
702 | /* Set size margins */
703 | margin-bottom: 28px;
704 | margin-left: auto;
705 | margin-right: auto;
706 | min-height: 290px;
707 | min-width: 320px;
708 | max-width: 412px;
709 | width: calc(100% - 40px);
710 | height: auto;
711 | padding: 23px 18px 0px 18px;
712 | /* Add drop shadow */
713 | box-shadow: 0 0 0 rgba(0,0,0,0);
714 | border: 0px solid rgba(0,0,0,0);
715 |
716 | /* Allow Scrolling */
717 | overflow-y: initial;
718 | max-height: initial;
719 | }
720 |
721 | #footer {
722 | background-color: rgba(0, 0, 0, 0.6);
723 | bottom: 0px;
724 | box-sizing: border-box;
725 | clear: both;
726 | color: rgb(38, 38, 38);
727 | direction: ltr;
728 | display: block;
729 | filter: none;
730 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
731 | font-size: 15px;
732 | font-weight: normal;
733 | height: 28px;
734 | line-height: 20px;
735 | overflow-x: visible;
736 | overflow-y: visible;
737 | position: fixed;
738 | text-align: left;
739 | text-size-adjust: 100%;
740 | width: 100%;
741 | }
742 |
743 | #brandingWrapper {
744 | display: none;
745 | }
746 |
747 | input.text {
748 | font-size: 16px;
749 | }
750 |
751 | .identityBanner {
752 | margin:16px 0px;
753 | padding:0px 80px 0px 40px;
754 | }
755 |
756 | .identityBannerImage {
757 | left: -50px;
758 | }
759 | }
760 |
761 | /* Targets displays using any of Windows’ High Contrast Mode themes: */
762 | @media screen and (-ms-high-contrast: active) {
763 | textarea::-webkit-input-placeholder {
764 | color: #00FF00;
765 | }
766 |
767 | textarea:-moz-placeholder { /* Firefox 18- */
768 | color: #00FF00;
769 | }
770 |
771 | textarea::-moz-placeholder { /* Firefox 19+ */
772 | color: #00FF00;
773 | }
774 |
775 | textarea:-ms-input-placeholder {
776 | color: #00FF00;
777 | }
778 | }
779 |
780 | /* Targets displays using the Windows’ "High Contrast Black" theme: */
781 | @media screen and (-ms-high-contrast: white-on-black) {
782 | #contentWrapper {
783 | background-color: #000000;
784 | color: #ffffff;
785 | }
786 | .idp:hover {
787 | background-color: #ffffff;
788 | color: #000000;
789 | }
790 | #brandingWrapper {
791 | background-color: #000000;
792 | color: #ffffff;
793 | }
794 | html, body {
795 | background-color: #000000;
796 | color: #ffffff;
797 | }
798 |
799 | textarea::-webkit-input-placeholder {
800 | color: #ffffff;
801 | }
802 |
803 | textarea:-moz-placeholder { /* Firefox 18- */
804 | color: #ffffff;
805 | }
806 |
807 | textarea::-moz-placeholder { /* Firefox 19+ */
808 | color: #ffffff;
809 | }
810 |
811 | textarea:-ms-input-placeholder {
812 | color: #ffffff;
813 | }
814 | }
815 |
816 | /* Targets displays using the Windows’ "High Contrast White" theme: */
817 | @media screen and (-ms-high-contrast: black-on-white) {
818 | #contentWrapper {
819 | background-color: #ffffff;
820 | color: #000000;
821 | }
822 |
823 | .idp:hover {
824 | background-color: #000000;
825 | color: #ffffff;
826 | }
827 |
828 | #brandingWrapper {
829 | background-color: #ffffff;
830 | color: #000000;
831 | }
832 |
833 | html, body {
834 | background-color: #ffffff;
835 | color: #000000;
836 | }
837 |
838 | textarea::-webkit-input-placeholder {
839 | color: #000000;
840 | }
841 |
842 | textarea:-moz-placeholder { /* Firefox 18- */
843 | color: #000000;
844 | }
845 |
846 | textarea::-moz-placeholder { /* Firefox 19+ */
847 | color: #000000;
848 | }
849 |
850 | textarea:-ms-input-placeholder {
851 | color: #000000;
852 | }
853 | }
854 |
--------------------------------------------------------------------------------
/centeredUi/ThemeCenterBrandRTL.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0px;
3 | padding: 0px;
4 | }
5 |
6 | html, body {
7 | height: 100%;
8 | width: 100%;
9 | background-color: #ffffff;
10 | color: #000000;
11 | font-weight: normal;
12 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
13 | -ms-overflow-style: -ms-autohiding-scrollbar;
14 | }
15 |
16 | body {
17 | font-size: 0.9em;
18 | }
19 |
20 | #noScript {
21 | margin: 16px;
22 | color: Black;
23 | }
24 |
25 | :lang(en-GB) {
26 | quotes: '\2018' '\2019' '\201C' '\201D';
27 | }
28 |
29 | :lang(zh) {
30 | font-family: 微软雅黑;
31 | }
32 |
33 | @-ms-viewport {
34 | width: device-width;
35 | }
36 |
37 | @-moz-viewport {
38 | width: device-width;
39 | }
40 |
41 | @-o-viewport {
42 | width: device-width;
43 | }
44 |
45 | @-webkit-viewport {
46 | width: device-width;
47 | }
48 |
49 | @viewport {
50 | width: device-width;
51 | }
52 |
53 | /* Theme layout styles */
54 |
55 | #fullPage {
56 | position: absolute;
57 | bottom: 28px;
58 | top: 0px;
59 | width: 100%;
60 | }
61 |
62 | #brandingWrapper {
63 | background-color: #4488dd;
64 | height: 100%;
65 | width: 100%;
66 | }
67 |
68 | #branding {
69 | /* A background image will be added to the #branding element at run-time once the illustration image is configured in the theme.
70 | Recommended image dimensions: 1420x1200 pixels, JPG or PNG, 200 kB average, 500 kB maximum. */
71 | background-color: inherit;
72 | background-repeat: no-repeat;
73 | background-size: cover;
74 | height: 100%;
75 | width: 100%;
76 | -webkit-background-size: cover;
77 | -moz-background-size: cover;
78 | -o-background-size: cover;
79 | }
80 |
81 | #contentWrapper {
82 | background-color: transparent;
83 | height: 0px;
84 | width: 100%;
85 | }
86 |
87 | #content {
88 | /* Set content to center */
89 | position: fixed;
90 | top: 50%;
91 | right: 50%;
92 | transform: translate(50%, -50%);
93 |
94 | background-color: #fff;
95 |
96 | /* Set size margins */
97 | margin-bottom: 28px;
98 | margin-right: auto;
99 | margin-left: auto;
100 | min-height: 235px;
101 | min-width: 320px;
102 | max-width: 412px;
103 | width: 338px; /*calc(100% - 40px); */
104 | height: auto;
105 | padding: 36px;
106 |
107 | /* Add drop shadow */
108 | box-shadow: 0 2px 3px rgba(0,0,0,0.55);
109 | border: 1px solid rgba(0,0,0,0.4);
110 |
111 | /* Allow Scrolling */
112 | overflow-y: auto;
113 | max-height: 80%;
114 | }
115 |
116 | #header {
117 | font-size: 2em;
118 | font-weight: lighter;
119 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
120 | padding: 0px 0px 0px 0px;
121 | margin: 0px;
122 | height: 36px;
123 | width: 338px;
124 | background-color: transparent;
125 | }
126 |
127 | #header img {
128 | /* Logo image recommended dimension: 108x24 or 338x24 (elongated), 4 kB average, 10 kB maximum. Transparent PNG strongly recommended. */
129 | width: auto;
130 | height: 100%;
131 | position: relative;
132 | top: -7px;
133 | }
134 |
135 | #loginMessage {
136 | box-sizing: border-box;
137 | color: rgb(38, 38, 38);
138 | direction: rtl;
139 | display: block;
140 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
141 | font-weight: 300;
142 | font-size: 1.2rem;
143 | height: auto;
144 | line-height: 28px;
145 | margin-bottom: 16px;
146 | margin-right: -2px;
147 | margin-left: -2px;
148 | margin-top: 16px;
149 | padding-bottom: 0px;
150 | padding-right: 0px;
151 | padding-left: 0px;
152 | padding-top: 0px;
153 | text-align: right;
154 | text-size-adjust: 100%;
155 | width: 342px;
156 | background-color: transparent;
157 | }
158 |
159 | #loginForm {
160 | width: 338px;
161 | }
162 |
163 | #workArea, #header {
164 | word-wrap: break-word;
165 | }
166 |
167 | #workArea {
168 | margin-bottom: 4%;
169 | margin-top: 16px;
170 | background-color: transparent;
171 | }
172 |
173 | #footerPlaceholder {
174 | height: 0px;
175 | }
176 |
177 | #footer {
178 | background-color: rgba(0, 0, 0, 0.6);
179 | bottom: 0px;
180 | box-sizing: border-box;
181 | clear: both;
182 | color: rgb(38, 38, 38);
183 | direction: rtl;
184 | display: block;
185 | filter: none;
186 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
187 | font-size: 15px;
188 | font-weight: normal;
189 | height: 28px;
190 | line-height: 20px;
191 | overflow-x: visible;
192 | overflow-y: visible;
193 | position: fixed;
194 | text-align: right;
195 | text-size-adjust: 100%;
196 | width: 100%;
197 | }
198 |
199 | #footerLinks {
200 | padding-right: 10px;
201 | }
202 |
203 | #copyright {
204 | box-sizing: border-box;
205 | color: rgb(255, 255, 255);
206 | direction: rtl;
207 | display: inline-block;
208 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
209 | font-size: 12px;
210 | font-weight: normal;
211 | height: 28px;
212 | line-height: 28px;
213 | margin-right: 8px;
214 | margin-left: 8px;
215 | text-align: right;
216 | text-size-adjust: 100%;
217 | }
218 |
219 | #userNameArea, #passwordArea {
220 | box-sizing: border-box;
221 | color: rgb(38, 38, 38);
222 | direction: rtl;
223 | display: block;
224 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
225 | font-size: 15px;
226 | font-weight: normal;
227 | height: 52px;
228 | line-height: 20px;
229 | margin-right: 0px;
230 | margin-left: -2px;
231 | text-align: right;
232 | text-size-adjust: 100%;
233 | width: 342px;
234 | }
235 |
236 | #updatePasswordForm #submitButton, #cancelButton {
237 | width: 48%;
238 | }
239 |
240 | #oldPasswordArea, #newPasswordArea {
241 | margin-bottom: 7px;
242 | }
243 |
244 | #errorMessage{
245 | margin-top: 5px;
246 | }
247 |
248 | .pageLink {
249 | padding-right: 5px;
250 | padding-left: 5px;
251 | box-sizing: border-box;
252 | color: rgb(0, 0, 0);
253 | direction: rtl;
254 | display: inline-block;
255 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
256 | font-size: 12px;
257 | font-weight: normal;
258 | height: 28px;
259 | line-height: 28px;
260 | margin-right: 0px;
261 | margin-left: 0px;
262 | text-align: right;
263 | text-size-adjust: 100%;
264 | text-decoration: underline;
265 | }
266 |
267 | /* Common content styles */
268 |
269 | .clear {
270 | clear: both;
271 | }
272 |
273 | .float {
274 | float: right;
275 | }
276 |
277 | .floatReverse {
278 | float: left;
279 | }
280 |
281 | .indent {
282 | margin-right: 16px;
283 | }
284 |
285 | .indentNonCollapsible {
286 | padding-right: 16px;
287 | }
288 |
289 | .hidden {
290 | display: none;
291 | }
292 |
293 | .notHidden {
294 | display: inherit;
295 | }
296 |
297 | .error {
298 | color: #e81123;
299 | }
300 |
301 | .actionLink {
302 | margin-top: 5px;
303 | margin-bottom: 8px;
304 | display: block;
305 | }
306 |
307 | a {
308 | color: #0067b8;
309 | text-decoration: none;
310 | background-color: transparent;
311 | word-wrap: normal;
312 | }
313 |
314 | ul {
315 | list-style-type: disc;
316 | }
317 |
318 | ul, ol, dd {
319 | padding: 0 16px 0 0;
320 | }
321 |
322 | h1, h2, h3, h4, h5, label {
323 | margin-bottom: 8px;
324 | }
325 |
326 | .submitMargin {
327 | margin-top: 18px;
328 | margin-bottom: 18px;
329 | }
330 |
331 | .topFieldMargin {
332 | margin-top: 8px;
333 | }
334 |
335 | .fieldMargin {
336 | margin-bottom: 8px;
337 | }
338 |
339 | .groupMargin {
340 | margin-bottom: 0px;
341 | }
342 |
343 | .sectionMargin {
344 | margin-bottom: 64px;
345 | }
346 |
347 | .block {
348 | display: block;
349 | }
350 |
351 | .autoWidth {
352 | width: auto;
353 | }
354 |
355 | .fullWidth {
356 | width: 342px;
357 | }
358 |
359 | .fullWidthIndent {
360 | width: 326px;
361 | }
362 |
363 | .smallTopSpacing {
364 | margin-top: 15px;
365 | }
366 |
367 | .mediumTopSpacing {
368 | margin-top: 25px;
369 | }
370 |
371 | .largeTopSpacing {
372 | margin-top: 35px;
373 | }
374 |
375 | .smallBottomSpacing {
376 | margin-bottom: 5px;
377 | }
378 |
379 | .mediumBottomSpacing {
380 | margin-bottom: 15px;
381 | }
382 |
383 | .largeBottomSpacing {
384 | margin-bottom: 25px;
385 | }
386 |
387 | #openingMessage {
388 | margin-bottom: 4px;
389 | }
390 |
391 | input {
392 | max-width: 100%;
393 | font-family: inherit;
394 | margin-top: 0px;
395 | margin-bottom: 0px;
396 | }
397 |
398 | input[type="radio"], input[type="checkbox"] {
399 | vertical-align: middle;
400 | margin-bottom: 0px;
401 | }
402 |
403 | span.submit, input[type="submit"] {
404 | align-items: flex-start;
405 | background-color: rgb(0, 103, 184);
406 | border-bottom-color: rgb(0, 103, 184);
407 | border-bottom-style: solid;
408 | border-bottom-width: 1px;
409 | border-image-outset: 0px;
410 | border-image-repeat: stretch;
411 | border-image-slice: 100%;
412 | border-image-source: none;
413 | border-image-width: 1;
414 | border-right-color: rgb(0, 103, 184);
415 | border-right-style: solid;
416 | border-right-width: 1px;
417 | border-left-color: rgb(0, 103, 184);
418 | border-left-style: solid;
419 | border-left-width: 1px;
420 | border-top-color: rgb(0, 103, 184);
421 | border-top-style: solid;
422 | border-top-width: 1px;
423 | box-sizing: border-box;
424 | color: rgb(255, 255, 255);
425 | cursor: pointer;
426 | direction: rtl;
427 | display: inline-block;
428 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
429 | font-size: 15px;
430 | font-stretch: normal;
431 | font-style: normal;
432 | font-variant-caps: normal;
433 | font-variant-ligatures: normal;
434 | font-variant-numeric: normal;
435 | font-weight: normal;
436 | height: 36px;
437 | letter-spacing: normal;
438 | line-height: 25px;
439 | margin-bottom: 0px;
440 | margin-right: 0px;
441 | margin-left: 0px;
442 | margin-top: 0px;
443 | max-width: 100%;
444 | overflow-x: hidden;
445 | overflow-y: hidden;
446 | padding-bottom: 4px;
447 | padding-right: 12px;
448 | padding-left: 12px;
449 | padding-top: 3px;
450 | position: relative;
451 | text-align: center;
452 | text-indent: 0px;
453 | text-overflow: ellipsis;
454 | text-rendering: auto;
455 | text-shadow: none;
456 | text-size-adjust: 100%;
457 | touch-action: manipulation;
458 | user-select: none;
459 | vertical-align: middle;
460 | white-space: nowrap;
461 | width: 100%;
462 | word-spacing: 0px;
463 | writing-mode: horizontal-tb;
464 | -webkit-appearance: none;
465 | -webkit-rtl-ordering: logical;
466 | -webkit-border-image: none;
467 | }
468 |
469 | input[type="submit"]:hover, span.submit:hover {
470 | background: rgb(0, 85, 152);
471 | border: 1px solid rgb(0, 85, 152);
472 | }
473 |
474 | input.text {
475 | background-color: rgba(255, 255, 255, 0.4);
476 | background-image: none;
477 | border-bottom-color: rgba(0, 0, 0, 0.6);
478 | border-bottom-style: solid;
479 | border-bottom-width: 1px;
480 | border-image-outset: 0px;
481 | border-image-repeat: stretch;
482 | border-image-slice: 100%;
483 | border-image-source: none;
484 | border-image-width: 1;
485 | border-top: none;
486 | border-left: none
487 | border-right: none;
488 | box-sizing: border-box;
489 | color: rgb(38, 38, 38);
490 | cursor: auto;
491 | direction: rtl;
492 | display: block;
493 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
494 | font-size: 15px;
495 | font-stretch: normal;
496 | font-style: normal;
497 | font-variant-caps: normal;
498 | font-variant-ligatures: normal;
499 | font-variant-numeric: normal;
500 | font-weight: normal;
501 | height: 36px;
502 | letter-spacing: normal;
503 | line-height: 20px;
504 | margin-bottom: 0px;
505 | margin-right: 0px;
506 | margin-left: 0px;
507 | margin-top: 0px;
508 | max-width: 100%;
509 | outline-color: rgb(38, 38, 38);
510 | outline-style: none;
511 | outline-width: 0px;
512 | padding-bottom: 6px;
513 | padding-right: 10px;
514 | padding-left: 10px;
515 | padding-top: 6px;
516 | text-align: start;
517 | text-indent: 0px;
518 | text-rendering: auto;
519 | text-shadow: none;
520 | text-size-adjust: 100%;
521 | text-transform: none;
522 | user-select: text;
523 | width: 338px;
524 | word-spacing: 0px;
525 | writing-mode: horizontal-tb;
526 | -webkit-appearance: none;
527 | -webkit-locale: "en";
528 | -webkit-rtl-ordering: logical;
529 | -webkit-border-image: none;
530 | }
531 |
532 | input.text:focus {
533 | border: 1px solid #0067B8;
534 | border-top: none;
535 | border-left: none;
536 | border-right: none;
537 | }
538 |
539 | select {
540 | height: 28px;
541 | min-width: 60px;
542 | max-width: 100%;
543 | margin-bottom: 8px;
544 | white-space: nowrap;
545 | overflow: hidden;
546 | box-shadow: none;
547 | padding: 2px;
548 | font-family: inherit;
549 | }
550 |
551 | h1, .giantText {
552 | font-size: 2.0em;
553 | font-weight: lighter;
554 | }
555 |
556 | h2, .bigText {
557 | font-size: 1.33em;
558 | font-weight: lighter;
559 | }
560 |
561 | h3, .normalText {
562 | font-size: 1.0em;
563 | font-weight: normal;
564 | }
565 |
566 | h4, .smallText {
567 | font-size: 0.9em;
568 | font-weight: normal;
569 | }
570 |
571 | h4 {
572 | font-size: 0.7em;
573 | font-weight: normal;
574 | }
575 |
576 | h5, .tinyText {
577 | font-size: 0.8em;
578 | font-weight: normal;
579 | }
580 |
581 | .hint {
582 | color: #999999;
583 | }
584 |
585 | .emphasis {
586 | font-weight: 700;
587 | color: #2F2F2F;
588 | }
589 |
590 | .smallIcon {
591 | height: 20px;
592 | padding-left: 12px;
593 | vertical-align: middle;
594 | }
595 |
596 | .largeIcon {
597 | height: 48px;
598 | /* width:48px; */
599 | vertical-align: middle;
600 | }
601 |
602 | .largeTextNoWrap {
603 | height: 48px;
604 | display: table-cell; /* needed when in float*/
605 | vertical-align: middle;
606 | white-space: nowrap;
607 | font-size: 1.2em;
608 | }
609 |
610 | .idp {
611 | height: 48px;
612 | clear: both;
613 | padding: 8px;
614 | overflow: hidden;
615 | cursor: pointer;
616 | }
617 |
618 | .idp:hover {
619 | background-color: #cccccc;
620 | cursor: pointer;
621 | }
622 |
623 | .idpDescription {
624 | width: 80%;
625 | cursor: pointer;
626 | }
627 |
628 | .identityBanner{
629 | color: black;
630 | text-align: left;
631 | white-space: nowrap;
632 | line-height: 28px;
633 | height: 28px;
634 | font-family: "Segoe UI Webfont",-apple-system,"Helvetica Neue","Lucida Grande","Roboto","Ebrima","Nirmala UI","Gadugi","Segoe Xbox Symbol","Segoe UI Symbol","Meiryo UI","Khmer UI","Tunga","Lao UI","Raavi","Iskoola Pota","Latha","Leelawadee","Microsoft YaHei UI","Microsoft JhengHei UI","Malgun Gothic","Estrangelo Edessa","Microsoft Himalaya","Microsoft New Tai Lue","Microsoft PhagsPa","Microsoft Tai Le","Microsoft Yi Baiti","Mongolian Baiti","MV Boli","Myanmar Text","Cambria Math";
635 | font-size: 15px;
636 | font-weight: 300;
637 | white-space: nowrap;
638 | overflow: hidden;
639 | -o-text-overflow: ellipsis;
640 | text-overflow: ellipsis;
641 | padding-left:20px;
642 | }
643 |
644 | .submit.backButton{
645 | color: black;
646 | width: 108px;
647 | float: left;
648 | background: #CCCCCC;
649 | border-color: #CCCCCC;
650 | margin-left: -2px;
651 | height: 32px;
652 | position: relative;
653 | left:110px;
654 | bottom:17px;
655 | }
656 |
657 | input[type="submit"].backButton:hover, span.submit.backButton:hover {
658 | background: #AAA;
659 | border: 1px solid #AAA;
660 | }
661 |
662 | .submit.nextButton{
663 | margin-left: -2px;
664 | left:229px;
665 | bottom:-40px;
666 | height: 32px;
667 | width: 108px;
668 | }
669 |
670 | .submit.modifiedSignIn{
671 | display: block;
672 | width: auto;
673 | position: relative;
674 | height: 32px;
675 | width: 108px;
676 | left:229px;
677 | bottom:-15px;
678 | }
679 |
680 | #submissionArea{
681 | margin-top: 8px;
682 | }
683 |
684 | @media (max-width: 600px), (max-height: 392px){
685 | #content {
686 | /* Set content to center */
687 | position: relative;
688 | top: 0;
689 | right: 0;
690 | transform: none;
691 | background-color: #fff;
692 | /* Set size margins */
693 | margin-bottom: 28px;
694 | margin-right: auto;
695 | margin-left: auto;
696 | min-height: 290px;
697 | min-width: 320px;
698 | max-width: 412px;
699 | width: calc(100% - 40px);
700 | height: auto;
701 | padding: 23px 18px 0px 18px;
702 | /* Add drop shadow */
703 | box-shadow: 0 0 0 rgba(0,0,0,0);
704 | border: 0px solid rgba(0,0,0,0);
705 |
706 | /* Allow Scrolling */
707 | overflow-y: initial;
708 | max-height: initial;
709 | }
710 |
711 | #footer {
712 | background-color: rgba(0, 0, 0, 0.6);
713 | bottom: 0px;
714 | box-sizing: border-box;
715 | clear: both;
716 | color: rgb(38, 38, 38);
717 | direction: rtl;
718 | display: block;
719 | filter: none;
720 | font-family: "Segoe UI Webfont", -apple-system, "Helvetica Neue", "Lucida Grande", Roboto, Ebrima, "Nirmala UI", Gadugi, "Segoe Xbox Symbol", "Segoe UI Symbol", "Meiryo UI", "Khmer UI", Tunga, "Lao UI", Raavi, "Iskoola Pota", Latha, Leelawadee, "Microsoft YaHei UI", "Microsoft JhengHei UI", "Malgun Gothic", "Estrangelo Edessa", "Microsoft Himalaya", "Microsoft New Tai Lue", "Microsoft PhagsPa", "Microsoft Tai Le", "Microsoft Yi Baiti", "Mongolian Baiti", "MV Boli", "Myanmar Text", "Cambria Math";
721 | font-size: 15px;
722 | font-weight: normal;
723 | height: 28px;
724 | line-height: 20px;
725 | overflow-x: visible;
726 | overflow-y: visible;
727 | position: fixed;
728 | text-align: right;
729 | text-size-adjust: 100%;
730 | width: 100%;
731 | }
732 |
733 | #brandingWrapper {
734 | display: none;
735 | }
736 | }
737 |
738 | /* Targets displays using any of Windows’ High Contrast Mode themes: */
739 | @media screen and (-ms-high-contrast: active) {
740 | textarea::-webkit-input-placeholder {
741 | color: #00FF00;
742 | }
743 |
744 | textarea:-moz-placeholder { /* Firefox 18- */
745 | color: #00FF00;
746 | }
747 |
748 | textarea::-moz-placeholder { /* Firefox 19+ */
749 | color: #00FF00;
750 | }
751 |
752 | textarea:-ms-input-placeholder {
753 | color: #00FF00;
754 | }
755 | }
756 |
757 | /* Targets displays using the Windows’ "High Contrast Black" theme: */
758 | @media screen and (-ms-high-contrast: white-on-black) {
759 | #contentWrapper {
760 | background-color: #000000;
761 | color: #ffffff;
762 | }
763 | .idp:hover {
764 | background-color: #ffffff;
765 | color: #000000;
766 | }
767 | #brandingWrapper {
768 | background-color: #000000;
769 | color: #ffffff;
770 | }
771 | html, body {
772 | background-color: #000000;
773 | color: #ffffff;
774 | }
775 |
776 | textarea::-webkit-input-placeholder {
777 | color: #ffffff;
778 | }
779 |
780 | textarea:-moz-placeholder { /* Firefox 18- */
781 | color: #ffffff;
782 | }
783 |
784 | textarea::-moz-placeholder { /* Firefox 19+ */
785 | color: #ffffff;
786 | }
787 |
788 | textarea:-ms-input-placeholder {
789 | color: #ffffff;
790 | }
791 | }
792 |
793 | /* Targets displays using the Windows’ "High Contrast White" theme: */
794 | @media screen and (-ms-high-contrast: black-on-white) {
795 | #contentWrapper {
796 | background-color: #ffffff;
797 | color: #000000;
798 | }
799 |
800 | .idp:hover {
801 | background-color: #000000;
802 | color: #ffffff;
803 | }
804 |
805 | #brandingWrapper {
806 | background-color: #ffffff;
807 | color: #000000;
808 | }
809 |
810 | html, body {
811 | background-color: #ffffff;
812 | color: #000000;
813 | }
814 |
815 | textarea::-webkit-input-placeholder {
816 | color: #000000;
817 | }
818 |
819 | textarea:-moz-placeholder { /* Firefox 18- */
820 | color: #000000;
821 | }
822 |
823 | textarea::-moz-placeholder { /* Firefox 19+ */
824 | color: #000000;
825 | }
826 |
827 | textarea:-ms-input-placeholder {
828 | color: #000000;
829 | }
830 | }
--------------------------------------------------------------------------------
/centeredUi/images/empty_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/centeredUi/images/empty_user.png
--------------------------------------------------------------------------------
/centeredUi/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/centeredUi/images/screenshot.png
--------------------------------------------------------------------------------
/centeredUi/images/screenshot_paginated.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/centeredUi/images/screenshot_paginated.png
--------------------------------------------------------------------------------
/centeredUi/images/screenshot_paginated2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/centeredUi/images/screenshot_paginated2.png
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/Digitude.Adfs.CustomImagesThemeGenerator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2018
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Digitude.Adfs.CustomImagesThemeGenerator", "src\Digitude.Adfs.CustomImagesThemeGenerator.csproj", "{F645A08A-3C1F-4260-BD12-823681D07E3E}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {F645A08A-3C1F-4260-BD12-823681D07E3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {F645A08A-3C1F-4260-BD12-823681D07E3E}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {F645A08A-3C1F-4260-BD12-823681D07E3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {F645A08A-3C1F-4260-BD12-823681D07E3E}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {EF0428E3-0C32-4E7F-A055-AA3E93EDDB72}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/README.md:
--------------------------------------------------------------------------------
1 | Update ADFS webtheme in order to display different IDP images
2 | ==============================================================
3 |
4 | Concept
5 | -------
6 |
7 | To modify the layout of the ADFS home realm detection page, ADFS provides the concept of a web theme.
8 |
9 | A web theme is a collection of following web artifacts:
10 |
11 | \+---css
12 | ¦ style.css
13 | ¦ style.rtl.css
14 | ¦
15 | +---illustration
16 | ¦ illustration.png
17 | ¦
18 | +---images
19 | ¦ +---idp
20 | ¦ idp.png
21 | ¦ localsts.png
22 | ¦ otherorganizations.png
23 | ¦
24 | +---script
25 | onload.js
26 |
27 | This tree structure holds the artifacts we will update in order to be able to show for each IDP (identity provider) a different image.
28 |
29 | ADFS also provides a couple of PowerShell cmdlets to work with the concept of web theme.
30 |
31 | The cmdlets we will use are:
32 |
33 | - **Get-AdfsWebTheme -Name WebThemeName**
34 |
35 | - Retrieve a reference to an existing web theme or null if it doesn’t exist.
36 |
37 |
38 | - **New-AdfsWebTheme –Name WebThemeName –SourceName default**
39 |
40 | - Create a new webtheme based on an existing webtheme.
41 |
42 |
43 | - **Set-AdfsWebTheme -TargetName WebThemeName -AdditionalFileResource …**
44 |
45 | - Associate additional artifacts with an existing webtheme (images, customized onload script, custom stylesheet, …)
46 |
47 |
48 | - **Set-AdfsWebConfig -ActiveThemeName WebThemeName**
49 |
50 | - Activate an existing webtheme via its name.
51 |
52 |
53 | Tooling
54 | -------
55 |
56 | Although the concept of performing these kind of updates is not difficult, the mere fact that the ADFS webtheme cmdlets have to be invoked using references to external resources makes it an repetitive and error prone task. To avoid this I have created a very small RAD utility that can speed up dramatically this task, even more if there are a lot of identity providers on the home realm detection page.
57 |
58 | 
59 |
60 | You can populate the list of display names manually. These names have to correspond with the display names they have been given in the ADFS management
61 | console. You can also export the list of claim provider trust display names to a text file as follows:
62 |
63 | **Get-AdfsClaimsProviderTrust \| select \@{Name="CPTName";Expression={\$_.Name}} \> [outputFile].txt**
64 |
65 | You can then import the list of display names in the utility by clicking on the button .
66 |
67 | Once the list of IDP display names is imported you can browse for a corresponding image. Optionally you can also change the translation of the display name (the utility provides translation to Dutch and French, but this is easy to change for other languages).
68 | You have the option of saving the definition file (extension \*.awi) and reloading a definition file.
69 |
70 | If you already have customized the onload.js or style.css, you can browse for these artifacts. The modifications for this utility will then be applied to the selected artifacts in stead of the default ones (the plain vanilla ADFS 2016 version).
71 |
72 | If all the display names have an associated image, you click on the button ‘Generate’. The tool will ask for a name to save the file as, and will create a zip archive with the selected name.
73 |
74 | The zip file contains the following artifacts:
75 |
76 | 
77 |
78 | Archive contents (example with the four IDP's)
79 | --------------------
80 |
81 | ### CreateOrUpdateAdfsWebTheme.ps1
82 |
83 | This file contains the collection of ADFS PowerShell cmdlets that perform the webtheme update:
84 |
85 | ```
86 |
87 | $webThemeName = 'Test'
88 |
89 | $webTheme = Get-AdfsWebTheme -Name \$webThemeName
90 |
91 | if (!\$webTheme)
92 | {
93 | New-AdfsWebTheme -Name Test -SourceName default
94 | }
95 |
96 | Set-AdfsWebTheme -TargetName Test -AdditionalFileResource \@{Uri="/adfs/portal/images/idp/IDP1.png";path="IDP1.png"}
97 |
98 | Set-AdfsWebTheme -TargetName Test -AdditionalFileResource \@{Uri="/adfs/portal/images/idp/IDP2.png";path="IDP2.png"}
99 |
100 | Set-AdfsWebTheme -TargetName Test -AdditionalFileResource \@{Uri="/adfs/portal/images/idp/IDP3.png";path="IDP3.png"}
101 |
102 | Set-AdfsWebTheme -TargetName Test -AdditionalFileResource \@{Uri="/adfs/portal/images/idp/IDP4.png";path="IDP4.png"}
103 |
104 | Set-AdfsWebTheme -TargetName Test -AdditionalFileResource \@{Uri="/adfs/portal/script/onload.js";path="customOnload.js"}
105 |
106 | Set-AdfsWebTheme -TargetName Test -StyleSheet \@{Path="customstyle.css"}
107 |
108 | Set-AdfsWebConfig -ActiveThemeName Test
109 |
110 | ```
111 |
112 | ### Customonload.js
113 |
114 | This file contains the javascript file that will perform the display of the images at runtime. It is based on the default ADFS onload.js (or a specific javascript file that already contains modifications) and adds only a small portion of code:
115 |
116 | ```
117 |
118 | // Added by the ADFS web theme generator (custom idp images).
119 |
120 | var language = document.documentElement.lang;
121 | var languageKey = 'en';
122 |
123 | if (language.lastIndexOf('nl', 0) === 0)
124 | {
125 | languageKey = 'nl';
126 | }
127 | else if (language.lastIndexOf('fr', 0) === 0)
128 | {
129 | languageKey = 'fr';
130 | }
131 |
132 | function renameLabels(oldDisplayName, newDisplayNameFR, newDisplayNameNL)
133 | {
134 | var listAllSpanForIdp = document.getElementsByClassName("idpDescription float");
135 | var inc;
136 |
137 | for (inc = 0; inc \< listAllSpanForIdp.length; inc++)
138 | {
139 | if (listAllSpanForIdp[inc].innerHTML.indexOf(oldDisplayName) !== -1)
140 | {
141 | switch (languageKey)
142 | {
143 | case 'fr':
144 | if (newDisplayNameFR !== '')
145 | {
146 | listAllSpanForIdp[inc].innerHTML = listAllSpanForIdp[inc].innerHTML.replace(oldDisplayName, newDisplayNameFR);
147 | }
148 | break;
149 | case 'nl':
150 | if (newDisplayNameNL !== '')
151 | {
152 | listAllSpanForIdp[inc].innerHTML = listAllSpanForIdp[inc].innerHTML.replace(oldDisplayName, newDisplayNameNL);
153 | }
154 | break;
155 | default:
156 | break;
157 | }
158 | }
159 | }
160 | }
161 |
162 | function mapIdpImages()
163 | {
164 | var listAllIdpImg = document.getElementsByTagName('img');
165 | var listAllIdpImg = document.getElementsByTagName('img');
166 | var inc;
167 |
168 | for (inc = 0; inc \< listAllIdpImg.length; inc++)
169 | {
170 | switch ( listAllIdpImg[inc].getAttribute('alt') )
171 | {
172 | case 'IDP1':
173 | listAllIdpImg[inc].src = '/adfs/portal/images/idp/IDP1.png';
174 | break
175 | case 'IDP2':
176 | listAllIdpImg[inc].src = '/adfs/portal/images/idp/IDP2.png';
177 | break
178 | case 'IDP3':
179 | listAllIdpImg[inc].src = '/adfs/portal/images/idp/IDP3.png';
180 | break
181 | case 'Active Directory':
182 | listAllIdpImg[inc].src = '/adfs/portal/images/idp/IDP4.png';
183 | break
184 | }
185 | }
186 | }
187 |
188 | if (typeof HRD != 'undefined')
189 | {
190 | mapIdpImages();
191 | renameLabels('IDP3', 'IDP French translation', 'IDP Dutch translation');
192 | }
193 |
194 | ```
195 |
196 | ### Customstyle.css
197 |
198 | This file holds the CSS styles used by the webtheme, the only change applied is that the pointer is changed from an arrow to a hand.
199 |
200 | ### Image files (\*.png with transparency)
201 |
202 | The archive file contains all the images that will be added to the webtheme.
203 |
204 | How to apply the web theme
205 | --------------------------
206 |
207 | The zip archive contains all the required artifacts.
208 |
209 | 1. Copy the zip archive to an ADFS server.
210 |
211 | 2. Extract the archive to a folder.
212 |
213 | 3. Launch a PowerShell console with elevated privileges
214 |
215 | 4. Navigate to the folder with the artifacts
216 |
217 | 5. Invoke the script ‘**CreateOrUpdateAdfsWebTheme.ps1**’
218 |
219 | The script will create a new ADFS web theme with the configured name and
220 | activate it.
221 |
222 | Remark: In case issues arise or you are not satisfied with the quality of the image(s) (wrong logo, problem with transparency, …) you can activate the default ADFS web theme again as follows:
223 |
224 | **Set-AdfsWebConfig -ActiveThemeName default**
225 |
226 | Taking the sample data used in this document result would something like this (Browser language is Dutch) :
227 |
228 | 
229 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/images/3bbc0a40203d4ce7ba0c4d97b8b7dd93.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/CustomImagesThemeGenerator/images/3bbc0a40203d4ce7ba0c4d97b8b7dd93.png
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/images/9544fd2a4ad07f3956b38493ab2d3453.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/CustomImagesThemeGenerator/images/9544fd2a4ad07f3956b38493ab2d3453.png
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/images/b7f27c9fe4d4a175d9b243f6bb694a85.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/CustomImagesThemeGenerator/images/b7f27c9fe4d4a175d9b243f6bb694a85.png
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/images/c587fa46b0cf0b5afbac869eb426db97.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/CustomImagesThemeGenerator/images/c587fa46b0cf0b5afbac869eb426db97.png
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Digitude.Adfs.CustomImagesThemeGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {F645A08A-3C1F-4260-BD12-823681D07E3E}
8 | WinExe
9 | Digitude.Adfs.CustomImagesThemeGenerator
10 | CustomImagesThemeGenerator
11 | v4.6
12 | 512
13 | true
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | Form
56 |
57 |
58 | Main.cs
59 |
60 |
61 |
62 |
63 | WebThemeInfo.xsd
64 |
65 |
66 | True
67 | True
68 | WebThemeInfo.xsd
69 |
70 |
71 | Main.cs
72 |
73 |
74 | ResXFileCodeGenerator
75 | Resources.Designer.cs
76 | Designer
77 |
78 |
79 | True
80 | Resources.resx
81 | True
82 |
83 |
84 | SettingsSingleFileGenerator
85 | Settings.Designer.cs
86 |
87 |
88 | True
89 | Settings.settings
90 | True
91 |
92 |
93 | WebThemeInfo.xsd
94 |
95 |
96 | Designer
97 | MSDataSetGenerator
98 | WebThemeInfo.Designer.cs
99 |
100 |
101 | WebThemeInfo.xsd
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Main.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace Digitude.Adfs.CustomImagesThemeGenerator
2 | {
3 | partial class Main
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | this.components = new System.ComponentModel.Container();
32 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main));
33 | this.dataGridView = new System.Windows.Forms.DataGridView();
34 | this.displayNameDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
35 | this.DisplayNameFR = new System.Windows.Forms.DataGridViewTextBoxColumn();
36 | this.DisplayNameNL = new System.Windows.Forms.DataGridViewTextBoxColumn();
37 | this.imageLocationDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
38 | this.identityProviderInfoBindingSource = new System.Windows.Forms.BindingSource(this.components);
39 | this.webThemeInfoBindingSource = new System.Windows.Forms.BindingSource(this.components);
40 | this.webThemeInfo = new Digitude.Adfs.CustomImagesThemeGenerator.WebThemeInfo();
41 | this.textBoxWebThemeName = new System.Windows.Forms.TextBox();
42 | this.label1 = new System.Windows.Forms.Label();
43 | this.label2 = new System.Windows.Forms.Label();
44 | this.buttonGenerate = new System.Windows.Forms.Button();
45 | this.toolStrip1 = new System.Windows.Forms.ToolStrip();
46 | this.newToolStripButton = new System.Windows.Forms.ToolStripButton();
47 | this.openToolStripButton = new System.Windows.Forms.ToolStripButton();
48 | this.saveToolStripButton = new System.Windows.Forms.ToolStripButton();
49 | this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
50 | this.toolStripButton1 = new System.Windows.Forms.ToolStripButton();
51 | this.openFileDialog = new System.Windows.Forms.OpenFileDialog();
52 | this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
53 | this.browseImageDialog = new System.Windows.Forms.OpenFileDialog();
54 | this.errorProvider = new System.Windows.Forms.ErrorProvider(this.components);
55 | this.saveZipDialog = new System.Windows.Forms.SaveFileDialog();
56 | this.label3 = new System.Windows.Forms.Label();
57 | this.textBoxWebThemeScriptFile = new System.Windows.Forms.TextBox();
58 | this.buttonBrowseJavascript = new System.Windows.Forms.Button();
59 | this.buttonBrowsStyleFile = new System.Windows.Forms.Button();
60 | this.label4 = new System.Windows.Forms.Label();
61 | this.textBoxWebThemeStyleFile = new System.Windows.Forms.TextBox();
62 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).BeginInit();
63 | ((System.ComponentModel.ISupportInitialize)(this.identityProviderInfoBindingSource)).BeginInit();
64 | ((System.ComponentModel.ISupportInitialize)(this.webThemeInfoBindingSource)).BeginInit();
65 | ((System.ComponentModel.ISupportInitialize)(this.webThemeInfo)).BeginInit();
66 | this.toolStrip1.SuspendLayout();
67 | ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).BeginInit();
68 | this.SuspendLayout();
69 | //
70 | // dataGridView
71 | //
72 | this.dataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
73 | | System.Windows.Forms.AnchorStyles.Left)
74 | | System.Windows.Forms.AnchorStyles.Right)));
75 | this.dataGridView.AutoGenerateColumns = false;
76 | this.dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
77 | this.dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
78 | this.displayNameDataGridViewTextBoxColumn,
79 | this.DisplayNameFR,
80 | this.DisplayNameNL,
81 | this.imageLocationDataGridViewTextBoxColumn});
82 | this.dataGridView.DataSource = this.identityProviderInfoBindingSource;
83 | this.dataGridView.Location = new System.Drawing.Point(11, 138);
84 | this.dataGridView.Name = "dataGridView";
85 | this.dataGridView.Size = new System.Drawing.Size(1056, 253);
86 | this.dataGridView.TabIndex = 10;
87 | this.dataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView_CellContentClick);
88 | //
89 | // displayNameDataGridViewTextBoxColumn
90 | //
91 | this.displayNameDataGridViewTextBoxColumn.DataPropertyName = "DisplayName";
92 | this.displayNameDataGridViewTextBoxColumn.HeaderText = "DisplayName";
93 | this.displayNameDataGridViewTextBoxColumn.Name = "displayNameDataGridViewTextBoxColumn";
94 | //
95 | // DisplayNameFR
96 | //
97 | this.DisplayNameFR.DataPropertyName = "DisplayNameFR";
98 | this.DisplayNameFR.HeaderText = "DisplayNameFR";
99 | this.DisplayNameFR.Name = "DisplayNameFR";
100 | this.DisplayNameFR.Width = 200;
101 | //
102 | // DisplayNameNL
103 | //
104 | this.DisplayNameNL.DataPropertyName = "DisplayNameNL";
105 | this.DisplayNameNL.HeaderText = "DisplayNameNL";
106 | this.DisplayNameNL.Name = "DisplayNameNL";
107 | this.DisplayNameNL.Width = 200;
108 | //
109 | // imageLocationDataGridViewTextBoxColumn
110 | //
111 | this.imageLocationDataGridViewTextBoxColumn.DataPropertyName = "ImageLocation";
112 | this.imageLocationDataGridViewTextBoxColumn.HeaderText = "ImageLocation";
113 | this.imageLocationDataGridViewTextBoxColumn.Name = "imageLocationDataGridViewTextBoxColumn";
114 | this.imageLocationDataGridViewTextBoxColumn.Width = 200;
115 | //
116 | // identityProviderInfoBindingSource
117 | //
118 | this.identityProviderInfoBindingSource.DataMember = "IdentityProviderInfo";
119 | this.identityProviderInfoBindingSource.DataSource = this.webThemeInfoBindingSource;
120 | //
121 | // webThemeInfoBindingSource
122 | //
123 | this.webThemeInfoBindingSource.DataSource = this.webThemeInfo;
124 | this.webThemeInfoBindingSource.Position = 0;
125 | //
126 | // webThemeInfo
127 | //
128 | this.webThemeInfo.DataSetName = "WebThemeInfo";
129 | this.webThemeInfo.SchemaSerializationMode = System.Data.SchemaSerializationMode.IncludeSchema;
130 | //
131 | // textBoxWebThemeName
132 | //
133 | this.textBoxWebThemeName.Location = new System.Drawing.Point(151, 28);
134 | this.textBoxWebThemeName.Name = "textBoxWebThemeName";
135 | this.textBoxWebThemeName.Size = new System.Drawing.Size(151, 20);
136 | this.textBoxWebThemeName.TabIndex = 2;
137 | this.textBoxWebThemeName.TextChanged += new System.EventHandler(this.textBoxWebThemeName_TextChanged);
138 | //
139 | // label1
140 | //
141 | this.label1.AutoSize = true;
142 | this.label1.Location = new System.Drawing.Point(8, 31);
143 | this.label1.Name = "label1";
144 | this.label1.Size = new System.Drawing.Size(122, 13);
145 | this.label1.TabIndex = 1;
146 | this.label1.Text = "ADFS web theme name:";
147 | //
148 | // label2
149 | //
150 | this.label2.AutoSize = true;
151 | this.label2.Location = new System.Drawing.Point(8, 112);
152 | this.label2.Name = "label2";
153 | this.label2.Size = new System.Drawing.Size(169, 13);
154 | this.label2.TabIndex = 9;
155 | this.label2.Text = "ADFS Identity Provider Information";
156 | //
157 | // buttonGenerate
158 | //
159 | this.buttonGenerate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
160 | this.buttonGenerate.Location = new System.Drawing.Point(992, 397);
161 | this.buttonGenerate.Name = "buttonGenerate";
162 | this.buttonGenerate.Size = new System.Drawing.Size(75, 23);
163 | this.buttonGenerate.TabIndex = 11;
164 | this.buttonGenerate.Text = "Generate";
165 | this.buttonGenerate.UseVisualStyleBackColor = true;
166 | this.buttonGenerate.Click += new System.EventHandler(this.buttonGenerate_Click);
167 | //
168 | // toolStrip1
169 | //
170 | this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
171 | this.newToolStripButton,
172 | this.openToolStripButton,
173 | this.saveToolStripButton,
174 | this.toolStripSeparator1,
175 | this.toolStripButton1});
176 | this.toolStrip1.Location = new System.Drawing.Point(0, 0);
177 | this.toolStrip1.Name = "toolStrip1";
178 | this.toolStrip1.Size = new System.Drawing.Size(1079, 25);
179 | this.toolStrip1.TabIndex = 0;
180 | this.toolStrip1.Text = "toolStrip1";
181 | //
182 | // newToolStripButton
183 | //
184 | this.newToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
185 | this.newToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("newToolStripButton.Image")));
186 | this.newToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
187 | this.newToolStripButton.Name = "newToolStripButton";
188 | this.newToolStripButton.Size = new System.Drawing.Size(23, 22);
189 | this.newToolStripButton.Text = "&New";
190 | this.newToolStripButton.Click += new System.EventHandler(this.newToolStripButton_Click);
191 | //
192 | // openToolStripButton
193 | //
194 | this.openToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
195 | this.openToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("openToolStripButton.Image")));
196 | this.openToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
197 | this.openToolStripButton.Name = "openToolStripButton";
198 | this.openToolStripButton.Size = new System.Drawing.Size(23, 22);
199 | this.openToolStripButton.Text = "&Open";
200 | this.openToolStripButton.Click += new System.EventHandler(this.openToolStripButton_Click);
201 | //
202 | // saveToolStripButton
203 | //
204 | this.saveToolStripButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
205 | this.saveToolStripButton.Image = ((System.Drawing.Image)(resources.GetObject("saveToolStripButton.Image")));
206 | this.saveToolStripButton.ImageTransparentColor = System.Drawing.Color.Magenta;
207 | this.saveToolStripButton.Name = "saveToolStripButton";
208 | this.saveToolStripButton.Size = new System.Drawing.Size(23, 22);
209 | this.saveToolStripButton.Text = "&Save";
210 | this.saveToolStripButton.Click += new System.EventHandler(this.saveToolStripButton_Click);
211 | //
212 | // toolStripSeparator1
213 | //
214 | this.toolStripSeparator1.Name = "toolStripSeparator1";
215 | this.toolStripSeparator1.Size = new System.Drawing.Size(6, 25);
216 | //
217 | // toolStripButton1
218 | //
219 | this.toolStripButton1.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
220 | this.toolStripButton1.Image = ((System.Drawing.Image)(resources.GetObject("toolStripButton1.Image")));
221 | this.toolStripButton1.ImageTransparentColor = System.Drawing.Color.Magenta;
222 | this.toolStripButton1.Name = "toolStripButton1";
223 | this.toolStripButton1.Size = new System.Drawing.Size(23, 22);
224 | this.toolStripButton1.Text = "&Import list of IDP names";
225 | this.toolStripButton1.Click += new System.EventHandler(this.toolStripButton1_Click);
226 | //
227 | // saveFileDialog
228 | //
229 | this.saveFileDialog.Filter = "ADFS WebTheme Info|*.awi";
230 | //
231 | // browseImageDialog
232 | //
233 | this.browseImageDialog.Filter = "Png files|*.png|Jpg files|*.jpg";
234 | //
235 | // errorProvider
236 | //
237 | this.errorProvider.ContainerControl = this;
238 | //
239 | // saveZipDialog
240 | //
241 | this.saveZipDialog.Filter = "Zip files|*.zip";
242 | //
243 | // label3
244 | //
245 | this.label3.AutoSize = true;
246 | this.label3.Location = new System.Drawing.Point(8, 58);
247 | this.label3.Name = "label3";
248 | this.label3.Size = new System.Drawing.Size(137, 13);
249 | this.label3.TabIndex = 3;
250 | this.label3.Text = "ADFS web theme script file:";
251 | //
252 | // textBoxWebThemeScriptFile
253 | //
254 | this.textBoxWebThemeScriptFile.Location = new System.Drawing.Point(151, 55);
255 | this.textBoxWebThemeScriptFile.Name = "textBoxWebThemeScriptFile";
256 | this.textBoxWebThemeScriptFile.Size = new System.Drawing.Size(607, 20);
257 | this.textBoxWebThemeScriptFile.TabIndex = 4;
258 | //
259 | // buttonBrowseJavascript
260 | //
261 | this.buttonBrowseJavascript.Location = new System.Drawing.Point(760, 53);
262 | this.buttonBrowseJavascript.Name = "buttonBrowseJavascript";
263 | this.buttonBrowseJavascript.Size = new System.Drawing.Size(28, 22);
264 | this.buttonBrowseJavascript.TabIndex = 5;
265 | this.buttonBrowseJavascript.Text = "...";
266 | this.buttonBrowseJavascript.UseVisualStyleBackColor = true;
267 | this.buttonBrowseJavascript.Click += new System.EventHandler(this.buttonBrowseJavascript_Click);
268 | //
269 | // buttonBrowsStyleFile
270 | //
271 | this.buttonBrowsStyleFile.Location = new System.Drawing.Point(760, 81);
272 | this.buttonBrowsStyleFile.Name = "buttonBrowsStyleFile";
273 | this.buttonBrowsStyleFile.Size = new System.Drawing.Size(28, 22);
274 | this.buttonBrowsStyleFile.TabIndex = 8;
275 | this.buttonBrowsStyleFile.Text = "...";
276 | this.buttonBrowsStyleFile.UseVisualStyleBackColor = true;
277 | this.buttonBrowsStyleFile.Click += new System.EventHandler(this.buttonBrowsStyleFile_Click);
278 | //
279 | // label4
280 | //
281 | this.label4.AutoSize = true;
282 | this.label4.Location = new System.Drawing.Point(8, 85);
283 | this.label4.Name = "label4";
284 | this.label4.Size = new System.Drawing.Size(133, 13);
285 | this.label4.TabIndex = 6;
286 | this.label4.Text = "ADFS web theme style file:";
287 | //
288 | // textBoxWebThemeStyleFile
289 | //
290 | this.textBoxWebThemeStyleFile.Location = new System.Drawing.Point(151, 82);
291 | this.textBoxWebThemeStyleFile.Name = "textBoxWebThemeStyleFile";
292 | this.textBoxWebThemeStyleFile.Size = new System.Drawing.Size(607, 20);
293 | this.textBoxWebThemeStyleFile.TabIndex = 7;
294 | //
295 | // Main
296 | //
297 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
298 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
299 | this.ClientSize = new System.Drawing.Size(1079, 432);
300 | this.Controls.Add(this.buttonBrowsStyleFile);
301 | this.Controls.Add(this.label4);
302 | this.Controls.Add(this.textBoxWebThemeStyleFile);
303 | this.Controls.Add(this.buttonBrowseJavascript);
304 | this.Controls.Add(this.label3);
305 | this.Controls.Add(this.textBoxWebThemeScriptFile);
306 | this.Controls.Add(this.toolStrip1);
307 | this.Controls.Add(this.buttonGenerate);
308 | this.Controls.Add(this.label2);
309 | this.Controls.Add(this.label1);
310 | this.Controls.Add(this.textBoxWebThemeName);
311 | this.Controls.Add(this.dataGridView);
312 | this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
313 | this.Name = "Main";
314 | this.Text = "ADFS Generate theme script (IDP\'s with different images)";
315 | this.Load += new System.EventHandler(this.Form1_Load);
316 | ((System.ComponentModel.ISupportInitialize)(this.dataGridView)).EndInit();
317 | ((System.ComponentModel.ISupportInitialize)(this.identityProviderInfoBindingSource)).EndInit();
318 | ((System.ComponentModel.ISupportInitialize)(this.webThemeInfoBindingSource)).EndInit();
319 | ((System.ComponentModel.ISupportInitialize)(this.webThemeInfo)).EndInit();
320 | this.toolStrip1.ResumeLayout(false);
321 | this.toolStrip1.PerformLayout();
322 | ((System.ComponentModel.ISupportInitialize)(this.errorProvider)).EndInit();
323 | this.ResumeLayout(false);
324 | this.PerformLayout();
325 |
326 | }
327 |
328 | #endregion
329 |
330 | private System.Windows.Forms.DataGridView dataGridView;
331 | private WebThemeInfo webThemeInfo;
332 | private System.Windows.Forms.BindingSource identityProviderInfoBindingSource;
333 | private System.Windows.Forms.BindingSource webThemeInfoBindingSource;
334 | private System.Windows.Forms.TextBox textBoxWebThemeName;
335 | private System.Windows.Forms.Label label1;
336 | private System.Windows.Forms.Label label2;
337 | private System.Windows.Forms.Button buttonGenerate;
338 | private System.Windows.Forms.ToolStrip toolStrip1;
339 | private System.Windows.Forms.ToolStripButton newToolStripButton;
340 | private System.Windows.Forms.ToolStripButton openToolStripButton;
341 | private System.Windows.Forms.ToolStripButton saveToolStripButton;
342 | private System.Windows.Forms.OpenFileDialog openFileDialog;
343 | private System.Windows.Forms.SaveFileDialog saveFileDialog;
344 | private System.Windows.Forms.OpenFileDialog browseImageDialog;
345 | private System.Windows.Forms.ErrorProvider errorProvider;
346 | private System.Windows.Forms.SaveFileDialog saveZipDialog;
347 | private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
348 | private System.Windows.Forms.ToolStripButton toolStripButton1;
349 | private System.Windows.Forms.Button buttonBrowsStyleFile;
350 | private System.Windows.Forms.Label label4;
351 | private System.Windows.Forms.TextBox textBoxWebThemeStyleFile;
352 | private System.Windows.Forms.Button buttonBrowseJavascript;
353 | private System.Windows.Forms.Label label3;
354 | private System.Windows.Forms.TextBox textBoxWebThemeScriptFile;
355 | private System.Windows.Forms.DataGridViewTextBoxColumn displayNameDataGridViewTextBoxColumn;
356 | private System.Windows.Forms.DataGridViewTextBoxColumn DisplayNameFR;
357 | private System.Windows.Forms.DataGridViewTextBoxColumn DisplayNameNL;
358 | private System.Windows.Forms.DataGridViewTextBoxColumn imageLocationDataGridViewTextBoxColumn;
359 | }
360 | }
361 |
362 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Main.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Data;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Linq;
7 | using System.Reflection;
8 | using System.Text;
9 | using System.Windows.Forms;
10 |
11 | namespace Digitude.Adfs.CustomImagesThemeGenerator
12 | {
13 | public partial class Main : Form
14 | {
15 | public Main()
16 | {
17 | InitializeComponent();
18 | }
19 |
20 | private void Form1_Load(object sender, EventArgs e)
21 | {
22 | this.Text += $" [v{Assembly.GetExecutingAssembly().GetName().Version}]";
23 | dataGridView.Columns[0].Width = 100;
24 | dataGridView.Columns[1].Width = 200;
25 | dataGridView.Columns[2].Width = 200;
26 | dataGridView.Columns[3].Width = 450;
27 | dataGridView.Columns[3].ReadOnly = true;
28 | var browseImageButtonColumn = new DataGridViewButtonColumn
29 | {
30 | Name = "BrowseImage",
31 | HeaderText = "...",
32 | Width = 30,
33 | Text = "...",
34 | UseColumnTextForButtonValue = true
35 | };
36 | browseImageButtonColumn.DefaultCellStyle.Padding = new Padding(2, 1, 2, 1);
37 | dataGridView.Columns.Add(browseImageButtonColumn);
38 | }
39 |
40 | private void saveToolStripButton_Click(object sender, EventArgs e)
41 | {
42 | try
43 | {
44 | if (string.IsNullOrEmpty(textBoxWebThemeName.Text))
45 | {
46 | errorProvider.SetError(textBoxWebThemeName, "Please provide a name for the web theme.");
47 | return;
48 | }
49 | else
50 | {
51 | errorProvider.Clear();
52 | }
53 |
54 | if (webThemeInfo.IdentityProviderInfo.Rows.Count == 0)
55 | {
56 | MessageBox.Show("Please add theme info.");
57 | return;
58 | }
59 |
60 | if (saveFileDialog.ShowDialog() == DialogResult.OK)
61 | {
62 | if (webThemeInfo.ThemeInfo.Rows.Count == 0)
63 | {
64 | var row = webThemeInfo.ThemeInfo.NewThemeInfoRow();
65 | row.Name = textBoxWebThemeName.Text;
66 | webThemeInfo.ThemeInfo.Rows.Add(row);
67 | }
68 | else
69 | {
70 | WebThemeInfo.ThemeInfoRow row = webThemeInfo.ThemeInfo.Rows[0] as WebThemeInfo.ThemeInfoRow;
71 | row.Name = textBoxWebThemeName.Text;
72 | }
73 |
74 | webThemeInfo.WriteXml(saveFileDialog.FileName);
75 | }
76 | }
77 | catch (Exception exception)
78 | {
79 | MessageBox.Show(exception.Message);
80 | }
81 | }
82 |
83 | private void openToolStripButton_Click(object sender, EventArgs e)
84 | {
85 | try
86 | {
87 | openFileDialog.Filter = "ADFS WebTheme Info | *.awi";
88 |
89 | if (openFileDialog.ShowDialog() == DialogResult.OK)
90 | {
91 | webThemeInfo.Clear();
92 | webThemeInfo.ReadXml(openFileDialog.FileName);
93 | webThemeInfoBindingSource.ResetBindings(false);
94 | identityProviderInfoBindingSource.ResetBindings(false);
95 |
96 | if (webThemeInfo.ThemeInfo.Rows.Count > 0)
97 | {
98 | WebThemeInfo.ThemeInfoRow row = webThemeInfo.ThemeInfo.Rows[0] as WebThemeInfo.ThemeInfoRow;
99 | textBoxWebThemeName.Text = row.Name;
100 | }
101 | }
102 | }
103 | catch (Exception exception)
104 | {
105 | MessageBox.Show(exception.Message);
106 | }
107 | }
108 |
109 | private void dataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
110 | {
111 | var senderGrid = (DataGridView)sender;
112 |
113 | if (senderGrid.Columns[e.ColumnIndex] is DataGridViewButtonColumn && e.ColumnIndex == 4 && e.RowIndex >= 0)
114 | {
115 | if (senderGrid.Rows[e.RowIndex].IsNewRow == false)
116 | {
117 | if (browseImageDialog.ShowDialog() == DialogResult.OK)
118 | {
119 | senderGrid.Rows[e.RowIndex].Cells[3].Value = browseImageDialog.FileName;
120 | }
121 | }
122 | }
123 | }
124 |
125 | private void buttonGenerate_Click(object sender, EventArgs e)
126 | {
127 | try
128 | {
129 | if (string.IsNullOrEmpty(textBoxWebThemeName.Text))
130 | {
131 | errorProvider.SetError(textBoxWebThemeName, "Please provide a name for the web theme.");
132 | return;
133 | }
134 | else
135 | {
136 | errorProvider.Clear();
137 | }
138 |
139 | var javascriptBuffer = new StringBuilder();
140 | var powerShellScriptBuffer = new StringBuilder();
141 |
142 | var customThemeFunctions =
143 | @"
144 | // Added by the ADFS web theme generator (custom idp images).
145 |
146 | var language = document.documentElement.lang;
147 | var languageKey = 'en';
148 |
149 | if (language.lastIndexOf('nl', 0) === 0)
150 | {
151 | languageKey = 'nl';
152 | }
153 | else if (language.lastIndexOf('fr', 0) === 0)
154 | {
155 | languageKey = 'fr';
156 | }
157 |
158 | function renameLabels(oldDisplayName, newDisplayNameFR, newDisplayNameNL) {
159 | var listAllSpanForIdp = document.getElementsByClassName('idpDescription float');
160 | var inc;
161 | for (inc = 0; inc < listAllSpanForIdp.length; inc++)
162 | {
163 | if (listAllSpanForIdp[inc].innerHTML.indexOf(oldDisplayName) !== -1)
164 | {
165 | switch (languageKey)
166 | {
167 | case 'fr':
168 | if (newDisplayNameFR !== '')
169 | {
170 | listAllSpanForIdp[inc].innerHTML = listAllSpanForIdp[inc].innerHTML.replace(oldDisplayName, newDisplayNameFR);
171 | }
172 | break;
173 | case 'nl':
174 | if (newDisplayNameNL !== '')
175 | {
176 | listAllSpanForIdp[inc].innerHTML = listAllSpanForIdp[inc].innerHTML.replace(oldDisplayName, newDisplayNameNL);
177 | }
178 | break;
179 | default:
180 | break;
181 | }
182 | }
183 | }
184 | }
185 |
186 | function mapIdpImages()
187 | {
188 | var listAllIdpImg = document.getElementsByTagName('img');
189 | var listAllIdpImg = document.getElementsByTagName('img');
190 | var inc;
191 | for (inc = 0; inc < listAllIdpImg.length; inc++)
192 | {
193 | switch ( listAllIdpImg[inc].getAttribute('alt') )
194 | {
195 | [SwitchStatements]
196 | }
197 | }
198 | }
199 |
200 | if (typeof HRD != 'undefined')
201 | {
202 | mapIdpImages();
203 | [RenameLabels]
204 | }";
205 |
206 | powerShellScriptBuffer.AppendLine($"$webThemeName = '{textBoxWebThemeName.Text}'");
207 | powerShellScriptBuffer.AppendLine("$webTheme = Get-AdfsWebTheme -Name $webThemeName");
208 | powerShellScriptBuffer.AppendLine("if (!$webTheme)");
209 | powerShellScriptBuffer.AppendLine("{");
210 | powerShellScriptBuffer.AppendLine($"New-AdfsWebTheme -Name {textBoxWebThemeName.Text} -SourceName default");
211 | powerShellScriptBuffer.AppendLine("}");
212 |
213 | var switchStatements = new StringBuilder();
214 | var renameLabelStatements = new StringBuilder();
215 |
216 | foreach (WebThemeInfo.IdentityProviderInfoRow providerInfoRow in webThemeInfo.Tables["IdentityProviderInfo"].Rows)
217 | {
218 | var fileName = new FileInfo(providerInfoRow.ImageLocation).Name;
219 |
220 | switchStatements.AppendLine($"\tcase '{providerInfoRow.DisplayName}':");
221 | switchStatements.AppendLine($"\t\tlistAllIdpImg[inc].src = '/adfs/portal/images/idp/{fileName}';");
222 | switchStatements.AppendLine("\t\tbreak");
223 |
224 | if(!string.IsNullOrEmpty(providerInfoRow.DisplayNameFR) && !string.IsNullOrEmpty(providerInfoRow.DisplayNameNL))
225 | {
226 | renameLabelStatements.AppendLine($"\trenameLabels('{providerInfoRow.DisplayName}', '{providerInfoRow.DisplayNameFR}', '{providerInfoRow.DisplayNameNL}');");
227 | }
228 |
229 | powerShellScriptBuffer.AppendLine($"Set-AdfsWebTheme -TargetName {textBoxWebThemeName.Text} -AdditionalFileResource @{{Uri =\"/adfs/portal/images/idp/{fileName}\";path=\"{fileName}\"}}");
230 | }
231 |
232 | customThemeFunctions = customThemeFunctions.Replace("[SwitchStatements]", switchStatements.ToString());
233 | customThemeFunctions = customThemeFunctions.Replace("[RenameLabels]", renameLabelStatements.ToString());
234 |
235 | javascriptBuffer.Append(this.GetJavascriptFile());
236 | javascriptBuffer.AppendLine();
237 | javascriptBuffer.AppendLine();
238 | javascriptBuffer.AppendLine(customThemeFunctions);
239 |
240 | powerShellScriptBuffer.AppendLine();
241 | powerShellScriptBuffer.AppendLine($"Set-AdfsWebTheme -TargetName {textBoxWebThemeName.Text} -AdditionalFileResource @{{Uri =\"/adfs/portal/script/onload.js\";path=\"customOnload.js\"}}");
242 | powerShellScriptBuffer.AppendLine();
243 | powerShellScriptBuffer.AppendLine($"Set-AdfsWebTheme -TargetName {textBoxWebThemeName.Text} -StyleSheet @{{Path=\"customstyle.css\"}}");
244 | powerShellScriptBuffer.AppendLine();
245 | powerShellScriptBuffer.AppendLine($"Set-AdfsWebConfig -ActiveThemeName {textBoxWebThemeName.Text}");
246 |
247 | using (var memoryStream = new MemoryStream())
248 | {
249 | using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
250 | {
251 | var powershellScriptFile = archive.CreateEntry("CreateOrUpdateAdfsWebTheme.ps1");
252 |
253 | using (var entryStream = powershellScriptFile.Open())
254 | using (var streamWriter = new StreamWriter(entryStream))
255 | {
256 | streamWriter.Write(powerShellScriptBuffer.ToString());
257 | }
258 |
259 | var javaScriptFile = archive.CreateEntry("customonload.js");
260 |
261 | using (var entryStream = javaScriptFile.Open())
262 | using (var streamWriter = new StreamWriter(entryStream))
263 | {
264 | streamWriter.Write(javascriptBuffer.ToString());
265 | }
266 |
267 | var styleFile = archive.CreateEntry("customstyle.css");
268 |
269 | using (var entryStream = styleFile.Open())
270 | using (var streamWriter = new StreamWriter(entryStream))
271 | {
272 | streamWriter.Write(this.GetStyleFile());
273 | }
274 |
275 | foreach (WebThemeInfo.IdentityProviderInfoRow providerInfoRow in webThemeInfo.Tables["IdentityProviderInfo"].Rows)
276 | {
277 | var fileName = new FileInfo(providerInfoRow.ImageLocation).Name;
278 |
279 | archive.CreateEntryFromFile(providerInfoRow.ImageLocation, fileName);
280 | }
281 | }
282 |
283 | if (saveZipDialog.ShowDialog() == DialogResult.OK)
284 | {
285 | using (var fileStream = new FileStream(saveZipDialog.FileName, FileMode.Create))
286 | {
287 | memoryStream.Seek(0, SeekOrigin.Begin);
288 | memoryStream.CopyTo(fileStream);
289 | }
290 |
291 | MessageBox.Show("Zip archive has been created.");
292 | }
293 | }
294 | }
295 | catch (Exception exception)
296 | {
297 | MessageBox.Show(exception.Message);
298 | }
299 | }
300 |
301 | private void toolStripButton1_Click(object sender, EventArgs e)
302 | {
303 | try
304 | {
305 | const string headerName = "CPT Name";
306 |
307 | Application.UseWaitCursor = true;
308 |
309 | openFileDialog.Filter = "Text files | *.txt";
310 |
311 | if (openFileDialog.ShowDialog() == DialogResult.OK)
312 | {
313 | var fileContent = new List(File.ReadLines(openFileDialog.FileName));
314 |
315 | IEnumerable claimProviderNames = fileContent.Select(s => s.Trim());
316 |
317 | if (!claimProviderNames.Contains(headerName))
318 | {
319 | MessageBox.Show("Please use only files generated by using the PowerShell command: \nGet-AdfsClaimsProviderTrust | select @{Name=\"CPT Name\";Expression={$_.Name}} > [outputFile].txt");
320 | }
321 | else
322 | {
323 | webThemeInfo.IdentityProviderInfo.Clear();
324 |
325 | foreach (var claimProviderName in claimProviderNames)
326 | {
327 | if (claimProviderName != headerName && !claimProviderName.Contains("------") && !string.IsNullOrEmpty(claimProviderName))
328 | {
329 | WebThemeInfo.IdentityProviderInfoRow row = webThemeInfo.IdentityProviderInfo.NewIdentityProviderInfoRow();
330 | row.DisplayName = claimProviderName;
331 | webThemeInfo.IdentityProviderInfo.AddIdentityProviderInfoRow(row);
332 | }
333 | }
334 | }
335 | }
336 | }
337 | catch (Exception exception)
338 | {
339 | MessageBox.Show(exception.Message);
340 | }
341 | finally
342 | {
343 | Application.UseWaitCursor = false;
344 | }
345 | }
346 |
347 | private void newToolStripButton_Click(object sender, EventArgs e)
348 | {
349 | webThemeInfo.ThemeInfo.Clear();
350 | webThemeInfo.IdentityProviderInfo.Clear();
351 | webThemeInfoBindingSource.ResetBindings(false);
352 | identityProviderInfoBindingSource.ResetBindings(false);
353 |
354 | textBoxWebThemeName.Text = string.Empty;
355 | }
356 |
357 | private void textBoxWebThemeName_TextChanged(object sender, EventArgs e)
358 | {
359 | if (string.IsNullOrEmpty(textBoxWebThemeName.Text))
360 | {
361 | errorProvider.SetError(textBoxWebThemeName, "Please provide a name for the web theme.");
362 | }
363 | else
364 | {
365 | errorProvider.Clear();
366 | }
367 | }
368 |
369 | private string GetJavascriptFile()
370 | {
371 | if(string.IsNullOrEmpty(textBoxWebThemeScriptFile.Text))
372 | {
373 | return Properties.Resources.onload;
374 | }
375 | else
376 | {
377 | return File.ReadAllText(textBoxWebThemeScriptFile.Text);
378 | }
379 | }
380 |
381 | private string GetStyleFile()
382 | {
383 | if (string.IsNullOrEmpty(textBoxWebThemeStyleFile.Text))
384 | {
385 | return Properties.Resources.customstyle;
386 | }
387 | else
388 | {
389 | return File.ReadAllText(textBoxWebThemeStyleFile.Text);
390 | }
391 | }
392 |
393 | private void buttonBrowseJavascript_Click(object sender, EventArgs e)
394 | {
395 | try
396 | {
397 | openFileDialog.Filter = "ADFS WebTheme onload| *.js";
398 |
399 | if (openFileDialog.ShowDialog() == DialogResult.OK)
400 | {
401 | textBoxWebThemeScriptFile.Text = openFileDialog.FileName;
402 | }
403 | }
404 | catch (Exception exception)
405 | {
406 | MessageBox.Show(exception.Message);
407 | }
408 | }
409 |
410 | private void buttonBrowsStyleFile_Click(object sender, EventArgs e)
411 | {
412 | try
413 | {
414 | openFileDialog.Filter = "ADFS WebTheme style sheet| *.css";
415 |
416 | if (openFileDialog.ShowDialog() == DialogResult.OK)
417 | {
418 | textBoxWebThemeStyleFile.Text = openFileDialog.FileName;
419 | }
420 | }
421 | catch (Exception exception)
422 | {
423 | MessageBox.Show(exception.Message);
424 | }
425 | }
426 | }
427 | }
428 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using System.Windows.Forms;
6 |
7 | namespace Digitude.Adfs.CustomImagesThemeGenerator
8 | {
9 | static class Program
10 | {
11 | ///
12 | /// The main entry point for the application.
13 | ///
14 | [STAThread]
15 | static void Main()
16 | {
17 | Application.EnableVisualStyles();
18 | Application.SetCompatibleTextRenderingDefault(false);
19 | Application.Run(new Main());
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("AdfsThemeGenerator")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("AdfsThemeGenerator")]
13 | [assembly: AssemblyCopyright("Copyright © 2019")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("f645a08a-3c1f-4260-bd12-823681d07e3e")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Digitude.Adfs.CustomImagesThemeGenerator.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Digitude.Adfs.CustomImagesThemeGenerator.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to * {
65 | /// margin:0px;
66 | /// padding:0px;
67 | ///}
68 | ///html, body
69 | ///{
70 | /// height:100%;
71 | /// width:100%;
72 | /// background-color:#ffffff;
73 | /// color:#000000;
74 | /// font-weight:normal;
75 | /// font-family:"Segoe UI" , "Segoe" , "SegoeUI-Regular-final", Tahoma, Helvetica, Arial, sans-serif;
76 | /// min-width:500px;
77 | /// -ms-overflow-style:-ms-autohiding-scrollbar;
78 | ///}
79 | ///
80 | ///body
81 | ///{
82 | /// font-size:0.9em;
83 | ///}
84 | ///
85 | ///#noScript { margin:16px; color:Black; }
86 | ///
87 | ///:lang(en-GB){quotes:'\2018' '\2019' '\201C' '\201D';}
88 | ///:lang(zh){font-family:微软雅黑;}
89 | ///
90 | ///@-m [rest of string was truncated]";.
91 | ///
92 | internal static string customstyle {
93 | get {
94 | return ResourceManager.GetString("customstyle", resourceCulture);
95 | }
96 | }
97 |
98 | ///
99 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
100 | ///
101 | internal static System.Drawing.Icon mockup_YCw_icon {
102 | get {
103 | object obj = ResourceManager.GetObject("mockup_YCw_icon", resourceCulture);
104 | return ((System.Drawing.Icon)(obj));
105 | }
106 | }
107 |
108 | ///
109 | /// Looks up a localized string similar to // Copyright (c) Microsoft Corporation. All rights reserved.
110 | ///
111 | ///// This file contains several workarounds on inconsistent browser behaviors that administrators may customize.
112 | ///"use strict";
113 | ///
114 | ///var language = document.documentElement.lang;
115 | ///var languageKey = 'en';
116 | ///
117 | ///if (language.lastIndexOf('nl', 0) === 0) {
118 | /// languageKey = 'nl';
119 | ///}
120 | ///else if (language.lastIndexOf('fr', 0) === 0) {
121 | /// languageKey = 'fr';
122 | ///}
123 | ///
124 | ///// iPhone email friendly keyboard does not include "\" key, use regular keyboard instead.
125 | ///// [rest of string was truncated]";.
126 | ///
127 | internal static string onload {
128 | get {
129 | return ResourceManager.GetString("onload", resourceCulture);
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\style.css;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8
123 |
124 |
125 | ..\Resources\mockup_YCw_icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
126 |
127 |
128 | ..\Resources\onload.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
129 |
130 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Digitude.Adfs.CustomImagesThemeGenerator.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Resources/mockup_YCw_icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/CustomImagesThemeGenerator/src/Resources/mockup_YCw_icon.ico
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Resources/onload.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 |
3 | // This file contains several workarounds on inconsistent browser behaviors that administrators may customize.
4 | "use strict";
5 |
6 | // iPhone email friendly keyboard does not include "\" key, use regular keyboard instead.
7 | // Note change input type does not work on all versions of all browsers.
8 | if (navigator.userAgent.match(/iPhone/i) != null) {
9 | var emails = document.querySelectorAll("input[type='email']");
10 | if (emails) {
11 | for (var i = 0; i < emails.length; i++) {
12 | emails[i].type = 'text';
13 | }
14 | }
15 | }
16 |
17 | // In the CSS file we set the ms-viewport to be consistent with the device dimensions,
18 | // which is necessary for correct functionality of immersive IE.
19 | // However, for Windows 8 phone we need to reset the ms-viewport's dimension to its original
20 | // values (auto), otherwise the viewport dimensions will be wrong for Windows 8 phone.
21 | // Windows 8 phone has agent string 'IEMobile 10.0'
22 | if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
23 | var msViewportStyle = document.createElement("style");
24 | msViewportStyle.appendChild(
25 | document.createTextNode(
26 | "@-ms-viewport{width:auto!important}"
27 | )
28 | );
29 | msViewportStyle.appendChild(
30 | document.createTextNode(
31 | "@-ms-viewport{height:auto!important}"
32 | )
33 | );
34 | document.getElementsByTagName("head")[0].appendChild(msViewportStyle);
35 | }
36 |
37 | // If the innerWidth is defined, use it as the viewport width.
38 | if (window.innerWidth && window.outerWidth && window.innerWidth !== window.outerWidth) {
39 | var viewport = document.querySelector("meta[name=viewport]");
40 | viewport.setAttribute('content', 'width=' + window.innerWidth + ', initial-scale=1.0, user-scalable=1');
41 | }
42 |
43 | // Gets the current style of a specific property for a specific element.
44 | function getStyle(element, styleProp) {
45 | var propStyle = null;
46 |
47 | if (element && element.currentStyle) {
48 | propStyle = element.currentStyle[styleProp];
49 | }
50 | else if (element && window.getComputedStyle) {
51 | propStyle = document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProp);
52 | }
53 |
54 | return propStyle;
55 | }
56 |
57 | // The script below is used for downloading the illustration image
58 | // only when the branding is displaying. This script work together
59 | // with the code in PageBase.cs that sets the html inline style
60 | // containing the class 'illustrationClass' with the background image.
61 | var computeLoadIllustration = function () {
62 | var branding = document.getElementById("branding");
63 | var brandingDisplay = getStyle(branding, "display");
64 | var brandingWrapperDisplay = getStyle(document.getElementById("brandingWrapper"), "display");
65 |
66 | if (brandingDisplay && brandingDisplay !== "none" &&
67 | brandingWrapperDisplay && brandingWrapperDisplay !== "none") {
68 | var newClass = "illustrationClass";
69 |
70 | if (branding.classList && branding.classList.add) {
71 | branding.classList.add(newClass);
72 | } else if (branding.className !== undefined) {
73 | branding.className += " " + newClass;
74 | }
75 | if (window.removeEventListener) {
76 | window.removeEventListener('load', computeLoadIllustration, false);
77 | window.removeEventListener('resize', computeLoadIllustration, false);
78 | }
79 | else if (window.detachEvent) {
80 | window.detachEvent('onload', computeLoadIllustration);
81 | window.detachEvent('onresize', computeLoadIllustration);
82 | }
83 | }
84 | };
85 |
86 | if (window.addEventListener) {
87 | window.addEventListener('resize', computeLoadIllustration, false);
88 | window.addEventListener('load', computeLoadIllustration, false);
89 | }
90 | else if (window.attachEvent) {
91 | window.attachEvent('onresize', computeLoadIllustration);
92 | window.attachEvent('onload', computeLoadIllustration);
93 | }
94 |
95 | // Function to change illustration image. Usage example below.
96 | function SetIllustrationImage(imageUri) {
97 | var illustrationImageClass = '.illustrationClass {background-image:url(' + imageUri + ');}';
98 |
99 | var css = document.createElement('style');
100 | css.type = 'text/css';
101 |
102 | if (css.styleSheet) css.styleSheet.cssText = illustrationImageClass;
103 | else css.appendChild(document.createTextNode(illustrationImageClass));
104 |
105 | document.getElementsByTagName("head")[0].appendChild(css);
106 | }
107 |
108 | // Example to change illustration image on HRD page after adding the image to active theme:
109 | // PSH> Set-AdfsWebTheme -TargetName -AdditionalFileResource @{uri='/adfs/portal/images/hrd.jpg';path='.\hrd.jpg'}
110 | //
111 | //if (typeof HRD != 'undefined') {
112 | // SetIllustrationImage('/adfs/portal/images/hrd.jpg');
113 | //}
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/Resources/style.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin:0px;
3 | padding:0px;
4 | }
5 | html, body
6 | {
7 | height:100%;
8 | width:100%;
9 | background-color:#ffffff;
10 | color:#000000;
11 | font-weight:normal;
12 | font-family:"Segoe UI" , "Segoe" , "SegoeUI-Regular-final", Tahoma, Helvetica, Arial, sans-serif;
13 | min-width:500px;
14 | -ms-overflow-style:-ms-autohiding-scrollbar;
15 | }
16 |
17 | body
18 | {
19 | font-size:0.9em;
20 | }
21 |
22 | #noScript { margin:16px; color:Black; }
23 |
24 | :lang(en-GB){quotes:'\2018' '\2019' '\201C' '\201D';}
25 | :lang(zh){font-family:微软雅黑;}
26 |
27 | @-ms-viewport { width: device-width; }
28 | @-moz-viewport { width: device-width; }
29 | @-o-viewport { width: device-width; }
30 | @-webkit-viewport { width: device-width; }
31 | @viewport { width: device-width; }
32 |
33 | /* Theme layout styles */
34 |
35 | #fullPage, #brandingWrapper
36 | {
37 | width:100%;
38 | height:100%;
39 | background-color:inherit;
40 | }
41 | #brandingWrapper
42 | {
43 | background-color:#4488dd;
44 | }
45 | #branding
46 | {
47 | /* A background image will be added to the #branding element at run-time once the illustration image is configured in the theme.
48 | Recommended image dimensions: 1420x1200 pixels, JPG or PNG, 200 kB average, 500 kB maximum. */
49 | height:100%;
50 | margin-right:500px; margin-left:0px;
51 | background-color:inherit;
52 | background-repeat: no-repeat;
53 | background-size:cover;
54 | -webkit-background-size:cover;
55 | -moz-background-size:cover;
56 | -o-background-size:cover;
57 | }
58 | #contentWrapper
59 | {
60 | position:relative;
61 | width:500px;
62 | height:100%;
63 | overflow:auto;
64 | background-color:#ffffff; /* for IE7 */
65 | margin-left:-500px; margin-right:0px;
66 | }
67 | #content
68 | {
69 | min-height:100%;
70 | height: auto !important;
71 | margin:0 auto -55px auto;
72 | padding:0px 150px 0px 50px;
73 | }
74 | #header
75 | {
76 | font-size:2em;
77 | font-weight:lighter;
78 | font-family:"Segoe UI Light" , "Segoe" , "SegoeUI-Light-final", Tahoma, Helvetica, Arial, sans-serif;
79 | padding-top: 90px;
80 | margin-bottom:60px;
81 | min-height:100px;
82 | overflow:hidden;
83 | }
84 | #header img
85 | {
86 | /* Logo image recommended dimension: 60x60 (square) or 350X35 (elongated), 4 kB average, 10 kB maximum. Transparent PNG strongly recommended. */
87 | width:auto;
88 | height:auto;
89 | max-width:100%;
90 | }
91 | #workArea, #header
92 | {
93 | word-wrap:break-word;
94 | width:350px;
95 | }
96 | #workArea
97 | {
98 | margin-bottom:90px;
99 | }
100 | #footerPlaceholder
101 | {
102 | height:40px;
103 | }
104 | #footer
105 | {
106 | height:40px;
107 | padding:10px 50px 0px 50px;
108 | position:relative;
109 | color:#666666;
110 | font-size:0.78em;
111 | }
112 | #footerLinks
113 | {
114 | float:none;
115 | padding-top:10px;
116 | }
117 | #copyright {color:#696969;}
118 | .pageLink { color:#000000; padding-left:16px; }
119 |
120 | /* Common content styles */
121 |
122 | .clear {clear:both;}
123 | .float { float:left; }
124 | .floatReverse { float:right; }
125 | .indent { margin-left:16px; }
126 | .indentNonCollapsible { padding-left:16px; }
127 | .hidden {display:none;}
128 | .notHidden {display:inherit;}
129 | .error { color:#c85305; }
130 | .actionLink { margin-bottom:8px; display:block; }
131 | a
132 | {
133 | color:#2672ec;
134 | text-decoration:none;
135 | background-color:transparent;
136 | }
137 | ul { list-style-type: disc; }
138 | ul,ol,dd { padding: 0 0 0 16px; }
139 | h1,h2,h3,h4,h5,label { margin-bottom: 8px; }
140 | .submitMargin { margin-top:38px; margin-bottom:30px; }
141 | .topFieldMargin { margin-top:8px; }
142 | .fieldMargin { margin-bottom:8px; }
143 | .groupMargin { margin-bottom:30px; }
144 | .sectionMargin { margin-bottom:64px; }
145 | .block { display: block; }
146 | .autoWidth { width:auto; }
147 | .fullWidth { width:342px; }
148 | .fullWidthIndent { width:326px; }
149 | .smallTopSpacing { margin-top:15px; }
150 | .mediumTopSpacing { margin-top:25px; }
151 | .largeTopSpacing { margin-top:35px; }
152 | .smallBottomSpacing { margin-bottom:5px; }
153 | .mediumBottomSpacing { margin-bottom:15px; }
154 | .largeBottomSpacing { margin-bottom:25px; }
155 | input
156 | {
157 | max-width:100%;
158 | font-family:inherit;
159 | margin-bottom:8px;
160 | }
161 | input[type="radio"], input[type="checkbox"] {
162 | vertical-align:middle;
163 | margin-bottom: 0px;
164 | }
165 | span.submit, input[type="submit"]
166 | {
167 | border:none;
168 | background-color:rgb(38, 114, 236);
169 | min-width:80px;
170 | width:auto;
171 | height:30px;
172 | padding:4px 20px 6px 20px;
173 | border-style:solid;
174 | border-width:1px;
175 | transition:background 0s;
176 | color:rgb(255, 255, 255);
177 | cursor:pointer;
178 | margin-bottom:8px;
179 |
180 | -ms-user-select:none;
181 | -moz-transition:background 0s;
182 | -webkit-transition:background 0s;
183 | -o-transition:background 0s;
184 | -webkit-touch-callout:none;
185 | -webkit-user-select:none;
186 | -khtml-user-select:none;
187 | -moz-user-select: none;
188 | -o-user-select: none;
189 | user-select:none;
190 | }
191 | input[type="submit"]:hover,span.submit:hover
192 | {
193 | background: rgb(212, 227, 251);
194 | }
195 | input.text{
196 | height:28px;
197 | padding:0px 3px 0px 3px ;
198 | border:solid 1px #BABABA;
199 | font-size:1.0em;
200 | }
201 | input.text:focus
202 | {
203 | border: 1px solid #6B6B6B;
204 | }
205 | select
206 | {
207 | height:28px;
208 | min-width:60px;
209 | max-width:100%;
210 | margin-bottom:8px;
211 |
212 | white-space:nowrap;
213 | overflow:hidden;
214 | box-shadow:none;
215 | padding:2px;
216 | font-family:inherit;
217 | }
218 | h1, .giantText
219 | {
220 | font-size:2.0em;
221 | font-weight:lighter;
222 | }
223 | h2, .bigText
224 | {
225 | font-size:1.33em;
226 | font-weight:lighter;
227 | }
228 | h3, .normalText
229 | {
230 | font-size:1.0em;
231 | font-weight:normal;
232 | }
233 | h4, .smallText
234 | {
235 | font-size:0.9em;
236 | font-weight:normal;
237 | }
238 | h5, .tinyText
239 | {
240 | font-size:0.8em;
241 | font-weight:normal;
242 | }
243 | .hint
244 | {
245 | color:#999999;
246 | }
247 | .emphasis
248 | {
249 | font-weight:700;
250 | color:#2F2F2F;
251 | }
252 | .smallIcon
253 | {
254 | height:20px;
255 | padding-right:12px;
256 | vertical-align:middle;
257 | }
258 | .largeIcon
259 | {
260 | height:48px;
261 | /* width:48px; */
262 | vertical-align:middle;
263 | }
264 | .largeTextNoWrap
265 | {
266 | height:48px;
267 | display:table-cell; /* needed when in float*/
268 | vertical-align:middle;
269 | white-space:nowrap;
270 | font-size:1.2em;
271 | }
272 | .idp
273 | {
274 | height:48px;
275 | clear:both;
276 | padding:8px;
277 | overflow:hidden;
278 | cursor:pointer;
279 | }
280 | .idp:hover
281 | {
282 | background-color:#cccccc;
283 | }
284 | .idpDescription
285 | {
286 | width:80%;
287 | }
288 |
289 | /* Form factor: intermediate height layout. Reduce space of the header. */
290 | @media only screen and (max-height: 820px) {
291 | #header {
292 | padding-top: 40px;
293 | min-height:0px;
294 | overflow: hidden;
295 | }
296 |
297 | #workArea
298 | {
299 | margin-bottom:60px;
300 | }
301 | }
302 |
303 | /* Form factor: intermediate height layout. Reduce space of the header. */
304 | @media only screen and (max-height: 500px) {
305 | #header {
306 | padding-top: 30px;
307 | margin-bottom: 30px;
308 | }
309 | #workArea{
310 | margin-bottom:40px;
311 | }
312 | }
313 |
314 | /* Form factor: intermediate layout (WAB in non-snapped view falls in here) */
315 | @media only screen and (max-width: 600px) {
316 | html, body {
317 | min-width: 260px;
318 | }
319 |
320 | #brandingWrapper {
321 | display: none;
322 | }
323 |
324 | #contentWrapper {
325 | float: none;
326 | width: 100%;
327 | margin: 0px auto;
328 | }
329 |
330 | #content, #footer, #header {
331 | width: 400px;
332 | padding-left: 0px;
333 | padding-right: 0px;
334 | margin-left: auto;
335 | margin-right: auto;
336 | }
337 |
338 | #workArea {
339 | width: 100%;
340 | }
341 |
342 | .fullWidth {
343 | width: 392px;
344 | }
345 |
346 | .fullWidthIndent {
347 | width: 376px;
348 | }
349 | }
350 |
351 | @media only screen and (max-width: 450px) {
352 | body {
353 | font-size: 0.8em;
354 | }
355 |
356 | #content, #footer {
357 | width:auto;
358 | margin-right:33px;
359 | margin-left:25px;
360 | }
361 |
362 | #header {
363 | width: auto;
364 | }
365 |
366 | span.submit, input[type="submit"] {
367 | font-size: 0.9em;
368 | }
369 |
370 | .fullWidth
371 | {
372 | width:100%;
373 | margin-left:auto;
374 | margin-right:auto;
375 | }
376 |
377 | .fullWidthIndent {
378 | width: 85%;
379 | }
380 |
381 | .idpDescription
382 | {
383 | width:70%;
384 | }
385 | }
386 |
387 | /* Form factor: snapped WAB (for WAB to work in snapped view, the content wrapper width has to be set to 260px) */
388 | @media only screen and (max-width:280px)
389 | {
390 | #contentWrapper
391 | {
392 | width:260px;
393 | }
394 | .idpDescription
395 | {
396 | max-width:160px;
397 | min-width:100px;
398 | }
399 | }
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/WebThemeInfo.cs:
--------------------------------------------------------------------------------
1 | namespace Digitude.Adfs.CustomImagesThemeGenerator
2 | {
3 | }
4 |
5 | namespace Digitude.Adfs.CustomImagesThemeGenerator
6 | {
7 |
8 |
9 | public partial class WebThemeInfo
10 | {
11 | }
12 | }
13 | namespace Digitude.Adfs.CustomImagesThemeGenerator {
14 |
15 |
16 | public partial class WebThemeInfo {
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/WebThemeInfo.xsc:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/WebThemeInfo.xsd:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/communityCustomizations/CustomImagesThemeGenerator/src/WebThemeInfo.xss:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/communityCustomizations/README.md:
--------------------------------------------------------------------------------
1 | # Community Customizations
2 |
3 | ## Overview
4 |
5 | This project contains a number of customizations provided by community members.
6 |
7 | ## Adding Your Customization
8 |
9 | To add your customizations, please do the following:
10 |
11 | 1. Create a subfolder in this directory with a descriptive name of your customization (ex. "showPasswordButton")
12 |
13 | 2. Update the list in this file with your change
14 |
15 | 3. In the subfolder with your customization, include the onload.js, any CSS changes you need, and a few screenshots of what your change does.
16 |
17 | 4. In the subfolder, include a `README.md` file, which explains your customization, and shows the screenshots
18 |
19 |
20 | ## Customizations
21 |
22 | 1. __[ShowPasswordButton](ShowPasswordButton)__ - A customization to allow users to click "show password" when entering their password on an AD FS page
23 | 2. __[CustomImagesThemeGenerator](CustomImagesThemeGenerator)__ - A GUI to build a list of custom images for the claim provider trusts on the AD FS home realm selection page.
24 |
25 | ## Contributing (Special Note)
26 |
27 | If you find any problems with the CSS, JavaScript, or docs, please fork and send us your fix. If you don't have a fix, please open an issue, and describe what you are seeing (feel free to include screenshots).
28 |
29 | For the full Contributing details, please see __[the root README](../README.md)__.
30 |
--------------------------------------------------------------------------------
/communityCustomizations/RenameAndReorderADCPTrust/ONLOAD.JS:
--------------------------------------------------------------------------------
1 | // Adjust The Display Name Of The Active Directory CP Trust And Reorder To Put Active Directory CP Trust Back At The Top (ADFS 2016)
2 | // Works with IE, Edge, Chrome, Firefox, Safari
3 | if (document.getElementById("hrdArea")) {
4 | if (document.getElementById("hrd")) {
5 | // THE NEXT LINE SHOULD BE UPDATED TO SPECIFY THE NAME DISPLAYED ON THE ADFS HED PAGE
6 | var strCPTrustADDisplayName = "";
7 | // THE LINE ABOVE SHOULD BE UPDATED TO SPECIFY THE NAME DISPLAYED ON THE ADFS HED PAGE
8 | var idp = document.getElementsByClassName("idp");
9 | var totalIdPElements = idp.length;
10 | var listAllSpanForIdpIcon = document.getElementsByClassName("largeIcon float");
11 | var listAllSpanForIdpDescription = document.getElementsByClassName("idpDescription float");
12 | var adAuthorityElementIsPresent = false;
13 |
14 | var i;
15 | for (i = 0; i < listAllSpanForIdpDescription.length; i++) {
16 | // IN THE LINE BELOW, IF NEEDED, SPECIFY THE LOCALIZED NAME OF AD. TO BE CERTAIN CHECK THE NAME OF THE AD CP TRUST
17 | var languageBasedADname = "Active Directory";
18 | // IN THE LINE ABOVE, IF NEEDED, SPECIFY THE LOCALIZED NAME OF AD. TO BE CERTAIN CHECK THE NAME OF THE AD CP TRUST
19 | if (listAllSpanForIdpDescription[i].innerText == languageBasedADname) {
20 | listAllSpanForIdpIcon[i].alt = strCPTrustADDisplayName;
21 | listAllSpanForIdpDescription[i].innerHTML = "" + strCPTrustADDisplayName + "";
22 | adAuthorityElementIsPresent = true;
23 | var adAuthorityElementIDnr = i;
24 | }
25 | }
26 |
27 | if ((totalIdPElements > 1) && (adAuthorityElementIsPresent)) {
28 | idp[adAuthorityElementIDnr].parentNode.insertBefore(idp[adAuthorityElementIDnr], idp[0]);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/communityCustomizations/RenameAndReorderADCPTrust/README.md:
--------------------------------------------------------------------------------
1 | # Rename & Reorder AD CP Trust In ADFS2016 Farm Level & Up
2 |
3 | ## Overview
4 |
5 | This project contains an `ONLOAD.JS` script for renaming and reordering the Active Directory (AD) CP Trust. After raising the farm level to at least ADFS 2016, the order of the CP Trust list is updated whereas the AD CP trust is moved to the bottom and it does not inherit the display name of the federation service. It just shows "Active Directory". This piece of code, fixes the order of the CP trust list and it allows to to specify a custom display name for the AD CP trust.
6 |
7 | ## Applying The Customization
8 |
9 | To change the name of the AD CP trust on the HRD page and put it back at the top again when the farm level is at least ADFS 2016, do the following:
10 |
11 | 1. Add the code from the `ONLOAD.JS` to the `ONLOAD.JS` of your own ADFS farm
12 |
13 | 2. For more information and examples, please see: https://jorgequestforknowledge.wordpress.com/2018/10/09/changing-ad-cp-trust-display-name-and-order-in-adfs-2016/
14 |
--------------------------------------------------------------------------------
/communityCustomizations/ShowPasswordButton/OnLoad.js:
--------------------------------------------------------------------------------
1 | var inputArea = document.getElementById("inputArea");
2 | var showButton = document.getElementById("showButton");
3 |
4 | if ( inputArea && !showButton )
5 | {
6 | var showButton = document.createElement("div");
7 | showButton.id = "showButton";
8 | showButton.innerHTML = "Show password";
9 | inputArea.appendChild(showButton);
10 |
11 | var appendedButton = document.getElementById("showButton");
12 | appendedButton.onmousedown = function(){ document.getElementById("password").type = "text"; };
13 | appendedButton.onmouseup = function(){ document.getElementById("password").type = "password";};
14 | }
--------------------------------------------------------------------------------
/communityCustomizations/ShowPasswordButton/README.md:
--------------------------------------------------------------------------------
1 | # ShowPasswordButton
2 |
3 | ## Overview
4 |
5 | This project contains an `onload.js` script for adding a "Show Password" button in the password field on AD FS logon pages.
6 | Works with password fields on Form based auth and with a custom provider using domain password as secondary auth.
7 |
8 | ## Applying the customization
9 |
10 | To add "Show password" button do the following:
11 |
12 | 1. Add the code from the `onload.js` to your webtheme `onload.js`
13 |
14 | 2. Apply the customization according to https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/advanced-customization-of-ad-fs-sign-in-pages
15 |
16 | ## Examples
17 |
18 | 
19 | 
--------------------------------------------------------------------------------
/communityCustomizations/ShowPasswordButton/images/Customization1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/ShowPasswordButton/images/Customization1.png
--------------------------------------------------------------------------------
/communityCustomizations/ShowPasswordButton/images/Customization2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/communityCustomizations/ShowPasswordButton/images/Customization2.png
--------------------------------------------------------------------------------
/mfaLoadingWheel/README.md:
--------------------------------------------------------------------------------
1 | # ADFS MFA Loading Wheel
2 |
3 | ## Overview
4 |
5 | This project provides an ADFS web customization to add as part of your onload.js customizations. The waiting wheel provides UI feedback when a user chooses an MFA method. Some MFA providers perform overhead operations before navigating away from the MFA options page, which means the user may wait up to 3 seconds before page navigation occurs.
6 |
7 | ## Getting Started - JavaScript Deployment
8 |
9 | 1. Download the ```loadWheel.js``` file to your ADFS server, wherever you host your JavaScript.
10 |
11 | Note: It is *__highly__* recommended that you minify your ```loadWheel.js``` before including it in a production environment. There are many popular tools online
12 | for minifying JavaScript code. Two popular choices are [minifier.org](http://www.minifier.org/) and [JSCompress](https://jscompress.com/).
13 |
14 | 2. Create a custom web theme using the following command in PowerShell:
15 |
16 | ```New-AdfsWebTheme –Name custom -SourceName default -AdditionalFileResource @{Uri=’/adfs/portal/script/onload.js’; path="c:\loadWheel.js"}```
17 |
18 | 3. Apply the new custom web theme using the following command in PowerShell:
19 |
20 | ```Set-AdfsWebConfig -ActiveThemeName custom```
21 |
22 | 4. For more information on JavaScript customization, see [Advanced ADFS Customization](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/advanced-customization-of-ad-fs-sign-in-pages)
23 |
24 | ## Example
25 |
26 | 
27 |
28 | ## Contributing (Special Note)
29 |
30 | If you find any problems with the CSS, JavaScript, or docs, please fork and send us your fix. If you don't
31 | have a fix, please open an issue, and describe what you are seeing (feel free to include screenshots).
32 |
33 | For the full Contributing details, please see __[the root README](../README.md)__.
--------------------------------------------------------------------------------
/mfaLoadingWheel/images/screenshot_wheel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/microsoft/adfsWebCustomization/cbf1c942ad622998bfa87f182b02c0d5dbe6b75a/mfaLoadingWheel/images/screenshot_wheel.png
--------------------------------------------------------------------------------
/mfaLoadingWheel/loadWheel.js:
--------------------------------------------------------------------------------
1 | function AuthSelectionPageSubmitCallback()
2 | {
3 |
4 | if (!document.getElementById("loadWheel"))
5 | {
6 | var divToAppendTo = document.getElementById("authArea");
7 |
8 | // Create the feedback loader
9 | var loader = document.createElement("loader");
10 | loader.innerHTML = ""
11 | loader.id = "loadWheel";
12 |
13 | // Add the loader to the HTML page
14 | if ( divToAppendTo && loader )
15 | {
16 | divToAppendTo.appendChild(loader);
17 | }
18 | }
19 | }
20 |
21 | var authOptions = document.getElementById('authOptions')
22 | var progress = document.getElementById('Progress')
23 | if (authOptions && !progress) {
24 |
25 | var azureOption = document.getElementById('WindowsAzureMultiFactorAuthentication');
26 | // var azureOption = document.getElementById('submitButton'); // use this option for MFA adapter, instead of AzureMFA
27 |
28 | if (azureOption) {
29 | azureOption.addEventListener("click", function() { AuthSelectionPageSubmitCallback(); }, false);
30 | }
31 | }
32 |
33 | // NOTE: If you wish to support the ADFS illustration (background image), you must use the following:
34 | /*
35 | function getStyle(element, styleProp) {
36 | var propStyle = null;
37 |
38 | if (element && element.currentStyle) {
39 | propStyle = element.currentStyle[styleProp];
40 | }
41 | else if (element && window.getComputedStyle) {
42 | propStyle = document.defaultView.getComputedStyle(element, null).getPropertyValue(styleProp);
43 | }
44 |
45 | return propStyle;
46 | }
47 |
48 | var computeLoadIllustration = function () {
49 | var branding = document.getElementById("branding");
50 | var brandingDisplay = getStyle(branding, "display");
51 | var brandingWrapperDisplay = getStyle(document.getElementById("brandingWrapper"), "display");
52 |
53 | if (brandingDisplay && brandingDisplay !== "none" &&
54 | brandingWrapperDisplay && brandingWrapperDisplay !== "none") {
55 | var newClass = "illustrationClass";
56 |
57 | if (branding.classList && branding.classList.add) {
58 | branding.classList.add(newClass);
59 | } else if (branding.className !== undefined) {
60 | branding.className += " " + newClass;
61 | }
62 | if (window.removeEventListener) {
63 | window.removeEventListener('load', computeLoadIllustration, false);
64 | window.removeEventListener('resize', computeLoadIllustration, false);
65 | }
66 | else if (window.detachEvent) {
67 | window.detachEvent('onload', computeLoadIllustration);
68 | window.detachEvent('onresize', computeLoadIllustration);
69 | }
70 | }
71 | };
72 |
73 | if (window.addEventListener) {
74 | window.addEventListener('resize', computeLoadIllustration, false);
75 | window.addEventListener('load', computeLoadIllustration, false);
76 | }
77 | else if (window.attachEvent) {
78 | window.attachEvent('onresize', computeLoadIllustration);
79 | window.attachEvent('onload', computeLoadIllustration);
80 | }
81 |
82 | function SetIllustrationImage(imageUri) {
83 | var illustrationImageClass = '.illustrationClass {background-image:url(' + imageUri + ');}';
84 |
85 | var css = document.createElement('style');
86 | css.type = 'text/css';
87 |
88 | if (css.styleSheet) css.styleSheet.cssText = illustrationImageClass;
89 | else css.appendChild(document.createTextNode(illustrationImageClass));
90 |
91 | document.getElementsByTagName("head")[0].appendChild(css);
92 | }
93 | */
94 | // NOTE: If you wish to support the ADFS illustration (background image), you must use the following:
95 | // PSH> Set-AdfsWebTheme -TargetName -AdditionalFileResource @{uri='/adfs/portal/images/illustration_mine.png';path='.\illustration_mine.jpg'}
96 | // SetIllustrationImage('/adfs/portal/images/illustration_mine.png');
--------------------------------------------------------------------------------
/pageDetectionTelemetry/InteractiveCompletionByPlatformQuery.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation. All rights reserved.
2 | Licensed under the MIT License.
3 |
4 |
5 | Client Interactive Completion Percentage (By Platform for 1 week):
6 |
7 | For all requests to ADFS, what percentage of those requests, per platform (Windows vs. iOS vs. Android, etc.), end up returning or erroring out? (i.e. (#succeeded + #failed) / (#started))
8 |
9 | Important filters:
10 |
11 | • We remove all traffic from known web crawlers, as marked by the App Insights library (using Synthetic Source)
12 | • We remove all traffic for which there is no correlation ID set
13 | • We remove all traffic that appears abnormal, meaning traffic for which there is a high page refresh or page navigation rate
14 |
15 | Important Caveats:
16 |
17 | • Because we cannot guarantee what the end state of the request is, the numbers here are based on the likely outcome of the request
18 |
19 | customEvents
20 | | where timestamp > ago(8d) and timestamp < ago(1d)
21 | | where isempty(operation_SyntheticSource)
22 | | where tostring(customDimensions.CorrelationID) != "NOTSET"
23 | | extend CleanOS = replace(@'\s', '', replace('[0-9.]+', '', client_OS))
24 | | summarize
25 | EventCount = count(),
26 | DistinctEventCount = dcount(name),
27 | EventNames = makelist(name)
28 | by CorrelationID = tostring(customDimensions.CorrelationID), CleanOS
29 | | where (1.0 * DistinctEventCount) / (1.0 * EventCount) >= 0.35
30 | | extend LikelyFormsEnded = iff(EventNames has "FormsPageEnd" and (EventNames !has "AuthSelectionPageStart" and EventNames !has "PhoneFactorWaitingStart"), 1, 0)
31 | | extend LikelyAuthSelectEnded = iff((EventNames has "AuthSelectionPageEnd" or EventNames has "AuthSelectionPicked") and (EventNames !has "PhoneFactorWaitingStart"), 1, 0)
32 | | extend LikelyPFAEnded = iff(EventNames has "PhoneFactorLatency" or EventNames has "PhoneFactorWaitingEnd", 1, 0)
33 | | extend LikelyErrorEnded = iff((EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart") and
34 | (EventNames !has "FormsPageStart" and
35 | EventNames !has "FormsPageEnd" and
36 | EventNames !has "AuthSelectionPageStart" and
37 | EventNames !has "AuthSelectionPageEnd" and
38 | EventNames !has "PhoneFactorWaitingStart" and
39 | EventNames !has "PhoneFactorWaitingEnd"), 1, 0)
40 | | extend LikelyEnded = iff(LikelyFormsEnded > 0 or LikelyAuthSelectEnded > 0 or LikelyPFAEnded > 0 or LikelyErrorEnded > 0, 1, 0)
41 | | project CorrelationID, LikelyEnded, CleanOS
42 | | summarize
43 | TotalRequests = count(),
44 | EndedRequests = countif(LikelyEnded > 0)
45 | by CleanOS
46 | | extend InteractiveCompletion = (1.0 * EndedRequests) / (1.0 * TotalRequests) * 100.0
47 | | sort by TotalRequests desc
48 |
--------------------------------------------------------------------------------
/pageDetectionTelemetry/InteractiveCompletionQuery.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation. All rights reserved.
2 | Licensed under the MIT License.
3 |
4 |
5 |
6 | Client Interactive Completion Percentage (Total for 1 week):
7 |
8 | For all requests that come to ADFS, what percentage of those requests end up returning or erroring out? (i.e. (#succeeded + #failed) / (#started))
9 |
10 | Important filters:
11 |
12 | • We remove all traffic from known web crawlers, as marked by the App Insights library (using Synthetic Source)
13 | • We remove all traffic for which there is no correlation ID set
14 | • We remove all traffic that appears abnormal, meaning traffic for which there is a high page refresh or page navigation rate
15 |
16 | Important Caveats:
17 |
18 | • Because we cannot guarantee what the end state of the request is, the numbers here are based on the likely outcome of the request
19 |
20 | customEvents
21 | | where timestamp > ago(8d) and timestamp < ago(1d)
22 | | where isempty(operation_SyntheticSource)
23 | | where tostring(customDimensions.CorrelationID) != "NOTSET"
24 | | summarize
25 | EventCount = count(),
26 | DistinctEventCount = dcount(name),
27 | EventNames = makelist(name)
28 | by CorrelationID = tostring(customDimensions.CorrelationID)
29 | | where (1.0 * DistinctEventCount) / (1.0 * EventCount) >= 0.35
30 | | extend LikelyFormsEnded = iff(EventNames has "FormsPageEnd" and (EventNames !has "AuthSelectionPageStart" and EventNames !has "PhoneFactorWaitingStart"), 1, 0)
31 | | extend LikelyAuthSelectEnded = iff((EventNames has "AuthSelectionPageEnd" or EventNames has "AuthSelectionPicked") and (EventNames !has "PhoneFactorWaitingStart"), 1, 0)
32 | | extend LikelyPFAEnded = iff(EventNames has "PhoneFactorLatency" or EventNames has "PhoneFactorWaitingEnd", 1, 0)
33 | | extend LikelyErrorEnded = iff((EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart") and
34 | (EventNames !has "FormsPageStart" and
35 | EventNames !has "FormsPageEnd" and
36 | EventNames !has "AuthSelectionPageStart" and
37 | EventNames !has "AuthSelectionPageEnd" and
38 | EventNames !has "PhoneFactorWaitingStart" and
39 | EventNames !has "PhoneFactorWaitingEnd"), 1, 0)
40 | | extend LikelyEnded = iff(LikelyFormsEnded > 0 or LikelyAuthSelectEnded > 0 or LikelyPFAEnded > 0 or LikelyErrorEnded > 0, 1, 0)
41 | | project CorrelationID, LikelyEnded
42 | | summarize
43 | TotalRequests = count(),
44 | EndedRequests = countif(LikelyEnded > 0)
45 | | extend InteractiveCompletion = (1.0 * EndedRequests) / (1.0 * TotalRequests) * 100.0
46 |
--------------------------------------------------------------------------------
/pageDetectionTelemetry/LoginReliabilityByPlatformQuery.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation. All rights reserved.
2 | Licensed under the MIT License.
3 |
4 |
5 |
6 | Login Reliability Percentage - By Platform for 1 week:
7 |
8 | For all requests to ADFS, what percentage of those requests, per platform (Windows vs. iOS vs. Android, etc.), end up returning? (i.e. #succeeded / (#succeeded + failed))
9 |
10 | In this query, instead of counting a request as an ADFS failure if we ever see an error page, we only count it as a failure if we end on an error page.
11 |
12 | Important filters:
13 |
14 | • We remove all traffic from known web crawlers, as marked by the App Insights library (using Synthetic Source)
15 | • We remove all traffic for which there is no correlation ID set
16 | • We remove all traffic that appears abnormal, meaning traffic for which there is a high page refresh or page navigation rate
17 |
18 | Important Caveats:
19 |
20 | • Because we cannot guarantee that a request returned or did not return to EVO, the numbers here are based on the likely outcome of the request
21 | • Login reliability for ADFS includes ONLY PROMPTING requests, no silent requests are included. This leads to an expectation of having a much lower login reliability than normal
22 |
23 | customEvents
24 | | where timestamp > ago(8d) and timestamp < ago(1d)
25 | | where isempty(operation_SyntheticSource)
26 | | where tostring(customDimensions.CorrelationID) != "NOTSET"
27 | | extend CleanOS = replace(@'\s', '', replace('[0-9.]+', '', client_OS))
28 | | summarize
29 | EventCount = count(),
30 | DistinctEventCount = dcount(name),
31 | EventNames = makelist(name)
32 | by CorrelationID = tostring(customDimensions.CorrelationID), CleanOS
33 | | where (1.0 * DistinctEventCount) / (1.0 * EventCount) >= 0.35
34 | | extend LikelyFormsNotAbandoned =
35 | iff((EventNames has "FormsPageEnd" and (EventNames !has "AuthSelectionPageStart" and EventNames !has "PhoneFactorWaitingStart"))
36 | or (EventNames has "FormsPageStart" and (EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart")), 1, 0)
37 | | extend LikelyAuthSelectNotAbandoned = iff(
38 | ((EventNames has "AuthSelectionPageEnd" or EventNames has "AuthSelectionPicked" or EventNames has "AuthSelectionLatency") and (EventNames !has "PhoneFactorWaitingStart"))
39 | or (EventNames has "AuthSelectionPageStart" and (EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart")), 1, 0)
40 | | extend LikelyPFANotAbandoned = iff(
41 | (EventNames has "PhoneFactorLatency" or EventNames has "PhoneFactorWaitingEnd") or
42 | (EventNames has "PhoneFactorWaitingStart" and (EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart")), 1, 0)
43 | | extend LikelyNotAbandoned = iff(LikelyFormsNotAbandoned > 0 or LikelyAuthSelectNotAbandoned > 0 or LikelyPFANotAbandoned > 0, 1, 0)
44 | | extend WasErrorRequest = iff(EventNames has "ErrorPageStart" or EventNames has "ErrorDetailedPageStart", 1, 0)
45 | | extend WasEndErrorState = iff(EventNames[ toint(arraylength(EventNames) - 1) ] has "ErrorPageStart" or EventNames[ toint(arraylength(EventNames) - 1) ] has "ErrorDetailedPageStart", 1, 0)
46 | | project CorrelationID, EventNames, LikelyNotAbandoned, WasErrorRequest, CleanOS, WasEndErrorState
47 | | summarize
48 | TotalRequests = count(),
49 | TotalErroredRequests = sum(WasEndErrorState),
50 | ReturnedRequests = countif(LikelyNotAbandoned > 0)
51 | by CleanOS
52 | | extend LoginReliabilityInteractive = (1.0 * ReturnedRequests) / (1.0 * ReturnedRequests + TotalErroredRequests) * 100.0
53 | | sort by TotalRequests desc
54 |
55 |
--------------------------------------------------------------------------------
/pageDetectionTelemetry/README.md:
--------------------------------------------------------------------------------
1 | # Page Detection with App Insights
2 |
3 | ## Overview
4 |
5 | This project performs page detection of common, uncustomized ADFS web pages, and then uploads
6 | telemetry about those pages to your [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) datastore.
7 |
8 | Note that this customization DOES NOT send any telemetry to the Microsoft ADFS team. All telemetry is sent to your datastore only.
9 |
10 | This project also includes some useful analysis scripts you can run against your Application Insights datastore.
11 |
12 | ## Requirements
13 |
14 | This tool requires that you have an [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) subscription.
15 |
16 | Additionally, it is recommended that you have minimal web customization of the standard ADFS pages, as customization could throw off the page
17 | detection. Please note that customization referres to onload.js changes, not logo changes, illustration changes, etc.
18 |
19 | Lastly, there is some page detection logic that relies on English strings in the pages. If you are presenting pages in languages other than
20 | English, you might need to make modifications to the JavaScript.
21 |
22 | ## Getting Started
23 |
24 | 1. Register for an [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) subscription
25 |
26 | 2. Download the ```onload.js``` in this repo locally, and update the ```instrumentationKey``` under ```GenerateAppInsightsObject``` to be your Application Insights API key
27 |
28 | (For more details, see [Copy the instrumentation key](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-create-new-resource#copy-the-instrumentation-key))
29 |
30 | 3. Replace the ```onload.js``` in your ADFS environment with the ```onload.js``` from this project. Alternatively, if you already have content in your ```onload.js```, you
31 | should append our content to yours.
32 |
33 | Note: It is *__highly__* recommended that you minify your ```onload.js``` before including it in a production environment. There are many popular tools online
34 | for minifying JavaScript code. Two popular choices are [minifier.org](http://www.minifier.org/) and [JSCompress](https://jscompress.com/).
35 |
36 | (For more information, see [Advanced ADFS Customization](https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/advanced-customization-of-ad-fs-sign-in-pages))
37 |
38 | ## What Gets Tracked
39 |
40 | The following pages are detected and tracked:
41 |
42 | * Forms Page - the username and password collection page
43 | * Auth Selection Page - the page served when MFA is required. This page lists MFA provider options
44 | * PFA Waiting Page - the page served when Phone Factor Authentication (PFA) is performed by the [MultiFactorAuthenticationAdfsAdapter](https://docs.microsoft.com/en-us/azure/multi-factor-authentication/multi-factor-authentication-get-started-adfs-w2k12)
45 | * Error Page - the ADFS or PFA error page
46 |
47 |
48 | ## Analyzing the Data
49 |
50 | To analyze your data, you will need to write analysis queries against your [Azure Application Insights](https://azure.microsoft.com/en-us/services/application-insights/) datastore.
51 | For more details, see [Analytics](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics).
52 |
53 | Included in this project are a number of useful queries for tracking:
54 |
55 | * User prompting rate served by ADFS server
56 | * Login reliability of your ADFS server
57 | * Interactive completion rate of your ADFS server
58 |
59 | ## Further Reading
60 |
61 | The following documentation is useful for making changes to the ```onload.js``` code and the included queries.
62 |
63 | * [Application Insights Overview](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-overview)
64 | * [Data Analysis Overivew](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics-tour)
65 | * [Custom App Insights Collection](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-api-custom-events-metrics#_flushing-data)
66 | * [App Insights in JavaScript](https://docs.microsoft.com/en-us/azure/application-insights/app-insights-javascript)
67 | * [App Insights JavaScript GitHub](https://github.com/Microsoft/ApplicationInsights-JS/blob/master/API-reference.md)
68 |
69 | ## Contributing (Special Note)
70 |
71 | If you are contributing code, please be sure that you __remove your instrumentation key__ from any code you
72 | put in a pull request. This project is public, and anyone on the Internet can see it.
73 |
74 | For the full Contributing details, please see __[the root README](../README.md)__.
--------------------------------------------------------------------------------
/pageDetectionTelemetry/UserPromptRateByPlatformQuery.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation. All rights reserved.
2 | Licensed under the MIT License.
3 |
4 |
5 |
6 |
7 | Browser Sessions with Exactly 1 Unforced Prompt Per Week (By Platform):
8 |
9 | What percentage of browser sessions that are making requests to ADFS are receiving exactly 1 unforced prompt per week, broken down by platform?
10 |
11 | Important filters:
12 |
13 | • We remove all traffic from known web crawlers, as marked by the App Insights library (using Synthetic Source)
14 | • We remove all traffic for which there is no correlation ID set
15 | • We remove all traffic that appears abnormal, meaning traffic for which there is a high page refresh or page navigation rate
16 | • We remove traffic for users who only saw an error page
17 | • We remove all traffic that has a query parameter that would force a prompt ("wfresh = 0")
18 |
19 | Important Caveats:
20 |
21 | • Session_Id is not a good field to use for this analysis, because we are under-reporting when we use this field. For more details, see this discussion (https://docs.microsoft.com/en-us/azure/application-insights/app-insights-web-track-usage#Sessions)
22 | • We count 2 or fewer prompts per session as a single user prompt, since some requests will have AuthSelect and MFA, which we count as 1 prompt
23 |
24 | customEvents
25 | | where timestamp > ago(8d) and timestamp < ago(1d)
26 | | where isempty(operation_SyntheticSource)
27 | | where tostring(customDimensions.CorrelationID) != "NOTSET"
28 | | where tostring(customDimensions.wfresh) != "0"
29 | | extend CleanBrowser = replace('Internet Explorer', 'IE', replace(@'\s', '', replace('UI/WKWebView', '', replace('Mobile', '', replace('[0-9.]+', '', client_Browser)))))
30 | | extend CleanOS = replace(@'\s', '', replace('[0-9.]+', '', client_OS))
31 | | summarize
32 | EventCount = count(),
33 | DistinctEventCount = dcount(name),
34 | FormsPromptCount = countif(name startswith "Forms"),
35 | AuthPromptCount = countif(name startswith "AuthSelection" and customDimensions.SelectionMethod !has "auto"),
36 | PFAPromptCount = countif(name startswith "Phone"),
37 | EventNames = makelist(name)
38 | by CorrelationID = tostring(customDimensions.CorrelationID), session_Id, CleanOS
39 | | where (1.0 * DistinctEventCount) / (1.0 * EventCount) >= 0.35
40 | | extend WasFormsPrompted = iff(FormsPromptCount > 0, 1, 0)
41 | | extend WasAuthPrompted = iff(AuthPromptCount > 0, 1, 0)
42 | | extend WasPFAPrompted = iff(PFAPromptCount > 0, 1, 0)
43 | | extend PromptsPerRequest = WasAuthPrompted + WasFormsPrompted + WasPFAPrompted
44 | | project CorrelationID, session_Id, PromptsPerRequest, CleanOS
45 | | where PromptsPerRequest > 0 // Some users are only getting an Error Page
46 | | summarize
47 | PromptsPerSession = sum(PromptsPerRequest)
48 | by session_Id, CleanOS
49 | | summarize
50 | TotalSessions = count(),
51 | SessionsWith1Prompt = countif(PromptsPerSession <= 3)
52 | by CleanOS
53 | | extend PercentWith1 = (1.0 * SessionsWith1Prompt) / (1.0 * TotalSessions) * 100.0
54 | | sort by TotalSessions desc
55 |
--------------------------------------------------------------------------------
/pageDetectionTelemetry/UserPromptRateQuery.txt:
--------------------------------------------------------------------------------
1 | Copyright (c) Microsoft Corporation. All rights reserved.
2 | Licensed under the MIT License.
3 |
4 |
5 |
6 | Browser Sessions with Exactly 1 Unforced Prompt Per Week:
7 |
8 | What percentage of browser sessions that are making requests to ADFS are receiving exactly 1 unforced prompt per week?
9 |
10 | Important filters:
11 |
12 | • We remove all traffic from known web crawlers, as marked by the App Insights library (using Synthetic Source)
13 | • We remove all traffic for which there is no correlation ID set
14 | • We remove all traffic that appears abnormal, meaning traffic for which there is a high page refresh or page navigation rate
15 | • We remove traffic for users who only saw an error page
16 | • We remove all traffic that has a query parameter that would force a prompt ("wfresh = 0")
17 |
18 | Important Caveats:
19 |
20 | • Session_Id is not a good field to use for this analysis, because we are under-reporting when we use this field. For more details, see this discussion (https://docs.microsoft.com/en-us/azure/application-insights/app-insights-web-track-usage#Sessions)
21 | • We count 2 or fewer prompts per session as a single user prompt, since some requests will have AuthSelect and MFA, which we count as 1 prompt
22 |
23 |
24 | customEvents
25 | | where timestamp > ago(8d) and timestamp < ago(1d)
26 | | where isempty(operation_SyntheticSource)
27 | | where tostring(customDimensions.CorrelationID) != "NOTSET"
28 | | where tostring(customDimensions.wfresh) != "0"
29 | | summarize
30 | EventCount = count(),
31 | DistinctEventCount = dcount(name),
32 | FormsPromptCount = countif(name startswith "Forms"),
33 | AuthPromptCount = countif(name startswith "AuthSelection" and customDimensions.SelectionMethod !has "auto"),
34 | PFAPromptCount = countif(name startswith "Phone"),
35 | EventNames = makelist(name)
36 | by CorrelationID = tostring(customDimensions.CorrelationID), session_Id
37 | | where (1.0 * DistinctEventCount) / (1.0 * EventCount) >= 0.35
38 | | extend WasFormsPrompted = iff(FormsPromptCount > 0, 1, 0)
39 | | extend WasAuthPrompted = iff(AuthPromptCount > 0, 1, 0)
40 | | extend WasPFAPrompted = iff(PFAPromptCount > 0, 1, 0)
41 | | extend PromptsPerRequest = WasAuthPrompted + WasFormsPrompted + WasPFAPrompted
42 | | project CorrelationID, session_Id, PromptsPerRequest
43 | | where PromptsPerRequest > 0 // Some users are only getting an Error Page
44 | | summarize
45 | PromptsPerSession = sum(PromptsPerRequest)
46 | by session_Id
47 | | summarize
48 | TotalSessions = count(),
49 | SessionsWith1Prompt = countif(PromptsPerSession <= 2) // We allow 2 here because MFA and Auth Select together count as 1 prompt
50 | | extend PercentWith1 = (1.0 * SessionsWith1Prompt) / (1.0 * TotalSessions) * 100.0
51 |
--------------------------------------------------------------------------------
/pageDetectionTelemetry/onload.js:
--------------------------------------------------------------------------------
1 | // Copyright (c) Microsoft Corporation. All rights reserved.
2 | // Licensed under the MIT License.
3 |
4 | //
5 | // Telemetry Manager is the App Insights telemetry management object
6 | // Callers MUST call 'Initialize' before using the 'ProducePageDetectionTelemetry' method
7 | //
8 | var TelemetryManager = {
9 |
10 | /*
11 | * Initialize the app state for the current page
12 | */
13 | Initialize: function () {
14 | var _self = this;
15 |
16 | // Collect some page details for later
17 | var NOT_SET_CONST = 'NOTSET';
18 | _self.currentUri = window.location.href.split('?')[0];
19 | _self.mswtrealm = _self.getQueryString("wtrealm") || NOT_SET_CONST;
20 | _self.decodedwtrealm = decodeURIComponent(_self.mswtrealm) || NOT_SET_CONST;
21 | _self.requestID = _self.getQueryString("client-request-id") || NOT_SET_CONST;
22 | _self.wfresh = _self.getQueryString("wfresh") || NOT_SET_CONST;
23 | _self.wauth = _self.getQueryString("wauth") || NOT_SET_CONST;
24 | _self.debugging = _self.getQueryString("debug") || NOT_SET_CONST;
25 | _self.wauth = decodeURIComponent(_self.wauth);
26 | _self.Username = NOT_SET_CONST;
27 |
28 | _self.startedPfaWaiting = false;
29 | _self.pfaTimestamp = null;
30 | _self.pfaTimestampOldBrowser = null;
31 | _self.startedAuthSelectionWaiting = false;
32 | _self.authSelectionTimestamp = null;
33 | _self.authSelectionTimestampOldBrowser = null;
34 | _self.authSelectionLinkClicked = null;
35 | _self.authSelectionMethod = null;
36 | _self.startedFormsPage = false;
37 |
38 | // Create App Insights object with settings
39 | if (!window.appInsights) {
40 | if(console && _self.debugging) console.log("TelemetryManager: Generating a new App Insights object");
41 | var appInsights = _self.GenerateAppInsightsObject.call();
42 |
43 | // Set App Insights object against the current window
44 | window.appInsights = appInsights;
45 | if(console && _self.debugging) console.log("TelemetryManager: Set new App Insights object against the current window");
46 | }
47 |
48 | //
49 | // Add unload callback to window, so we can capture telemetry
50 | //
51 | if (window.addEventListener) {
52 | window.addEventListener("unload", function () { _self.LeavingCurrentPageCallback(_self); }, false); // Modern browsers
53 | } else if (window.attachEvent) {
54 | window.attachEvent('onunload', function () { _self.LeavingCurrentPageCallback(_self); }); // Old IE
55 | }
56 |
57 | if(console && _self.debugging) console.log("Exit: TelemetryManager.Initialize");
58 | },
59 |
60 | /*
61 | * Generate an App Insights object to use when
62 | * sending telemetry.
63 | */
64 | GenerateAppInsightsObject: function() {
65 | return function (config) {
66 | function r(config) { t[config] = function () { var i = arguments; t.queue.push(function () { t[config].apply(t, i) }) } } var t = { config: config }, u = document, e = window, o = "script", s = u.createElement(o), i, f; for (s.src = config.url || "//az416426.vo.msecnd.net/scripts/a/ai.0.js", u.getElementsByTagName(o)[0].parentNode.appendChild(s), t.cookie = u.cookie, t.queue = [], i = ["Event", "Exception", "Metric", "PageView", "Trace"]; i.length;) r("track" + i.pop()); return r("setAuthenticatedUserContext"), r("clearAuthenticatedUserContext"), config.disableExceptionTracking || (i = "onerror", r("_" + i), f = e[i], e[i] = function (config, r, u, e, o) { var s = f && f(config, r, u, e, o); return s !== !0 && t["_" + i](config, r, u, e, o), s }), t
67 | }({
68 | samplingPercentage: 100,
69 | instrumentationKey: "YOUR-KEY-HERE",
70 | endpointUrl: "https://dc.services.visualstudio.com/v2/track" // modify endpoint for Azure Government or Azure China as per https://docs.microsoft.com/en-us/azure/azure-monitor/app/custom-endpoints
71 | });
72 | },
73 |
74 | /*
75 | * Helper function to get a querystring parameter
76 | */
77 | getQueryString: function(qsName) {
78 | qsName = qsName.replace(/[\[\]]/g, "\\$&");
79 | var regex = new RegExp("[?&]" + qsName + "(=([^]*)|&|#|$)"),
80 | results = regex.exec(location.href);
81 | if (!results) return "";
82 | if (!results[2]) return "";
83 | return decodeURIComponent(results[2].replace(/\+/g, " "));
84 | },
85 |
86 | /*
87 | * Produces all telemetry for the following pages:
88 | * Forms Page
89 | * AuthSelection Page
90 | * Home Realm Discovery Page
91 | * Phone Factor Authentication Page
92 | * Phone Factor Error Page
93 | * ADFS Error Page
94 | * Phone Factor Authentication Options Page
95 | */
96 | ProducePageDetectionTelemetry: function () {
97 |
98 | var _self = this;
99 |
100 | if(console && _self.debugging) console.log("Enter: TelemetryManager.ProducePageDetectionTelemetry");
101 |
102 | //
103 | // Generic Page view tracking
104 | //
105 | window.appInsights.trackPageView("Generic");
106 |
107 | //
108 | // Home Realm Discovery Page
109 | //
110 | var hrd = document.getElementById('hrd');
111 | if (hrd) {
112 | window.appInsights.trackPageView("HomeRealmDiscovery");
113 | if(console && _self.debugging) console.log("ProducePageDetectionTelemetry: Found HRD Page");
114 | return;
115 | }
116 |
117 | //
118 | // Forms Page (before creds are entered)
119 | // NOTE: This only works for pages presented in English
120 | //
121 | var pageloginForm = document.getElementById('loginForm');
122 | if (!hrd && pageloginForm && document.title == 'Sign In') {
123 | window.appInsights.trackPageView("FormsPage");
124 | window.appInsights.trackEvent("FormsPageStart",
125 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wauth: _self.wauth, wfresh: _self.wfresh, wtrealm: _self.decodedwtrealm }
126 | );
127 | _self.startedFormsPage = true;
128 | if(console && _self.debugging) console.log("ProducePageDetectionTelemetry: Found Forms Page");
129 | return;
130 | }
131 |
132 | //
133 | // Error Page
134 | //
135 | var ierrorText = document.getElementById("errorText");
136 | if (ierrorText) {
137 | var ierrorCurrent = ierrorText.innerHTML;
138 | if (ierrorCurrent.length > 0) {
139 | var pageTitle = document.title;
140 |
141 | //
142 | // Try to gather more error information from the page
143 | //
144 | var erruserAccount = _self.GetUserNameFromAuthArea();
145 | var erractivityId = (document.getElementById('activityId') || {innerText:''}).innerText;
146 | var errcontextId = (document.getElementById('contextId')|| {innerText:''}).innerText;
147 | var errtimestamp = (document.getElementById('timestamp')|| {innerText:''}).innerText;
148 |
149 | if(erractivityId || errcontextId || errtimestamp){
150 | window.appInsights.trackPageView("ErrorDetailedPage");
151 | window.appInsights.trackEvent("ErrorDetailedPageStart",
152 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, PageTitle: pageTitle, Username: erruserAccount, ActivityID: erractivityId, ContextId: errcontextId, ErrorTimestamp: errtimestamp }
153 | );
154 | if(console && _self.debugging) console.log("ProducePageDetectionTelemetry: Found Detailed Error Page");
155 | }
156 | return;
157 | }
158 | }
159 |
160 | //
161 | // AuthSelection Page
162 | //
163 | var authOptions = document.getElementById('authOptions')
164 | var progress = document.getElementById('Progress')
165 | if (authOptions && !progress) {
166 |
167 | var foundUsername = _self.GetUserNameFromAuthArea();
168 | _self.Username = foundUsername;
169 |
170 | window.appInsights.trackPageView("AuthSelectionPage");
171 | window.appInsights.trackEvent("AuthSelectionPageStart",
172 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username }
173 | );
174 | if(console && _self.debugging) console.log("ProducePageDetectionTelemetry: Found Auth Selection Page");
175 |
176 | //
177 | // Add click callbacks to the auth selection options
178 | // NOTE: If you have other options you wish to track, add them here
179 | //
180 | var certOption = document.getElementById('CertificateAuthentication');
181 | if (certOption) {
182 | certOption.addEventListener("click", function () { _self.AuthSelectionPageSubmitCallback("cert", "manual", _self); }, false);
183 | }
184 | var azureOption = document.getElementById('WindowsAzureMultiFactorAuthentication');
185 | if (azureOption) {
186 | azureOption.addEventListener("click", function () { _self.AuthSelectionPageSubmitCallback("phonefactor", "manual", _self); }, false);
187 | }
188 |
189 | return;
190 | }
191 |
192 | //
193 | // Phone Factor Waiting Page
194 | //
195 | var workArea = document.getElementById('workArea');
196 | var authArea = document.getElementById('authArea');
197 | var progressDiv = document.getElementById('Progress');
198 | var authMethod = document.getElementById('authMethod');
199 | var errorDiv = document.getElementById('errorDiv');
200 | if (workArea && authArea && progressDiv && authMethod && !errorDiv) {
201 |
202 | var phonefactorUserID = _self.GetUserNameFromAuthArea();
203 | var authchildren = authArea.childNodes;
204 | for (var i = 0; i < authchildren.length; i++) {
205 | if (authchildren[i].className === 'fieldMargin bigText') {
206 | window.appInsights.trackPageView("PhoneFactorWaitingPage");
207 | window.appInsights.trackEvent("PhoneFactorWaitingStart",
208 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: phonefactorUserID }
209 | );
210 | if(console && _self.debugging) console.log("ProducePageDetectionTelemetry: Found PFA Waiting Page");
211 |
212 | // Once we detect the pfa page, add a timer to collect the PFA latency
213 | _self.startedPfaWaiting = true;
214 | _self.Username = phonefactorUserID;
215 |
216 | if (performance && performance.now()) {
217 | _self.pfaTimestamp = performance.now();
218 | }
219 |
220 | if (Date && Date.now()) {
221 | _self.pfaTimestampOldBrowser = Date.now();
222 | }
223 |
224 | return;
225 | }
226 | }
227 | }
228 |
229 | if(console && _self.debugging) console.log("Exit: TelemetryManager.ProducePageDetectionTelemetry");
230 | },
231 |
232 | /*
233 | * Collect the username from the auth area message
234 | * NOTE: This method only works for pages presented in English
235 | */
236 | GetUserNameFromAuthArea: function () {
237 | var authchildren = document.getElementById('authArea').childNodes;
238 | for (var i = 0; i < authchildren.length; i++) {
239 | if (authchildren[i].className === 'fieldMargin bigText') {
240 | var tempuserAccount = authchildren[i].innerText;
241 | return tempuserAccount.replace("Welcome ", "");
242 | }
243 | }
244 | },
245 |
246 | /*
247 | * Callback function when the AuthSelection page is being submitted after an
248 | * auth option was chosen.
249 | */
250 | AuthSelectionPageSubmitCallback: function (linkClicked, selectionMethod, _self) {
251 | if(console && _self.debugging) console.log("Enter: TelemetryManager.AuthSelectionPageSubmitCallback");
252 |
253 | // Collect telemetry
254 | window.appInsights.trackEvent("AuthSelectionPicked",
255 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wauth: _self.wauth, wfresh: _self.wfresh, Type: linkClicked, SelectionMethod: selectionMethod, wtrealm: _self.decodedwtrealm, Username: _self.Username }
256 | );
257 |
258 | if(console && _self.debugging) console.log("AuthSelectionPageSubmitCallback: Link Clicked: " + linkClicked);
259 |
260 | // Start the auth selection timer to time from page submit to page unload
261 | _self.startedAuthSelectionWaiting = true;
262 | _self.authSelectionLinkClicked = linkClicked;
263 | _self.authSelectionMethod = selectionMethod;
264 |
265 | if (performance && performance.now()) {
266 | _self.authSelectionTimestamp = performance.now();
267 | }
268 |
269 | if (Date && Date.now()) {
270 | _self.authSelectionTimestampOldBrowser = Date.now();
271 | }
272 |
273 | if(console && _self.debugging) console.log("Exit: TelemetryManager.AuthSelectionPageSubmitCallback");
274 | },
275 |
276 | /*
277 | * Callback function when any page is being left
278 | * NOTE: Due to browser unload calls, there is no guarantee that the
279 | * processing in this method will complete. Some of the XHR requests made for
280 | * trackEvent calls may not succeed. This telemetry is a best-effort collection
281 | */
282 | LeavingCurrentPageCallback: function (_self) {
283 |
284 | // Grab the window appInsights object for local use
285 | var localAppInsights = window.appInsights;
286 | var flushMePlease = false;
287 |
288 | if (_self.startedFormsPage) {
289 | _self.Username = document.getElementById(Login.userNameInput).value;
290 | localAppInsights.trackEvent("FormsPageEnd",
291 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username }
292 | );
293 | _self.startedFormsPage = false;
294 | flushMePlease = true;
295 | }
296 |
297 | var pfaTime = null;
298 | if (_self.pfaTimestamp) {
299 | pfaTime = (performance.now() - _self.pfaTimestamp) / 1000.0;
300 | }
301 |
302 | var pfaTimeOldBrowser = null;
303 | if (_self.pfaTimestampOldBrowser) {
304 | pfaTimeOldBrowser = (Date.now() - _self.pfaTimestampOldBrowser) / 1000.0;
305 | }
306 |
307 | if (pfaTime) {
308 | localAppInsights.trackEvent("PhoneFactorLatency",
309 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username },
310 | { Latency: pfaTime }
311 | );
312 | flushMePlease = true;
313 | } else if (pfaTimeOldBrowser) {
314 | localAppInsights.trackEvent("PhoneFactorLatency",
315 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username },
316 | { OldBrowserLatency: pfaTimeOldBrowser }
317 | );
318 | flushMePlease = true;
319 | }
320 |
321 | if (_self.startedPfaWaiting) {
322 |
323 | localAppInsights.trackEvent("PhoneFactorWaitingEnd",
324 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username }
325 | );
326 | _self.startedPfaWaiting = false;
327 | flushMePlease = true;
328 | }
329 |
330 | if (_self.startedAuthSelectionWaiting) {
331 | localAppInsights.trackEvent("AuthSelectionPageEnd",
332 | { CorrelationID: _self.requestID, CurrentUri: _self.currentUri, CurrentRealm: _self.mswtrealm, wtrealm: _self.decodedwtrealm, wfresh: _self.wfresh, wauth: _self.wauth, Username: _self.Username, Type: _self.authSelectionLinkClicked, SelectionMethod: _self.authSelectionMethod }
333 | );
334 | _self.startedAuthSelectionWaiting = false;
335 | flushMePlease = true;
336 | }
337 |
338 | var authSelectionTime = null;
339 | if (_self.authSelectionTimestamp) {
340 | authSelectionTime = (performance.now() - _self.authSelectionTimestamp) / 1000.0;
341 | }
342 |
343 | var authSelectionTimeOldBrowser = null;
344 | if (_self.authSelectionTimestampOldBrowser) {
345 | authSelectionTimeOldBrowser = (Date.now() - _self.authSelectionTimestampOldBrowser) / 1000.0;
346 | }
347 |
348 | if (authSelectionTime ) {
349 | localAppInsights.trackEvent("AuthSelectionLatency",
350 | { CorrelationID: _self.requestID, Username: _self.Username, wauth: _self.wauth, wfresh: _self.wfresh, wtrealm: _self.decodedwtrealm, Type: _self.authSelectionLinkClicked, SelectionMethod: _self.authSelectionMethod },
351 | { Latency: authSelectionTime }
352 | );
353 | flushMePlease = true;
354 | } else if (authSelectionTimeOldBrowser) {
355 | localAppInsights.trackEvent("AuthSelectionLatency",
356 | { CorrelationID: _self.requestID, Username: _self.Username, wauth: _self.wauth, wfresh: _self.wfresh, wtrealm: _self.decodedwtrealm, Type: _self.authSelectionLinkClicked, SelectionMethod: _self.authSelectionMethod },
357 | { OldBrowserLatency: authSelectionTimeOldBrowser }
358 | );
359 | flushMePlease = true;
360 | }
361 |
362 | if (flushMePlease) {
363 | if (localAppInsights) {
364 | if (localAppInsights.flush) {
365 | localAppInsights.flush();
366 | }
367 | }
368 | }
369 | },
370 | };
371 |
372 | // Produce telemetry
373 | if(console) console.log("TelemetryManager: Start trying to produce telemetry");
374 | var pageTelemetryManager = TelemetryManager;
375 | pageTelemetryManager.Initialize();
376 | pageTelemetryManager.ProducePageDetectionTelemetry();
377 | if(console) console.log("TelemetryManager: End trying to produce telemetry");
378 |
--------------------------------------------------------------------------------