Skip to content

Commit d6f47aa

Browse files
committed
Bug 1200765 - Make login UX mobile friendly to assist mobile authentication workflow
1 parent 4ce3037 commit d6f47aa

File tree

7 files changed

+157
-60
lines changed

7 files changed

+157
-60
lines changed

.htaccess

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,4 @@ RewriteRule ^rest/(.*)$ rest.cgi/$1 [NE]
9191
RewriteRule ^(?:latest|1\.2|1\.3)/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE]
9292
RewriteRule ^bzapi/(.*)$ extensions/BzAPI/bin/rest.cgi/$1 [NE]
9393
RewriteRule ^data/assets/ZeroClipboard.swf(.*)$ extensions/BugModal/web/ZeroClipboard/ZeroClipboard.swf$1 [NE]
94+
RewriteRule ^login$ index.cgi?GoAheadAndLogIn=1 [NE]

Bugzilla/Template.pm

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,11 @@ sub create {
11171117
return \@optional;
11181118
},
11191119
'default_authorizer' => sub { return Bugzilla::Auth->new() },
1120+
1121+
# It is almost always better to do mobile feature detection, client side in js.
1122+
# However, we need to set the meta[name=viewport] server-side or the behavior is
1123+
# not as predictable. It is possible other parts of the frontend may use this feature too.
1124+
'is_mobile_browser' => sub { return Bugzilla->cgi->user_agent =~ /Mobi/ },
11201125
},
11211126
};
11221127
# Use a per-process provider to cache compiled templates in memory across

skins/standard/global.css

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,21 @@ input.required, select.required, span.required_explanation {
724724
background-repeat: no-repeat !important;
725725
background-position: right 8px center !important;
726726
}
727+
728+
#login .field-login, #login .field-password {
729+
line-height: 32px;
730+
display: block;
731+
padding-top: 2px;
732+
padding-bottom: 2px;
733+
}
734+
735+
#login .field-login label, #login .field-password label {
736+
clear: left;
737+
width: 7em;
738+
display: inline-block;
739+
font-weight: bold;
740+
}
741+
742+
#login .field-restrict, #login .field-remember {
743+
margin-left: 7em;
744+
}

skins/standard/mobile.css

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
4+
*
5+
* This Source Code Form is "Incompatible With Secondary Licenses", as
6+
* defined by the Mozilla Public License, v. 2.0.
7+
*/
8+
9+
@media
10+
only screen and (max-device-width : 720px) {
11+
#header, #footer {
12+
display: none;
13+
}
14+
.cookie-notify {
15+
display: none;
16+
}
17+
18+
#login .field-login label, #login .field-password label {
19+
display: block;
20+
}
21+
22+
#login .field-login, #login .field-password {
23+
line-height: auto;
24+
padding-top: 0px;
25+
padding-bottom: 0px;
26+
}
27+
28+
#login .field-restrict, #login .field-remember {
29+
margin-left: 0px;
30+
}
31+
#login .field-submit {
32+
padding-top: 4px;
33+
}
34+
35+
h1 {
36+
font-size: 1.5em;
37+
}
38+
39+
.verify-totp input[type="text"] {
40+
font-size: 28px;
41+
}
42+
43+
.verify-totp input[type="submit"] {
44+
font-size: 1em;
45+
}
46+
}
47+
48+
@media
49+
only screen and (-webkit-min-device-pixel-ratio: 2),
50+
only screen and (min--moz-device-pixel-ratio: 2),
51+
only screen and (-o-min-device-pixel-ratio: 2/1),
52+
only screen and (min-device-pixel-ratio: 2),
53+
only screen and (min-resolution: 192dpi),
54+
only screen and (min-resolution: 2dppx) {
55+
#privacy_policy {
56+
font-size: small;
57+
}
58+
59+
body {
60+
font-size: medium;
61+
}
62+
63+
label.checkbox-note {
64+
font-size: small;
65+
}
66+
}

template/en/default/account/auth/login.html.tmpl

Lines changed: 46 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@
3030
[% PROCESS global/variables.none.tmpl %]
3131

