Bug 506913 - Re-organize account registration page and add fields;

Bug 506952 - Add descriptive/help text to BYOB registration process;
Refactored profile and login models, profile now creates login on registration instead of vice versa;
Reworked DB schema files to start tracking versioned changes;

git-svn-id: https://svn.mozilla.org/projects/byob/trunk@48448 4eb1ac78-321c-0410-a911-ec516a8615a5
This commit is contained in:
lorchard@mozilla.com 2009-08-03 19:05:06 +00:00
Родитель 3b8e9aad34
Коммит b0022b23be
20 изменённых файлов: 773 добавлений и 192 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -8,6 +8,7 @@ Thumbs.db
*.swp *.swp
*.swo *.swo
xfers/ xfers/
bin/exhaust-queue.sh
application/cache/ application/cache/
application/logs/ application/logs/
application/tests/log/ application/tests/log/

Просмотреть файл

@ -0,0 +1,14 @@
--
-- Changes for bug 506913 for registration reorg
--
ALTER TABLE `profiles`
DROP COLUMN `org_address`,
CHANGE COLUMN `full_name` `first_name` varchar(255),
ADD COLUMN `last_name` varchar(128),
ADD COLUMN `is_personal` tinyint(2) NOT NULL default '0',
ADD COLUMN `address_1` varchar(255),
ADD COLUMN `address_2` varchar(255),
ADD COLUMN `city` varchar(255),
ADD COLUMN `state` varchar(32),
ADD COLUMN `zip` varchar(32),
ADD COLUMN `country` varchar(255);

Просмотреть файл

