Add Litmus Authentication System and db schema upgrading tools.

This commit is contained in:
zach%zachlipton.com 2006-01-08 03:56:10 +00:00
parent 1e28d5751a
commit 46e0cae954
38 changed files with 1406 additions and 113 deletions

View File

@ -30,8 +30,7 @@
=cut
# Persistant object store for various objects used by
# different modules within Litmus
# Global object store and function library for Litmus
package Litmus;
@ -39,15 +38,19 @@ use strict;
use Litmus::Template;
use Litmus::Config;
use CGI;
use Litmus::Auth;
use Litmus::CGI;
if ($Litmus::Config::disabled) {
my $c = new CGI();
print $c->header();
print "Litmus has been shutdown by the administrator. Please try again later.";
exit;
BEGIN {
if ($Litmus::Config::disabled) {
my $c = new CGI();
print $c->header();
print "Litmus has been shutdown by the administrator. Please try again later.";
exit;
}
}
# Global Template object
my $_template;
sub template() {
my $class = shift;
@ -55,4 +58,20 @@ sub template() {
return $_template;
}
# Global CGI object
my $_cgi;
sub cgi() {
my $class = shift;
$_cgi ||= Litmus::CGI->new();
return $_cgi;
}
# hook to handle a login in progress for any CGI script:
BEGIN {
my $c = cgi();
if ($c->param("login_type")) {
Litmus::Auth::processLoginForm();
}
}
1;

View File

@ -34,10 +34,17 @@ package Litmus::Auth;
use strict;
# IMPORTANT!
## You _must_ call Litmus::Auth methods before sending a Content-type
## header so that any required cookies can be sent.
require Exporter;
use Litmus;
use Litmus::DB::User;
use Litmus::Error;
use Litmus::Template;
use Time::Piece;
use Time::Seconds;
use CGI;
@ -45,8 +52,10 @@ our @ISA = qw(Exporter);
our @EXPORT = qw();
my $logincookiename = $Litmus::Config::user_cookiename;
my $cookie_expire_days = 7;
my $curSession;
# (XXX) stub
# Given a username and password, validate the login. Returns the
# Lutmus::DB::User object associated with the username if the login
# is sucuessful. Returns false otherwise.
@ -54,16 +63,270 @@ sub validate_login($$) {
my $username = shift;
my $password = shift;
# we'll fill this in later once we actually _have_ passwords.
# for now, just return the user object or false:
my @usrobjs = Litmus::DB::User->search(email => $username);
if (@usrobjs) {
return $usrobjs[0];
my $userobj = $usrobjs[0];
if (!$userobj) { return 0; }
if ($userobj->disabled() && $userobj->disabled() == 1) {
die "account ".$userobj->username()." has been disabled by
the administrator";
}
# for security reasons, we use the real (correct) crypt'd passwd
# as the salt:
my $realPasswordCrypted = $userobj->getRealPasswd();
my $enteredPasswordCrypted = crypt($password, $realPasswordCrypted);
if ($enteredPasswordCrypted eq $enteredPasswordCrypted) {
return $userobj;
} else {
return 0;
}
}
# Used by a CGI when a login is required to proceed beyond a certain point.
# requireLogin() will return a Litmus::User object to indicate that the user
# is logged in, or it will redirect to a login page to allow the login to be
# completed. Once the login is complete, the user will be redirected back to
# $return_to and any parameters in the current CGI.pm object will be passed
# to the new script.
#
sub requireLogin {
my $return_to = shift;
my $cgi = Litmus->cgi();
# see if we are already logged in:
my $user = getCurrentUser();
return $user if $user; # well, that was easy...
my $vars = {
return_to => $return_to,
params => $cgi,
};
print $cgi->header();
Litmus->template()->process("auth/login.html.tmpl", $vars) ||
internalError(Litmus->template()->error());
exit;
}
# Used by a CGI in much the same way as requireLogin() when the user must
# be an admin to proceed.
sub requireAdmin {
my $return_to = shift;
my $cgi = Litmus->cgi();
my $user = requireLogin();
if (!$user || !$user->is_admin()) {
basicError("You must be a Litmus administrator to perform this function.");
}
return $user;
}
# Returns the Litmus::User object corresponding to the current logged-in
# user, or 0 if no valid login cookie exists
sub getCurrentUser() {
my $c = Litmus->cgi();
# we're actually processing the login form right now, so the cookie hasn't
# been sent yet...
if ($curSession) { return $curSession->user_id() }
my $sessionCookie = $c->cookie($logincookiename);
if (! $sessionCookie) { return 0 }
my @sessions = Litmus::DB::Session->search(sessioncookie => $sessionCookie);
my $session = $sessions[0];
if (! $session) { return 0 }
# see if it's still valid and that the user hasn't been disabled
if (! $session->isValid()) { return 0 }
return $session->user_id();
}
#
# ONLY NON-PUBLIC API BEYOND THIS POINT
#
# Processes data from a login form (auth/login.html.tmpl) using data
# from the current Litmus::CGI object in use. When complete, returns the
# flow of control to the script the user wanted to reach before the login,
# setting a login cookie for the user.
sub processLoginForm {
my $c = Litmus->cgi();
my $type = $c->param("login_type");
if ($c->param("accountCreated") &&
$c->param("accountCreated") eq "true") {
# make sure they really are logged in and didn't just set
# the accountCreated flag:
requireLogin("index.cgi");
return; # we're done here
}
if ($type eq "litmus") {
my $username = $c->param("email");
my $password = $c->param("password");
my $user = validate_login($username, $password);
if (!$user) {
loginError($c, "Username/Password incorrect. Please try again.");
}
my $session = makeSession($user);
$c->storeCookie(makeCookie($session));
} elsif ($type eq "newaccount") {
my $email = $c->param("email");
my $name = $c->param("realname");
my $password = $c->param("password");
# some basic form-field validation:
my $emailregexp = q:^[\\w\\.\\+\\-=]+@[\\w\\.\\-]+\\.[\\w\\-]+$:;
if (! $email || ! $email =~ /$emailregexp/) {
loginError($c, "You must enter a valid email address");
}
if (! $password eq $c->param("password_confirm")) {
loginError($c, "Passwords do not match. Please try again.");
}
if (! $name || $name eq "") {
loginError($c, "You must enter your real name (or a pseudonym) to identify yourself");
}
my @users = Litmus::DB::User->search(email => $email);
if ($users[0]) {
loginError($c, "User ".$users[0]->email() ." already exists. Please try again.");
}
my $userobj =
Litmus::DB::User->create({email => $email,
password => bz_crypt($password),
realname => $name,
disabled => 0,
is_admin => 0});
my $session = makeSession($userobj);
$c->storeCookie(makeCookie($session));
my $vars = {
email => $email,
realname => $name,
return_to => $c->param("login_loc"),
params => $c
};
print $c->header();
Litmus->template()->process("auth/accountcreated.html.tmpl", $vars) ||
internalError(Litmus->template()->error());
exit;
} elsif ($type eq "bugzilla") {
my $username = $c->param("username");
my $password = $c->param("password");
} else {
internalError("Unknown login scheme attempted");
}
}
# Given a userobj, process the login and return a session cookie
sub makeSession {
my $userobj = shift;
my $c = Litmus->cgi();
my $expires = localtime() + ONE_DAY * $cookie_expire_days;
my $sessioncookie = randomToken(64);
my $session = Litmus::DB::Session->create({
user_id => $userobj,
sessioncookie => $sessioncookie,
expires => $expires});
$curSession = $session;
return $session;
}
# Given a session, create a login cookie to go with it
sub makeCookie {
my $session = shift;
my $cookie = Litmus->cgi()->cookie(
-name => $logincookiename,
-value => $session->sessioncookie(),
-domain => $main::ENV{"HTTP_HOST"},
-expires => $session->expires(),
);
return $cookie;
}
sub loginError {
my $c = shift;
my $message = shift;
print $c->header();
my $return_to = $c->param("login_loc") || "";
# We need to unset some params in $c since otherwise we end up with
# a hidden form field set for "email" and friends and madness results:
$c->param('email', '');
$c->param('login_type', '');
$c->param('login_loc', '');
$c->param('realname', '');
$c->param('password', '');
$c->param('password_confirm', '');
my $vars = {
return_to => $return_to,
params => $c,
message => "Error: ".$message,
};
Litmus->template()->process("auth/login.html.tmpl", $vars) ||
internalError(Litmus->template()->error());
exit;
}
# Like crypt(), but with a random salt. Thanks to Bugzilla for this.
sub bz_crypt {
my ($password) = @_;
# The list of characters that can appear in a salt. Salts and hashes
# are both encoded as a sequence of characters from a set containing
# 64 characters, each one of which represents 6 bits of the salt/hash.
# The encoding is similar to BASE64, the difference being that the
# BASE64 plus sign (+) is replaced with a forward slash (/).
my @saltchars = (0..9, 'A'..'Z', 'a'..'z', '.', '/');
# Generate the salt. We use an 8 character (48 bit) salt for maximum
# security on systems whose crypt uses MD5. Systems with older
# versions of crypt will just use the first two characters of the salt.
my $salt = '';
for ( my $i=0 ; $i < 8 ; ++$i ) {
$salt .= $saltchars[rand(64)];
}
# Crypt the password.
my $cryptedpassword = crypt($password, $salt);
# Return the crypted password.
return $cryptedpassword;
}
sub randomToken {
my $size = shift || 10; # default to 10 chars if nothing specified
return join("", map{ ('0'..'9','a'..'z','A'..'Z')[rand 62] } (1..$size));
}
# Deprecated:
# DO NOT USE
sub setCookie {
my $user = shift;
my $expires = shift;
@ -77,7 +340,7 @@ sub setCookie {
$expires = '+3d';
}
my $c = new CGI;
my $c = Litmus->cgi();
my $cookie = $c->cookie(
-name => $logincookiename,
@ -89,20 +352,9 @@ sub setCookie {
return $cookie;
}
# Deprecated:
sub getCookie() {
my $c = new CGI;
my $cookie = $c->cookie($logincookiename);
my $user = Litmus::DB::User->retrieve($cookie);
if (! $user) {
return 0;
} else {
unless ($user->userid() == $cookie) {
invalidInputError("Invalid login cookie");
}
}
return $user;
return getCurrentUser();
}
sub istrusted($) {
@ -126,13 +378,18 @@ sub canEdit($) {
#########################################################################
# logout()
#
# Unset the user's cookie, and return them to the main index.
# Unset the user's cookie
#########################################################################
sub logout() {
my $c = new CGI;
my $c = Litmus->cgi();
my $cookie = Litmus::Auth::setCookie(undef,'-1d');
return $cookie;
my $cookie = $c->cookie(
-name => $logincookiename,
-value => '',
-domain => $main::ENV{"HTTP_HOST"},
-expires => '-1d'
);
$c->storeCookie($cookie);
}

View File

@ -0,0 +1,53 @@
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
=head1 COPYRIGHT
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
=cut
package Litmus::BugzillaDBI;
use strict;
use warnings;
use Litmus::Config;
use Litmus::Error;
use Memoize;
use base 'Litmus::DBI';
my $dsn = "dbi:mysql:$Litmus::Config::bugzilla_db:$Litmus::Config::bugzilla_host";
Litmus::BugzillaDBI->set_db('Main',
$dsn,
$Litmus::Config::bugzilla_user,
$Litmus::Config::bugzilla_pass);
1;

72
webtools/litmus/Litmus/CGI.pm Executable file
View File

@ -0,0 +1,72 @@
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
=head1 COPYRIGHT
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
=cut
package Litmus::CGI;
use strict;
use base 'CGI';
# Subclass of CGI.pm that can store cookies in advance and output them
# later when the header() method is called. This feature should probably
# be submitted as a patch to CGI.pm itself.
sub new {
my $class = shift;
my @args = @_;
my $self = $class->SUPER::new(@args);
$self->{'litmusCookieStore'} = [];
return $self;
}
# Stores a cookie to be set later by the header() method
sub storeCookie {
my $self = shift;
my $cookie = shift;
# "we're like kids in a candy shop"
my @cookieStore = @{$self->{'litmusCookieStore'}};
push(@cookieStore, $cookie);
$self->{'litmusCookieStore'} = \@cookieStore;
}
sub header {
my $self = shift;
my @args = @_;
foreach my $cur ($self->{'litmusCookieStore'}) {
push(@args, {-cookie => $cur});
}
$self->SUPER::header(@args);
}
1;

View File

@ -56,7 +56,7 @@ sub rebuildCache {
# whole thing out as javascript with Data::JavaScript
# shield your eyes, we're in for a long trip
my @litmusconfig;
my @products = Litmus::DB::Product->search(enabled => 'Yes');
my @products = Litmus::DB::Product->search(enabled => 1);
my $prodcount = 0;
foreach my $curproduct (@products) {
my $prodrow = \%{$litmusconfig[$prodcount]};

View File

@ -36,7 +36,7 @@ use strict;
do 'localconfig';
our $version = "0.0.2";
our $version = "0.3";
# if true, then Litmus will not accept any requests
our $disabled = 0;

View File

@ -0,0 +1,44 @@
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
=head1 COPYRIGHT
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
=cut
package Litmus::DB::BugzillaUser;
use strict;
use base 'Litmus::BugzillaDBI';
Litmus::DB::BugzillaUser->table('profiles');
Litmus::DB::BugzillaUser->columns(All => qw/userid login_name cryptpassword realname
disabledtext mybugslink refreshed_when extern_id/);
1;

View File

@ -0,0 +1,69 @@
# -*- mode: cperl; c-basic-offset: 8; indent-tabs-mode: nil; -*-
=head1 COPYRIGHT
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
=cut
package Litmus::DB::Session;
use strict;
use base 'Litmus::DBI';
use Time::Piece;
Litmus::DB::Session->table('sessions');
Litmus::DB::Session->columns(All => qw/session_id user_id sessioncookie expires/);
Litmus::DB::Session->has_a(user_id => "Litmus::DB::User");
Litmus::DB::Session->autoinflate(dates => 'Time::Piece');
# expire the current Session object
sub makeExpire {
my $self = shift;
$self->delete();
}
sub isValid {
my $self = shift;
if ($self->expires() <= localtime()) {
$self->makeExpire();
return 0;
}
if ($self->user_id()->disabled() && $self->user_id()->disabled() == 1) {
$self->makeExpire();
return 0;
}
return 1;
}
1;

View File

@ -48,6 +48,7 @@ Litmus::DB::Testresult->columns(All => qw/testresult_id test_id last_updated sub
Litmus::DB::Testresult->column_alias("testresult_id", "testresultid");
Litmus::DB::Testresult->column_alias("test_id", "testid");
Litmus::DB::Testresult->column_alias("test_id", "test");
Litmus::DB::Testresult->column_alias("submission_time", "timestamp");
Litmus::DB::Testresult->column_alias("user_id", "user");
Litmus::DB::Testresult->column_alias("platform_id", "platform");

View File

@ -37,11 +37,26 @@ use base 'Litmus::DBI';
Litmus::DB::User->table('users');
Litmus::DB::User->columns(All => qw/user_id email is_trusted/);
Litmus::DB::User->columns(All => qw/user_id bugzilla_uid email password realname
disabled is_admin/);
Litmus::DB::User->column_alias("user_id", "userid");
Litmus::DB::User->column_alias("is_trusted", "istrusted");
Litmus::DB::User->column_alias("is_admin", "is_trusted");
Litmus::DB::User->has_many(testresults => "Litmus::DB::Testresult");
Litmus::DB::User->has_many(sessions => "Litmus::DB::Session");
Litmus::DB::User->has_a(bugzilla_uid => "Litmus::DB::BugzillaUser");
# returns the crypt'd password from a linked Bugzilla account if it
# exists or the Litmus user account
sub getRealPasswd {
my $self = shift;
if ($self->bugzilla_uid()) {
return $self->bugzilla_uid()->cryptpassword();
} else {
return $self->password();
}
}
1;

197
webtools/litmus/Litmus/DBTools.pm Executable file
View File

@ -0,0 +1,197 @@
# The contents of this file are subject to the Mozilla Public License
# Version 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and
# limitations under the License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
# Utility functions (based on those from Bugzilla's checksetup.pl to
# create new fields and indicies to the schema when upgrading an installation.
package Litmus::DBTools;
use strict;
sub new {
my ($class, $dbh) = @_;
my $self = {};
$self->{'dbh'} = $dbh;
bless($self);
return $self;
}
sub ChangeFieldType
{
my ($self, $table, $field, $newtype) = @_;
my $ref = GetFieldDef($table, $field);
#print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n";
my $oldtype = $ref->[1];
if (! $ref->[2]) {
$oldtype .= qq{ not null};
}
if ($ref->[4]) {
$oldtype .= qq{ default "$ref->[4]"};
}
if ($oldtype ne $newtype) {
print "Updating field type $field in table $table ...\n";
print "old: $oldtype\n";
print "new: $newtype\n";
# 'not null' should be passed as part of the call to ChangeFieldType()
# $newtype .= " NOT NULL" if $$ref[3];
$self->{'dbh'}->do("ALTER TABLE $table
CHANGE $field
$field $newtype");
}
}
sub RenameField
{
my ($self, $table, $field, $newname) = @_;
my $ref = GetFieldDef($table, $field);
return unless $ref; # already fixed?
#print "0: $$ref[0] 1: $$ref[1] 2: $$ref[2] 3: $$ref[3] 4: $$ref[4]\n";
if ($$ref[1] ne $newname) {
print "Updating field $field in table $table ...\n";
my $type = $$ref[1];
$type .= " NOT NULL" if !$$ref[2];
$type .= " auto_increment" if $$ref[5] =~ /auto_increment/;
$self->{'dbh'}->do("ALTER TABLE $table
CHANGE $field
$newname $type");
}
}
sub AddField
{
my ($self, $table, $field, $definition) = @_;
my $ref = GetFieldDef($table, $field);
return if $ref; # already added?
print "Adding new field $field to table $table ...\n";
$self->{'dbh'}->do("ALTER TABLE $table
ADD COLUMN $field $definition");
}
sub DropField
{
my ($self, $table, $field) = @_;
my $ref = GetFieldDef($table, $field);
return unless $ref; # already dropped?
print "Deleting unused field $field from table $table ...\n";
$self->{'dbh'}->do("ALTER TABLE $table
DROP COLUMN $field");
}
# this uses a mysql specific command.
sub TableExists
{
my ($self, $table) = @_;
my @tables;
my $dbtable;
my $exists = 0;
my $sth = $self->{'dbh'}->prepare("SHOW TABLES");
$sth->execute;
while ( ($dbtable) = $sth->fetchrow_array ) {
if ($dbtable eq $table) {
$exists = 1;
}
}
return $exists;
}
sub GetFieldDef
{
my ($self, $table, $field) = @_;
my $sth = $self->{'dbh'}->prepare("SHOW COLUMNS FROM $table");
$sth->execute;
while (my $ref = $sth->fetchrow_arrayref) {
next if $$ref[0] ne $field;
return $ref;
}
}
sub GetIndexDef
{
my ($self, $table, $field) = @_;
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
$sth->execute;
while (my $ref = $sth->fetchrow_arrayref) {
next if $$ref[2] ne $field;
return $ref;
}
}
sub CountIndexes
{
my ($self, $table) = @_;
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
$sth->execute;
if ( $sth->rows == -1 ) {
die ("Unexpected response while counting indexes in $table:" .
" \$sth->rows == -1");
}
return ($sth->rows);
}
sub DropIndexes
{
my ($self, $table) = @_;
my %SEEN;
# get the list of indexes
#
my $sth = $self->{'dbh'}->prepare("SHOW INDEX FROM $table");
$sth->execute;
# drop each index
#
while ( my $ref = $sth->fetchrow_arrayref) {
# note that some indexes are described by multiple rows in the
# index table, so we may have already dropped the index described
# in the current row.
#
next if exists $SEEN{$$ref[2]};
if ($$ref[2] eq 'PRIMARY') {
# The syntax for dropping a PRIMARY KEY is different
# from the normal DROP INDEX syntax.
$self->{'dbh'}->do("ALTER TABLE $table DROP PRIMARY KEY");
}
else {
$self->{'dbh'}->do("ALTER TABLE $table DROP INDEX $$ref[2]");
}
$SEEN{$$ref[2]} = 1;
}
}
1;

View File

@ -40,11 +40,19 @@ use Litmus;
our @ISA = qw(Exporter);
@Litmus::Error::EXPORT = qw(
basicError
invalidInputError
internalError
lastDitchError
);
# just a general run of the mill error
sub basicError {
my $message = shift;
_doError($message);
exit;
}
# used to alert the user when an unexpected input value is found
sub invalidInputError($) {
my $message = shift;

View File

@ -68,7 +68,7 @@ sub setCookie {
my %cookiedata;
my $c = new CGI;
my $c = Litmus->cgi();
my $cookie = $c->cookie(
-name => $configcookiename.'_'.$self->{"product"}->productid(),
-value => join('|', $self->{"product"}->productid(), $self->{"platform"}->platformid(),
@ -81,7 +81,7 @@ sub setCookie {
sub getCookie() {
my $self = shift;
my $c = new CGI;
my $c = Litmus->cgi();
my $product = shift;
my $cookie = $c->cookie($configcookiename.'_'.$product->productid());
@ -165,7 +165,6 @@ sub displayForm {
my @locales = Litmus::DB::Locale->retrieve_all(
{ order_by => 'abbrev' }
);
my $vars = {
locales => \@locales,
products => \@products,
@ -184,7 +183,7 @@ sub displayForm {
$vars->{"defaultlocale"} = $sysconfig->locale();
}
my $cookie = Litmus::Auth::getCookie();
my $cookie = Litmus::Auth::getCurrentUser();
$vars->{"defaultemail"} = $cookie;
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);

View File

@ -52,7 +52,7 @@ sub requireField {
my ($fieldname, $field) = @_;
unless($field && $field ne "---") {
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
invalidInputError("You must make a valid selection for field ".$fieldname.".");
}

View File

@ -30,7 +30,8 @@ templates: index.html.tmpl
return if (\$$name !~ /\.tmpl\$$/); \
\$$name =~ s/templates\/en\/default\///; \
if (-M 'templates/en/default/'.\$$name < -M 'data/templates/en/default/'.\$$name \
|| ! -e 'data/templates/en/default/'.\$$name) { \
|| ! -e 'data/templates/en/default/'.\$$name \
|| -M 'Litmus/Template.pm' < -M 'data/templates/en/default/'.\$$name) { \
system("make", "\$$name"); \
} \
}, no_chdir => 1 }, 'templates/en/default'); \
@ -40,6 +41,12 @@ templates: index.html.tmpl
# requires Exuberant Ctags to be installed (http://ctags.sf.net/)
ctags:
`which ctags` --excmd=number --tag-relative=no --fields=+a+m+n+S -R `pwd`
tags: ctags
test:
perl runtests.pl
$(PERL) runtests.pl
clean:
rm -rf data
make install

View File

@ -40,7 +40,7 @@ use Litmus::FormWidget;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
use diagnostics;
@ -244,13 +244,3 @@ Litmus->template()->process("reporting/advanced_search.tmpl", $vars) ||
internalError(Litmus->template()->error());
exit 0;

View File

@ -41,7 +41,7 @@ use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
my $results;

View File

@ -754,6 +754,7 @@ div.error {
padding: 5px;
}
.skipLink {
position: absolute;
left: -1200px;
@ -959,3 +960,27 @@ div.formButtons {
padding-bottom: 15px;
}
/* 12 Authentication and login **************************************** */
div.loginbox {
background: #DDD;
border: solid #BBB 1px;
padding: 5px;
font-size: 120%;
}
div.login_form {
background: #FF9;
border: solid #BBB 1px;
padding: 5px;
width: 40%;
}
.login_sectitle {
font-weight: bold;
font-size: 120%;
}
.login_important {
color: #F00;
}

View File

@ -44,7 +44,9 @@ use Time::Piece::MySQL;
use diagnostics;
my $c = new CGI;
Litmus::Auth::requireAdmin("edit_categories.cgi");
my $c = Litmus->cgi();
print $c->header();
if ($c->param) {
@ -53,7 +55,7 @@ if ($c->param) {
if ($c->param('add_product')) {
my $name = $c->param('new_product_name');
my $icon_path = $c->param('new_icon_path') || "";
my $enabled = $c->param('new_enabled') ? 'Yes' : 'No';
my $enabled = $c->param('new_enabled') ? 1 : 0;
eval {
$product = Litmus::DB::Product->create({
name => $name,
@ -73,9 +75,9 @@ if ($c->param) {
$product->name($c->param('modify_product_name'));
$product->iconpath($c->param('modify_icon_path'));
if ($c->param('modify_enabled')) {
$product->enabled('Yes');
$product->enabled(1);
} else {
$product->enabled('No');
$product->enabled(0);
}
eval {
$product->update;

View File

@ -52,7 +52,7 @@ my $test_groups = Litmus::FormWidget->getTestGroups();
my $result_statuses = Litmus::FormWidget->getResultStatuses;
my $branches = Litmus::FormWidget->getBranches();
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
my $vars = {
@ -70,9 +70,11 @@ if ($results and scalar @$results > 0) {
$vars->{results} = $results;
}
my $cookie = Litmus::Auth::getCookie();
$vars->{"defaultemail"} = $cookie;
$vars->{"show_admin"} = Litmus::Auth::istrusted($cookie);
my $user = Litmus::Auth::getCurrentUser();
if ($user) {
$vars->{"defaultemail"} = $user;
$vars->{"show_admin"} = $user->is_admin();
}
Litmus->template()->process("index.tmpl", $vars) ||
internalError(Litmus->template()->error());

View File

@ -38,10 +38,10 @@ use diagnostics;
my $title = "Log out";
my $c = new CGI;
my $cookie = Litmus::Auth::logout();
Litmus::Auth::logout();
print $c->header(-cookie => $cookie);
my $c = Litmus->cgi();
print $c->header();
my $vars = {
title => $title,

View File

@ -30,6 +30,7 @@
use strict;
use Getopt::Long;
use Litmus::Config;
use DBI;
$|++;
my $reset_db;
@ -67,31 +68,74 @@ EOT
exit;
}
if ($reset_db) {
my $schema_file = "createdb.sql";
my $data_file = "populatedb.sql";
print "Creating tables...";
my $cmd = "mysql --user=$Litmus::Config::db_user --password=$Litmus::Config::db_pass $Litmus::Config::db_name < $schema_file";
my $rv = system($cmd);
if ($rv) {
die "Error creating database $Litmus::Config::db_name";
}
print "done.\n";
# Create database tables, in large part borrowed from the old-school
# --TABLE-- code in checksetup.pl:
print "\nChecking for missing/new database tables...\n";
our %table;
do 'schema.pl'; # import the schema (%table)
my $dbh = DBI->connect("dbi:mysql:;$Litmus::Config::db_host",
$Litmus::Config::db_user,
$Litmus::Config::db_pass) ||
die "Could not connect to mysql database $Litmus::Config::db_host";
print "Populating tables...";
$cmd = "mysql --user=$Litmus::Config::db_user --password=$Litmus::Config::db_pass $Litmus::Config::db_name < $data_file";
$rv = system($cmd);
my @databases = $dbh->func('_ListDBs');
unless (grep($_ eq $Litmus::Config::db_name, @databases)) {
print "Creating database $Litmus::Config::db_name ...\n";
if (!$dbh->func('createdb', $Litmus::Config::db_name, 'admin')) {
my $error = $dbh->errstr;
die "Could not create database $Litmus::Config::db_name: $error\n";
}
}
$dbh->disconnect if $dbh;
$dbh = DBI->connect(
"dbi:mysql:$Litmus::Config::db_name:$Litmus::Config::db_host",
$Litmus::Config::db_user,
$Litmus::Config::db_pass);
# Get a list of the existing tables (if any) in the database
my $sth = $dbh->table_info(undef, undef, undef, "TABLE");
my @tables = @{$dbh->selectcol_arrayref($sth, { Columns => [3] })};
# go throught our %table hash and create missing tables
while (my ($tabname, $fielddef) = each %table) {
next if grep($_ eq $tabname, @tables);
print "Creating table $tabname ...\n";
$dbh->do("CREATE TABLE $tabname (\n$fielddef\n) TYPE = MYISAM")
or die "Could not create table '$tabname'. Please check your database access.\n";
}
# populate the database with the default data in populatedb.sql:
if ($reset_db) {
my $data_file = "populatedb.sql";
print "Populating tables with default data...";
my $cmd = "mysql --user=$Litmus::Config::db_user --password=$Litmus::Config::db_pass $Litmus::Config::db_name < $data_file";
my $rv = system($cmd);
if ($rv) {
die "Error populating database $Litmus::Config::db_name";
}
print "done.\n";
}
# javascript cache
# UPGRADE THE SCHEMA
# Now we need to deal with upgrading old installations by adding new fields and
# indicies to the schema. To do this, we use the helpful Litmus::DBTools module
# Note that anything changed here should also be added to schema.pl for new
# installations
use Litmus::DBTools;
my $dbtool = Litmus::DBTools->new($dbh);
# for example:
# $dbtool->AddField('tests', 'newfield', 'varchar(64)');
# javascript cache
print "Rebuilding JS cache...";
use Litmus::Cache;
rebuildCache();
require Litmus::Cache;
Litmus::Cache::rebuildCache();
print "done.\n";
exit;

View File

@ -97,7 +97,7 @@ UNLOCK TABLES;
/*!40000 ALTER TABLE `products` DISABLE KEYS */;
LOCK TABLES `products` WRITE;
INSERT INTO `products` VALUES (1,'Firefox','ff.gif','Yes'),(2,'Seamonkey','moz.png','Yes'),(3,'Thunderbird','tb.gif','Yes');
INSERT INTO `products` VALUES (1,'Firefox','ff.gif',1),(2,'Seamonkey','moz.png',1),(3,'Thunderbird','tb.gif',1);
UNLOCK TABLES;
/*!40000 ALTER TABLE `products` ENABLE KEYS */;

View File

@ -42,7 +42,7 @@ use CGI;
use Date::Manip;
use diagnostics;
my $c = new CGI;
my $c = Litmus->cgi();
my $user;
my $sysconfig;
@ -250,4 +250,3 @@ if ($c->param("isSimpleTest")) {
Litmus->template()->process("process/process.html.tmpl", $vars) ||
internalError(Litmus->template()->error());
}

View File

@ -41,7 +41,7 @@ use Time::Piece::MySQL;
my $title = "Run Tests";
my $c = new CGI;
my $c = Litmus->cgi();
if ($c->param("group")) { # display the test screen
page_test();
@ -73,7 +73,6 @@ sub page_sysSetup {
# an area to test:
sub page_pickGroupSubgroup {
my $sysconfig;
my $user;
my $product = Litmus::DB::Product->retrieve($c->param("product"));
if (! $product) {
@ -81,29 +80,24 @@ sub page_pickGroupSubgroup {
invalidInputError("Invalid product ".$c->param("product"));
}
Litmus::Auth::requireLogin("run_tests.cgi");
if ($c->param("continuetesting")) {
# they have already gotten setup and just want to
# pick a new group or subgroup:
$sysconfig = Litmus::SysConfig->getCookie($product);
if (!$sysconfig) {page_pickProduct()};
$user = Litmus::Auth::getCookie();
print $c->header();
} else {
$sysconfig = Litmus::SysConfig->processForm($c);
# get the user id and set a login cookie:
my $email = $c->param("email");
if (!$email) {
print $c->header();
invalidInputError("You must enter your email address so we can track your results and contact you if we have any questions.");
}
$user = Litmus::DB::User->find_or_create(email => $email);
print $c->header(-cookie => [$sysconfig->setCookie(), Litmus::Auth::setCookie($user)]);
# get the user id and set a sysconfig cookie
$c->storeCookie($sysconfig->setCookie());
print $c->header();
}
# get all groups for the product:
my @groups = Litmus::DB::Testgroup->search(product => $sysconfig->product(), obsolete => 'No');
my @groups = Litmus::DB::Testgroup->search(product => $sysconfig->product(), obsolete => 0);
# all possible subgroups per group:
my %subgroups;
@ -120,7 +114,7 @@ sub page_pickGroupSubgroup {
my $vars = {
title => $title,
user => $user,
user => Litmus::Auth::getCurrentUser(),
opsys => $sysconfig->opsys(),
groups => \@groups,
subgroups => \%subgroups,

277
webtools/litmus/schema.pl Executable file
View File

@ -0,0 +1,277 @@
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# the Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
# Litmus database schema
our $table;
$table{branches} =
'branch_id smallint not null primary key auto_increment,
product_id tinyint not null,
name varchar(64) not null,
detect_regexp varchar(255),
index(product_id),
index(name)';
$table{build_type_lookup} =
'build_type_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';
$table{exit_status_lookup} =
'exit_status_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';
$table{locale_lookup} =
'abbrev varchar(16) not null primary key,
name varchar(64) not null,
index(name)';
$table{log_type_lookup} =
'log_type_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';
$table{opsyses} =
'opsys_id smallint not null primary key auto_increment,
platform_id smallint not null,
name varchar(64) not null,
detect_regexp varchar(255),
index(platform_id),
index(name)';
$table{platforms} =
'platform_id smallint not null primary key auto_increment,
product_id tinyint not null,
name varchar(64) not null,
detect_regexp varchar(255),
iconpath varchar(255),
index(product_id),
index(name)';
$table{products} =
'product_id tinyint not null primary key auto_increment,
name varchar(64) not null,
iconpath varchar(255),
enabled tinyint default \'1\',
index(name),
index(enabled)';
$table{subgroups} =
'subgroup_id smallint not null primary key auto_increment,
testgroup_id smallint not null,
name varchar(64) not null,
sort_order smallint(6),
testrunner_group_id int,
index(testgroup_id),
index(name),
index(sort_order),
index(testrunner_group_id)';
$table{test_format_lookup} =
'format_id tinyint not null primary key auto_increment,
name varchar(255) not null,
index(name)';
$table{test_groups} =
'testgroup_id smallint not null primary key auto_increment,
product_id tinyint not null,
name varchar(64) not null,
expiration_days smallint not null,
obsolete tinyint default \'1\',
testrunner_plan_id int,
index(product_id),
index(name),
index(expiration_days),
index(obsolete),
index(testrunner_plan_id)';
$table{test_result_bugs} =
'test_result_id int not null primary key auto_increment,
last_updated datetime not null,
submission_time datetime not null,
user_id int,
bug_id int not null,
index(test_result_id),
index(last_updated),
index(submission_time),
index(user_id)';
$table{test_result_comments} =
'comment_id int not null primary key auto_increment,
test_result_id int not null,
last_updated datetime not null,
submission_time datetime not null,
user_id int,
comment text,
index(test_result_id),
index(last_updated),
index(submission_time),
index(user_id)';
$table{test_result_logs} =
'log_id int not null primary key auto_increment,
test_result_id int not null,
last_updated datetime not null,
submission_time datetime not null,
log_path varchar(255),
log_type_id tinyint not null default \'1\',
index(test_result_id),
index(last_updated),
index(submission_time),
index(log_path),
index(log_type_id)';
$table{test_result_status_lookup} =
'result_status_id smallint not null primary key auto_increment,
name varchar(64) not null,
style varchar(255) not null,
class_name varchar(16),
index(name),
index(style),
index(class_name)';
$table{test_results} =
'testresult_id int not null primary key auto_increment,
test_id int not null,
last_updated datetime,
submission_time datetime,
user_id int,
platform_id smallint,
opsys_id smallint,
branch_id smallint,
buildid varchar(45),
user_agent varchar(255),
result_id smallint,
build_type_id tinyint not null default \'1\',
machine_name varchar(64),
exit_status_id tinyint not null default \'1\',
duration_ms int unsigned,
talkback_id int unsigned,
validity_id tinyint not null default \'1\',
vetting_status_id tinyint not null default \'1\',
locale_abbrev varchar(16) not null default \'en-US\',
index(test_id),
index(last_updated),
index(submission_time),
index(user_id),
index(platform_id),
index(opsys_id),
index(branch_id),
index(user_agent),
index(result_id),
index(build_type_id),
index(machine_name),
index(exit_status_id),
index(duration_ms),
index(talkback_id),
index(validity_id),
index(vetting_status_id),
index(locale_abbrev)';
$table{test_status_lookup} =
'test_status_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';
$table{tests} =
'test_id int not null primary key auto_increment,
subgroup_id smallint not null,
summary varchar(255) not null,
details text,
status_id tinyint not null,
community_enabled tinyint(1),
format_id tinyint not null default \'1\',
regression_bug_id int,
steps longtext,
expected_results longtext,
sort_order smallint not null default \'1\',
author_id int not null,
creation_date datetime not null,
last_updated datetime not null,
version smallint not null default \'1\',
testrunner_case_id int,
testrunner_case_version int,
index(subgroup_id),
index(summary),
index(status_id),
index(community_enabled),
index(regression_bug_id),
index(steps(255)),
index(expected_results(255)),
index(author_id),
index(creation_date),
index(last_updated),
index(testrunner_case_id)';
$table{users} =
'user_id int not null primary key auto_increment,
bugzilla_uid int,
email varchar(255),
password varchar(255),
realname varchar(255),
disabled mediumtext,
is_admin tinyint(1),
index(bugzilla_uid),
index(email),
index(is_admin)';
$table{sessions} =
'session_id int not null primary key auto_increment,
user_id int not null,
sessioncookie varchar(255) not null,
expires datetime not null,
index(user_id),
index(sessioncookie),
index(expires)';
$table{validity_lookup} =
'validity_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';
$table{vetting_status_lookup} =
'vetting_status_id tinyint not null primary key auto_increment,
name varchar(64) not null,
index(name)';

View File

@ -40,7 +40,7 @@ use Litmus::FormWidget;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
use diagnostics;

View File

@ -32,7 +32,7 @@ use Litmus::Auth;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();

View File

@ -39,7 +39,7 @@ use Litmus::Auth;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
# how old of a build do we want to allow? default is 10 days
my $maxbuildage = 10;

View File

@ -40,7 +40,7 @@ use Litmus::DB::Resultbug;
use CGI;
use Date::Manip;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();
if ($c->param && $c->param('id')) {

View File

@ -37,7 +37,7 @@ use Litmus::DB::Product;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();

View File

@ -26,7 +26,7 @@ function updateProductForm() {
document.getElementById("modify_product_icon_path").disabled = false;
document.getElementById("modify_product_icon_path").value = update[2];
document.getElementById("modify_product_enabled").disabled = false;
document.getElementById("modify_product_enabled").checked = update[3] == 'Yes' ? true : false;
document.getElementById("modify_product_enabled").checked = update[3] == 1 ? true : false;
document.getElementById("delete_product").disabled = false;
document.getElementById("update_product").disabled = false;
} else {

View File

@ -0,0 +1,77 @@
[%# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# The Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
#%]
[%# INTERFACE:
# $email - the email address of the new user
# $realname - the real name of the user
# $return_to - where to go after logging in
# $params - CGI.pm object containing parameters to store in hidden fields
#%]
[% INCLUDE global/html_header.tmpl title = 'Account Created' %]
[% INCLUDE global/litmus_header.tmpl title = 'Account Created' %]
[% # Dump CGI data into hidden fields for safekeeping as the user
# logs in
%]
[% BLOCK create_hidden_fields %]
[% IF params %]
[% FOREACH curdata = params.param() %]
[% IF ! params.param(curdata) %] [% NEXT %] [% END %]
<input name="[%curdata FILTER html %]" type="hidden"
value="[% params.param(curdata) FILTER html %]">
[% END %]
[% END %]
[% END %]
<div id="page">
[% INCLUDE sidebar/sidebar.tmpl %]
<div id="content">
<h1 class="breadcrumbs">Account Created</h1>
<h3>Thank you [% realname FILTER html %], your account
[% email FILTER html %] has been created</h3>
<h3>Thank you for helping out
with Mozilla QA</h3>
<form name="form" action="[% return_to FILTER html %]" method="post">
[% INCLUDE create_hidden_fields %]
<input type="hidden" name="accountCreated" value="true" />
<input type="submit" name="submit" value="Continue" />
</form>
</div> <!--END content-->
</div> <!--END page-->
[% INCLUDE global/html_footer.tmpl %]

View File

@ -0,0 +1,139 @@
[%# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (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.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Litmus.
#
# The Initial Developer of the Original Code is
# The Mozilla Corporation.
# Portions created by the Initial Developer are Copyright (C) 2005
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Chris Cooper <ccooper@deadsquid.com>
# Zach Lipton <zach@zachlipton.com>
#
# ***** END LICENSE BLOCK *****
#%]
[%# INTERFACE:
# $return_to - the CGI script to redirect to after the login is complete
# %params - the CGI object containing parameters to pass to the
# script in $redirect_to
# $message (optional) - an error message to display
#%]
[% INCLUDE global/html_header.tmpl title = 'Login' %]
[% INCLUDE global/litmus_header.tmpl title = 'Login' %]
[% # Dump CGI data into hidden fields for safekeeping as the user
# logs in
%]
[% BLOCK create_hidden_fields %]
[% IF params %]
[% FOREACH curdata = params.param() %]
[% IF ! params.param(curdata) %] [% NEXT %] [% END %]
<input name="[%curdata FILTER html %]" type="hidden"
value="[% params.param(curdata) FILTER html %]">
[% END %]
[% END %]
[% END %]
<div id="page">
[% INCLUDE sidebar/sidebar.tmpl %]
<div id="content">
<h1 class="breadcrumbs">Login</h1>
<h1 class="errorHeading">[% message FILTER html %]</h1>
<div class="loginbox">
<div class="login_form">
<h2 class="login_sectitle">Login to your Litmus account:</h2>
<form name="litmus_login" action="[% return_to | none %]" method="post">
<input name="login_type" type="hidden" value="litmus">
<input name="login_loc" type="hidden" value="[% return_to FILTER html %]">
[% INCLUDE create_hidden_fields params=params %]
<table border=0>
<tr>
<td>Email:</td>
<td><input name="email" type="text" size="25"></td>
</tr><tr>
<td>Password:</td>
<td><input name="password" type="password" size="25"></td>
</table>
<br /><input type="submit" name="Submit" value="Login">
</form>
</div>
<div class="login_form" style="position: relative; left: 350px; bottom:143px;">
<h2 class="login_important">New Users: Start Here - </h2>
<h2 class="login_sectitle">Create a new Litmus account:</h2>
<form name="litmus_login" action="[% return_to | none %]" method="post">
<input name="login_type" type="hidden" value="newaccount">
<input name="login_loc" type="hidden" value="[% return_to FILTER html %]">
[% INCLUDE create_hidden_fields params=params %]
<table border=0>
<tr>
<td>Email:</td>
<td><input name="email" type="text" size="25"></td>
</tr><tr>
<td>Real Name:</td>
<td><input name="realname" type="text" size="25"></td>
</tr><tr>
<td>Password:</td>
<td><input name="password" type="password" size="25"></td>
</tr><tr>
<td>Confirm Password:</td>
<td><input name="password_confirm" type="password" size="25"></td>
</table>
<br /><input type="submit" name="Submit" value="Create Account">
</form>
</div>
<!--
<div class="login_form" style="position: relative; left: 350px; bottom:130px;">
<h2 class="login_sectitle">Login to an existing Bugzilla account:</h2>
<form name="litmus_login" action="[% return_to | none %]" method="post">
<input name="login_type" type="hidden" value="bugzilla">
<input name="login_loc" type="hidden" value="[% return_to FILTER html %]">
[% INCLUDE create_hidden_fields params=params %]
<table border=0>
<tr>
<td>Email:</td>
<td><input name="email" type="text" size="25"></td>
</tr><tr>
<td>Password:</td>
<td><input name="password" type="password" size="25"></td>
</table>
<br /><input type="submit" name="Submit" value="Login">
</form>
</div>
-->
</div>
</div> <!--END content-->
</div> <!--END page-->
[% INCLUDE global/html_footer.tmpl %]

View File

@ -131,12 +131,8 @@ System Information
<td><input type="text" id="buildid" name="buildid" size="25" value="[% ua.buildid FILTER html %]"></td>
</tr>
<tr>
<td><div align="right">Email Address:</div></td>
<td><input type="text" id="email" name="email" size="25" value="[% defaultemail.email FILTER html %]"></td>
</tr>
<tr>
<td></td>
<td>PRIVACY NOTICE: Like <a href="https://bugzilla.mozilla.org/">Bugzilla</a>, Litmus is an open system. All testing results you submit will be visible to the public, including the email addresses of those involved. Although steps are taken to hide addresses from email harvesters, the spammers are continually getting better technology and it is almost guaranteed that the address you use with Litmus will get spam. We recommend using a free web email service (such as Yahoo or Hotmail or similar) and not your primary email address if this concerns you. There are solutions being worked on to allow you to hide your email address from other users, but that option is not yet available.
<td>PRIVACY NOTICE: Like <a href="https://bugzilla.mozilla.org/">Bugzilla</a>, Litmus is an open system used by the Mozilla community. All testing results you submit will be visible to the public, including the email addresses of those involved and any comments you enter. Although Litmus takes steps to prevent spammers from harvesting your email address, spam cannot be eliminated entirely. We recommend using a free web email service (such as Yahoo, Gmail, or similar) and not your primary email address if this concerns you. There are solutions being worked on to allow you to hide your email address from other users, but that option is not yet available.
<tr>
<td colspan="2">
<input class="category" type="submit" name="Submit" value="Submit">

View File

@ -149,8 +149,6 @@
<div class="dh">Result:</div>
[% INCLUDE form_widgets/radio_testresults.tmpl test_id=curtest.testid %]
<br/>
<div><input class="button" type="submit" id="Submit[% curtest.testid | html %]" name="Submit[% curtest.testid | html %]" value="Submit This Result" onClick="return confirmPartialSubmission()"/>
</div>
</td>
<td>
@ -169,7 +167,14 @@
</td>
</tr>
</table>
[% IF i < tests.size - 1 %]
<div class="r"><a name="next"
onClick="allStretch.showThisHideOpen(
document.getElementById('t[% i %]-content'), 100, 'height');
allStretch.showThisHideOpen(
document.getElementById('t[% i+1 %]-content'), 100, 'height');">
next</a></div>
[% END %]
</div>
</div>

View File

@ -10,4 +10,6 @@
[% INCLUDE sidebar/widget_legend.tmpl %]
<div class="version">Litmus - [% constants.litmus_version FILTER html %]</div>
</div>

View File

@ -37,7 +37,7 @@ use Litmus::DB::Product;
use CGI;
use Time::Piece::MySQL;
my $c = new CGI;
my $c = Litmus->cgi();
print $c->header();