3232
[% PROCESS global/header.html.tmpl
33-
title = "Log in to $terms.Bugzilla",
34-
onload = "document.forms['login'].Bugzilla_login.focus()"
33+
title = "Log in to $terms.Bugzilla",
34+
onload = "document.forms['login'].Bugzilla_login.focus()"
35+
allow_mobile = 1
3536
%]
3637

3738
[% USE Bugzilla %]
@@ -40,59 +41,55 @@
4041
I need an email address and password to continue.
4142
</p>
4243

43-
<form name="login" action="[% target FILTER html %]" method="POST"
44-
[%- IF Bugzilla.cgi.param("data") %] enctype="multipart/form-data"[% END %]>
45-
<table>
46-
<tr>
47-
<th align="right"><label for="Bugzilla_login">Email Address:</label></th>
48-
<td>
49-
<input size="35" id="Bugzilla_login" name="Bugzilla_login"
50-
[%- ' type="email"' UNLESS Param('emailsuffix') %]>
51-
[% Param('emailsuffix') FILTER html %]
52-
</td>
53-
</tr>
54-
<tr>
55-
<th align="right"><label for="Bugzilla_password">Password:</label></th>
56-
<td>
57-
<input type="password" size="35" id="Bugzilla_password" name="Bugzilla_password">
58-
</td>
59-
</tr>
44+
<div id="login" class="login-form">
45+
<form name="login" action="[% target FILTER html %]" method="POST"
46+
[%- IF Bugzilla.cgi.param("data") %] enctype="multipart/form-data"[% END %]>
47+
<div class="field-login">
48+
<label for="Bugzilla_login">Email Address:</label>
49+
<input id="Bugzilla_login" name="Bugzilla_login"
50+
[%- ' type="email"' UNLESS Param('emailsuffix') %]>
51+
[% Param('emailsuffix') FILTER html %]
52+
</div>
53+
54+
<div class="field-password">
55+
<label for="Bugzilla_password">Password:</label>
56+
<input type="password" id="Bugzilla_password" name="Bugzilla_password">
57+
</div>
6058

6159
[% IF Param('rememberlogin') == 'defaulton' ||
62-
Param('rememberlogin') == 'defaultoff' %]
63-
<tr>
64-
<th>&nbsp;</th>
65-
<td>
66-
<input type="checkbox" id="Bugzilla_remember" name="Bugzilla_remember" value="on"
67-
[%+ "checked" IF Param('rememberlogin') == "defaulton" %]>
68-
<label for="Bugzilla_remember">Remember my email address</label>
69-
</td>
70-
</tr>
60+
Param('rememberlogin') == 'defaultoff' %]
61+
<div class="field-remember">
62+
<input type="checkbox" id="Bugzilla_remember" name="Bugzilla_remember" value="on"
63+
[%+ "checked" IF Param('rememberlogin') == "defaulton" %]>
64+
<label for="Bugzilla_remember" class="checkbox-note">
65+
Remember my email address
66+
</label>
67+
</div>
7168
[% END %]
7269

73-
<tr>
74-
<th>&nbsp;</th>
75-
<td>
76-
<input type="checkbox" id="Bugzilla_restrictlogin" name="Bugzilla_restrictlogin"
77-
checked="checked">
78-
<label for="Bugzilla_restrictlogin">Restrict this session to this IP address
79-
(using this option improves security)</label>
80-
</td>
81-
</tr>
82-
</table>
70+
[% PROCESS "global/hidden-fields.html.tmpl"
71+
exclude="^Bugzilla_(login|password|restrictlogin)$" %]
8372

84-
[% PROCESS "global/hidden-fields.html.tmpl"
85-
exclude="^Bugzilla_(login|password|restrictlogin)$" %]
73+
<div class="field-restrict">
74+
<input type="checkbox" id="Bugzilla_restrictlogin" name="Bugzilla_restrictlogin"
75+
checked="checked">
76+
<label for="Bugzilla_restrictlogin" class="checkbox-note">
77+
Restrict this session to this IP address
78+
(using this option improves security)</label>
79+
</div>
8680

