Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Bill Burke 2013-10-25 18:04:35 -04:00
commit 0f03670c06
164 changed files with 6418 additions and 78561 deletions

View file

@ -5,83 +5,238 @@ body {
color: #4d5258;
font-family: "Open Sans", sans-serif;
}
/* Feedback */
.feedback-aligner {
top: 0.9em;
}
.alert {
position: relative;
display: inline-block;
text-align: left;
border-width: 1px;
margin: 0;
font-size: 1.1em;
padding: 0.90909090909091em 3.63636363636364em;
background-repeat: no-repeat;
background-position: 1.27272727272727em center;
line-height: 1.4em;
border-radius: 2px;
color: #4d5258;
}
.alert.alert-success {
border-color: #4b9e39;
background-image: url(img/feedback-success-sign.png);
background-color: #e4f1e1;
}
.alert.alert-error {
border-color: #b91415;
background-image: url(img/feedback-error-sign.png);
background-color: #f8e7e7;
}
.alert.alert-warning {
border-color: #f17528;
background-image: url(img/feedback-warning-sign.png);
background-color: #fef1e9;
}
.alert.alert-info {
border-color: #5994b2;
background-image: url(img/feedback-info-sign.png);
background-color: #e4f3fa;
}
/* Header */
.header.rcue .navbar.utility {
background-color: #393F45;
border-bottom: 1px solid #53565B;
}
.header.rcue .navbar.utility .navbar-inner {
border-bottom: 0;
max-width: 1170px;
}
.header.rcue .navbar.primary {
background-image: -moz-linear-gradient(top, #474c50, #383f43);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#474c50), to(#383f43));
background-image: -webkit-linear-gradient(top, #474c50, #383f43);
background-image: -o-linear-gradient(top, #474c50, #383f43);
background-image: linear-gradient(to bottom, #474c50, #383f43);
background-repeat: repeat-x;
background-color: #41474b;
}
.header.rcue .navbar.primary .navbar-inner {
min-height: 42px;
max-width: 1170px;
}
.header.rcue .navbar.primary .select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.png);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding-left: 0;
}
.header.rcue .navbar.primary .select-rcue:hover {
border-color: #7e8385;
}
.header.rcue .navbar.primary .select-rcue select {
color: #fff;
}
.header.rcue .navbar.primary .select-rcue select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
.header.rcue .navbar.primary .select-rcue select option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
.header.rcue .navbar.primary .nav > li {
/*
.dropdown {
display: inline-block;
margin-left: 0.53846153846154em;
margin-top: 0.46153846153846em;
min-width: 15.3846153846154em;
width: auto;
.dropdown-toggle {
font-size: 0.84615384615385em;
color: #fff;
display: inline-block;
line-height: 2.36363636363636em;
border: 1px solid #676c6e;
border-radius: 2px;
padding: 0 0.54545454545455em;
background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px;
display: block;
&:hover,
&:focus {
text-decoration: none;
}
&:hover {
border-color: #7e8385;
}
}
.dropdown-menu {
left: 0;
min-width: 0;
width: 100%;
overflow: hidden;
li,
li.selected {
width: auto;
a {
width: auto;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
&:hover {
background-color: #D5ECF9;
background-image: none;
border-bottom: 1px solid #A7D7F1;
border-top: 1px solid #A7D7F1;
color: #4D5258;
}
}
}
li.selected a {
background-color: #2B99C0;
background-image: linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -o-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -moz-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -ms-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, #2EA1CA),
color-stop(1,0, #2792B6)
);
background-repeat: repeat-x;
color: #FFFFFF;
}
}
&.open .dropdown-toggle {
text-decoration: none;
box-shadow: inset 0px 2px 5px rgba(0,0,0,0.2);
border-color: #7e8385;
}
}
*/
}
.header.rcue .navbar.primary .nav > li .dropdown-label {
font-size: 0.84615384615385em;
color: #dbdada;
margin-left: 1.36363636363636em;
display: inline-block;
}
.header.rcue .navbar.primary .nav > li .dropdown {
display: inline-block;
margin-left: 0.53846153846154em;
margin-top: 0.46153846153846em;
min-width: 15.3846153846154em;
width: auto;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle {
font-size: 0.84615384615385em;
color: #fff;
display: inline-block;
float: left;
margin-top: 0.63636363636364em;
line-height: 2.36363636363636em;
}
.header.rcue .navbar.primary .nav > li .select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.svg);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding: 0 0.54545454545455em;
background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px;
display: block;
padding-left: 0;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:hover,
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:focus {
text-decoration: none;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-toggle:hover {
.header.rcue .navbar.primary .nav > li .select-rcue:hover {
border-color: #7e8385;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu {
left: 0;
min-width: 0;
width: 100%;
overflow: hidden;
.header.rcue .navbar.primary .nav > li .select-rcue select {
color: #fff;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li,
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected {
width: auto;
.header.rcue .navbar.primary .nav > li .select-rcue select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li a,
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a {
width: auto;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
.header.rcue .navbar.primary .nav > li .select-rcue select option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li a:hover,
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a:hover {
background-color: #D5ECF9;
background-image: none;
border-bottom: 1px solid #A7D7F1;
border-top: 1px solid #A7D7F1;
color: #4D5258;
.header.rcue .navbar.primary .nav > li a#refresh {
border: none;
margin: 0;
padding: 0;
display: inline-block;
vertical-align: middle;
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
}
.header.rcue .navbar.primary .nav > li .dropdown .dropdown-menu li.selected a {
background-color: #2B99C0;
background-image: linear-gradient(top, #2ea1ca 0%, #2792b6 100%);
background-image: -o-linear-gradient(top, #2ea1ca 0%, #2792b6 100%);
background-image: -moz-linear-gradient(top, #2ea1ca 0%, #2792b6 100%);
background-image: -webkit-linear-gradient(top, #2ea1ca 0%, #2792b6 100%);
background-image: -ms-linear-gradient(top, #2ea1ca 0%, #2792b6 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #2ea1ca), color-stop(1, 0, #2792b6));
background-repeat: repeat-x;
color: #FFFFFF;
.header.rcue .navbar.primary .nav > li a#refresh:hover {
background: none;
}
.header.rcue .navbar.primary .nav > li .dropdown.open .dropdown-toggle {
text-decoration: none;
box-shadow: inset 0px 2px 5px rgba(0, 0, 0, 0.2);
border-color: #7e8385;
.header.rcue .navbar.primary .nav > li a#refresh .icon-spinner6 {
margin-right: 0;
background-image: url('img/sprites-gray.png');
}
.header.rcue .navbar.primary .nav > li a#refresh .icon-spinner6:hover {
background-image: url('img/sprites-white.png');
background-color: transparent;
}
.header.rcue .navbar.primary .button {
font-size: 0.84615384615385em;
@ -100,11 +255,12 @@ body {
}
.bs-sidebar ul li {
margin-bottom: 0.5em;
margin-left: -1em;
}
.bs-sidebar ul li a {
font-size: 1.3em;
font-family: "Open Sans", sans-serif;
padding-left: 3em;
padding-left: 1.92307692307692em;
color: #4d5258;
line-height: 2.07692307692308em;
display: block;
@ -112,7 +268,8 @@ body {
border-style: solid;
border-color: #f9f9f9;
}
.bs-sidebar ul li a:hover {
.bs-sidebar ul li a:hover,
.bs-sidebar ul li a:focus {
text-decoration: none;
color: #777777;
}
@ -120,6 +277,9 @@ body {
background-color: #c7e5f0;
border-color: #56bae0;
font-weight: bold;
background-image: url(img/icon-sidebar-active.svg);
background-repeat: no-repeat;
background-position: right center;
}
#content-area {
padding: 0;
@ -135,6 +295,7 @@ body {
border-color: #cecece;
font-size: 1em;
margin-bottom: 0;
min-height: 4.4em;
}
#content-area .top-nav ul.rcue-tabs li {
margin: 0 0.5em -1px 0;
@ -164,6 +325,7 @@ body {
font-weight: 100;
font-size: 2.4em;
margin-bottom: 1.04166666666667em;
margin-top: 1em;
}
#content-area #content h2 span {
color: #a1a1a1;
@ -241,10 +403,6 @@ body {
top: 70px;
width: 877.5px;
}
/* Page: Realm Users */
.realm-users caption {
display: none;
}
table thead tr th {
font-size: 1.1em;
}
@ -260,6 +418,9 @@ table thead tr:first-child th {
table a:hover {
color: #0099D3;
}
table + .feedback.inline.warning {
margin-top: 1em;
}
.advanced-search-comp {
position: relative;
display: inline-block;
@ -271,7 +432,7 @@ table a:hover {
.tooltip-box {
position: absolute;
font-size: 1em;
background-image: url("img/tooltip-box-arrow-right-up.svg");
background-image: url("img/tooltip-box-arrow-right-up.png");
background-position: right top;
background-repeat: no-repeat;
padding-top: 1em;
@ -422,20 +583,37 @@ table.list tbody tr.expanded .form-actions {
}
/* Break Points */
@media (max-width: 1200px) {
.header.rcue .navbar.utility .navbar-inner,
.header.rcue .navbar.primary .navbar-inner {
max-width: 970px;
}
#container-right-bg {
margin-left: 242.5px;
width: 727.5px;
}
}
@media (max-width: 992px) {
.header.rcue .navbar.utility .navbar-inner,
.header.rcue .navbar.primary .navbar-inner {
max-width: 750px;
}
.bs-sidebar,
.user .bs-sidebar {
padding-top: 2em;
padding-top: 1em;
width: 100%;
}
.bs-sidebar ul li,
.user .bs-sidebar ul li {
margin-left: 0;
}
.bs-sidebar ul li a,
.user .bs-sidebar ul li a {
border-width: 1px;
padding-left: 1.53846153846154em;
}
.bs-sidebar ul li.active a,
.user .bs-sidebar ul li.active a {
background-image: none;
}
#content-area .top-nav {
border-top: 1px solid #cecece;
@ -449,8 +627,17 @@ table.list tbody tr.expanded .form-actions {
}
}
@media (max-width: 768px) {
.header.rcue .navbar.utility .navbar-inner,
.header.rcue .navbar.primary .navbar-inner {
max-width: 100%;
}
.bs-sidebar ul li.active a,
.user .bs-sidebar ul li.active a {
border-left: none;
border-right: none;
}
.container {
min-width: 580px;
min-width: 56em;
}
#content-area {
border: none;

View file

@ -15,121 +15,281 @@ body {
font-family: @open-sans;
}
/* Feedback */
.feedback-aligner {
top: 0.9em;
}
.alert {
position: relative;
display: inline-block;
text-align: left;
border-width: 1px;
margin: 0;
font-size: 1.1em;
padding: 0.90909090909091em 3.63636363636364em;
background-repeat: no-repeat;
background-position: 1.27272727272727em center;
line-height: 1.4em;
border-radius: 2px;
color: #4d5258;
&.alert-success {
border-color: #4b9e39;
background-image: url(img/feedback-success-sign.png);
background-color: #e4f1e1;
}
&.alert-error {
border-color: #b91415;
background-image: url(img/feedback-error-sign.png);
background-color: #f8e7e7;
}
&.alert-warning {
border-color: #f17528;
background-image: url(img/feedback-warning-sign.png);
background-color: #fef1e9;
}
&.alert-info {
border-color: #5994b2;
background-image: url(img/feedback-info-sign.png);
background-color: #e4f3fa;
}
}
/* Header */
.header.rcue .navbar.primary {
.header.rcue {
.navbar-inner {
min-height: 42px;
.navbar.utility {
background-color: #393F45;
border-bottom: 1px solid #53565B;
.navbar-inner {
border-bottom: 0;
max-width: 1170px;
}
}
.nav > li {
.dropdown-label {
font-size: 0.84615384615385em;
color: #dbdada;
margin-left: 1.36363636363636em;
.navbar.primary {
background-image:-moz-linear-gradient(top,#474c50,#383f43);
background-image:-webkit-gradient(linear,0 0,0 100%,from(#474c50),to(#383f43));
background-image:-webkit-linear-gradient(top,#474c50,#383f43);
background-image:-o-linear-gradient(top,#474c50,#383f43);
background-image:linear-gradient(to bottom,#474c50,#383f43);
background-repeat:repeat-x;
background-color: #41474b;
.navbar-inner {
min-height: 42px;
max-width: 1170px;
}
.select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.png);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding-left: 0;
&:hover {
border-color: #7e8385;
}
select {
color: #fff;
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
}
}
.dropdown {
display: inline-block;
margin-left: 0.53846153846154em;
margin-top: 0.46153846153846em;
min-width: 15.3846153846154em;
width: auto;
.dropdown-toggle {
.nav > li {
.dropdown-label {
font-size: 0.84615384615385em;
color: #fff;
color: #dbdada;
margin-left: 1.36363636363636em;
float: left;
margin-top: 0.63636363636364em;
line-height: 2.36363636363636em
}
.select-rcue {
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
display: inline-block;
line-height: 2.36363636363636em;
vertical-align: middle;
background-color: #555a5e;
background-image: none;
background-image: url(img/sprite-arrow-down.svg);
background-repeat: no-repeat;
background-position: right -26px;
border: 1px solid #676c6e;
border-radius: 2px;
padding: 0 0.54545454545455em;
background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px;
display: block;
&:hover,
&:focus {
text-decoration: none;
}
padding-left: 0;
&:hover {
border-color: #7e8385;
}
select {
color: #fff;
&:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #fff;
}
option {
background-color: #fff;
color: #333;
padding: 0.36363636363636em 0.90909090909091em;
}
}
}
.dropdown-menu {
left: 0;
min-width: 0;
width: 100%;
overflow: hidden;
li,
li.selected {
width: auto;
a#refresh {
border: none;
margin: 0;
padding: 0;
display: inline-block;
vertical-align: middle;
font-size: 0.76923076923077em;
margin-left: 1em;
margin-top: 0.7em;
&:hover {
background: none;
}
.icon-spinner6 {
margin-right: 0;
background-image: url('img/sprites-gray.png');
a {
width: auto;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
&:hover {
background-color: #D5ECF9;
background-image: none;
border-bottom: 1px solid #A7D7F1;
border-top: 1px solid #A7D7F1;
color: #4D5258;
}
&:hover {
background-image: url('img/sprites-white.png');
background-color: transparent;
}
}
}
/*
.dropdown {
display: inline-block;
margin-left: 0.53846153846154em;
margin-top: 0.46153846153846em;
min-width: 15.3846153846154em;
width: auto;
.dropdown-toggle {
font-size: 0.84615384615385em;
color: #fff;
display: inline-block;
line-height: 2.36363636363636em;
border: 1px solid #676c6e;
border-radius: 2px;
padding: 0 0.54545454545455em;
background: #555a5e url(img/sprite-arrow-down.svg) no-repeat right -26px;
display: block;
&:hover,
&:focus {
text-decoration: none;
}
&:hover {
border-color: #7e8385;
}
}
li.selected a {
background-color: #2B99C0;
background-image: linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -o-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -moz-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -ms-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, #2EA1CA),
color-stop(1,0, #2792B6)
);
background-repeat: repeat-x;
color: #FFFFFF;
.dropdown-menu {
left: 0;
min-width: 0;
width: 100%;
overflow: hidden;
li,
li.selected {
width: auto;
a {
width: auto;
border-top: 1px solid transparent;
border-bottom: 1px solid transparent;
&:hover {
background-color: #D5ECF9;
background-image: none;
border-bottom: 1px solid #A7D7F1;
border-top: 1px solid #A7D7F1;
color: #4D5258;
}
}
}
li.selected a {
background-color: #2B99C0;
background-image: linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -o-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -moz-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -ms-linear-gradient(top, #2EA1CA 0%, #2792B6 100%);
background-image: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, #2EA1CA),
color-stop(1,0, #2792B6)
);
background-repeat: repeat-x;
color: #FFFFFF;
}
}
&.open .dropdown-toggle {
text-decoration: none;
box-shadow: inset 0px 2px 5px rgba(0,0,0,0.2);
border-color: #7e8385;
}
}
&.open .dropdown-toggle {
text-decoration: none;
box-shadow: inset 0px 2px 5px rgba(0,0,0,0.2);
border-color: #7e8385;
}
*/
}
}
.button {
font-size: 0.84615384615385em;
margin-right: 1.36363636363636em;
margin-top: 0.63636363636364em;
&:focus {
text-decoration: none;
.button {
font-size: 0.84615384615385em;
margin-right: 1.36363636363636em;
margin-top: 0.63636363636364em;
&:focus {
text-decoration: none;
}
}
}
}
.bs-sidebar {
background-color: @bg-grey;
padding-top: 4.3em;
@ -139,11 +299,12 @@ body {
ul li {
margin-bottom: 0.5em;
margin-left: -1em;
a {
font-size: 1.3em;
font-family: @open-sans;
padding-left: 3em;
padding-left: 1.92307692307692em;
color: @text-black;
line-height: 2.07692307692308em;
display: block;
@ -151,7 +312,8 @@ body {
border-style: solid;
border-color: @bg-grey;
&:hover {
&:hover,
&:focus {
text-decoration: none;
color: @text-grey;
}
@ -161,6 +323,9 @@ body {
background-color: #c7e5f0;
border-color: #56bae0;
font-weight: bold;
background-image: url(img/icon-sidebar-active.svg);
background-repeat: no-repeat;
background-position: right center;
}
}
}
@ -179,6 +344,7 @@ body {
border-color: @border-dark-grey;
font-size: 1em;
margin-bottom: 0;
min-height: 4.4em;
li {
margin: 0 0.5em -1px 0;
@ -215,6 +381,7 @@ body {
font-weight: 100;
font-size: 2.4em;
margin-bottom: 1.04166666666667em;
margin-top: 1em;
span {
color: @text-light-grey;
@ -312,13 +479,6 @@ body {
width: 877.5px;
}
/* Page: Realm Users */
.realm-users caption {
display: none;
}
table {
thead tr {
@ -343,6 +503,10 @@ table {
a:hover {
color: #0099D3;
}
+ .feedback.inline.warning {
margin-top: 1em;
}
}
@ -359,7 +523,7 @@ table {
.tooltip-box {
position: absolute;
font-size: 1em;
background-image: url("img/tooltip-box-arrow-right-up.svg");
background-image: url("img/tooltip-box-arrow-right-up.png");
background-position: right top;
background-repeat: no-repeat;
padding-top: 1em;
@ -559,6 +723,14 @@ table.list {
@media (max-width: 1200px) {
.header.rcue {
.navbar.utility .navbar-inner,
.navbar.primary .navbar-inner {
max-width: 970px;
}
}
#container-right-bg {
margin-left: 242.5px;
width: 727.5px;
@ -567,14 +739,32 @@ table.list {
@media (max-width: 992px) {
.header.rcue {
.navbar.utility .navbar-inner,
.navbar.primary .navbar-inner {
max-width: 750px;
}
}
.bs-sidebar,
.user .bs-sidebar {
padding-top: 2em;
padding-top: 1em;
width: 100%;
ul li a {
border-width: 1px;
}
ul li {
margin-left: 0;
a {
border-width: 1px;
padding-left: 1.53846153846154em;
}
&.active a {
background-image: none;
}
}
}
#content-area .top-nav {
@ -594,8 +784,25 @@ table.list {
@media (max-width: 768px) {
.header.rcue {
.navbar.utility .navbar-inner,
.navbar.primary .navbar-inner {
max-width: 100%;
}
}
.bs-sidebar,
.user .bs-sidebar {
ul li.active a {
border-left: none;
border-right: none;
}
}
.container {
min-width: 580px;
min-width: 56em;
}
#content-area {
@ -607,9 +814,3 @@ table.list {
width: 100%;
}
}

View file

@ -6,9 +6,14 @@ fieldset {
color: #838383;
font-style: italic;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #333;
}
input[type="text"],
input[type="password"],
input[type="email"] {
input[type="email"],
textarea {
font-size: 1.1em;
padding: 0 0.545454545454545em;
min-width: 18.1818181818182em;
@ -20,18 +25,21 @@ input[type="email"] {
}
input[type="text"]:hover,
input[type="password"]:hover,
input[type="email"]:hover {
input[type="email"]:hover,
textarea:hover {
border-color: #62afdb;
}
input[type="text"]:focus,
input[type="password"]:focus,
input[type="email"]:focus {
input[type="email"]:focus,
textarea:focus {
border-color: #62afdb;
box-shadow: #62afdb 0 0 5px;
}
input[type="text"].error,
input[type="password"].error,
input[type="email"].error {
input[type="email"].error,
textarea.error {
border-color: #ba1212;
transition: all 0.33s ease-in-out;
-moz-transition: all 0.33s ease-in-out;
@ -39,15 +47,39 @@ input[type="email"].error {
}
input[type="text"].error:focus,
input[type="password"].error:focus,
input[type="email"].error:focus {
input[type="email"].error:focus,
textarea.error:focus {
box-shadow: 0 0 5px #ba1212;
}
textarea {
padding: 0.45em 0.545454545454545em;
height: auto;
}
input[type="text"][readonly],
input[type="password"][readonly],
input[type="email"][readonly],
textarea[readonly] {
background-color: #f0f0f0;
}
input[type="text"][readonly]:hover,
input[type="password"][readonly]:hover,
input[type="email"][readonly]:hover,
textarea[readonly]:hover {
border-color: #62afdb;
}
input[type="text"][readonly]:focus,
input[type="password"][readonly]:focus,
input[type="email"][readonly]:focus,
textarea[readonly]:focus {
border-color: #b6b6b6;
box-shadow: none;
}
.input-below {
clear: both;
display: inline-block;
margin-left: 10.9090909090909em;
margin-top: 0.45454545454545em;
padding-left: 3.63636363636364em;
clear: both;
display: inline-block;
margin-left: 10.9090909090909em;
margin-top: 0.45454545454545em;
padding-left: 3.63636363636364em;
}
input[type="button"],
button,
@ -171,6 +203,23 @@ button.primary:enabled:active,
background-color: #0099d4;
box-shadow: inset 0 0 5px 3px #0074ae;
}
.destructive {
background-image: linear-gradient(top, #d90000 0%, #ad0101 100%);
background-image: -o-linear-gradient(top, #d90000 0%, #ad0101 100%);
background-image: -moz-linear-gradient(top, #d90000 0%, #ad0101 100%);
background-image: -webkit-linear-gradient(top, #d90000 0%, #ad0101 100%);
background-image: -ms-linear-gradient(top, #d90000 0%, #ad0101 100%);
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #d90000), color-stop(1, 0, #ad0101));
border-color: #350000;
color: #FFFFFF;
}
.destructive:hover {
background-color: #AD0101;
}
.destructive:enabled:active {
background-color: #AD0101;
box-shadow: 0 0 5px 3px #750101 inset;
}
/* Code from Gabriel */
button.primary,
.button.primary {
@ -205,9 +254,14 @@ button.primary:focus,
right: 0.2em;
top: 0.4em;
opacity: 0.5;
filter: alpha(opacity=50);
}
.search-comp .icon-search:hover {
.search-comp .icon-search:hover,
.search-comp .icon-search:active,
.search-comp .icon-search:focus {
background-image: url('img/sprites.png');
opacity: 1;
filter: alpha(opacity=100);
-webkit-transition: ease-in-out opacity 0.25s;
-moz-transition: ease-in-out opacity 0.25s;
-o-transition: ease-in-out opacity 0.25s;
@ -265,26 +319,44 @@ button.primary:focus,
background-position: 1.27272727272727em 1.63636363636364em;
}
.feedback.error {
background-image: url(img/feedback-error-arrow-down.svg);
background-image: url(img/feedback-error-arrow-down.png);
}
.feedback.error p {
border-color: #b91415;
background-image: url(img/feedback-error-sign.svg);
background-image: url(img/feedback-error-sign.png);
background-color: #f8e7e7;
}
.feedback.success {
background-image: url(img/feedback-success-arrow-down.svg);
background-image: url(img/feedback-success-arrow-down.png);
}
.feedback.success p {
border-color: #4b9e39;
background-image: url(img/feedback-success-sign.svg);
background-image: url(img/feedback-success-sign.png);
background-color: #e4f1e1;
}
.feedback.warning p {
border-color: #f17528;
background-image: url(img/feedback-warning-sign.svg);
background-image: url(img/feedback-warning-sign.png);
background-color: #fef1e9;
}
.feedback.info p {
border-color: #5994b2;
background-image: url(img/feedback-info-sign.png);
background-color: #e4f3fa;
}
.feedback.inline {
opacity: 1;
position: relative;
margin-bottom: 20px;
}
.feedback.inline p {
border-width: 1px;
}
.feedback .button {
display: inline-block;
font-size: 0.90909090909091em;
margin-left: 0.90909090909091em;
}
button,
a.button {
background-color: #eeeeee;
@ -366,6 +438,7 @@ fieldset.border-top {
margin-bottom: 0;
float: left;
margin-top: 0.45454545454545em;
font-weight: 400;
}
.form-group > label.two-lines {
margin-top: -2px;
@ -612,6 +685,9 @@ input[type="email"].tiny {
border-bottom: 1px solid #a7d7f1;
color: #4d5258;
}
.select2-container-multi .select2-choices {
width: 40em;
}
.input-group input + .select-rcue {
border-radius: 0 2px 2px 0;
border-left: 0;
@ -720,6 +796,9 @@ input[type="email"].tiny {
font-size: 1.1em;
margin-right: 0.90909090909091em;
}
.changing-selectors.application {
padding-left: 12em;
}
.changing-selectors .select-title {
display: inline-block;
}
@ -774,7 +853,7 @@ input[type="email"].tiny {
}
.breadcrumb {
background: none;
margin: 5px 0 5px 0;
margin: 5px 0 0 0;
padding: 0;
}
.breadcrumb li a {
@ -783,11 +862,3 @@ input[type="email"].tiny {
.breadcrumb > li + li:before {
content: "» ";
}
.item-deletable:hover .btn-delete {
display: inline-block;
}
.btn-delete {
display: none;
}

View file

@ -11,10 +11,16 @@ fieldset {
font-style: italic;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #333;
}
input[type="text"],
input[type="password"],
input[type="email"],
{
textarea {
font-size: 1.1em;
padding: 0 0.545454545454545em;
min-width: 18.1818181818182em;
@ -45,6 +51,35 @@ input[type="email"],
}
}
textarea {
padding: 0.45em 0.545454545454545em;
height: auto;
}
input[type="text"][readonly],
input[type="password"][readonly],
input[type="email"][readonly],
textarea[readonly] {
background-color: #f0f0f0;
&:hover {
border-color: #62afdb;
}
&:focus {
border-color: #b6b6b6;
box-shadow: none;
}
}
.input-below {
clear: both;
display: inline-block;
margin-left: 10.9090909090909em;
margin-top: 0.45454545454545em;
padding-left: 3.63636363636364em;
}
input[type="button"],
button,
a.button {
@ -180,6 +215,32 @@ button.primary:enabled:active,
box-shadow: inset 0 0 5px 3px #0074ae;
}
.destructive {
background-image: linear-gradient(top, #D90000 0%, #AD0101 100%);
background-image: -o-linear-gradient(top, #D90000 0%, #AD0101 100%);
background-image: -moz-linear-gradient(top, #D90000 0%, #AD0101 100%);
background-image: -webkit-linear-gradient(top, #D90000 0%, #AD0101 100%);
background-image: -ms-linear-gradient(top, #D90000 0%, #AD0101 100%);
background-image: -webkit-gradient(
linear,
left top,
left bottom,
color-stop(0.0, #D90000),
color-stop(1,0, #AD0101)
);
border-color: #350000;
color: #FFFFFF;
&:hover {
background-color: #AD0101;
}
&:enabled:active {
background-color: #AD0101;
box-shadow: 0 0 5px 3px #750101 inset;
}
}
/* Code from Gabriel */
@ -221,9 +282,14 @@ button.primary,
right: 0.2em;
top: 0.4em;
opacity: 0.5;
filter: alpha(opacity=50);
&:hover {
&:hover,
&:active,
&:focus {
background-image: url('img/sprites.png');
opacity: 1;
filter: alpha(opacity=100);
-webkit-transition: ease-in-out opacity 0.25s;
-moz-transition: ease-in-out opacity 0.25s;
-o-transition: ease-in-out opacity 0.25s;
@ -292,21 +358,21 @@ button.primary,
}
&.error {
background-image: url(img/feedback-error-arrow-down.svg);
background-image: url(img/feedback-error-arrow-down.png);
p {
border-color: #b91415;
background-image: url(img/feedback-error-sign.svg);
background-image: url(img/feedback-error-sign.png);
background-color: #f8e7e7;
}
}
&.success {
background-image: url(img/feedback-success-arrow-down.svg);
background-image: url(img/feedback-success-arrow-down.png);
p {
border-color: #4b9e39;
background-image: url(img/feedback-success-sign.svg);
background-image: url(img/feedback-success-sign.png);
background-color: #e4f1e1;
}
}
@ -315,11 +381,37 @@ button.primary,
p {
border-color: #f17528;
background-image: url(img/feedback-warning-sign.svg);
background-image: url(img/feedback-warning-sign.png);
background-color: #fef1e9;
}
}
&.info {
p {
border-color: #5994b2;
background-image: url(img/feedback-info-sign.png);
background-color: #e4f3fa;
}
}
&.inline {
opacity: 1;
position: relative;
margin-bottom: 20px;
p {
border-width: 1px;
}
}
.button {
display: inline-block;
font-size: 0.90909090909091em;
margin-left: 0.90909090909091em;
}
}
button,
@ -416,6 +508,7 @@ fieldset.border-top {
margin-bottom: 0;
float: left;
margin-top: 0.45454545454545em;
font-weight: 400;
&.two-lines {
margin-top: -2px;
@ -731,6 +824,10 @@ input[type="email"] {
}
}
.select2-container-multi .select2-choices {
width: 40em;
}
.input-group input + .select-rcue {
border-radius: 0 2px 2px 0;
border-left: 0;
@ -856,6 +953,10 @@ input[type="email"] {
.changing-selectors {
&.application {
padding-left: 12em;
}
.select-title {
display: inline-block;
@ -927,7 +1028,7 @@ input[type="email"] {
.breadcrumb {
background: none;
margin: 5px 0 5px 0;
margin: 5px 0 0 0;
padding: 0;
li {

View file

@ -1,164 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1680px" height="1080px" viewBox="0 0 1680 1080" enable-background="new 0 0 1680 1080" xml:space="preserve">
<rect fill="#383D42" width="1680" height="1080"/>
<g>
<defs>
<filter id="Adobe_OpacityMaskFilter" filterUnits="userSpaceOnUse" x="1057" y="597" width="704" height="572">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="1057" y="597" width="704" height="572" id="SVGID_1_">
<g filter="url(#Adobe_OpacityMaskFilter)">
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="1302.4395" y1="-269.9951" x2="1622.4397" y2="22.0046" gradientTransform="matrix(-1 0 0 -1 3112 976.6914)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<rect x="1035" y="573" fill="url(#SVGID_2_)" width="752" height="620"/>
</g>
</mask>
<path mask="url(#SVGID_1_)" fill="#D8D8D8" d="M1761,597c-140,214-410,510-704,572h704V597z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_1_" filterUnits="userSpaceOnUse" x="1056.742" y="596.315" width="705.304" height="573.907">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="1056.742" y="596.315" width="705.304" height="573.907" id="SVGID_3_">
<g filter="url(#Adobe_OpacityMaskFilter_1_)">
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="1302.4395" y1="-269.9951" x2="1622.4397" y2="22.0046" gradientTransform="matrix(-1 0 0 -1 3112 976.6914)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<rect x="1035" y="573" fill="url(#SVGID_4_)" width="752" height="620"/>
</g>
</mask>
<path opacity="0.1" mask="url(#SVGID_3_)" fill="none" stroke="#D8D8D8" stroke-width="2.5" stroke-miterlimit="10" d="M1761,597
c-140,214-410,510-704,572"/>
</g>
<defs>
<filter id="Adobe_OpacityMaskFilter_2_" filterUnits="userSpaceOnUse" x="168.747" y="365.576" width="1798.615" height="822.631">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="168.747" y="365.576" width="1798.615" height="822.631" id="SVGID_5_">
<g filter="url(#Adobe_OpacityMaskFilter_2_)">
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="1476.5908" y1="477.5732" x2="2688.6016" y2="21.5689" gradientTransform="matrix(-0.9876 -0.1569 0.1569 -0.9876 3048.3154 1294.3018)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon fill="url(#SVGID_6_)" points="329.208,58.782 1976.554,320.459 1810.26,1367.334 162.914,1105.656 "/>
</g>
</mask>
<path mask="url(#SVGID_5_)" fill="#E0E0E0" d="M1967.361,365.576c-393.429,322.269-1126.777,782.925-1798.615,728.856
c379.652,70.433,1428.387,135.769,1651.069,59.762L1967.361,365.576z"/>
<path fill="#D8D8D8" d="M-114.695,1221h1160.347c80.272-27.337,158.024-70.67,230.989-123.266
C792.789,983.34-203.837,592.187-368.695,373L-114.695,1221z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_3_" filterUnits="userSpaceOnUse" x="-395" y="514.337" width="1423.831" height="829.326">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="-395" y="514.337" width="1423.831" height="829.326" id="SVGID_7_">
<g filter="url(#Adobe_OpacityMaskFilter_3_)">
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="2111.374" y1="319.9775" x2="3115.3701" y2="81.9786" gradientTransform="matrix(-1.075 0.1117 -0.1167 -1.1224 3229.8789 866.1523)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="0.0972" style="stop-color:#060606"/>
<stop offset="0.2213" style="stop-color:#151515"/>
<stop offset="0.36" style="stop-color:#303030"/>
<stop offset="0.5093" style="stop-color:#545454"/>
<stop offset="0.6671" style="stop-color:#838383"/>
<stop offset="0.8323" style="stop-color:#BDBDBD"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon fill="url(#SVGID_8_)" points="-543.126,587.599 1129.521,413.746 1223.549,1318.382 -449.1,1492.233 "/>
</g>
</mask>
<path mask="url(#SVGID_7_)" fill="#C6C6C5" d="M-395,1343.663c225.178-156.636,836.847-511.283,1423.831-382.832l-17.056-446.355
C360.896,503.847-136.975,1108.097-395,1343.663z"/>
<path opacity="0.2" fill="none" stroke="#D8D8D8" stroke-width="5" stroke-miterlimit="10" d="M-368.695,373
c164.858,219.187,1161.484,610.34,1645.336,724.734c154.24,39.423,293.768,67.189,413.359,86.745"/>
<path opacity="0.4" fill="none" stroke="#777D82" stroke-width="3" stroke-miterlimit="10" d="M-218.695,311
c338,316,1048,836,1440,990"/>
<path opacity="0.1" fill="none" stroke="#C6C6C5" stroke-width="2" stroke-miterlimit="10" d="M1257.069,1089.063
c124.494-56.997,382.481-291.736,437.979-414.73"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_4_" filterUnits="userSpaceOnUse" x="535.169" y="-135" width="1423.831" height="829.326">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="535.169" y="-135" width="1423.831" height="829.326" id="SVGID_9_">
<g filter="url(#Adobe_OpacityMaskFilter_4_)">
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="664.5693" y1="382.668" x2="1668.5647" y2="144.6692" gradientTransform="matrix(1.075 -0.1117 0.1167 1.1224 -117.8789 110.5386)">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<polygon fill="url(#SVGID_10_)" points="2107.126,621.064 434.478,794.917 340.452,-109.719 2013.1,-283.57 "/>
</g>
</mask>
<path mask="url(#SVGID_9_)" fill="#C6C6C5" d="M1959-135C1733.822,21.636,1122.153,376.284,535.169,247.832l17.055,446.356
C1203.104,704.816,1700.975,100.567,1959-135z"/>
<path fill="#D8D8D8" d="M1743-27H582.653C502.38,0.337,424.629,43.67,351.665,96.265C835.516,210.66,1832.142,601.813,1997,821
L1743-27z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_5_" filterUnits="userSpaceOnUse" x="-49" y="-47" width="704" height="572">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="-49" y="-47" width="704" height="572" id="SVGID_11_">
<g filter="url(#Adobe_OpacityMaskFilter_5_)">
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="-97.5605" y1="-124.686" x2="222.4397" y2="167.3137">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<rect x="-75" y="-71" fill="url(#SVGID_12_)" width="752" height="620"/>
</g>
</mask>
<path mask="url(#SVGID_11_)" fill="#D8D8D8" d="M-49,525C91,311,361,15,655-47H-49V525z"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_6_" filterUnits="userSpaceOnUse" x="-209" y="-147.691" width="1662" height="1002.691">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="-209" y="-147.691" width="1662" height="1002.691" id="SVGID_13_">
<g filter="url(#Adobe_OpacityMaskFilter_6_)">
<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="54.2939" y1="584.9688" x2="1266.2927" y2="128.9689">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<rect x="-211" y="-159" fill="url(#SVGID_14_)" width="1668" height="1060"/>
</g>
</mask>
<path mask="url(#SVGID_13_)" fill="#E0E0E0" d="M-209,855C129,475,781-95,1453-147C1067-157,21-57-187,53L-209,855z"/>
<path opacity="0.2" fill="none" stroke="#D8D8D8" stroke-width="5" stroke-miterlimit="10" d="M1997,821
C1832.142,601.813,835.516,210.66,351.665,96.265C197.423,56.843,57.896,29.076-61.695,9.521"/>
<path opacity="0.4" fill="none" stroke="#777D82" stroke-width="3" stroke-miterlimit="10" d="M1847,883C1509,567,799,47,407-107"/>
<defs>
<filter id="Adobe_OpacityMaskFilter_7_" filterUnits="userSpaceOnUse" x="-50.046" y="-48.223" width="705.304" height="573.907">
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0"/>
</filter>
</defs>
<mask maskUnits="userSpaceOnUse" x="-50.046" y="-48.223" width="705.304" height="573.907" id="SVGID_15_">
<g filter="url(#Adobe_OpacityMaskFilter_7_)">
<linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="-97.5605" y1="-124.686" x2="222.4397" y2="167.3137">
<stop offset="0" style="stop-color:#000000"/>
<stop offset="1" style="stop-color:#FFFFFF"/>
</linearGradient>
<rect x="-75" y="-71" fill="url(#SVGID_16_)" width="752" height="620"/>
</g>
</mask>
<path opacity="0.1" mask="url(#SVGID_15_)" fill="none" stroke="#D8D8D8" stroke-width="2.5" stroke-miterlimit="10" d="M-49,525
C91,311,361,15,655-47"/>
<circle fill="#D8D8D8" cx="1247.084" cy="399.5" r="2.5"/>
<circle fill="#D8D8D8" cx="436.333" cy="117.667" r="2"/>
<circle fill="#D8D8D8" cx="254.667" cy="173.667" r="2"/>
<circle fill="#D8D8D8" cx="375" cy="101.667" r="4"/>
<circle fill="#D8D8D8" cx="351.665" cy="96.265" r="5"/>
</svg>

Before

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 B

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="svg7384" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="17px" height="17px" viewBox="0 0 17 17" enable-background="new 0 0 17 17" xml:space="preserve">
<g>
<g>
<g>
<path fill="#418093" d="M8.24,12.734c-0.544,0.018-1.048-0.486-1.031-1.031V7.766c-0.007-0.527,0.473-1.014,1-1.014
c0.528,0,1.008,0.486,1,1.014v3.938c0.008,0.467-0.354,0.914-0.813,1C8.345,12.719,8.292,12.729,8.24,12.734L8.24,12.734z"/>
</g>
<g>
<rect x="7.208" y="3.766" fill="#418093" width="2" height="2"/>
</g>
</g>
<g id="path3869_3_">
<path fill="#418093" d="M8.209,16.418C3.683,16.418,0,12.736,0,8.209C0,3.683,3.683,0,8.209,0s8.208,3.683,8.208,8.209
C16.418,12.736,12.736,16.418,8.209,16.418z M8.209,1.5C4.51,1.5,1.5,4.51,1.5,8.209s3.01,6.709,6.709,6.709
c3.699,0,6.708-3.01,6.708-6.709S11.908,1.5,8.209,1.5z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="1000px" height="11px" viewBox="0 0 1000 11" enable-background="new 0 0 1000 11" xml:space="preserve">
<path fill="#E4F1E1" d="M1000,0c0,1.104-0.896,2-2,2H38l-8,9l-8-9H2C0.896,2,0,1.104,0,0"/>
<path fill="#4B9E39" d="M999,0c0,0.551-0.448,1-1,1H38h-0.449l-0.298,0.335L30,9.495l-7.253-8.159L22.449,1H22H2
C1.449,1,1,0.551,1,0 M0,0c0,1.104,0.896,2,2,2h20l8,9l8-9h960c1.104,0,2-0.896,2-2"/>
</svg>

After

Width:  |  Height:  |  Size: 779 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 B

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="17px" height="17px" viewBox="0 0 17 17" enable-background="new 0 0 17 17" xml:space="preserve">
<path fill="#58A846" d="M12.524,4.654l-5.472,5.893L4.57,8.022L3.145,9.425l3.222,3.276C6.555,12.893,6.811,13,7.079,13
c0.006,0,0.013,0,0.019,0c0.273-0.005,0.535-0.122,0.72-0.325L14,6.003L12.524,4.654z"/>
</svg>

After

Width:  |  Height:  |  Size: 691 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 387 B

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="svg7384" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="18px" height="17px" viewBox="0 0 18 17" enable-background="new 0 0 18 17" xml:space="preserve">
<g>
<path fill="#EE7700" d="M17.077,12.955L10.745,1.39c-0.518-0.838-1.271-1.343-2.064-1.387L8.572,0C7.773,0,7.057,0.478,6.54,1.364
L0.295,12.951c-0.41,0.738-0.391,1.695,0.047,2.439c0.381,0.646,1.002,1.02,1.703,1.02H15.2c0.77,0,1.498-0.436,1.902-1.137
C17.521,14.551,17.507,13.701,17.077,12.955z M1.61,13.672L7.847,2.099c0.131-0.224,0.396-0.598,0.75-0.598l0,0
c0.295,0.016,0.621,0.27,0.853,0.643l6.32,11.546c0.213,0.369,0.135,0.658,0.033,0.834c-0.133,0.23-0.377,0.387-0.604,0.387H2.045
c-0.203,0-0.336-0.154-0.411-0.281C1.492,14.387,1.428,14,1.61,13.672z"/>
<path fill="#EE7700" d="M8.732,10.672c0.527,0,1.007-0.486,1-1.014V5.72c0.018-0.544-0.486-1.048-1.031-1.031
C8.648,4.696,8.595,4.706,8.544,4.72c-0.459,0.087-0.82,0.533-0.812,1v3.938C7.725,10.186,8.205,10.672,8.732,10.672z"/>
<rect x="7.732" y="11.658" fill="#EE7700" width="1.999" height="2"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="23px" height="14px" viewBox="0 0 23 14" enable-background="new 0 0 23 14" xml:space="preserve">
<polyline fill="none" stroke="#009AD8" stroke-miterlimit="10" points="1,13 7,7 1,1 "/>
</svg>

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="324px" height="400px" viewBox="0 0 324 400" enable-background="new 0 0 324 400" xml:space="preserve">
<rect x="6.001" opacity="0.07" fill="#FFFFFF" enable-background="new " width="0.997" height="190"/>
<rect x="6" y="209" opacity="0.07" fill="#FFFFFF" enable-background="new " width="1" height="191"/>
<g opacity="0.15">
<path fill="#FFFFFF" d="M6.501,200.066c0,1.047-0.264,1.864-0.791,2.452S4.454,203.4,3.524,203.4c-0.574,0-1.084-0.135-1.529-0.404
c-0.445-0.27-0.789-0.656-1.031-1.16c-0.242-0.504-0.363-1.094-0.363-1.77c0-1.047,0.262-1.862,0.785-2.446
c0.523-0.584,1.25-0.876,2.18-0.876c0.898,0,1.612,0.299,2.142,0.896C6.238,198.237,6.501,199.047,6.501,200.066z M1.608,200.066
c0,0.821,0.164,1.446,0.492,1.875s0.811,0.645,1.447,0.645c0.636,0,1.12-0.214,1.45-0.643c0.33-0.428,0.495-1.053,0.495-1.877
c0-0.816-0.165-1.437-0.495-1.86s-0.817-0.636-1.462-0.636c-0.637,0-1.117,0.209-1.441,0.627
C1.77,198.615,1.608,199.238,1.608,200.066z"/>
<path fill="#FFFFFF" d="M11.136,196.744c0.285,0,0.541,0.023,0.768,0.07l-0.135,0.902c-0.266-0.059-0.5-0.088-0.703-0.088
c-0.52,0-0.964,0.211-1.333,0.633c-0.369,0.422-0.554,0.947-0.554,1.576v3.445H8.206v-6.422h0.803l0.111,1.189h0.047
c0.238-0.418,0.525-0.74,0.861-0.967C10.364,196.855,10.733,196.744,11.136,196.744z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 79 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 80 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 270 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

View file

@ -10,6 +10,14 @@ body {
background-size: auto;
background-repeat: no-repeat;
color: #fff;
/*
.form-area {
background-image: url(img/login-register-separator.svg);
background-repeat: no-repeat;
background-position: 40.2em center;
}
*/
/* Login area */
/* Social login area */
@ -60,11 +68,6 @@ body {
.rcue-login-register .background-area section:first-child {
padding-right: 4.5em;
}
.rcue-login-register .form-area {
background-image: url(img/login-register-separator.svg);
background-repeat: no-repeat;
background-position: 40.2em center;
}
.rcue-login-register .form-area.social {
background-image: url(img/login-register-social-separators.svg);
background-position: 39.6em center;
@ -138,6 +141,9 @@ body {
top: -9.2em;
min-width: 35em;
}
.rcue-login-register.reset .feedback {
left: 35.7em;
}
.rcue-login-register section.social-login > span {
display: none;
}

View file

@ -65,11 +65,13 @@ body {
}
/*
.form-area {
background-image: url(img/login-register-separator.svg);
background-repeat: no-repeat;
background-position: 40.2em center;
}
*/
.form-area.social {
background-image: url(img/login-register-social-separators.svg);
@ -147,6 +149,10 @@ body {
min-width: 35em;
}
&.reset .feedback {
left: 35.7em;
}
/* Social login area */

File diff suppressed because one or more lines are too long

View file

@ -64,8 +64,8 @@
</div>
<div class="alert-container" data-ng-show="notification" data-ng-click="notification = null">
<div class="alert alert-{{notification.type}}">{{notification.message}}</div>
<div class="feedback-aligner" data-ng-show="notification" data-ng-click="notification = null">
<div class="alert alert-{{notification.type}}"><strong>{{notification.header}}</strong> {{notification.message}}</div>
</div>
<div id="wrap">

View file

@ -41,7 +41,7 @@ module.config([ '$routeProvider', function($routeProvider) {
return RealmLoader();
}
},
controller : 'RealmTokenDetailCtrl'
controller : 'RealmSocialCtrl'
})
.when('/realms/:realm/required-credentials', {
templateUrl : 'partials/realm-credentials.html',

View file

@ -120,7 +120,7 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.id + "/applications/" + application.id + "/roles/" + id);
Notifications.success("Created role");
Notifications.success("The role has been created.");
});
} else {
@ -131,7 +131,7 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati
}, $scope.role, function() {
$scope.changed = false;
role = angular.copy($scope.role);
Notifications.success("Saved changes to role");
Notifications.success("Your changes have been saved to the role.");
});
}
};
@ -154,7 +154,7 @@ module.controller('ApplicationRoleDetailCtrl', function($scope, realm, applicati
role : $scope.role.name
}, function() {
$location.url("/realms/" + realm.id + "/applications/" + application.id + "/roles");
Notifications.success("Deleted role");
Notifications.success("The role has been deleted.");
});
});
};
@ -222,7 +222,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.id + "/applications/" + id);
Notifications.success("Created application");
Notifications.success("The application has been created.");
});
} else {
Application.update({
@ -231,7 +231,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
}, $scope.application, function() {
$scope.changed = false;
application = angular.copy($scope.application);
Notifications.success("Saved changes to application");
Notifications.success("Your changes have been saved to the application.");
});
}
@ -257,7 +257,7 @@ module.controller('ApplicationDetailCtrl', function($scope, realm, application,
id : $scope.application.id
}, function() {
$location.url("/realms/" + realm.id + "/applications");
Notifications.success("Deleted application");
Notifications.success("The application has been deleted.");
});
});
};

View file

@ -40,7 +40,6 @@ module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $l
};
$scope.showNav = function() {
var show = Current.realms.length > 0;
console.log('Show dropdown? ' + show);
return Auth.loggedIn && show;
}
});
@ -104,7 +103,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
}
}
$location.url("/realms/" + id);
Notifications.success("Created realm");
Notifications.success("The realm has been created.");
});
});
} else {
@ -122,7 +121,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
}
});
$location.url("/realms/" + id);
Notifications.success("Saved changes to realm");
Notifications.success("Your changes have been saved to the realm.");
});
}
} else {
@ -137,15 +136,16 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, $ht
};
$scope.cancel = function() {
$location.url("/realms");
//$location.url("/realms");
window.history.back();
};
$scope.remove = function() {
Dialog.confirmDelete($scope.realm.name, 'realm', function() {
Dialog.confirmDelete($scope.realm.realm, 'realm', function() {
Realm.remove($scope.realm, function() {
Current.realms = Realm.get();
$location.url("/realms");
Notifications.success("Deleted realm");
Notifications.success("The realm has been deleted.");
});
});
};
@ -182,7 +182,7 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
$scope.changed = false;
Realm.update(realmCopy, function () {
$location.url("/realms/" + realm.id + "/required-credentials");
Notifications.success("Saved changes to realm");
Notifications.success("Your changes have been saved to the realm.");
});
} else {
$scope.realmForm.showErrors = true;
@ -196,13 +196,131 @@ module.controller('RealmRequiredCredentialsCtrl', function($scope, Realm, realm,
};
});
module.controller('RealmSocialCtrl', function($scope, realm, Realm, $location, Notifications) {
console.log('RealmSocialCtrl');
$scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan };
if (!realm["socialProviders"]){
$scope.realm["socialProviders"] = {};
} else {
$scope.realm["socialProviders"] = realm.socialProviders;
}
// Hardcoded provider list
$scope.availableProviders = [ "google", "facebook", "twitter"];
var oldCopy = angular.copy($scope.realm);
$scope.changed = false;
$scope.callbackUrl = $location.absUrl().replace(/\/admin.*/, "/rest/social/callback");
// To get rid of the "undefined" option in the provider select list
// Setting the 1st option from the list (if the list is not empty)
var selectFirstProvider = function(){
if ($scope.unsetProviders.length > 0){
$scope.newProviderId = $scope.unsetProviders[0];
} else {
$scope.newProviderId = null;
}
}
// Fill in configured providers
var initSocial = function() {
$scope.unsetProviders = [];
$scope.configuredProviders = [];
for (var providerConfig in $scope.realm.socialProviders){
// Get the provider ID which is before the '.' (i.e. google in google.key or google.secret)
if ($scope.realm.socialProviders.hasOwnProperty(providerConfig)){
var pId = providerConfig.split('.')[0];
if ($scope.configuredProviders.indexOf(pId) < 0){
$scope.configuredProviders.push(pId);
}
}
}
// If no providers are already configured, you can add any of them
if ($scope.configuredProviders.length == 0){
$scope.unsetProviders = $scope.availableProviders;
} else {
for (var i = 0; i < $scope.availableProviders.length; i++){
var providerId = $scope.availableProviders[i];
if ($scope.configuredProviders.indexOf(providerId) < 0){
$scope.unsetProviders.push(providerId);
}
}
}
selectFirstProvider();
};
initSocial();
$scope.addProvider = function() {
if ($scope.availableProviders.indexOf($scope.newProviderId) > -1){
$scope.realm.socialProviders[$scope.newProviderId+".key"]="";
$scope.realm.socialProviders[$scope.newProviderId+".secret"]="";
$scope.configuredProviders.push($scope.newProviderId);
$scope.unsetProviders.remove($scope.unsetProviders.indexOf($scope.newProviderId));
selectFirstProvider();
}
};
$scope.removeProvider = function(pId) {
delete $scope.realm.socialProviders[pId+".key"];
delete $scope.realm.socialProviders[pId+".secret"];
$scope.configuredProviders.remove($scope.configuredProviders.indexOf(pId));
$scope.unsetProviders.push(pId);
};
$scope.$watch('realm', function() {
if (!angular.equals($scope.realm, oldCopy)) {
$scope.changed = true;
}
}, true);
$scope.save = function() {
$scope.saveClicked = true;
if ($scope.realmForm.$valid) {
var realmCopy = angular.copy($scope.realm);
realmCopy.social = true;
$scope.changed = false;
Realm.update(realmCopy, function () {
$location.url("/realms/" + realm.id + "/social-settings");
Notifications.success("Saved changes to realm");
});
} else {
$scope.realmForm.showErrors = true;
Notifications.error("Some required fields are missing values.");
}
};
$scope.reset = function() {
$scope.realm = angular.copy(oldCopy);
$scope.changed = false;
// Initialize lists of configured and unset providers again
initSocial();
};
$scope.openHelp = function(pId) {
$scope.helpPId = pId;
$scope.providerHelpModal = true;
};
$scope.closeHelp = function() {
$scope.providerHelpModal = false;
};
});
module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http, $location, Dialog, Notifications) {
console.log('RealmTokenDetailCtrl');
$scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan };
$scope.realm = { id : realm.id, realm : realm.realm, social : realm.social, tokenLifespan : realm.tokenLifespan, accessCodeLifespan : realm.accessCodeLifespan , accessCodeLifespanUserAction : realm.accessCodeLifespanUserAction };
$scope.realm.tokenLifespanUnit = 'Seconds';
$scope.realm.accessCodeLifespanUnit = 'Seconds';
$scope.realm.accessCodeLifespanUserActionUnit = 'Seconds';
var oldCopy = angular.copy($scope.realm);
$scope.changed = false;
@ -218,6 +336,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
var realmCopy = angular.copy($scope.realm);
delete realmCopy["tokenLifespanUnit"];
delete realmCopy["accessCodeLifespanUnit"];
delete realmCopy["accessCodeLifespanUserActionUnit"];
if ($scope.realm.tokenLifespanUnit == 'Minutes') {
realmCopy.tokenLifespan = $scope.realm.tokenLifespan * 60;
} else if ($scope.realm.tokenLifespanUnit == 'Hours') {
@ -232,10 +351,17 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
} else if ($scope.realm.accessCodeLifespanUnit == 'Days') {
realmCopy.accessCodeLifespan = $scope.realm.accessCodeLifespan * 60 * 60 * 24;
}
if ($scope.realm.accessCodeLifespanUserActionUnit == 'Minutes') {
realmCopy.accessCodeLifespanUserAction = $scope.realm.accessCodeLifespanUserAction * 60;
} else if ($scope.realm.accessCodeLifespanUserActionUnit == 'Hours') {
realmCopy.accessCodeLifespanUserAction = $scope.realm.accessCodeLifespanUserAction * 60 * 60;
} else if ($scope.realm.accessCodeLifespanUserActionUnit == 'Days') {
realmCopy.accessCodeLifespanUserAction = $scope.realm.accessCodeLifespanUserAction * 60 * 60 * 24;
}
$scope.changed = false;
Realm.update(realmCopy, function () {
$location.url("/realms/" + realm.id + "/token-settings");
Notifications.success("Saved changes to realm");
Notifications.success("Your changes have been saved to the realm.");
});
} else {
$scope.realmForm.showErrors = true;
@ -290,7 +416,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
$location.url("/realms/" + realm.id + "/roles/" + id);
Notifications.success("Created role");
Notifications.success("The role has been created.");
});
} else {
@ -300,7 +426,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
}, $scope.role, function() {
$scope.changed = false;
role = angular.copy($scope.role);
Notifications.success("Saved changes to role");
Notifications.success("Your changes have been saved to the role.");
});
}
};
@ -322,7 +448,7 @@ module.controller('RoleDetailCtrl', function($scope, realm, role, Role, $locatio
role : $scope.role.name
}, function() {
$location.url("/realms/" + realm.id + "/roles");
Notifications.success("Deleted role");
Notifications.success("The role has been deleted.");
});
});
};

View file

@ -134,7 +134,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
$scope.user = angular.copy(user);
$scope.create = !user.username;
$scope.changed = $scope.create;
$scope.changed = false; // $scope.create;
$scope.$watch('user', function() {
if (!angular.equals($scope.user, user)) {
@ -151,7 +151,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
user = angular.copy($scope.user);
$location.url("/realms/" + realm.id + "/users/" + $scope.user.username);
Notifications.success("Created user");
Notifications.success("The user has been created.");
});
} else {
User.update({
@ -160,7 +160,7 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
}, $scope.user, function () {
$scope.changed = false;
user = angular.copy($scope.user);
Notifications.success("Saved changes to user");
Notifications.success("Your changes have been saved to the user.");
});
}
@ -176,13 +176,13 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, $locatio
};
$scope.remove = function() {
Dialog.confirmDelete($scope.user.userId, 'user', function() {
Dialog.confirmDelete($scope.user.username, 'user', function() {
$scope.user.$remove({
realm : realm.id,
userId : $scope.user.username
}, function() {
$location.url("/realms/" + realm.id + "/users");
Notifications.success("Deleted user");
Notifications.success("The user has been deleted.");
});
});
};
@ -210,7 +210,7 @@ module.controller('RoleMappingCtrl', function($scope, realm, User, users, role,
realm : $scope.realmId,
role : role
});
Notifications.success("Added role mapping for user");
Notifications.success("The role mapping has been added for the user.");
});
}
}
@ -229,7 +229,7 @@ module.controller('RoleMappingCtrl', function($scope, realm, User, users, role,
role : role
});
Notifications.success("Removed role mapping for user");
Notifications.success("The role mapping has been removed for the user.");
});
}
}

View file

@ -55,9 +55,10 @@ module.factory('Notifications', function($rootScope, $timeout) {
$rootScope.notifications = [];
}
notifications.message = function(type, message) {
notifications.message = function(type, header, message) {
$rootScope.notification = {
type : type,
header: header,
message : message
};
@ -65,19 +66,19 @@ module.factory('Notifications', function($rootScope, $timeout) {
}
notifications.info = function(message) {
notifications.message("info", message);
notifications.message("info", "Info!", message);
};
notifications.success = function(message) {
notifications.message("success", message);
notifications.message("success", "Success!", message);
};
notifications.error = function(message) {
notifications.message("error", message);
notifications.message("error", "Error!", message);
};
notifications.warn = function(message) {
notifications.message("warn", message);
notifications.message("warn", "Warning!", message);
};
return notifications;

View file

@ -970,8 +970,8 @@ dialogModule.provider("$dialog", function(){
transitionClass: 'fade',
triggerClass: 'in',
resolve:{},
backdropFade: false,
dialogFade:false,
backdropFade: true,
dialogFade: true,
keyboard: true, // close with esc key
backdropClick: true // only in conjunction with backdrop=true
/* other options: template, templateUrl, controller */
@ -1027,6 +1027,7 @@ dialogModule.provider("$dialog", function(){
this.modalEl.addClass(options.transitionClass);
this.modalEl.removeClass(options.triggerClass);
}
this.modalEl.css("display", "block"); /* FIXME: For BS 3.x support */
this.handledEscapeKey = function(e) {
if (e.which === 27) {
@ -2997,6 +2998,8 @@ angular.module("template/datepicker/datepicker.html", []).run(["$templateCache",
angular.module("template/dialog/message.html", []).run(["$templateCache", function($templateCache) {
$templateCache.put("template/dialog/message.html",
"<div class=\"modal-dialog\">\n" +
"<div class=\"modal-content\">\n" +
"<div class=\"modal-header\">\n" +
" <h3>{{ title }}</h3>\n" +
"</div>\n" +
@ -3006,6 +3009,8 @@ angular.module("template/dialog/message.html", []).run(["$templateCache", functi
"<div class=\"modal-footer\">\n" +
" <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=\"btn\" ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" +
"</div>\n" +
"</div>\n" +
"</div>\n" +
"");
}]);

View file

@ -4,8 +4,6 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/create/application/{{realm.id}}">New Application</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">Settings</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/applications/{{application.id}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
@ -15,8 +13,13 @@
</ul>
</div>
<div id="content">
<h2 class="pull-left" data-ng-hide="create">Application <span>{{application.name}}</span> Credentials</h2>
<p class="subtitle"></p>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Credentials</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Credentials</h2>
<form name="credentialForm" novalidate >
<fieldset data-ng-show="passwordRequired">
<legend uncollapsed><span class="text">Change Password</span></legend>
@ -28,13 +31,13 @@
</div>
</div>
<div class="form-group">
<label for="password">Confirm New Password</label>
<label class="two-lines" for="password">New Password Confirmation</label>
<div class="controls">
<input type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword" autofocus
<input type="password" id="confirmPassword" name="confirmPassword" data-ng-model="confirmPassword"
required>
</div>
</div>
<div class="form-group">
<div class="form-actions">
<button type="submit" data-ng-click="changePassword()" class="primary" ng-show="password != null">Save
</button>
</div>
@ -50,7 +53,7 @@
</button>
</div>
</div>
<div class="form-group">
<div class="form-actions">
<label></label>
<button type="submit" data-ng-click="changeTotp()" class="primary" ng-show="totp != null">Save
</button>

View file

@ -4,8 +4,6 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav" data-ng-show="!create">
<ul class="rcue-tabs">
<li><a href="#/create/application/{{realm.id}}">New Application</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/applications/{{application.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
@ -16,19 +14,28 @@
</div>
<div class="top-nav" data-ng-show="create">
<ul class="rcue-tabs">
<li class="active"><a href="#/create/application/{{realm.id}}">New Application</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li></li>
</ul>
</div>
<div id="content">
<h2 class="pull-left" data-ng-show="create">New Application</h2>
<h2 class="pull-left" data-ng-hide="create">Application <span>{{application.name}}</span></h2>
<p class="subtitle"><span class="required">*</span> Required fields</p>
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li class="active">Add Application</li>
</ol>
<h2 class="pull-left" data-ng-show="create"><span>{{realm.realm}}</span> Add Application</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Settings</li>
</ol>
<h2 data-ng-hide="create"><span>{{application.name}}</span> Settings</h2>
<form name="applicationForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Application Settings</span></legend>
<fieldset class="border-top">
<div class="form-group">
<label for="name">Name</label><span class="required">*</span>
<label for="name">Name <span class="required" data-ng-show="create">*</span></label>
<div class="controls">
<input type="text" id="name" name="name" data-ng-model="application.name" autofocus
required>
@ -36,7 +43,7 @@
</div>
<div class="form-group clearfix block">
<label class="control-label">Enabled</label>
<label for="enabled" class="control-label">Enabled</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="application.enabled" class="onoffswitch-checkbox"
name="enabled" id="enabled">
@ -97,20 +104,18 @@
</div>
</fieldset>
<div class="form-actions" data-ng-show="create">
<button type="submit" data-ng-click="save()" class="primary">Save
<button type="submit" data-ng-click="save()" data-ng-show="changed" class="primary">Save
</button>
<button type="submit" data-ng-click="cancel()" data-ng-click="cancel()"
data-ng-show="changed">Cancel
<button type="submit" data-ng-click="cancel()">Cancel
</button>
</div>
<div class="form-actions" data-ng-show="!create">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
changes
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
<button type="submit" data-ng-click="remove()" class="danger">
<button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
Delete
</button>
</div>

View file

@ -4,31 +4,36 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/create/application/{{realm.id}}">New Application</a></li>
<li class="active"><a href="#">Applications</a></li>
<li></li>
</ul>
</div>
<div id="content">
<h2 class="pull-left">Applications</h2>
<table>
<caption data-ng-show="!applications || applications.length == 0">No configured applications...</caption>
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li class="active">Applications</li>
</ol>
<h2><span>{{realm.realm}}</span> Applications</h2>
<div class="feedback info inline" data-ng-show="!applications || applications.length == 0">
<p><strong>You have not configured applications.</strong> <a class="button" href="#/create/application/{{realm.id}}">Add Application</a></p>
</div>
<table data-ng-hide="applications.length == 0">
<thead>
<tr >
<tr>
<th class="rcue-table-actions" colspan="3">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
<button class="icon-search tooltipRightTrigger"
data-original-title="Search by application name.">
<input type="text" placeholder="Search..." class="search"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button class="icon-search" tooltip-placement="right"
tooltip="Search by application name.">
Icon: search
</button>
</div>
<div class="actions">
<a class="button" href="#/create/application/{{realm.id}}">Add</a>
<button class="remove disabled">Remove</button>
<a class="button" href="#/create/application/{{realm.id}}">Add Application</a>
</div>
</th>
</tr>
<tr>
<tr data-ng-show="applications.length > 0">
<th>Application Name</th>
<th>Enabled</th>
<th>Base URL</th>
@ -46,14 +51,17 @@
</td>
</tr>
</tfoot>
<tbody class="selectable-rows">
<tbody>
<tr ng-repeat="app in applications">
<td><a href="#/realms/{{realm.id}}/applications/{{app.id}}">{{app.name}}</a></td>
<td>{{app.enabled}}</td>
<td></td>
<td ng-class="{'text-muted': !app.baseUrl}">{{app.baseUrl || "Not defined"}}</td>
</tr>
</tbody>
</table>
<div class="feedback warning inline" data-ng-show="search && applications.length == 0">
<p><strong>Your search returned no results.</strong><br>Try modifying the query and try again.</p>
</div>
</div>
</div>
<div id="container-right-bg"></div>

View file

@ -4,20 +4,38 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav">
<ul class="rcue-tabs">
<li class="active"><a href="#/create/role/{{realm.id}}/applications/{{application.id}}">New {{application.name}} Role</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">{{application.name}} Roles</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}} Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
<li><a href="#">Sessions</a></li>
</ul>
</div>
<div id="content">
<h2 class="pull-left" data-ng-show="create">New Application {{application.name}} Role</h2>
<h2 class="pull-left" data-ng-hide="create">Application {{application.name}} Role <span>{{role.name}}</span></h2>
<ol class="breadcrumb" data-ng-show="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">Roles</a></li>
<li class="active">Add role</li>
</ol>
<h2 class="pull-left" data-ng-show="create"><span>{{application.name}}</span> Add Role</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">Roles</a></li>
<li class="active">{{role.name}}</li>
</ol>
<h2 class="pull-left" data-ng-hide="create"><span>{{application.name}}</span> {{role.name}}</h2>
<form name="realmForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Details</span> </legend>
<fieldset class="border-top">
<div class="form-group">
<label for="name">Role name </label><span class="required" data-ng-show="create">*</span>
<label for="name">Role name <span class="required" data-ng-show="create">*</span></label>
<div class="controls">
<input type="text" id="name" name="name" data-ng-model="role.name" autofocus
@ -28,8 +46,10 @@
<label for="description">Description </label>
<div class="controls">
<input type="text" id="description" name="description" data-ng-model="role.description" autofocus
required>
<textarea rows="5" cols="50" id="description" name="description" data-ng-model="role.description" required></textarea>
<!-- Replaced by the textarea above <input type="text" id="description" name="description" data-ng-model="role.description" autofocus
required> -->
</div>
</div>
</fieldset>
@ -43,11 +63,10 @@
<div class="form-actions" data-ng-show="!create">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
changes
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
<button type="submit" data-ng-click="remove()" class="danger" data-ng-hide="changed">
<button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
Delete
</button>
</div>

View file

@ -4,32 +4,53 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/create/role/{{realm.id}}/applications/{{application.id}}">New {{application.name}} Role</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">{{application.name}} Roles</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}} Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/scope-mappings">Scope</a></li>
<li><a href="#">Sessions</a></li>
</ul>
</div>
<div id="content">
<h2 class="pull-left">Application <span>{{application.name}}</span> Roles</h2>
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Roles</li>
</ol>
<h2><span>{{application.name}}</span> Roles</h2>
<div class="feedback info" data-ng-show="!roles || roles.length == 0">
<p><strong>You have not configured application roles.</strong><br><a class="button" href="#/create/role/{{realm.id}}/applications/{{application.id}}">Add Role</a></p>
</div>
<table>
<caption data-ng-show="roles && roles.length > 0">Table of realm roles</caption>
<caption data-ng-show="!roles || roles.length == 0">No configured realm roles...</caption>
<caption data-ng-show="roles && roles.length > 0" class="hidden">Table of realm roles</caption>
<thead>
<tr data-ng-show="roles && roles.length > 5">
<th class="rcue-table-actions" colspan="2">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
<button class="icon-search tooltipRightTrigger"
data-original-title="Search by role name.">
Icon: search
</button>
</div>
</th>
</tr>
<tr>
<th>Role Name</th>
<th>Description</th>
</tr>
<tr>
<th class="rcue-table-actions" colspan="2">
<div class="actions">
<a class="button" href="#/create/role/{{realm.id}}/applications/{{application.id}}">Add Role</a>
<!-- <button class="remove disabled">Remove</button> -->
</div>
</th>
</tr>
<tr data-ng-show="roles && roles.length > 5">
<th class="rcue-table-actions" colspan="2">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
<button class="icon-search" tooltip-placement="right"
tooltip="Search by role name.">
Icon: search
</button>
</div>
</th>
</tr>
<tr>
<th>Role Name</th>
<th>Description</th>
</tr>
</thead>
<tfoot data-ng-show="roles && roles.length > 5"> <!-- todo -->
<tr>
@ -43,7 +64,7 @@
</td>
</tr>
</tfoot>
<tbody class="selectable-rows">
<tbody>
<tr ng-repeat="role in roles">
<td><a href="#/realms/{{realm.id}}/applications/{{application.id}}/roles/{{role.id}}">{{role.name}}</a></td>
<td>{{role.description}}</td>

View file

@ -4,8 +4,6 @@
<div id="content-area" class="col-md-9" role="main">
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/create/application/{{realm.id}}">New Application</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}/credentials">Credentials</a></li>
<li><a href="#">Installation</a></li>
@ -15,11 +13,17 @@
</ul>
</div>
<div id="content">
<ol class="breadcrumb" data-ng-hide="create">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}/applications">Applications</a></li>
<li><a href="#/realms/{{realm.id}}/applications/{{application.id}}">{{application.name}}</a></li>
<li class="active">Scope</li>
</ol>
<h2 class="pull-left"><span>{{application.name}}</span> Scope Mappings</h2>
<p class="subtitle"></p>
<form name="realmForm" novalidate>
<legend uncollapsed><span class="text">Realm Roles</span> </legend>
<fieldset class="border-top">
<fieldset>
<legend uncollapsed><span class="text">Realm Roles</span></legend>
<div class="form-group">
<div class="controls changing-selectors">
<div class="select-title">
@ -31,8 +35,8 @@
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addRealmRole()" data-original-title="Move right" class="tooltipRightTrigger"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteRealmRole()" data-original-title="Move left" class="tooltipRightTrigger"><span class="icon-arrow-left">Move left</span></button>
<button type="submit" ng-click="addRealmRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteRealmRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="assigned">Assigned Roles</label>
@ -49,16 +53,17 @@
<fieldset ng-show="applications.length > 0">
<legend collapsed><span class="text">Application Roles</span> </legend>
<div class="form-group input-select">
<label for="applications">Application: </label>
<label for="applications">Application</label>
<div class="input-group">
<div class="select-rcue">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="targetApp" ng-options="a.name for a in applications">
<option value="" selected> Select an Application </option>
</select>
</div>
</div>
</div>
<div class="form-group">
<div class="controls changing-selectors">
<div class="form-group" ng-show="targetApp">
<div class="controls changing-selectors application">
<div class="select-title">
<label for="app-available">Available Roles</label>
<select id="app-available" class="form-control" multiple size="5"
@ -68,8 +73,8 @@
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addApplicationRole()" data-original-title="Move right" class="tooltipRightTrigger"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteApplicationRole()" data-original-title="Move left" class="tooltipRightTrigger"><span class="icon-arrow-left">Move left</span></button>
<button type="submit" ng-click="addApplicationRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteApplicationRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="app-assigned">Assigned Roles</label>

View file

@ -1,6 +1,6 @@
<div class="header rcue">
<div class="navbar utility">
<div class="navbar-inner clearfix">
<div class="navbar-inner clearfix container">
<h1><a href="#"><strong>Keycloak</strong> Central Login</a></h1>
<ul class="nav pull-right" data-ng-hide="auth.loggedIn">
<li><a href="/auth-server/rest/saas/login">Login</a></li>
@ -19,13 +19,13 @@
</div>
</div>
<div class="navbar primary">
<div class="navbar-inner clearfix" data-ng-controller="RealmDropdownCtrl">
<div class="navbar-inner clearfix container" data-ng-controller="RealmDropdownCtrl">
<ul class="nav pull-left">
<li>
<span class="dropdown-label" data-ng-show="showNav()">Realm:</span>
<div class="dropdown" data-ng-show="showNav()">
<div class="select-rcue" data-ng-show="showNav()">
<select ng-change="changeRealm()" ng-model="current.realm" ng-options="r.realm for r in current.realms"></select>
</div>
</div><a href="#/realms/{{realm.id}}" id="refresh" data-ng-show="showNav()"><span class="icon-spinner6">Icon: spinner</span></a>
</li>
</ul>
<div class="pull-right" data-ng-show="auth.loggedIn">

View file

@ -0,0 +1,32 @@
<p>Open <a href="https://developers.facebook.com/apps" target="_blank">https://developers.facebook.com/apps</a>. Click on <i>Create New App</i></p>
<p>Use any app name that you'd like, click <i>Continue</i></p>
<p>Select <i>Disabled</i> for <i>Sandbox Mode</i></p>
<p>Under <i>Select how your app integrates with Facebook</i> select <i>Website with Facebook login</i>. Fill in the form with the following values:</p>
<ul>
<li><b>Site URL:</b> {{callbackUrl}}</li>
</ul>
<p>Click on <i>Save changes</i>. Insert <i>App ID</i> and <i>App Secret</i> in the form below.</p>
<form class="form-horizontal" name="fbHelpForm">
<div class="control-group">
<label class="control-label" for="providerHelp.key">App ID </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.key" ng-model="realm.socialProviders[helpPId+'.key']">
</div>
</div>
<div class="control-group">
<label class="control-label" for="providerHelp.secret">App Secret </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.secret" ng-model="realm.socialProviders[helpPId+'.secret']">
</div>
</div>
</form>
<p>
Close the help panel and click <i>save changes</i>.
</p>

View file

@ -21,20 +21,16 @@
<form class="form-horizontal" name="googleHelpForm">
<div class="control-group">
<label class="control-label" for="providerHelp.key">Client ID </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.key"
data-ng-model="application.providers[providerHelp.index].key">
<input type="text" class="input-xlarge" id="providerHelp.key" ng-model="realm.socialProviders[helpPId+'.key']">
</div>
</div>
<div class="control-group">
<label class="control-label" for="providerHelp.secret">Client secret </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.secret"
data-ng-model="application.providers[providerHelp.index].secret">
<input type="text" class="input-xlarge" id="providerHelp.secret" ng-model="realm.socialProviders[helpPId+'.secret']">
</div>
</div>
</form>
<p>Close the help panel and click <i>save changes</i>.</p>
<p>Close the help panel and click <i>save changes</i>.</p>

View file

@ -1,6 +1,5 @@
<p>
Open <a href="https://dev.twitter.com/apps" target="_blank">https://dev.twitter.com/apps</a>. Click on <i>Create a
new
Open <a href="https://dev.twitter.com/apps" target="_blank">https://dev.twitter.com/apps</a>. Click on <i>Create a new
application</i>.
</p>
@ -14,33 +13,28 @@
<p>Agree to the rules, fill in the captcha and click on <i>Create your Twitter application</i></p>
<p>Insert <i>Consumer key</i> and <i>Consumer secret</i> in the form below.</p>
<p>
Now click on <i>Settings</i> and tick the box <i>Allow this application to be used to Sign in with Twitter</i>, and click on <i>Update
this Twitter application's settings</i>.
</p>
<form class="form-horizontal" name="googleHelpForm">
<p>Click on <i>Details</i>. Insert <i>Consumer key</i> and <i>Consumer secret</i> in the form below.</p>
<form class="form-horizontal" name="twitterHelpForm">
<div class="control-group">
<label class="control-label" for="providerHelp.key">Consumer key </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.key"
data-ng-model="application.providers[providerHelp.index].key">
<input type="text" class="input-xlarge" id="providerHelp.key" ng-model="realm.socialProviders[helpPId+'.key']">
</div>
</div>
<div class="control-group">
<label class="control-label" for="providerHelp.secret">Consumer secret </label>
<div class="controls">
<input type="text" class="input-xlarge" id="providerHelp.secret"
data-ng-model="application.providers[providerHelp.index].secret">
<input type="text" class="input-xlarge" id="providerHelp.secret" ng-model="realm.socialProviders[helpPId+'.secret']">
</div>
</div>
</form>
<p>
Now click on <i>Settings</i> and tick the box <i>Allow this application to be used to Sign in with Twitter</i>, and
click on <i>Update
this Twitter application's settings</i>.
</p>
<p>
Close the help panel and click <i>save changes</i>.
</p>

View file

@ -21,24 +21,24 @@
<form name="realmForm" novalidate>
<fieldset class="border-top">
<div class="form-group clearfix">
<label for="user" class="control-label">Required User Credentials</label>
<label for="user" class="control-label two-lines">Required User Credentials</label>
<div class="controls">
<input id="user" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredCredentials" class="form-control tokenfield" placeholder="Type a role and enter">
<input id="user" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredCredentials" placeholder="Type a role and enter">
</div>
</div>
<div class="form-group clearfix">
<label for="application" class="control-label">Required Application Credentials</label>
<label for="application" class="control-label two-lines">Required Application Credentials</label>
<div class="controls">
<input id="application" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredApplicationCredentials" class="form-control tokenfield" placeholder="Type a role and enter">
<input id="application" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredApplicationCredentials" placeholder="Type a role and enter">
</div>
</div>
<div class="form-group clearfix">
<label for="oauth" class="control-label">Required OAuth Credentials</label>
<label for="oauth" class="control-label two-lines">Required OAuth Credentials</label>
<div class="controls">
<input id="oauth" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredOAuthClientCredentials" class="form-control tokenfield" placeholder="Type a role and enter">
<input id="oauth" type="text" ui-select2="userCredentialOptions" ng-model="realm.requiredOAuthClientCredentials" placeholder="Type a role and enter">
</div>
</div>
</fieldset>

View file

@ -18,7 +18,7 @@
<li class="active">General</li>
</ol>
<h2 class="pull-left" data-ng-show="createRealm">Add Realm</h2>
<h2 class="pull-left" data-ng-hide="createRealm"><span>{{realm.realm}}</span> General Settings</h2>
<h2 data-ng-hide="createRealm"><span>{{realm.realm}}</span> General Settings</h2>
<p class="subtitle" data-ng-show="createRealm"><span class="required">*</span> Required fields</p>
<form name="realmForm" novalidate>
<fieldset>
@ -76,7 +76,7 @@
</div>
</div>
<div class="form-group clearfix block">
<label for="accountManagement" class="control-label">User account management</label>
<label for="accountManagement" class="control-label two-lines">User account management</label>
<div class="onoffswitch">
<input type="checkbox" data-ng-model="realm.accountManagement" class="onoffswitch-checkbox"
name="accountManagement" id="accountManagement">
@ -119,8 +119,7 @@
<div class="form-actions" data-ng-show="createRealm">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
</button>
<button type="submit" data-ng-click="cancel()" data-ng-click="cancel()"
data-ng-show="changed">Cancel
<button type="submit" data-ng-click="cancel()">Cancel
</button>
</div>
@ -129,7 +128,7 @@
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
<button type="submit" data-ng-click="remove()" class="danger" data-ng-hide="changed">
<button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
Delete
</button>
</div>

View file

@ -1,4 +1,4 @@
<div id="wrapper" class="container">
<div id="wrapper" class="container social-provider">
<div class="row">
<div class="bs-sidebar col-md-3 clearfix" data-ng-include data-src="'partials/realm-menu.html'"></div>
<div id="content-area" class="col-md-9" role="main">
@ -12,11 +12,61 @@
</ul>
</div>
<div id="content">
<h2 class="pull-left"><span>{{realm.realm}}</span> Social Providers</h2>
<p class="subtitle"></p>
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}">Settings</a></li>
<li class="active">Social</li>
</ol>
<h2><span>{{realm.realm}}</span> Social Providers Settings</h2>
<form name="realmForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Social Settings</span></legend>
<div>
<table>
<caption class="hidden">Table of social providers</caption>
<thead>
<tr>
<th colspan="5" class="rcue-table-actions">
<div class="actions">
<div class="select-rcue">
<select ng-model="newProviderId"
ng-options="p for p in unsetProviders"></select>
</div>
<div>
<button ng-click="addProvider()" ng-disabled="">Add Provider</button>
</div>
</div>
</th>
</tr>
<tr ng-show="configuredProviders.length > 0">
<th>Provider</th>
<th>Key <span class="required">*</span></th>
<th>Secret <span class="required">*</span></th>
<th colspan="1">Actions</th>
</tr>
</thead>
<tbody ng-show="configuredProviders.length > 0">
<tr ng-repeat="pId in configuredProviders">
<td>
<div class="clearfix">
<input class="input-small disabled" type="text" placeholder="Key" value="{{pId}}" readonly>
</div>
</td>
<td>
<input class="input-small" type="text" placeholder="Key" ng-model="realm.socialProviders[pId+'.key']"
ng-class="{'dirty': saveClicked}" required>
</td>
<td>
<input class="input-small" type="text" placeholder="Secret" ng-model="realm.socialProviders[pId+'.secret']"
ng-class="{'dirty': saveClicked}" required>
</td>
<td>
<div class="action-div"><i class="icon-question" ng-click="openHelp(pId)"></i></div>
<div class="action-div"><i class="icon-remove" ng-click="removeProvider(pId)"></i></div>
</td>
</tr>
</tbody>
</table>
</div>
</fieldset>
<div class="form-actions">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
@ -25,9 +75,47 @@
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
</div>
</form>
</form>
</div>
</div>
<div id="container-right-bg"></div>
</div>
</div>
</div>
<div modal="providerHelpModal" close="closeHelp()" options="opts">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h3>Configure {{helpPId}}</h3>
</div>
<div class="modal-body">
<div ng-include src="'partials/provider/'+ helpPId +'-help.html'"></div>
</div>
<div class="modal-footer">
<button class="btn" ng-click="closeHelp()">Close</button>
</div>
</div>
</div>
</div>
<!-- TODO remove once this page is properly styled -->
<style type="text/css">
.social-provider input.ng-invalid.dirty,
.social-provider input.ng-invalid.ng-dirty {
background-color: #FFEEEE;
}
.social-provider .actions > div {
display: inline-block;
overflow: hidden;
}
.social-provider td {
font-size: 10px;
}
.social-provider .action-div {
display: inline-block;
margin: 5px;
}
</style>

View file

@ -19,8 +19,7 @@
</ol>
<h2><span>{{realm.realm}}</span> Token Settings</h2>
<form name="realmForm" novalidate>
<fieldset>
<legend uncollapsed><span class="text">Token Settings</span></legend>
<fieldset class="border-top">
<div class="form-group input-select">
<label for="tokenLifespan">Token lifespan</label>
<div class="input-group">
@ -49,6 +48,20 @@
</div>
</div>
</div>
<div class="form-group input-select">
<label for="accessCodeLifespanUserAction">Access code user action lifespan</label>
<div class="input-group">
<input type="text" data-ng-model="realm.accessCodeLifespanUserAction" id="accessCodeLifespanUserAction" name="accessCodeLifespanUserAction" class="tiny">
<div class="select-rcue">
<select name="accessCodeLifespanUserActionUnit" data-ng-model="realm.accessCodeLifespanUserActionUnit">
<option data-ng-selected="!realm.accessCodeLifespanUserActionUnit">Seconds</option>
<option>Minutes</option>
<option>Hours</option>
<option>Days</option>
</select>
</div>
</div>
</div>
</fieldset>
<div class="form-actions">
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save

View file

@ -5,7 +5,7 @@
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#">Social</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
@ -25,7 +25,7 @@
<li><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li class="active">Add Role</li>
</ol>
<h2 class="pull-left" data-ng-show="create">Add Realm Role</h2>
<h2 class="pull-left" data-ng-show="create"><span>{{realm.realm}}</span> Add Role</h2>
<p class="subtitle" data-ng-show="create"><span class="required">*</span> Required fields</p>
<form name="realmForm" novalidate>
<fieldset class="border-top">
@ -42,7 +42,6 @@
<div class="controls">
<textarea rows="5" cols="50" id="description" name="description" data-ng-model="role.description" required></textarea>
<!-- Replaced by the textarea above <input type="text" id="description" name="description" data-ng-model="role.description" required> -->
</div>
</div>
</fieldset>
@ -59,7 +58,7 @@
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
<button type="submit" data-ng-click="remove()" class="danger" data-ng-hide="changed">
<button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
Delete
</button>
</div>

View file

@ -5,21 +5,21 @@
<div class="top-nav">
<ul class="rcue-tabs">
<li><a href="#/realms/{{realm.id}}">General</a></li>
<li data-ng-show="realm.social"><a href="#">Social</a></li>
<li data-ng-show="realm.social"><a href="#/realms/{{realm.id}}/social-settings">Social</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/roles">Roles</a></li>
<li><a href="#/realms/{{realm.id}}/required-credentials">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/token-settings">Token</a></li>
</ul>
</div>
<div id="content">
<ol class="breadcrumb" data-ng-hide="createRealm">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.id}}">{{realm.realm}}</a></li>
<li><a href="#/realms/{{realm.id}}">Settings</a></li>
<li><a href="#/realms/{{realm.id}}">Settings</a></li>
<li class="active">Roles</li>
</ol>
<h2 class="pull-left"><span>{{realm.realm}}</span> Roles</h2>
<div class="feedback info" data-ng-show="!roles || roles.length == 0">
<p><strong>You have not configured realm roles.</strong><br><a class="button" href="#/create/role/{{realm.id}}">Add Role</a></p>
<h2><span>{{realm.realm}}</span> Roles</h2>
<div class="feedback info inline" data-ng-show="!roles || roles.length == 0">
<p><strong>You have not configured realm roles.</strong> <a class="button" href="#/create/role/{{realm.id}}">Add Role</a></p>
</div>
<table>
<caption class="hidden" data-ng-show="roles && roles.length > 0">Table of realm roles</caption>
@ -36,8 +36,8 @@
<th class="rcue-table-actions" colspan="2">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." class="search">
<button class="icon-search tooltipRightTrigger"
data-original-title="Search by role name.">
<button class="icon-search" tooltip-placement="right"
tooltip="Search by role name.">
Icon: search
</button>
</div>

View file

@ -6,7 +6,7 @@
<ul class="rcue-tabs" >
<li><a href="#/realms/{{realm.id}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#">Credentials</a></li>
<li class="active"><a href="#">Role Mappings</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
</ul>
</div>
<div id="content">
@ -32,8 +32,8 @@
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addRealmRole()" data-original-title="Move right" class="tooltipRightTrigger"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteRealmRole()" data-original-title="Move left" class="tooltipRightTrigger"><span class="icon-arrow-left">Move left</span></button>
<button type="submit" ng-click="addRealmRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteRealmRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="assigned">Assigned Roles</label>
@ -50,16 +50,17 @@
<fieldset ng-show="applications.length > 0">
<legend collapsed><span class="text">Application Roles</span> </legend>
<div class="form-group input-select">
<label for="applications">Application: </label>
<label for="applications">Application</label>
<div class="input-group">
<div class="select-rcue">
<select id="applications" name="applications" ng-change="changeApplication()" ng-model="application" ng-options="a.name for a in applications">
<option value="" selected> Select an Application...</option>
</select>
</div>
</div>
</div>
<div class="form-group" ng-show="application">
<div class="controls changing-selectors">
<div class="controls changing-selectors application">
<div class="select-title">
<label for="available-app">Available Roles</label>
<select id="available-app" class="form-control" multiple size="5"
@ -69,8 +70,8 @@
</select>
</div>
<div class="middle-buttons">
<button type="submit" ng-click="addApplicationRole()" data-original-title="Move right" class="tooltipRightTrigger"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteApplicationRole()" data-original-title="Move left" class="tooltipRightTrigger"><span class="icon-arrow-left">Move left</span></button>
<button type="submit" ng-click="addApplicationRole()" tooltip="Move right" tooltip-placement="right"><span class="icon-arrow-right">Move right</span></button>
<button type="submit" ng-click="deleteApplicationRole()" tooltip="Move left" tooltip-placement="right"><span class="icon-arrow-left">Move left</span></button>
</div>
<div class="select-title">
<label for="assigned-app">Assigned Roles</label>

View file

@ -9,7 +9,7 @@
</div>
<div class="top-nav" data-ng-show="!create">
<ul class="rcue-tabs" >
<li class="active"><a href="#">Attributes</a></li>
<li class="active"><a href="#/realms/{{realm.id}}/users/{{user.username}}">Attributes</a></li>
<li><a href="#">Credentials</a></li>
<li><a href="#/realms/{{realm.id}}/users/{{user.username}}/role-mappings">Role Mappings</a></li>
</ul>
@ -67,10 +67,9 @@
</div>
</fieldset>
<div class="form-actions" data-ng-show="create">
<button type="submit" data-ng-click="save()" class="primary">Save
<button type="submit" data-ng-click="save()" class="primary" data-ng-show="changed">Save
</button>
<button type="submit" data-ng-click="cancel()" data-ng-click="cancel()"
data-ng-show="changed">Cancel
<button type="submit" data-ng-click="cancel()">Cancel
</button>
</div>
@ -79,7 +78,7 @@
</button>
<button type="submit" data-ng-click="reset()" data-ng-show="changed">Clear changes
</button>
<button type="submit" data-ng-click="remove()" class="danger">
<button type="submit" data-ng-click="remove()" class="destructive" data-ng-hide="changed">
Delete
</button>
</div>

View file

@ -19,21 +19,21 @@
<tr>
<th class="rcue-table-actions" colspan="4">
<div class="search-comp clearfix">
<input type="text" placeholder="Search..." data-ng-model="search" class="search">
<button data-ng-click="searchQuery()"
class="icon-search tooltipRightTrigger"
data-original-title="Search by full name, last name, email, or username.">
<input type="text" placeholder="Search..." data-ng-model="search" class="search"
onkeyup="if(event.keyCode == 13){$(this).next('button').click();}">
<button data-ng-click="searchQuery()" type="submit"
class="icon-search" tooltip-placement="right"
tooltip="Search by full name, last name, email, or username.">
Icon: search
</button>
</div>
<div class="actions">
<a class="button" href="#/create/user/{{realm.id}}">Add User</a>
<button class="remove disabled">Remove</button>
</div>
</th>
</tr>
<tr>
<tr data-ng-show="users && search">
<tr data-ng-show="users.length > 0">
<th>Username</th>
<th>Last Name</th>
<th>First Name</th>
@ -53,7 +53,7 @@
</td>
</tr>
</tfoot>
<tbody class="selectable-rows">
<tbody>
<tr ng-repeat="user in users">
<td><a href="#/realms/{{realm.id}}/users/{{user.username}}">{{user.username}}</a></td>
<td>{{user.lastName}}</td>
@ -62,6 +62,9 @@
</tr>
</tbody>
</table>
<div class="feedback warning inline" data-ng-show="users.length == 0">
<p><strong>Your search returned no results.</strong><br>Try modifying the query and try again.</p>
</div>
</div>
</div>
<div id="container-right-bg"></div>

View file

@ -2,6 +2,7 @@ package org.keycloak.representations.idm;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@ -36,7 +37,8 @@ public class RealmRepresentation {
protected List<ScopeMappingRepresentation> scopeMappings;
protected List<SocialMappingRepresentation> socialMappings;
protected List<ApplicationRepresentation> applications;
protected Map<String, String> socialProviders;
protected Map<String, String> smtpServer;
public String getSelf() {
return self;
@ -102,7 +104,7 @@ public class RealmRepresentation {
this.enabled = enabled;
}
public Boolean isAccountManagement() {
public Boolean getAccountManagement() {
return accountManagement;
}
@ -281,4 +283,20 @@ public class RealmRepresentation {
public void setAutomaticRegistrationAfterSocialLogin(Boolean automaticRegistrationAfterSocialLogin) {
this.automaticRegistrationAfterSocialLogin = automaticRegistrationAfterSocialLogin;
}
public Map<String, String> getSocialProviders() {
return socialProviders;
}
public void setSocialProviders(Map<String, String> socialProviders) {
this.socialProviders = socialProviders;
}
public Map<String, String> getSmtpServer() {
return smtpServer;
}
public void setSmtpServer(Map<String, String> smtpServer) {
this.smtpServer = smtpServer;
}
}

View file

@ -19,7 +19,9 @@ window.keycloak = (function () {
}
}
processCallback();
if (!processCallback()) {
window.location.href = getLoginUrl() + '&prompt=none';
}
}
kc.login = function () {

View file

@ -22,9 +22,7 @@
package org.keycloak.forms;
import java.net.URI;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.*;
import javax.imageio.spi.ServiceRegistry;
import javax.ws.rs.core.UriBuilder;
@ -45,7 +43,7 @@ public class SocialBean {
private List<SocialProvider> providers;
public SocialBean(RealmBean realm, RegisterBean registerBean, UrlBean url) {
public SocialBean(RealmBean realm, List<org.keycloak.social.SocialProvider> providers, RegisterBean registerBean, UrlBean url) {
this.realm = realm;
this.registerBean = registerBean;
this.url = url;
@ -54,13 +52,10 @@ public class SocialBean {
UriBuilder socialLoginUrlBuilder = UriBuilder.fromUri(Urls.socialRedirectToProviderAuth(baseURI, realm.getId()));
providers = new LinkedList<SocialProvider>();
for (Iterator<org.keycloak.social.SocialProvider> itr = ServiceRegistry
.lookupProviders(org.keycloak.social.SocialProvider.class); itr.hasNext();) {
org.keycloak.social.SocialProvider p = itr.next();
this.providers = new LinkedList<SocialProvider>();
for (org.keycloak.social.SocialProvider p : providers) {
String loginUrl = socialLoginUrlBuilder.replaceQueryParam("provider_id", p.getId()).build().toString();
providers.add(new SocialProvider(p.getId(), p.getName(), loginUrl));
this.providers.add(new SocialProvider(p.getId(), p.getName(), loginUrl));
}
}
@ -70,7 +65,7 @@ public class SocialBean {
// Display panel with social providers just in case that social is enabled for realm, but we are not in the middle of registration with social
public boolean isDisplaySocialProviders() {
return realm.isSocial() && !registerBean.isSocialRegistration();
return realm.isSocial() && !providers.isEmpty() && !registerBean.isSocialRegistration();
}
public RealmBean getRealm() {

View file

@ -174,7 +174,7 @@ public class FormServiceImpl implements FormService {
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
SocialBean social = new SocialBean(realm, register, url);
SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url);
attributes.put("social", social);
}
}
@ -215,7 +215,7 @@ public class FormServiceImpl implements FormService {
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
SocialBean social = new SocialBean(realm, register, url);
SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url);
attributes.put("social", social);
}
}
@ -236,7 +236,7 @@ public class FormServiceImpl implements FormService {
RegisterBean register = new RegisterBean(dataBean.getFormData(), dataBean.getSocialRegistration());
attributes.put("register", register);
SocialBean social = new SocialBean(realm, register, url);
SocialBean social = new SocialBean(realm, dataBean.getSocialProviders(), register, url);
attributes.put("social", social);
}
}

View file

@ -51,20 +51,24 @@ body {
}
.rcue-login-register .background-area .section {
float: left;
padding: 1.5em 4.5em 1.5em 4.6em;
padding: 0 4.5em 0 4.6em;
width: auto;
position: relative;
}
.rcue-login-register .background-area .separator .section {
padding-top: 1.5em;
padding-bottom: 1.5em;
}
.rcue-login-register .background-area .section h3 {
display: none;
}
.rcue-login-register .background-area .section:first-child {
padding-right: 4.5em;
}
.rcue-login-register .form-area {
.rcue-login-register .form-area.separator {
background-image: url(img/login-register-separator.png);
background-repeat: no-repeat;
background-position: 40.2em center;
background-position: 43.2em center;
}
.rcue-login-register .form-area.social {
background-image: url(img/login-register-social-separators.png);
@ -89,9 +93,7 @@ body {
font-weight: 400;
}
.rcue-login-register label {
width: 6.07142857142857em;
/* 85px */
width: 8.21428571428571em;
}
.rcue-login-register label.two-lines {
float: left;
@ -107,12 +109,8 @@ body {
.rcue-login-register form > div.aside-btn {
float: left;
font-size: 1.1em;
margin-left: 7.72727272727273em;
/* 85px */
margin-left: 10.4545454545454em;
margin-top: 0.90909090909091em;
/* 10px */
margin-bottom: 0;
}
.rcue-login-register form > div.aside-btn label {
@ -143,6 +141,9 @@ body {
top: -9.2em;
min-width: 35em;
}
.rcue-login-register.reset .feedback.bottom-left {
left: 35.7em;
}
.rcue-login-register input.error[type="text"],
.rcue-login-register input.error[type="password"],
.rcue-login-register input.error[type="email"] {
@ -281,19 +282,13 @@ a.zocial:before {
color: #0099D3;
}
/* Forgot Password page */
.rcue-login-register.reset .background-area .section {
padding-top: 0;
padding-bottom: 0;
}
.rcue-login-register.reset .background-area .section.app-form {
width: 43.2em;
}
.rcue-login-register.oauth .form-actions {
margin-bottom: 0;
margin-top: 2em;
}
.rcue-login-register .background-area .content-area {
width: 50em;
}
@ -342,7 +337,7 @@ a.zocial:before {
font-size: 1.3em;
}
.rcue-login-register.reset .form-area, .rcue-login-register.totp .form-area {
.rcue-login-register.totp .form-area {
background-image: none;
}
.rcue-login-register.reset .form-area p.instruction {
@ -350,9 +345,6 @@ a.zocial:before {
line-height: 1.3em;
margin-bottom: 1.81818181818182em;
}
.rcue-login-register.reset label {
width: 8.21428571428571em;
}
.rcue-login-register.totp {
min-height: 0;
}

View file

@ -23,9 +23,6 @@
</form>
</div>
<#elseif section = "info" >
<div id="info">
</div>
<p><a href="#">&laquo; Back to Login</a></p>
</#if>
</@layout.registrationLayout>

View file

@ -16,8 +16,7 @@
<input id="password" name="password" value="${login.password!''}" type="hidden" />
<div>
<label for="totp">${rb.getString('authenticatorCode')}</label>
<input id="totp" name="totp" type="text" />
<label for="totp">${rb.getString('authenticatorCode')}</label><input id="totp" name="totp" type="text" />
</div>
<div class="aside-btn">

View file

@ -16,7 +16,7 @@
<label for="password-new">${rb.getString('passwordNew')}</label><input type="password" id="password-new" name="password-new" />
</div>
<div>
<label for="password-confirm">${rb.getString('passwordConfirm')}</label><input type="password" id="password-confirm" name="password-confirm" />
<label for="password-confirm" class="two-lines">${rb.getString('passwordConfirm')}</label><input type="password" id="password-confirm" name="password-confirm" />
</div>
<input class="btn-primary" type="submit" value="Submit" />

View file

@ -21,16 +21,13 @@
</div>
<p class="subtitle">All fields required</p>
<div>
<label for="email">${rb.getString('email')}</label>
<input type="text" id="email" name="email" value="${user.email!''}" />
<label for="email">${rb.getString('email')}</label><input type="text" id="email" name="email" value="${user.email!''}" />
</div>
<div>
<label for="firstName">${rb.getString('firstName')}</label>
<input type="text" id="firstName" name="firstName" value="${user.firstName!''}" />
<label for="firstName">${rb.getString('firstName')}</label><input type="text" id="firstName" name="firstName" value="${user.firstName!''}" />
</div>
<div>
<label for="lastName">${rb.getString('lastName')}</label>
<input type="text" id="lastName" name="lastName" value="${user.lastName!''}" />
<label for="lastName">${rb.getString('lastName')}</label><input type="text" id="lastName" name="lastName" value="${user.lastName!''}" />
</div>
<div class="aside-btn">

View file

@ -13,13 +13,12 @@
<div id="form">
<form action="${url.loginAction}" method="post">
<div>
<label for="username">${rb.getString('username')}</label>
<input id="username" name="username" value="${login.username!''}" type="text" />
<label for="username">${rb.getString('username')}</label><input id="username" name="username" value="${login.username!''}" type="text" />
</div>
<#list login.requiredCredentials as c>
<div>
<label for="${c.name}">${rb.getString(c.label)}</label> <input id="${c.name}" name="${c.name}" type="${c.inputType}" />
<label for="${c.name}">${rb.getString(c.label)}</label><input id="${c.name}" name="${c.name}" type="${c.inputType}" />
</div>
</#list>

View file

@ -14,28 +14,22 @@
<form action="${url.registrationAction}" method="post">
<p class="subtitle">${rb.getString('allRequired')}</p>
<div>
<label for="firstName">${rb.getString('firstName')}</label>
<input type="text" id="firstName" name="firstName" value="${register.formData.firstName!''}" />
<label for="firstName">${rb.getString('firstName')}</label><input type="text" id="firstName" name="firstName" value="${register.formData.firstName!''}" />
</div>
<div>
<label for="lastName">${rb.getString('lastName')}</label>
<input type="text" id="lastName" name="lastName" value="${register.formData.lastName!''}" />
<label for="lastName">${rb.getString('lastName')}</label><input type="text" id="lastName" name="lastName" value="${register.formData.lastName!''}" />
</div>
<div>
<label for="email">${rb.getString('email')}</label>
<input type="text" id="email" name="email" value="${register.formData.email!''}" />
<label for="email">${rb.getString('email')}</label><input type="text" id="email" name="email" value="${register.formData.email!''}" />
</div>
<div>
<label for="username">${rb.getString('username')}</label>
<input type="text" id="username" name="username" value="${register.formData.username!''}" />
<label for="username">${rb.getString('username')}</label><input type="text" id="username" name="username" value="${register.formData.username!''}" />
</div>
<div>
<label for="password">${rb.getString('password')}</label>
<input type="password" id="password" name="password" />
<label for="password">${rb.getString('password')}</label><input type="password" id="password" name="password" />
</div>
<div>
<label for="password-confirm">${rb.getString('passwordConfirm')}</label>
<input type="password" id="password-confirm" name="password-confirm" />
<label for="password-confirm">${rb.getString('passwordConfirm')}</label><input type="password" id="password-confirm" name="password-confirm" />
</div>
<div class="aside-btn">

View file

@ -25,7 +25,7 @@
</div>
<#if (template.themeConfig.logo)?has_content>
<h1>
<a href="#" title="Go to the home page"><img src="${template.themeConfig.logo}" alt="Logo" /></a>
<a href="#" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a>
</h1>
</#if>

View file

@ -18,7 +18,7 @@
<body class="rcue-login-register ${bodyClass}">
<#if (template.themeConfig.logo)?has_content>
<h1>
<a href="#" title="Go to the home page"><img src="${template.themeConfig.logo}" alt="Logo" /></a>
<a href="#" title="Go to the login page"><img src="${template.themeConfig.logo}" alt="Red Hat Logo" /></a>
</h1>
</#if>

View file

@ -15,7 +15,7 @@ firstName=First name
lastName=Last name
email=Email
password=Password
passwordConfirm=Confirm Password
passwordConfirm=Password confirmation
passwordNew=New Password
passwordNewConfirm=New Password confirmation

View file

@ -2,6 +2,7 @@ package org.keycloak.models;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -136,4 +137,12 @@ public interface RealmModel extends RoleContainerModel, RoleMapperModel, ScopeMa
OAuthClientModel getOAuthClient(String name);
List<OAuthClientModel> getOAuthClients();
HashMap<String, String> getSmtpConfig();
void setSmtpConfig(HashMap<String, String> smtpConfig);
HashMap<String, String> getSocialConfig();
void setSocialConfig(HashMap<String, String> socialConfig);
}

View file

@ -3,6 +3,7 @@ package org.keycloak.models.jpa.entities;
import javax.persistence.*;
import java.util.Collection;
import java.util.HashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -24,6 +25,10 @@ public class RealmEntity {
@Column(length = 2048)
protected String privateKeyPem;
protected String[] defaultRoles;
@Lob
protected HashMap<String, String> smtpConfig;
@Lob
protected HashMap<String, String> socialConfig;
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true)
Collection<RequiredCredentailEntity> requiredCredentials;

View file

@ -679,7 +679,7 @@ public class RealmAdapter implements RealmModel {
@Override
public void addScopeMapping(UserModel agent, RoleModel role) {
ScopeRelationship scope = new ScopeRelationship();
scope.setClient(((UserAdapter)agent).getUser());
scope.setClient(((UserAdapter) agent).getUser());
scope.setScope(((RoleAdapter)role).getRole());
getRelationshipManager().add(scope);
}
@ -873,4 +873,26 @@ public class RealmAdapter implements RealmModel {
}
return userModels;
}
@Override
public HashMap<String, String> getSmtpConfig() {
return realm.getSmtpConfig();
}
@Override
public void setSmtpConfig(HashMap<String, String> smtpConfig) {
realm.setSmtpConfig(smtpConfig);
updateRealm();
}
@Override
public HashMap<String, String> getSocialConfig() {
return realm.getSocialConfig();
}
@Override
public void setSocialConfig(HashMap<String, String> socialConfig) {
realm.setSocialConfig(socialConfig);
updateRealm();
}
}

View file

@ -3,6 +3,9 @@ package org.keycloak.models.picketlink.mappings;
import org.picketlink.idm.model.AbstractPartition;
import org.picketlink.idm.model.annotation.AttributeProperty;
import java.io.Serializable;
import java.util.HashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@ -23,6 +26,8 @@ public class RealmData extends AbstractPartition {
private String publicKeyPem;
private String privateKeyPem;
private String[] defaultRoles;
private HashMap<String, String> smtpConfig;
private HashMap<String, String> socialConfig;
public RealmData() {
super(null);
@ -163,4 +168,22 @@ public class RealmData extends AbstractPartition {
public void setDefaultRoles(String[] defaultRoles) {
this.defaultRoles = defaultRoles;
}
@AttributeProperty
public HashMap<String, String> getSmtpConfig() {
return smtpConfig;
}
public void setSmtpConfig(HashMap<String, String> smtpConfig) {
this.smtpConfig = smtpConfig;
}
@AttributeProperty
public HashMap<String, String> getSocialConfig() {
return socialConfig;
}
public void setSocialConfig(HashMap<String, String> socialConfig) {
this.socialConfig = socialConfig;
}
}

View file

@ -5,11 +5,9 @@ import org.picketlink.idm.jpa.annotations.OwnerReference;
import org.picketlink.idm.jpa.annotations.entity.IdentityManaged;
import org.picketlink.idm.jpa.model.sample.simple.PartitionTypeEntity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.*;
import java.io.Serializable;
import java.util.HashMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -56,6 +54,12 @@ public class RealmEntity implements Serializable {
private String privateKeyPem;
@AttributeValue
private String[] defaultRoles;
@AttributeValue
@Lob
private HashMap<String, String> smtpConfig;
@AttributeValue
@Lob
private HashMap<String, String> socialConfig;
public PartitionTypeEntity getPartitionTypeEntity() {
@ -177,4 +181,20 @@ public class RealmEntity implements Serializable {
public void setPrivateKeyPem(String privateKeyPem) {
this.privateKeyPem = privateKeyPem;
}
public HashMap<String, String> getSmtpConfig() {
return smtpConfig;
}
public void setSmtpConfig(HashMap<String, String> smtpConfig) {
this.smtpConfig = smtpConfig;
}
public HashMap<String, String> getSocialConfig() {
return socialConfig;
}
public void setSocialConfig(HashMap<String, String> socialConfig) {
this.socialConfig = socialConfig;
}
}

View file

@ -22,14 +22,19 @@
package org.keycloak.services;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import javax.imageio.spi.ServiceRegistry;
import javax.ws.rs.core.MultivaluedMap;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.resources.flows.FormFlows;
import org.keycloak.social.SocialProvider;
/**
* @author <a href="mailto:vrockai@redhat.com">Viliam Rockai</a>
@ -51,6 +56,8 @@ public interface FormService {
private MultivaluedMap<String, String> formData;
private URI baseURI;
private List<SocialProvider> socialProviders;
public Boolean getSocialRegistration() {
return socialRegistration;
}
@ -81,11 +88,22 @@ public interface FormService {
private String contextPath;
public FormServiceDataBean(RealmModel realm, UserModel userModel, MultivaluedMap<String, String> formData, String message){
public FormServiceDataBean(RealmModel realm, UserModel userModel, MultivaluedMap<String, String> formData, String message) {
this.realm = realm;
this.userModel = userModel;
this.formData = formData;
this.message = message;
socialProviders = new LinkedList<SocialProvider>();
HashMap<String, String> socialConfig = realm.getSocialConfig();
if (socialConfig != null) {
for (Iterator<SocialProvider> itr = ServiceRegistry.lookupProviders(org.keycloak.social.SocialProvider.class); itr.hasNext(); ) {
SocialProvider p = itr.next();
if (socialConfig.containsKey(p.getId() + ".key") && socialConfig.containsKey(p.getId() + ".secret")) {
socialProviders.add(p);
}
}
}
}
public URI getBaseURI() {
@ -128,6 +146,10 @@ public interface FormService {
this.userModel = userModel;
}
public List<SocialProvider> getSocialProviders() {
return socialProviders;
}
public FormFlows.MessageType getMessageType() {
return messageType;
}

View file

@ -22,6 +22,7 @@
package org.keycloak.services.email;
import java.net.URI;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
@ -52,20 +53,15 @@ public class EmailSender {
private Properties properties;
public EmailSender() {
public EmailSender(Map<String, String> config) {
properties = new Properties();
for (Entry<Object, Object> e : System.getProperties().entrySet()) {
String key = (String) e.getKey();
if (key.startsWith("keycloak.mail.smtp.")) {
key = key.replace("keycloak.mail.smtp.", "mail.smtp.");
properties.put(key, e.getValue());
}
for (Entry<String, String> e : config.entrySet()) {
properties.put("mail.smtp." + e.getKey(), e.getValue());
}
}
public void send(String address, String subject, String body) throws AddressException, MessagingException {
Session session = Session.getDefaultInstance(properties);
public void send(String address, String subject, String body) throws MessagingException {
Session session = Session.getInstance(properties);
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress(properties.getProperty("mail.smtp.from")));

View file

@ -91,11 +91,19 @@ public class RealmManager {
realm.updateDefaultRoles(rep.getDefaultRoles());
}
if (rep.isAccountManagement()) {
if (rep.getAccountManagement() != null && rep.getAccountManagement()) {
enableAccountManagement(realm);
} else {
disableAccountManagement(realm);
}
if (rep.getSmtpServer() != null) {
realm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
}
if (rep.getSocialProviders() != null) {
realm.setSocialConfig(new HashMap(rep.getSocialProviders()));
}
}
private void enableAccountManagement(RealmModel realm) {
@ -245,9 +253,17 @@ public class RealmManager {
}
}
if (rep.isAccountManagement() != null && rep.isAccountManagement()) {
if (rep.getAccountManagement() != null && rep.getAccountManagement()) {
enableAccountManagement(newRealm);
}
if (rep.getSmtpServer() != null) {
newRealm.setSmtpConfig(new HashMap(rep.getSmtpServer()));
}
if (rep.getSocialProviders() != null) {
newRealm.setSocialConfig(new HashMap(rep.getSocialProviders()));
}
}
public void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
@ -403,6 +419,8 @@ public class RealmManager {
rep.setTokenLifespan(realm.getTokenLifespan());
rep.setAccessCodeLifespan(realm.getAccessCodeLifespan());
rep.setAccessCodeLifespanUserAction(realm.getAccessCodeLifespanUserAction());
rep.setSmtpServer(realm.getSmtpConfig());
rep.setSocialProviders(realm.getSocialConfig());
ApplicationModel accountManagementApplication = realm.getApplicationNameMap().get(Constants.ACCOUNT_MANAGEMENT_APPLICATION);
rep.setAccountManagement(accountManagementApplication != null && accountManagementApplication.isEnabled());

View file

@ -260,7 +260,7 @@ public class RequiredActionsService {
accessCode.setRequiredActions(requiredActions);
accessCode.setExpiration(System.currentTimeMillis() / 1000 + realm.getAccessCodeLifespanUserAction());
new EmailSender().sendPasswordReset(user, realm, accessCode, uriInfo);
new EmailSender(realm.getSmtpConfig()).sendPasswordReset(user, realm, accessCode, uriInfo);
return Flows.forms(realm, request, uriInfo).setError("emailSent").setErrorType(FormFlows.MessageType.SUCCESS)
.forwardToPasswordReset();

View file

@ -136,8 +136,8 @@ public class SocialResource {
return oauth.forwardToSecurityFailure("Login requester not enabled.");
}
String key = System.getProperty("keycloak.social." + requestData.getProviderId() + ".key");
String secret = System.getProperty("keycloak.social." + requestData.getProviderId() + ".secret");
String key = realm.getSocialConfig().get(requestData.getProviderId() + ".key");
String secret = realm.getSocialConfig().get(requestData.getProviderId() + ".secret");
String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);
@ -228,8 +228,8 @@ public class SocialResource {
return Flows.forms(realm, request, uriInfo).setError("Social provider not found").forwardToErrorPage();
}
String key = System.getProperty("keycloak.social." + providerId + ".key");
String secret = System.getProperty("keycloak.social." + providerId + ".secret");
String key = realm.getSocialConfig().get(providerId + ".key");
String secret = realm.getSocialConfig().get(providerId + ".secret");
String callbackUri = Urls.socialCallback(uriInfo.getBaseUri()).toString();
SocialProviderConfig config = new SocialProviderConfig(key, secret, callbackUri);

View file

@ -42,10 +42,7 @@ import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -446,7 +443,7 @@ public class TokenService {
@GET
public Response loginPage(final @QueryParam("response_type") String responseType,
final @QueryParam("redirect_uri") String redirect, final @QueryParam("client_id") String clientId,
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state) {
final @QueryParam("scope") String scopeParam, final @QueryParam("state") String state, final @QueryParam("prompt") String prompt) {
OAuthFlows oauth = Flows.oauth(realm, request, uriInfo, authManager, tokenManager);
if (!realm.isEnabled()) {
@ -483,6 +480,10 @@ public class TokenService {
return oauth.processAccessCode(scopeParam, state, redirect, client, user);
}
if (prompt != null && prompt.equals("none")) {
return oauth.redirectError(client, "access_denied", state, redirect);
}
return Flows.forms(realm, request, uriInfo).forwardToLogin();
}

View file

@ -89,7 +89,7 @@ public class FormFlows {
case UPDATE_PASSWORD:
return forwardToActionForm(Pages.LOGIN_UPDATE_PASSWORD, Messages.ACTION_WARN_PASSWD);
case VERIFY_EMAIL:
new EmailSender().sendEmailVerification(userModel, realm, accessCode, uriInfo);
new EmailSender(realm.getSmtpConfig()).sendEmailVerification(userModel, realm, accessCode, uriInfo);
return forwardToActionForm(Pages.LOGIN_VERIFY_EMAIL, Messages.ACTION_WARN_EMAIL);
default:
return Response.serverError().build();

View file

@ -85,6 +85,20 @@ public class OAuthFlows {
return location.build();
}
public Response redirectError(UserModel client, String error, String state, String redirect) {
Set<String> redirectUris = client.getRedirectUris();
if (!redirectUris.isEmpty() && !redirectUris.contains(redirect)) {
return forwardToSecurityFailure("Invalid redirect_uri " + redirect);
}
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam("error", error);
if (state != null) {
redirectUri.queryParam("state", state);
}
return Response.status(302).location(redirectUri.build()).build();
}
public Response processAccessCode(String scopeParam, String state, String redirect, UserModel client, UserModel user) {
RoleModel resourceRole = realm.getRole(Constants.APPLICATION_ROLE);
RoleModel identityRequestRole = realm.getRole(Constants.IDENTITY_REQUESTER_ROLE);

View file

@ -3,6 +3,7 @@ package org.keycloak.services.email;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.net.SocketException;
import java.util.HashMap;
import javax.mail.MessagingException;
import javax.mail.internet.AddressException;
@ -19,6 +20,7 @@ import com.icegreen.greenmail.util.ServerSetup;
public class EmailSenderTest {
private GreenMail greenMail;
private EmailSender emailSender;
@Before
public void before() {
@ -27,9 +29,12 @@ public class EmailSenderTest {
greenMail = new GreenMail(setup);
greenMail.start();
System.setProperty("keycloak.mail.smtp.from", "auto@keycloak.org");
System.setProperty("keycloak.mail.smtp.host", "localhost");
System.setProperty("keycloak.mail.smtp.port", "3025");
HashMap<String,String> config = new HashMap<String, String>();
config.put("from", "auto@keycloak.org");
config.put("host", "localhost");
config.put("port", "3025");
emailSender = new EmailSender(config);
}
@After
@ -53,7 +58,6 @@ public class EmailSenderTest {
@Test
public void sendMail() throws AddressException, MessagingException, IOException {
EmailSender emailSender = new EmailSender();
emailSender.send("test@test.com", "Test subject", "Test body");
MimeMessage[] receivedMessages = greenMail.getReceivedMessages();

View file

@ -1,7 +1,9 @@
package org.keycloak.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Assert;
@ -51,6 +53,16 @@ public class ModelTest extends AbstractKeycloakServerTest {
realm.setPrivateKeyPem("1234234");
realm.addDefaultRole("default-role");
HashMap<String, String> smtp = new HashMap<String,String>();
smtp.put("from", "auto@keycloak");
smtp.put("hostname", "localhost");
realm.setSmtpConfig(smtp);
HashMap<String, String> social = new HashMap<String,String>();
social.put("google.key", "1234");
social.put("google.secret", "5678");
realm.setSmtpConfig(social);
RealmModel peristed = manager.getRealm(realm.getId());
assertEquals(realm, peristed);
@ -75,6 +87,9 @@ public class ModelTest extends AbstractKeycloakServerTest {
Assert.assertEquals(expected.getPrivateKeyPem(), actual.getPrivateKeyPem());
assertEquals(expected.getDefaultRoles(), actual.getDefaultRoles());
Assert.assertEquals(expected.getSmtpConfig(), actual.getSmtpConfig());
Assert.assertEquals(expected.getSocialConfig(), actual.getSocialConfig());
}
public static void assertEquals(List<RoleModel> expected, List<RoleModel> actual) {

View file

@ -1,59 +0,0 @@
Keycloak social
===============
This document describes how to configure social providers for Keycloak. At the moment social providers are configured globally using system properties. These can either be passed using '-D' when starting the application server or added to the standalone.xml file, for example:
<system-properties>
<property name="keycloak.social.facebook.key" value="<facebook key>"/>
<property name="keycloak.social.facebook.secret" value="<facebook secret>"/>
<property name="keycloak.social.google.key" value="<google key>"/>
<property name="keycloak.social.google.secret" value="<google secret>"/>
<property name="keycloak.social.twitter.key" value="<twitter key>"/>
<property name="keycloak.social.twitter.secret" value="<twitter secret>"/>
</system-properties>
Social provides implementations for Facebook, Google and Twitter.
Configure Facebook
------------------
Open https://developers.facebook.com/apps. Click on Create New App
Use any app name that you'd like, click Continue
Select Disabled for Sandbox Mode
Under Select how your app integrates with Facebook select Website with Facebook login. Fill in the form with the following values:
* Site URL: http://<HOSTNAME>[<PORT>]/auth-server/rest/social/callback
Click on Save changes. Use the value of App ID as the value of the system property "keycloak.social.facebook.key", and the value of App Secret as the value of "keycloak.social.facebook.secret".
Configure Google
----------------
Open https://code.google.com/apis/console/. From the drop-down menu select Create.
Use any name that you'd like, click Create Project, select API Access and click on Create an OAuth 2.0 client ID.
Use any product name you'd like and leave the other fields empty, then click Next. On the next page select Web application as the application type. Click more options next> to Your site or hostname. Fill in the form with the following values:
* Authorized Redirect URIs: http://<HOSTNAME>[<PORT>]/auth-server/rest/social/callback
Click on Create client ID. Use the value of Client ID as the value of the system property "keycloak.social.google.key", and the value of Client secret as the value of "keycloak.social.google.secret".
Configure Twitter
-----------------
Open https://dev.twitter.com/apps. Click on Create a new application.
Fill in name, description and website. Leave Callback URL empty!
Agree to the rules, fill in the captcha and click on Create your Twitter application.
Now click on Settings and tick the box Allow this application to be used to Sign in with Twitter, and click on Update this Twitter application's settings.
Finally click on Details. Use the value of Client key as the value of the system property "keycloak.social.twitter.key", and the value of Client secret as the value of "keycloak.social.twitter.secret".

View file

@ -25,11 +25,15 @@ import io.undertow.Undertow;
import io.undertow.Undertow.Builder;
import io.undertow.server.handlers.resource.*;
import io.undertow.servlet.Servlets;
import io.undertow.servlet.api.DefaultServletConfig;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import java.io.*;
import java.net.URL;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.servlet.DispatcherType;
@ -221,6 +225,10 @@ public class KeycloakServer {
}
new ApplianceBootstrap().bootstrap(session);
// No need to require admin to change password as this server is for dev/test
manager.getRealm(Constants.ADMIN_REALM).getUser("admin").removeRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
session.getTransaction().commit();
} finally {
session.close();
@ -243,6 +251,10 @@ public class KeycloakServer {
di.setDeploymentName("Keycloak");
di.setResourceManager(new KeycloakResourceManager(config.getResourcesHome()));
Set<String> allowed = new HashSet<String>(Arrays.asList(new String[]{"js", "css", "png", "jpg", "gif", "html", "svg"}));
di.setDefaultServletConfig(new DefaultServletConfig(false, allowed));
di.addWelcomePage("index.html");
FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
di.addFilter(filter);
di.addFilterUrlMapping("SessionFilter", "/rest/*", DispatcherType.REQUEST);
@ -288,23 +300,16 @@ public class KeycloakServer {
public Resource getResource(String path) throws IOException {
if (resourcesHome == null) {
String realPath = "META-INF/resources" + path;
if (realPath.endsWith("/admin/")) {
realPath += "index.html";
}
URL url = getClass().getClassLoader().getResource(realPath);
return new URLResource(url, url.openConnection(), path);
} else {
File file;
if (path.startsWith("/forms")) {
if (path.startsWith("/forms/")) {
file = file(resourcesHome, "forms", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar));
} else if (path.startsWith("/admin")) {
} else if (path.startsWith("/admin/")) {
file = file(resourcesHome, "admin-ui", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar));
if (!file.isFile()) {
file = file(resourcesHome, "admin-ui-styles", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar));
}
} else if (path.startsWith("/admin-ui/")) {
file = file(resourcesHome, "admin-ui-styles", "src", "main", "resources", "META-INF", "resources", path.replace('/', File.separatorChar));
} else {
throw new IOException("Unknown resource " + path);
}

View file

@ -41,23 +41,8 @@ public class GreenMailRule extends ExternalResource {
private GreenMail greenMail;
private Properties originalProperties = new Properties();
@Override
protected void before() throws Throwable {
Iterator<Entry<Object, Object>> itr = System.getProperties().entrySet().iterator();
while (itr.hasNext()) {
Entry<Object, Object> e = itr.next();
if (((String) e.getKey()).startsWith("keycloak.mail")) {
originalProperties.put(e.getKey(), e.getValue());
itr.remove();
}
}
System.setProperty("keycloak.mail.smtp.from", "auto@keycloak.org");
System.setProperty("keycloak.mail.smtp.host", "localhost");
System.setProperty("keycloak.mail.smtp.port", "3025");
ServerSetup setup = new ServerSetup(3025, "localhost", "smtp");
greenMail = new GreenMail(setup);
@ -81,11 +66,6 @@ public class GreenMailRule extends ExternalResource {
greenMail.stop();
}
System.getProperties().remove("keycloak.mail.smtp.from");
System.getProperties().remove("keycloak.mail.smtp.host");
System.getProperties().remove("keycloak.mail.smtp.port");
System.getProperties().putAll(originalProperties);
}
public MimeMessage[] getReceivedMessages() {

View file

@ -43,6 +43,9 @@ import org.keycloak.testsuite.rule.WebRule;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@ -54,6 +57,11 @@ public class SocialLoginTest {
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
appRealm.setSocial(true);
appRealm.setAutomaticRegistrationAfterSocialLogin(true);
HashMap<String, String> socialConfig = new HashMap<String, String>();
socialConfig.put("dummy.key", "1234");
socialConfig.put("dummy.secret", "1234");
appRealm.setSocialConfig(socialConfig);
}
});

View file

@ -16,6 +16,11 @@
"requiredApplicationCredentials": [ "password" ],
"requiredOAuthClientCredentials": [ "password" ],
"defaultRoles": [ "user" ],
"smtpServer": {
"from": "auto@keycloak.org",
"host": "localhost",
"port":"3025"
},
"users" : [
{
"username" : "test-user@localhost",

View file

@ -1,22 +0,0 @@
<?xml version="1.0"?>
<project>
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.0-alpha-1</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-ui</artifactId>
<name>Keycloak UI</name>
<description/>
<dependencies>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View file

@ -1,230 +0,0 @@
package org.keycloak.ui.example;
import java.net.URI;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
@ApplicationPath("ui/api")
@Path("")
public class Admin extends javax.ws.rs.core.Application {
private static Map<String, Application> applications = new HashMap<String, Application>();
private static Map<String, Realm> realms = new HashMap<String, Realm>();
private static Map<String, List<User>> users = new HashMap<String, List<User>>();
private static Map<Id, List<String>> roles = new HashMap<Id, List<String>>();
static {
Realm realm = new Realm();
realm.setId(UUID.randomUUID().toString());
realm.setName("Test realm");
realm.setEnabled(true);
realm.setRoles(new String[] { "admin", "user" });
realms.put(realm.getId(), realm);
User user = new User();
user.setUserId("user");
user.setPassword("password");
List<User> l = new LinkedList<User>();
l.add(user);
users.put(realm.getId(), l);
}
@DELETE
@Path("applications/{id}")
public void deleteApplication(@PathParam("id") String id) {
applications.remove(id);
}
@DELETE
@Path("realms/{id}")
public void deleteRealm(@PathParam("id") String id) {
realms.remove(id);
}
@GET
@Path("applications/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Application getApplication(@PathParam("id") String id) {
return applications.get(id);
}
@GET
@Path("applications")
@Produces(MediaType.APPLICATION_JSON)
public List<Application> getApplications() {
return new LinkedList<Application>(applications.values());
}
@GET
@Path("realms/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Realm getRealm(@PathParam("id") String id) {
return realms.get(id);
}
@GET
@Path("realms")
@Produces(MediaType.APPLICATION_JSON)
public List<Realm> getRealms() {
return new LinkedList<Realm>(realms.values());
}
@POST
@Path("applications")
@Consumes(MediaType.APPLICATION_JSON)
public Response save(Application application) {
String id = UUID.randomUUID().toString();
application.setId(id);
applications.put(id, application);
return Response.created(URI.create("/applications/" + id)).build();
}
@POST
@Path("realms")
@Consumes(MediaType.APPLICATION_JSON)
public Response save(Realm realm) {
String id = UUID.randomUUID().toString();
realm.setId(id);
realms.put(id, realm);
return Response.created(URI.create("/realms/" + id)).build();
}
@PUT
@Path("applications/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void save(@PathParam("id") String id, Application application) {
try {
deleteApplication(id);
} catch (WebApplicationException e) {
}
applications.put(id, application);
}
@PUT
@Path("realms/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void save(@PathParam("id") String id, Realm realm) {
try {
deleteRealm(id);
} catch (WebApplicationException e) {
}
realms.put(id, realm);
}
@GET
@Path("realms/{realm}/users/{id}")
@Produces(MediaType.APPLICATION_JSON)
public User getUser(@PathParam("realm") String realm, @PathParam("id") String id) {
for (User u : getUsers(realm)) {
if (u.getUserId().equals(id)) {
return u;
}
}
throw new WebApplicationException(Status.NOT_FOUND);
}
@GET
@Path("realms/{realm}/users")
@Produces(MediaType.APPLICATION_JSON)
public List<User> getUsers(@PathParam("realm") String realm) {
List<User> l = users.get(realm);
if (l == null) {
l = new LinkedList<User>();
users.put(realm, l);
}
return l;
}
@PUT
@Path("realms/{realm}/users/{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void save(@PathParam("realm") String realm, @PathParam("id") String id, User user) {
try {
deleteUser(realm, id);
} catch (WebApplicationException e) {
}
getUsers(realm).add(user);
}
@DELETE
@Path("realms/{realm}/users/{id}")
public void deleteUser(@PathParam("realm") String realm, @PathParam("id") String id) {
getUsers(realm).remove(getUser(realm, id));
}
@GET
@Path("roles/{realm}/{role}")
@Produces(MediaType.APPLICATION_JSON)
public List<User> getRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role) {
List<String> ids = getRoleMapping(new Id(realm, role));
List<User> users = new LinkedList<User>();
List<User> realmUsers = getUsers(realm);
for (String id : ids) {
for (User u : realmUsers) {
if (u.getUserId().equals(id)) {
users.add(u);
}
}
}
return users;
}
private List<String> getRoleMapping(Id id) {
List<String> l = roles.get(id);
if (l == null) {
l = new LinkedList<String>();
roles.put(id, l);
}
return l;
}
@PUT
@Path("roles/{realm}/{role}/{user}")
@Consumes(MediaType.APPLICATION_JSON)
public void addRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
@PathParam("user") String user, User u) {
getRoleMapping(new Id(realm, role)).add(user);
}
@DELETE
@Path("roles/{realm}/{role}/{user}")
public void deleteRoleMapping(@PathParam("realm") String realm, @PathParam("role") String role,
@PathParam("user") String user) {
Iterator<String> itr = getRoleMapping(new Id(realm, role)).iterator();
while (itr.hasNext()) {
if (itr.next().equals(user)) {
itr.remove();
return;
}
}
throw new WebApplicationException(Status.NOT_FOUND);
}
}

View file

@ -1,102 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.ui.example;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@XmlRootElement
public class Application {
private String[] callbackUrl;
private boolean enabled;
private String id;
private String[] initialRoles;
private String name;
private String realm;
private String[] roles;
public String[] getCallbackUrl() {
return callbackUrl;
}
public String getId() {
return id;
}
public String[] getInitialRoles() {
return initialRoles;
}
public String getName() {
return name;
}
public String getRealm() {
return realm;
}
public String[] getRoles() {
return roles;
}
public boolean isEnabled() {
return enabled;
}
public void setCallbackUrl(String[] callbackUrl) {
this.callbackUrl = callbackUrl;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setId(String id) {
this.id = id;
}
public void setInitialRoles(String[] initialRoles) {
this.initialRoles = initialRoles;
}
public void setName(String name) {
this.name = name;
}
public void setRealm(String realm) {
this.realm = realm;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
}

View file

@ -1,40 +0,0 @@
package org.keycloak.ui.example;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Attribute implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String value;
public Attribute() {
}
public Attribute(String name, String value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setName(String name) {
this.name = name;
}
public void setValue(String value) {
this.value = value;
}
}

View file

@ -1,43 +0,0 @@
package org.keycloak.ui.example;
import java.util.Arrays;
public class Id {
private String[] id;
public Id(String... id) {
this.id = id;
}
public String[] getId() {
return id;
}
public String getId(int i) {
return id[i];
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(id);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Id other = (Id) obj;
if (!Arrays.equals(id, other.id))
return false;
return true;
}
}

View file

@ -1,68 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.ui.example;
import javax.xml.bind.annotation.XmlRootElement;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@XmlRootElement
public class IdentityProviderConfig {
private Long id;
private String key;
private String providerId;
private String secret;
public Long getId() {
return id;
}
public String getKey() {
return key;
}
public String getProviderId() {
return providerId;
}
public String getSecret() {
return secret;
}
public void setKey(String key) {
this.key = key;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public void setSecret(String secret) {
this.secret = secret;
}
}

View file

@ -1,121 +0,0 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.ui.example;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Realm {
private boolean enabled;
private String[] initialRoles;
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
private String name;
private String[] roles;
private boolean social;
private long tokenExpiration;
private TimeUnit tokenExpirationUnit;
private boolean userRegistration;
public String[] getInitialRoles() {
return initialRoles;
}
public String getName() {
return name;
}
public String[] getRoles() {
return roles;
}
public long getTokenExpiration() {
return tokenExpiration;
}
public TimeUnit getTokenExpirationUnit() {
return tokenExpirationUnit;
}
public boolean isEnabled() {
return enabled;
}
public boolean isSocial() {
return social;
}
public boolean isUserRegistration() {
return userRegistration;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public void setInitialRoles(String[] initialRoles) {
this.initialRoles = initialRoles;
}
public void setName(String name) {
this.name = name;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
public void setSocial(boolean social) {
this.social = social;
}
public void setTokenExpiration(long tokenExpiration) {
this.tokenExpiration = tokenExpiration;
}
public void setTokenExpirationUnit(TimeUnit tokenExpirationUnit) {
this.tokenExpirationUnit = tokenExpirationUnit;
}
public void setUserRegistration(boolean userRegistration) {
this.userRegistration = userRegistration;
}
}

View file

@ -1,93 +0,0 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2012, Red Hat, Inc., and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.ui.example;
import java.io.Serializable;
import java.util.List;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private List<Attribute> attributes;
private String email;
private String firstName;
private String lastName;
private String userId;
private String password;
private String[] roles;
public String[] getRoles() {
return roles;
}
public void setRoles(String[] roles) {
this.roles = roles;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<Attribute> getAttributes() {
return attributes;
}
public String getEmail() {
return email;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getUserId() {
return userId;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
public void setEmail(String email) {
this.email = email;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public void setUserId(String userId) {
this.userId = userId;
}
}

Some files were not shown because too many files have changed in this diff Show more