@ -0,0 +1,287 @@
-- MySQL dump 10.11
--
-- Host: localhost Database: byob2_test
-- ------------------------------------------------------
-- Server version 5.0.77-log
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `logevents`
--
DROP TABLE IF EXISTS `logevents`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `logevents` (
`id` int(11) NOT NULL auto_increment,
`uuid` char(64) default NULL,
`profile_id` int(11) default NULL,
`action` varchar(255) default NULL,
`details` text,
`data` text,
`created` datetime default NULL,
PRIMARY KEY (`id`),
KEY `uuid` (`uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `login_email_verification_tokens`
--
DROP TABLE IF EXISTS `login_email_verification_tokens`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `login_email_verification_tokens` (
`id` int(11) NOT NULL auto_increment,
`login_id` int(11) default NULL,
`token` varchar(32) default NULL,
`value` varchar(255) default NULL,
PRIMARY KEY (`id`),
KEY `login_email_verification_tokens_ibfk_2` (`login_id`),
CONSTRAINT `login_email_verification_tokens_ibfk_2` FOREIGN KEY (`login_id`) REFERENCES `logins` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `login_password_reset_tokens`
--
DROP TABLE IF EXISTS `login_password_reset_tokens`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `login_password_reset_tokens` (
`id` int(11) NOT NULL auto_increment,
`login_id` int(11) default NULL,
`token` varchar(32) default NULL,
PRIMARY KEY (`id`),
KEY `login_password_reset_tokens_ibfk_2` (`login_id`),
CONSTRAINT `login_password_reset_tokens_ibfk_2` FOREIGN KEY (`login_id`) REFERENCES `logins` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `logins`
--
DROP TABLE IF EXISTS `logins`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `logins` (
`id` int(11) NOT NULL auto_increment,
`login_name` varchar(64) NOT NULL,
`email` varchar(255) NOT NULL,
`password` varchar(32) NOT NULL,
`created` datetime default NULL,
`last_login` datetime default NULL,
`active` tinyint(2) NOT NULL default '1',
`modified` datetime default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `login_name` (`login_name`),
KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `logins_profiles`
--
DROP TABLE IF EXISTS `logins_profiles`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `logins_profiles` (
`id` int(11) NOT NULL auto_increment,
`login_id` int(11) NOT NULL,
`profile_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `login_id_profile_id` (`login_id`,`profile_id`),
KEY `logins_profiles_ibfk_2` (`profile_id`),
CONSTRAINT `logins_profiles_ibfk_1` FOREIGN KEY (`login_id`) REFERENCES `logins` (`id`) ON DELETE CASCADE,
CONSTRAINT `logins_profiles_ibfk_2` FOREIGN KEY (`profile_id`) REFERENCES `profiles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `message_queue`
--
DROP TABLE IF EXISTS `message_queue`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `message_queue` (
`uuid` varchar(40) NOT NULL,
`owner` varchar(255) default NULL,
`batch_uuid` varchar(40) default NULL,
`batch_seq` int(11) default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
`scheduled_for` datetime default NULL,
`reserved_at` datetime default NULL,
`reserved_until` datetime default NULL,
`finished_at` datetime default NULL,
`priority` int(11) default '0',
`topic` varchar(255) default NULL,
`object` varchar(255) default NULL,
`method` varchar(255) default NULL,
`context` text,
`body` text,
`signature` char(32) default NULL,
PRIMARY KEY (`uuid`),
KEY `created` (`created`),
KEY `priority` (`priority`),
KEY `batch_seq` (`batch_seq`),
KEY `signature` (`signature`),
KEY `reserved_at` (`reserved_at`),
KEY `finished_at` (`finished_at`),
KEY `scheduled_for` (`scheduled_for`),
KEY `batch_uuid` (`batch_uuid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `products`
--
DROP TABLE IF EXISTS `products`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `products` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(32) NOT NULL default 'Firefox',
`version` varchar(32) NOT NULL default '',
`url` varchar(255) NOT NULL default '',
`locales` varchar(255) NOT NULL default 'en-US',
`disable_migration` tinyint(1) default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `profile_attributes`
--
DROP TABLE IF EXISTS `profile_attributes`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `profile_attributes` (
`id` int(11) NOT NULL auto_increment,
`profile_id` int(11) NOT NULL,
`name` varchar(255) default NULL,
`value` text,
PRIMARY KEY (`id`),
UNIQUE KEY `profile_id_name` (`profile_id`,`name`),
CONSTRAINT `profile_attributes_ibfk_1` FOREIGN KEY (`profile_id`) REFERENCES `profiles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `profiles`
--
DROP TABLE IF EXISTS `profiles`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `profiles` (
`id` int(11) NOT NULL auto_increment,
`uuid` varchar(40) NOT NULL,
`screen_name` varchar(64) NOT NULL,
`first_name` varchar(128) NOT NULL,
`last_name` varchar(128) NOT NULL,
`phone` varchar(24) default NULL,
`fax` varchar(24) default NULL,
`org_name` varchar(255) default NULL,
`org_type` varchar(32) default NULL,
`org_type_other` varchar(32) default NULL,
`address_1` varchar(255),
`address_2` varchar(255),
`city` varchar(255),
`state` varchar(32),
`zip` varchar(32),
`country` varchar(255),
`created` datetime default NULL,
`modified` datetime default NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
UNIQUE KEY `screen_name` (`screen_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `profiles_roles`
--
DROP TABLE IF EXISTS `profiles_roles`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `profiles_roles` (
`id` int(11) NOT NULL auto_increment,
`role_id` int(11) NOT NULL,
`profile_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `role_id_profile_id` (`role_id`,`profile_id`),
KEY `profiles_roles_ibfk_2` (`profile_id`),
CONSTRAINT `profiles_roles_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE,
CONSTRAINT `profiles_roles_ibfk_2` FOREIGN KEY (`profile_id`) REFERENCES `profiles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `repacks`
--
DROP TABLE IF EXISTS `repacks`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `repacks` (
`id` int(10) unsigned NOT NULL auto_increment,
`uuid` char(64) NOT NULL default '0',
`created` datetime default NULL,
`modified` datetime default NULL,
`profile_id` int(10) unsigned NOT NULL,
`product_id` int(10) unsigned NOT NULL,
`short_name` varchar(128) default NULL,
`title` varchar(255) default NULL,
`description` text,
`state` int(11) default '0',
`json_data` text,
PRIMARY KEY (`id`),
KEY `created_by` (`profile_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
--
-- Table structure for table `roles`
--
DROP TABLE IF EXISTS `roles`;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
CREATE TABLE `roles` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(32) default NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET character_set_client = @saved_cs_client;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2009-06-26 3:33:57

Просмотреть файл

@ -40,6 +40,8 @@ $lang = array(
'password' => array( 'password' => array(
'default' 'default'
=> 'Password is invalid.', => 'Password is invalid.',
'length'
=> 'Password must be at least 6 characters long.',
'required' 'required'
=> 'Password is required.' => 'Password is required.'
), ),
@ -50,6 +52,8 @@ $lang = array(
=> 'Password and confirmation must match.' => 'Password and confirmation must match.'
), ),
'screen_name' => array( 'screen_name' => array(
'default'
=> 'Screen name is not available.',
'required' 'required'
=> 'Screen name is required.', => 'Screen name is required.',
'length' 'length'
@ -59,11 +63,17 @@ $lang = array(
'isScreenNameAvailable' 'isScreenNameAvailable'
=> 'Screen name is not available.', => 'Screen name is not available.',
), ),
'full_name' => array( 'first_name' => array(
'required' 'required'
=> 'Full name is required', => 'First name is required',
'standard_text' 'standard_text'
=> 'Full name must contain only alphanumeric characters' => 'First name must contain only alphanumeric characters'
),
'last_name' => array(
'required'
=> 'Last name is required',
'standard_text'
=> 'Last name must contain only alphanumeric characters'
), ),
'org_name' => array( 'org_name' => array(
'required' 'required'
@ -99,4 +109,23 @@ $lang = array(
'default' => 'default' =>
'A valid new email confirmation is required' 'A valid new email confirmation is required'
), ),
'phone' => array(
'default' => 'Phone number is required.'
),
'address_1' => array(
'default' => 'Street address is required.'
),
'city' => array(
'default' => 'City is required.'
),
'state' => array(
'default' => 'State is required.'
),
'zip' => array(
'default' => 'Zip is required.'
),
'country' => array(
'default' => 'Country is required.'
),
); );

Просмотреть файл

@ -122,41 +122,4 @@ class Login_Model extends Auth_Login_Model
return $is_valid; return $is_valid;
} }
/**
* Validate registration data
*/
public function validate_registration(&$data)
{
// Force screen name to match login name.
if (isset($data['login_name']))
$data['screen_name'] = $data['login_name'];
$profile_model = new Profile_Model();
$data = Validation::factory($data)
->pre_filter('trim')
->add_rules('login_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($this, 'is_login_name_available'))
->add_rules('email',
'required', 'length[3,255]', 'valid::email',
array($this, 'is_email_available'))
->add_rules('email_confirm',
'required', 'valid::email', 'matches[email]')
->add_rules('password', 'required')
->add_rules('password_confirm', 'required', 'matches[password]')
->add_rules('screen_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($profile_model, 'is_screen_name_available'))
->add_rules('full_name', 'required', 'valid::standard_text')
->add_rules('org_name', 'required')
;
if ('post' == request::method() && !recaptcha::check()) {
$data->add_error('recaptcha', recaptcha::error());
}
return $data->validate();
}
} }

Просмотреть файл

@ -19,20 +19,34 @@ class Profile_Model extends Auth_Profile_Model
public $table_column_titles = array( public $table_column_titles = array(
'id' => 'ID', 'id' => 'ID',
'uuid' => 'UUID', 'uuid' => 'UUID',
'screen_name' => 'Screen name', 'screen_name' => 'Screen name',
'full_name' => 'Full name', 'first_name' => 'First name',
'last_name' => 'Last name',
'phone' => 'Phone', 'phone' => 'Phone',
'fax' => 'Fax', 'fax' => 'Fax',
'org_address' => 'Organization address',
'is_personal' => 'Is personal account',
'org_name' => 'Organization name', 'org_name' => 'Organization name',
'org_type' => 'Organization type', 'org_type' => 'Organization type',
'org_type_other' => 'Organization type (other)', 'org_type_other' => 'Organization type (other)',
'address_1' => 'Street Address 1',
'address_2' => 'Street Address 2',
'city' => 'City',
'state' => 'State',
'zip' => 'Zip / Postal Code',
'country' => 'Country',
'created' => 'Created', 'created' => 'Created',
'modified' => 'Modified', 'modified' => 'Modified',
); );
public $search_column_names = array( public $search_column_names = array(
'screen_name', 'full_name', 'org_name', 'modified', 'screen_name', 'first_name', 'last_name', 'org_name', 'modified',
'org_type', 'org_type_other', 'phone', 'fax',
'address_1', 'address_2', 'city', 'state', 'zip', 'country',
); );
// }}} // }}}
@ -80,6 +94,54 @@ class Profile_Model extends Auth_Profile_Model
} }
/**
* Validate registration data
*/
public function validate_registration(&$data)
{
// Force screen name to match login name.
if (isset($data['login_name']))
$data['screen_name'] = $data['login_name'];
// TODO: Use login model to validate login properties
$login_model = new Login_Model();
$data = Validation::factory($data)
->pre_filter('trim')
->add_rules('login_name',
'required', 'length[4,12]', 'valid::alpha_dash',
array($login_model, 'is_login_name_available'))
->add_rules('email',
'required', 'length[3,255]', 'valid::email',
array($login_model, 'is_email_available'))
->add_rules('email_confirm',
'required', 'valid::email', 'matches[email]')
->add_rules('password', 'length[6,255]', 'required')
->add_rules('password_confirm', 'required', 'matches[password]')
->add_rules('screen_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($this, 'is_screen_name_available'))
->add_rules('first_name', 'required', 'valid::standard_text')
->add_rules('last_name', 'required')
->add_rules('phone', 'required')
->add_rules('website', 'url')
->add_rules('address_1', 'required')
->add_rules('city', 'required')
->add_rules('state', 'required')
->add_rules('zip', 'required')
->add_rules('country', 'required')
;
if (!isset($data['is_personal']) || $data['is_personal'] != 1) {
$data->add_rules('org_name', 'required');
}
if ('post' == request::method() && !recaptcha::check()) {
$data->add_error('recaptcha', recaptcha::error());
}
return $data->validate();
}
/** /**
* Validate form data for profile modification, optionally saving if valid. * Validate form data for profile modification, optionally saving if valid.
*/ */

Просмотреть файл

@ -176,10 +176,8 @@ class Repack_Model extends ManagedORM
break; break;
case 'bookmarks': case 'bookmarks':
$data->add_callbacks('bookmarks_toolbar', $data->add_callbacks('bookmarks_toolbar', array($this, 'extractBookmarks'));
array($this, 'extractBookmarks')); $data->add_callbacks('bookmarks_menu', array($this, 'extractBookmarks'));
$data->add_callbacks('bookmarks_menu',
array($this, 'extractBookmarks'));
break; break;
case 'addons': case 'addons':

Просмотреть файл

@ -1,3 +1,5 @@
<?php slot::set('head_title', 'login'); ?>
<?php slot::set('crumbs', 'account login'); ?>
<?php if (!empty($login_inactive)): ?> <?php if (!empty($login_inactive)): ?>
<p>Sorry, that login has been disabled.</p> <p>Sorry, that login has been disabled.</p>
<?php endif ?> <?php endif ?>

Просмотреть файл

@ -1,32 +1,174 @@
<?php slot::set('head_title', 'register'); ?>
<?php slot::set('crumbs', 'register a new account'); ?>
<?php <?php
echo form::build('register', array('class'=>'register'), array( $state_list = array(
form::fieldset('login details', array('class'=>'login'), array( 'AL'=>"Alabama", 'AK'=>"Alaska", 'AZ'=>"Arizona", 'AR'=>"Arkansas",
form::field('input', 'login_name', 'Login name'), 'CA'=>"California", 'CO'=>"Colorado", 'CT'=>"Connecticut",
form::field('input', 'email', 'Email'), 'DE'=>"Delaware", 'DC'=>"District Of Columbia", 'FL'=>"Florida",
form::field('input', 'email_confirm', 'Email (confirm)'), 'GA'=>"Georgia", 'HI'=>"Hawaii", 'ID'=>"Idaho", 'IL'=>"Illinois",
form::field('password', 'password', 'Password'), 'IN'=>"Indiana", 'IA'=>"Iowa", 'KS'=>"Kansas", 'KY'=>"Kentucky",
form::field('password', 'password_confirm', 'Password (confirm)'), 'LA'=>"Louisiana", 'ME'=>"Maine", 'MD'=>"Maryland",
)), 'MA'=>"Massachusetts", 'MI'=>"Michigan", 'MN'=>"Minnesota",
form::fieldset('personal details', array('class'=>'profile'), array( 'MS'=>"Mississippi", 'MO'=>"Missouri", 'MT'=>"Montana",
form::field('input', 'full_name', 'Full Name'), 'NE'=>"Nebraska", 'NV'=>"Nevada", 'NH'=>"New Hampshire",
form::field('input', 'phone', 'Phone'), 'NJ'=>"New Jersey", 'NM'=>"New Mexico", 'NY'=>"New York",
form::field('input', 'fax', 'Fax'), 'NC'=>"North Carolina", 'ND'=>"North Dakota", 'OH'=>"Ohio",
)), 'OK'=>"Oklahoma", 'OR'=>"Oregon", 'PA'=>"Pennsylvania",
form::fieldset('organization details', array('class'=>'organization'), array( 'RI'=>"Rhode Island", 'SC'=>"South Carolina", 'SD'=>"South Dakota",
form::field('input', 'org_name', 'Name'), 'TN'=>"Tennessee", 'TX'=>"Texas", 'UT'=>"Utah", 'VT'=>"Vermont",
form::field('textarea', 'org_address', 'Address'), 'VA'=>"Virginia", 'WA'=>"Washington", 'WV'=>"West Virginia",
form::field('dropdown', 'org_type', 'Type', array( 'WI'=>"Wisconsin", 'WY'=>"Wyoming"
'options' => array( );
'corp' => 'Corporation',
'nonprofit' => 'Non-Profit', $countries = array(
'other' => 'Other', "United States", "Canada",
) "Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Antigua and
)), Barbuda", "Argentina", "Armenia", "Australia", "Austria", "Azerbaijan",
form::field('input', 'org_type_other', 'Type (other)'), "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium",
)), "Belize", "Benin", "Bhutan", "Bolivia", "Bosnia and Herzegovina",
form::fieldset('finish', array(), array( "Botswana", "Brazil", "Brunei", "Bulgaria", "Burkina Faso", "Burundi",
'<li><label for="recaptcha">Captcha</label><span>' . recaptcha::html() . '</span></li>', "Cambodia", "Cameroon", "Cape Verde", "Central African Republic",
form::field('submit', 'register', null, array('value'=>'Register')), "Chad", "Chile", "China", "Colombi", "Comoros", "Congo (Brazzaville)",
)) "Congo", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech
)); Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "East
Timor (Timor Timur)", "Ecuador", "Egypt", "El Salvador", "Equatorial
Guinea", "Eritrea", "Estonia", "Ethiopia", "Fiji", "Finland", "France",
"Gabon", "Gambia, The", "Georgia", "Germany", "Ghana", "Greece", "Grenada",
"Guatemala", "Guinea", "Guinea-Bissau", "Guyana", "Haiti", "Honduras",
"Hungary", "Iceland", "India", "Indonesia", "Iran", "Iraq", "Ireland",
"Israel", "Italy", "Jamaica", "Japan", "Jordan", "Kazakhstan", "Kenya",
"Kiribati", "Korea, North", "Korea, South", "Kuwait", "Kyrgyzstan", "Laos",
"Latvia", "Lebanon", "Lesotho", "Liberia", "Libya", "Liechtenstein",
"Lithuania", "Luxembourg", "Macedonia", "Madagascar", "Malawi", "Malaysia",
"Maldives", "Mali", "Malta", "Marshall Islands", "Mauritania", "Mauritius",
"Mexico", "Micronesia", "Moldova", "Monaco", "Mongolia", "Morocco",
"Mozambique", "Myanmar", "Namibia", "Nauru", "Nepa", "Netherlands", "New
Zealand", "Nicaragua", "Niger", "Nigeria", "Norway", "Oman", "Pakistan",
"Palau", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
"Poland", "Portugal", "Qatar", "Romania", "Russia", "Rwanda", "Saint Kitts
and Nevis", "Saint Lucia", "Saint Vincent", "Samoa", "San Marino", "Sao
Tome and Principe", "Saudi Arabia", "Senegal", "Serbia and Montenegro",
"Seychelles", "Sierra Leone", "Singapore", "Slovakia", "Slovenia", "Solomon
Islands", "Somalia", "South Africa", "Spain", "Sri Lanka", "Sudan",
"Suriname", "Swaziland", "Sweden", "Switzerland", "Syria", "Taiwan",
"Tajikistan", "Tanzania", "Thailand", "Togo", "Tonga", "Trinidad and
Tobago", "Tunisia", "Turkey", "Turkmenistan", "Tuvalu", "Uganda",
"Ukraine", "United Arab Emirates", "United Kingdom",
"Uruguay", "Uzbekistan", "Vanuatu", "Vatican City", "Venezuela", "Vietnam",
"Yemen", "Zambia", "Zimbabwe"
);
$country_list = array();
foreach ($countries as $country) {
$country_list[$country] = $country;
}
slot::start('login_details_intro');
?>
<div class="intro">
<p>
This is your basic account information, and will be used to login to the Build
Your Own Browser application. Your e-mail address will be used for account
verification, status notifications, and password resets, and must be a valid
address. If you are creating a customized version of Firefox for distribution
by an organization, you must use an email account using that organization's
domain name to ensure your submissions are approved.
</p>
<p class="required_note"><span>*</span> = Required field</p>
</div>
<?php
slot::end();
slot::start('account_details_intro');
?>
<div class="intro">
<p>
We require information about you and, if applicable, the organization you
represent. It helps us understand who is using BYOB, and to give us additional
ways to contact you should the need arise. This information is used solely by
Mozilla, and will never be shared with or sold to anyone else.
</p>
<p class="required_note"><span>*</span> = Required field</p>
</div>
<?php
slot::end();
echo form::build('register', array('class'=>'register'), array(
form::fieldset('Login Details', array('class'=>'login'), array(
'<li>' . slot::get('login_details_intro') . '</li>',
form::field('input', 'login_name', 'Login name', array('class'=>'required'), array(
"Enter the account name you will use to login to BYOB (4-12 ",
"characters in length; alphanumeric, underscore, and hyphens only)"
)),
form::field('input', 'email', 'Email', array('class'=>'required'), array(
)),
form::field('input', 'email_confirm', 'Email (confirm)', array('class'=>'required'), array(
"Enter a vaild email address. Verification will be ",
"required before your account is activated. If you are ",
"representing an organization, you must use an account with ",
"that organization's domain name or your submissions may be ",
"rejected."
)),
form::field('password', 'password', 'Password', array('class'=>'required'), array(
)),
form::field('password', 'password_confirm', 'Password (confirm)', array('class'=>'required'), array(
"Enter your password here. Passwords must be a minimum ",
"of six characters in length. If you forget your password, reset ",
"information will be sent to the email address above."
)),
)),
form::fieldset('Account Details', array('class'=>'account'), array(
'<li>' . slot::get('account_details_intro') . '</li>',
form::field('input', 'first_name', 'First Name', array('class'=>'required'), array(
'Your given name.'
)),
form::field('input', 'last_name', 'Last Name', array('class'=>'required'), array(
'Your surname.'
)),
form::field('checkbox', 'is_personal', 'Personal account?', array('value'=>'1'), array(
"Please check this box if you are using the versions of ",
"Firefox you create for personal use (i.e. sharing with ",
"friends and family, etc.)"
)),
form::field('dropdown', 'org_type', 'Organization Type', array(
'options' => array(
'corp' => 'Corporation',
'nonprofit' => 'Non-Profit',
'other' => 'Other',
),
'class'=>'required'
)),
form::field('input', 'org_type_other', '(other)'),
form::field('input', 'org_name', 'Organization Name', array('class'=>'required'), array(
"Please enter the full, legal name of the organization you represent here."
)),
form::field('input', 'phone', 'Phone', array('class'=>'required'), array(
'Your daytime contact number, with country code (US/Canada is "1").'
)),
form::field('input', 'fax', 'Fax', array(), array(
'Your fax number, with country code (US/Canada is "1")'
)),
form::field('input', 'website', 'Website', array(), array(
'Please provide the URL for your organizational or personal website.'
)),
form::field('input', 'address_1', 'Street Address 1', array('class'=>'required')),
form::field('input', 'address_2', 'Street Address 2'),
form::field('input', 'city', 'City', array('class'=>'required')),
form::field('dropdown', 'state', 'State', array('class'=>'required', 'options'=>$state_list)),
form::field('input', 'zip', 'Zip / Postal Code', array('class'=>'required', 'class'=>'required'), array(
)),
form::field('dropdown', 'country', 'Country', array('class'=>'required', 'options'=>$country_list), array(
"Please provide your current mailing address or, if you are ",
"representing an organization, your organization's mailing address ",
"here."
)),
)),
form::fieldset('finish', array(), array(
'<li class="required"><label for="recaptcha">Captcha</label><span>' . recaptcha::html() . '</span></li>',
form::field('submit', 'register', null, array('value'=>'Register')),
))
));
?> ?>

Просмотреть файл

@ -0,0 +1,24 @@
<?php slot::set('head_title', 'registration successful'); ?>
<h2>Registration successful</h2>
<p>
Your account has been created, but is not yet active. An email containing
information on how to activate your account has been sent to the address you
provided. You must activate your account before you can login to the Build Your
Own Browser application. Please check your email for this information, and
activate your account.
</p>
<p>
If you do not receive this email within twenty-four hours, please contact us at
byob-help@mozilla.com. If you are using any spam prevention software, please
ensure that byob-registration@mozilla.com is whitelisted.
</p>
<p>
You can also use the button below to re-send the account activation email:
</p>
<form action="<?=url::base().'reverifyemail/'.urlencode($login_name)?>" method="POST">
<input type="submit" value="Re-send Account Activation Information" />
</form>

Просмотреть файл

@ -0,0 +1,14 @@
<?php if (!empty($invalid_token)): ?>
<p>Invalid email verification token.</p>
<?php else: ?>
<?php slot::set('head_title', 'account verification successful'); ?>
<h2>Account verification successful</h2>
<p>
Welcome to Mozilla's Build Your Own Browser (BYOB) web application.
Your account has been successfully activated, and we've logged you in.
We've also set a cookie for you that will keep you logged in to the
application, so if you're using a computer that isn't always under your
control, you should log out when you're finished using BYOB by using the
"logout" link in the upper-right corner of every page.
</p>
<?php endif ?>

Просмотреть файл

@ -2,16 +2,24 @@
<?php slot::set('crumbs', 'home') ?> <?php slot::set('crumbs', 'home') ?>
<?php if (!authprofiles::is_logged_in()): ?> <?php if (!authprofiles::is_logged_in()): ?>
<p>
<h2>Welcome!</h2> Welcome to Mozilla's Build Your Own Browser (BYOB) web application, a
web-based tool that allows you to lightly customize versions of Firefox
<p>To get started building your own browser, that you'd like to distribute to other people. To get started, we require
<a href="<?= url::base().'register' ?>">register</a> and that you <a href="<?= url::base().'register' ?>">create an account</a> and
<a href="<?= url::base().'login' ?>">login</a>! <a href="<?= url::base().'login' ?>">login to the application</a>.
All of the customized versions of Firefox you create with BYOB will be
associated with, and accessible through, this account. Registration takes just
a couple of minutes, requires information about you and the organization
you represent, if applicable.
</p>
<?php else: ?> <?php else: ?>
<h2>Welcome back!</h2> <p>
Welcome back to Mozilla's Build Your Own Browser (BYOB) web application, a
web-based tool that allows you to lightly customize versions of Firefox
that you'd like to distribute to other people.
</p>
<p> <p>
<?php <?php

Просмотреть файл

@ -125,6 +125,11 @@ body {
font-size: 0.75em; font-size: 0.75em;
color: #666; color: #666;
} }
#content form fieldset .intro {
border-bottom: 1px solid #ccc;
padding-bottom: 1em;
margin: 0 0 1em 0;
}
#content form ul.errors { #content form ul.errors {
color: red; color: red;
font-size: 0.85em; font-size: 0.85em;
@ -144,14 +149,19 @@ body {
width: 14ex; width: 14ex;
padding-top: 0.125em; padding-top: 0.125em;
font-size: 0.85em; font-size: 0.85em;
margin-right: 1ex; margin-right: 1.5ex;
text-align: right; text-align: right;
} }
#content form ul.fields li.required label { #content form ul.fields li.required label {
font-weight: bold;
} }
#content form ul.fields li.required label:before { #content form ul.fields li.required label:before {
content: "*\00a0"; content: "*\00a0";
color: #f33;
}
#content form ul.fields li .required_note {
}
#content form ul.fields li .required_note span {
color: #f33;
} }
#content form ul li p.notes { #content form ul li p.notes {
margin: 0 0 0 24ex; margin: 0 0 0 24ex;
@ -163,8 +173,16 @@ body {
#content form ul.fields li.error input { #content form ul.fields li.error input {
background-color: #ffeeee; background-color: #ffeeee;
} }
.ctrl_auth_act_register #content form ul.fields li label { .ctrl_auth_profiles_act_register #content form ul.fields li label {
width: 18ex; width: 24ex;
}
.ctrl_auth_profiles_act_register #content form ul.fields li .notes {
margin: 0.5em 0 1em 30ex;
width: 40%;
}
.ctrl_auth_profiles_act_register #content form ul.fields li .recaptchatable #recaptcha_response_field {
position: inherit !important;
bottom: 0px !important;
} }
#content form ul.fields li.text input, #content form ul.fields li.password input { #content form ul.fields li.text input, #content form ul.fields li.password input {
width: 50ex; width: 50ex;
@ -478,3 +496,4 @@ body {
text-align: center; text-align: center;
} }