87-
<input type="hidden" name="Bugzilla_login_token"
88-
value="[% get_login_request_token() FILTER html %]">
89-
<input type="submit" name="GoAheadAndLogIn" value="Log in" id="log_in">
81+
<div class="field-submit">
82+
<input type="hidden" name="Bugzilla_login_token"
83+
value="[% get_login_request_token() FILTER html %]">
84+
<input type="submit" name="GoAheadAndLogIn" value="Log in" id="log_in">
85+
</div>
9086

91-
<p>
92-
(Note: you should make sure cookies are enabled for this site.
93-
Otherwise, you will be required to log in frequently.)
94-
</p>
95-
</form>
87+
<p class="cookie-notify">
88+
(Note: you should make sure cookies are enabled for this site.
89+
Otherwise, you will be required to log in frequently.)
90+
</p>
91+
</form>
92+
</div>
9693

9794
[% Hook.process('additional_methods') %]
9895

@@ -117,7 +114,7 @@
117114
If you have an account, but have forgotten your password,
118115
enter your email address below and submit a request
119116
to change your password.<br>
120-
<input size="35" name="loginname">
117+
<input name="loginname">
121118
<input type="hidden" id="token" name="token" value="[% issue_hash_token(['reqpw']) FILTER html %]">
122119
<input type="submit" id="request" value="Reset Password">
123120
</form>

template/en/default/global/header.html.tmpl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
# atomlink: Atom link URL, May contain HTML
3838
# generate_api_token: generate a token which can be used to make authenticated webservice calls
3939
# no_body: if true the body element will not be generated
40+
# allow_mobile: allow special CSS and viewport for detected mobile useragents
4041
#%]
4142

4243
[% IF message %]
@@ -103,6 +104,10 @@
103104
[%# Add our required jQuery plugins %]
104105
[% jquery.push("cookie", "devbridgeAutocomplete") %]
105106

107+
[% IF allow_mobile && is_mobile_browser %]
108+
[% style_urls.push("skins/standard/mobile.css") %]
109+
[% END %]
110+
106111
[%# We should be able to set the default value of the header variable
107112
# to the value of the title variable using the DEFAULT directive,
108113
# but that doesn't work if a caller sets header to the empty string
@@ -260,6 +265,9 @@
260265
[%# Required for the 'Autodiscovery' feature in Firefox 2 and IE 7. %]
261266
<link rel="search" type="application/opensearchdescription+xml"
262267
title="[% terms.BugzillaTitle %]" href="./search_plugin.cgi">
268+
[% IF allow_mobile && is_mobile_browser %]
269+
<meta name="viewport" content="width=device-width, initial-scale=1">
270+
[% END %]
263271
[% Hook.process("additional_header") %]
264272
</head>
265273

template/en/default/mfa/totp/verify.html.tmpl

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
[% INCLUDE global/header.html.tmpl
1010
title = "Account Verification"
11+
allow_mobile = 1
1112
%]
1213

1314
<h1>Account Verification</h1>
@@ -16,16 +17,17 @@
1617
<b>[% reason FILTER html %]</b> requires verification.<br>
1718
Please enter your verification code from your TOTP application:
1819
</p>
19-
20-
<form method="POST" action="[% postback.action FILTER none %]">
21-
[% FOREACH field IN postback.fields.keys %]
22-
<input type="hidden" name="[% field FILTER html %]" value="[% postback.fields.item(field) FILTER html %]">
23-
[% END %]
24-
<input type="text" name="code" id="code"
25-
placeholder="123456" maxlength="9" pattern="\d{6,9}" size="10"
26-
autocomplete="off" required autofocus><br>
27-
<br>
28-
<input type="submit" value="Submit">
29-
</form>
20+
<div class="verify-totp">
21+
<form method="POST" action="[% postback.action FILTER none %]">
22+
[% FOREACH field IN postback.fields.keys %]
23+
<input type="hidden" name="[% field FILTER html %]" value="[% postback.fields.item(field) FILTER html %]">
24+
[% END %]
25+
<input type="text" name="code" id="code"
26+
placeholder="123456" maxlength="9" pattern="\d{6,9}" size="10"
27+
autocomplete="off" required autofocus><br>
28+
<br>
29+
<input type="submit" value="Submit">
30+
</form>
31+
</div>
3032

3133
[% INCLUDE global/footer.html.tmpl %]

0 commit comments

Comments
 (0)