Просмотреть файл

@ -85,8 +85,8 @@ BYOB_Main = function() {
}); });
$('form .organization').each(function() { $('form .account').each(function() {
$this.wireUpHideShowOrgTypeOther($(this)); $this.wireUpHideShowOrg($(this));
}); });
return this; return this;
@ -96,7 +96,23 @@ BYOB_Main = function() {
* Wire up the type (other) input field to appear when "other" is * Wire up the type (other) input field to appear when "other" is
* selected in the associated drop down. * selected in the associated drop down.
*/ */
wireUpHideShowOrgTypeOther: function(org_fieldset) { wireUpHideShowOrg: function(org_fieldset) {
// Enable / disable organization fields on personal checkbox toggle.
var org_personal = org_fieldset.find('#is_personal');
var personal_cb = function(ev) {
var checked = ( $('#is_personal:checked').length > 0 );
$.each([ 'org_name', 'org_type', 'org_type_other' ], function() {
if (checked) {
$('#'+this).attr('disabled', true)
.parent().removeClass('required');
} else {
$('#'+this).removeAttr('disabled');
$('#'+this).parent().addClass('required');
}
});
};
org_personal.each(personal_cb).change(personal_cb);
var org_type_other = org_fieldset.find('#org_type_other'); var org_type_other = org_fieldset.find('#org_type_other');

Просмотреть файл

@ -27,7 +27,8 @@ class Auth_Profiles_Controller extends Local_Controller
} }
} }
$this->login_model = new Login_Model(); $this->login_model = new Login_Model();
$this->profile_model = new Profile_Model();
} }
/** /**
@ -62,32 +63,28 @@ class Auth_Profiles_Controller extends Local_Controller
public function register() public function register()
{ {
$form_data = form::validate( $form_data = form::validate(
$this, $this->login_model, $this, $this->profile_model,
'validate_registration', 'form_errors_auth' 'validate_registration', 'form_errors_auth'
); );
if (null===$form_data) return; if (null===$form_data) return;
$new_login = $this->login_model $new_profile = $this->profile_model
->register_with_profile($form_data); ->register_with_login($form_data);
if (!empty($new_login->email_verification_token)) { if (!empty($new_profile->email_verification_token)) {
email::send_view( email::send_view(
$new_login->new_email, $new_profile->new_email,
'auth_profiles/register_email', 'auth_profiles/register_email',
array( array(
'email_verification_token' => 'email_verification_token' =>
$new_login->email_verification_token, $new_profile->email_verification_token,
'login_name' => 'login_name' =>
$new_login->login_name $new_profile->login_name
) )
); );
} }
Session::instance()->set_flash( $this->view->login_name = $new_profile->login_name;
'message', $this->view->set_filename('auth_profiles/register_success');
'Check your email to verify your address before login.'
);
return url::redirect('login');
} }
/** /**
@ -233,6 +230,8 @@ class Auth_Profiles_Controller extends Local_Controller
) )
); );
$this->view->email_sent = TRUE; $this->view->email_sent = TRUE;
$this->view->login_name = $params['login_name'];
$this->view->set_filename('auth_profiles/register_success');
} }
$this->view->login = $login; $this->view->login = $login;
@ -243,6 +242,11 @@ class Auth_Profiles_Controller extends Local_Controller
*/ */
public function verifyemail() public function verifyemail()
{ {
if (false !== $this->input->get('success', false)) {
$this->view->valid_token = true;
return;
}
$token = ('post' == request::method()) ? $token = ('post' == request::method()) ?
$this->input->post('email_verification_token') : $this->input->post('email_verification_token') :
$this->input->get('email_verification_token'); $this->input->get('email_verification_token');
@ -253,21 +257,13 @@ class Auth_Profiles_Controller extends Local_Controller
$this->view->invalid_token = true; $this->view->invalid_token = true;
return; return;
} }
$login->change_email($new_email); //$login->change_email($new_email);
// TODO: Make auto-login on email verification configurable? // TODO: Make auto-login on email verification configurable?
if (!authprofiles::is_logged_in()) { $profile = $login->find_default_profile_for_login();
$login->login();
$profile = $login->find_default_profile_for_login(); authprofiles::login($login->login_name, $login, $profile);
$login->login(); url::redirect(url::current().'?success=true');
authprofiles::login($login->login_name, $login, $profile);
Session::instance()->set_flash(
'message',
'Email address verified. Welcome!'
);
return url::redirect('/home');
}
} }

Просмотреть файл

@ -88,44 +88,6 @@ class Auth_Login_Model extends ORM
return true; return true;
} }
/**
* Create a new login and associated profile.
*/
public function register_with_profile($data, $force_email_verified=false)
{
$profile_data = array(
'login_name' => $data['login_name'],
'email' => ($force_email_verified) ? $data['email'] : '',
'created' => gmdate('c', time())
);
$new_login = ORM::factory('login')->set($profile_data)->save();
$new_login->change_password($data['password']);
$profile_data['id'] = $new_login->id;
if (!$force_email_verified) {
$profile_data['new_email'] = $data['email'];
$profile_data['email_verification_token'] =
$new_login->set_email_verification_token($data['email']);
}
$new_profile = ORM::factory('profile')->set($data)->save();
$new_login->add($new_profile);
$new_login->save();
$data = array(
'login' => $new_login->as_array(),
'profile' => $new_profile->as_array()
);
Event::run('auth_profiles.registered', $data);
return arr::to_object($profile_data);
}
/** /**
* Find the default profile for this login, usually the first registered. * Find the default profile for this login, usually the first registered.
* @TODO: Change point for future multiple profiles per login * @TODO: Change point for future multiple profiles per login
@ -307,38 +269,6 @@ class Auth_Login_Model extends ORM
} }
/**
* Replace incoming data with registration validator and return whether
* validation was successful.
*
* @param array Form data to validate
* @return boolean Validation success
*/
public function validate_registration(&$data)
{
$profile_model = new Profile_Model();
$data = Validation::factory($data)
->pre_filter('trim')
->add_rules('login_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($this, 'is_login_name_available'))
->add_rules('email',
'required', 'length[3,255]', 'valid::email',
array($this, 'is_email_available'))
->add_rules('email_confirm',
'required', 'valid::email', 'matches[email]')
->add_rules('password', 'required')
->add_rules('password_confirm', 'required', 'matches[password]')
->add_rules('screen_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($profile_model, 'is_screen_name_available'))
->add_rules('full_name', 'required', 'valid::standard_text')
->add_rules('captcha', 'required', 'Captcha::valid')
;
return $data->validate();
}
/** /**
* Replace incoming data with login validator and return whether * Replace incoming data with login validator and return whether
* validation was successful. * validation was successful.

Просмотреть файл

@ -95,6 +95,76 @@ class Auth_Profile_Model extends ORM
} }
/**
* Create a new login and associated profile.
*/
public function register_with_login($data, $force_email_verified=false)
{
$profile_data = array(
'login_name' => $data['login_name'],
'email' => ($force_email_verified) ? $data['email'] : '',
'created' => gmdate('c', time())
);
$new_login = ORM::factory('login')->set($profile_data)->save();
$new_login->change_password($data['password']);
$profile_data['id'] = $new_login->id;
if (!$force_email_verified) {
$profile_data['new_email'] = $data['email'];
$profile_data['email_verification_token'] =
$new_login->set_email_verification_token($data['email']);
}
$new_profile = ORM::factory('profile')->set($data)->save();
$new_login->add($new_profile);
$new_login->save();
$data = array(
'login' => $new_login->as_array(),
'profile' => $new_profile->as_array()
);
Event::run('auth_profiles.registered', $data);
return arr::to_object($profile_data);
}
/**
* Replace incoming data with registration validator and return whether
* validation was successful.
*
* @param array Form data to validate
* @return boolean Validation success
*/
public function validate_registration(&$data)
{
$login_model = new Login_Model();
$data = Validation::factory($data)
->pre_filter('trim')
->add_rules('login_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($this, 'is_login_name_available'))
->add_rules('email',
'required', 'length[3,255]', 'valid::email',
array($this, 'is_email_available'))
->add_rules('email_confirm',
'required', 'valid::email', 'matches[email]')
->add_rules('password', 'required')
->add_rules('password_confirm', 'required', 'matches[password]')
->add_rules('screen_name',
'required', 'length[3,64]', 'valid::alpha_dash',
array($profile_model, 'is_screen_name_available'))
->add_rules('full_name', 'required', 'valid::standard_text')
->add_rules('captcha', 'required', 'Captcha::valid')
;
return $data->validate();
}
/** /**
* Validate form data for profile creation, optionally saving it if valid. * Validate form data for profile creation, optionally saving it if valid.
*/ */

Просмотреть файл

@ -0,0 +1 @@
Check your email to verify your address before login.

Просмотреть файл

@ -121,6 +121,10 @@ class form extends form_Core
{ {
if (null == $params) $params = array(); if (null == $params) $params = array();
$params_class = (isset($params['class'])) ?
$params['class'] : '';
unset($params['class']);
if ('checkbox' == $type) { if ('checkbox' == $type) {
// For checkboxes, the checked attribute is the significant thing. // For checkboxes, the checked attribute is the significant thing.
$value = form::value($name, @$params['checked']); $value = form::value($name, @$params['checked']);
@ -137,9 +141,8 @@ class form extends form_Core
} else { } else {
$classes = array($type); $classes = array($type);
if (!empty($params['class'])) { if (!empty($params_class)) {
$classes[] = $params['class']; $classes[] = $params_class;
unset($params['class']);
} }
if ('checkbox' == $type) { if ('checkbox' == $type) {
@ -180,6 +183,8 @@ class form extends form_Core
// List item classes consist of the name of the field type, along // List item classes consist of the name of the field type, along
// with 'error' if field present in $form::errors // with 'error' if field present in $form::errors
$classes = array($type); $classes = array($type);
if (!empty($params_class))
$classes[] = $params_class;
if ('input' == $type) if ('input' == $type)
$classes[] = isset($params['type']) ? $params['type'] : 'text'; $classes[] = isset($params['type']) ? $params['type'] : 'text';
if (!empty(self::$errors) && array_key_exists($name, self::$errors)) if (!empty(self::$errors) && array_key_exists($name, self::$errors))