GET
method.
+ *
+ * @param request servlet request
+ * @param response servlet response
+ *
+ * @throws ServletException if a servlet-specific error occurs
+ * @throws IOException if an I/O error occurs
+ */
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response)
+ throws ServletException, IOException {
+
+ LOG.debug("Requesting child privacy page");
+
+ request.getRequestDispatcher(
+ "WEB-INF/servlet/coppa/privacy-policy.jsp")
+ .forward(request, response);
+ }
+
+ /**
+ * Returns a short description of the servlet.
+ *
+ * @return a String containing servlet description
+ */
+ @Override
+ public String getServletInfo() {
+ return "Parallax COPPA policy page";
+ }// + * This retrieves the form values and pass them on to another method + * that validates the information and creates the user account. + *
+ * The register() method will throw an exception if the data collected from + * the form is considered invalid. The exact exception depends on the nature + * of the error. For example, null values in required fields will throw a + * NullPointerException or a poorly formatted email address will cause a + * IllegalStateException to be thrown. + *
+ * This method traps those exceptions and resubmits the form with a helpful + * error message to guide the user to a successful registration experience. + * Each exception contains a mnemonic that is used here to identify the + * exact error. The exception handler in this method then sets an attribute + * on the form and resubmits it. The form is then presented to the user to + * provide him or her with an opportunity to correct the data and resubmit + * the registration. + *
+ *
+ * @param req
+ * @param resp
+ * @throws ServletException
+ * @throws IOException
*/
@Override
- protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ protected void doPost(
+ HttpServletRequest req,
+ HttpServletResponse resp) throws ServletException, IOException {
+
resp.setContentType("application/json");
String screenname = Strings.emptyToNull(req.getParameter("screenname"));
String email = Strings.emptyToNull(req.getParameter("email"));
String password = Strings.emptyToNull(req.getParameter("password"));
String confirmPassword = Strings.emptyToNull(req.getParameter("confirmpassword"));
+ String birthMonth = Strings.emptyToNull(req.getParameter("bdmonth"));
+ String birthYear = Strings.emptyToNull(req.getParameter("bdyear"));
+ String sponsorEmail = Strings.emptyToNull(req.getParameter("sponsoremail"));
+ String sponsorEmailType = Strings.emptyToNull(req.getParameter("sponsoremailtype"));
+
+ LOG.info("Raw screen name: {}", screenname);
+ LOG.info("Raw sponsor email address is: {}", sponsorEmail);
+ // Clean up any possible null fields
req.setAttribute("screenname", screenname == null ? "" : screenname);
req.setAttribute("email", email == null ? "" : email);
+ req.setAttribute("bdmonth", birthMonth == null ? "" : birthMonth );
+ req.setAttribute("bdyear", birthYear == null ? "" : birthYear );
+ req.setAttribute("sponsoremail", sponsorEmail == null ? "" : sponsorEmail);
+ req.setAttribute("sponsoremailtype", sponsorEmailType == null ? "0" : sponsorEmailType);
+
+ // Log a few things
+ LOG.info("Registering screen name: {}", screenname);
+ LOG.info("Registering email: {}", email);
+ LOG.info("Registering month: {}", birthMonth);
+ LOG.info("Registering year: {}", birthYear);
+ LOG.info("Registering sponsor email: {}", sponsorEmail);
+ LOG.info("Registering sponsor type selection: {}", sponsorEmailType);
+ LOG.info("Checking REQ Year setting: {}",req.getAttribute("bdmonth"));
+
Long idUser;
+
try {
- idUser = securityService.register(screenname, email, password, confirmPassword);
+ LOG.info("Calling securityService.register()");
+ // Validate the collected information
+ idUser = securityService.register(
+ screenname, email, password, confirmPassword,
+ Integer.parseInt(birthMonth),
+ Integer.parseInt(birthYear),
+ sponsorEmail,
+ Integer.parseInt(sponsorEmailType));
+
+ LOG.info("Returning from register(). ID assigned is: {}", idUser);
+
if (idUser != null && idUser > 0) {
+ LOG.info("Transfering to registered.jsp");
req.getRequestDispatcher("WEB-INF/servlet/register/registered.jsp").forward(req, resp);
} else {
+ LOG.info("Returning to the user registration page");
req.setAttribute("error", true);
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
}
} catch (NonUniqueEmailException ex) {
+ LOG.warn("Attempt to register already assigned email: {}", email);
req.setAttribute("emailAlreadyUsed", true);
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
} catch (PasswordVerifyException ex) {
+ LOG.info("Paswword mismatch");
req.setAttribute("passwordsDontMatch", true);
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
} catch (NullPointerException npe) {
- req.setAttribute("missingFields", true);
+ LOG.warn("Null Pointer Exception. Data is: {}", npe.getMessage());
+
+ // Figure out which data field triggered the exception
+ switch(npe.getMessage()) {
+ case "ScreenNameNull":
+ LOG.warn("Null screen name trigger exception");
+ req.setAttribute("ScreenNameNull", true);
+ break;
+
+ case "PasswordIsNull":
+ LOG.warn("Null password trigger exception");
+ req.setAttribute("PasswordIsNull", true);
+ break;
+
+ case "PasswordConfirmIsNull":
+ LOG.warn("Null confirmation password trigger exception");
+ req.setAttribute("PasswordConfirmIsNull", true);
+ break;
+
+ case "BirthMonthNull":
+ LOG.warn("Null birth month trigger exception");
+ req.setAttribute("BirthMonthNull", true);
+ break;
+
+ case "BirthYearNull":
+ LOG.warn("Null birth yeartrigger exception");
+ req.setAttribute("BirthYearNull", true);
+ break;
+
+ case "UserEmailNull":
+ LOG.warn("Null user email");
+ req.setAttribute("UserEmailNull", true);
+ break;
+
+ case "SponsorEmailNull":
+ LOG.warn("Null sponsor email");
+ req.setAttribute("SponsorEmailNull", true);
+ break;
+
+ default:
+ LOG.warn("Unknown exception trigger");
+ req.setAttribute("missingFields", true);
+ }
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
+
} catch (PasswordComplexityException pce) {
+ LOG.info("Paswword is not complex enough");
req.setAttribute("passwordComplexity", true);
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
} catch (ScreennameUsedException sue) {
+ LOG.info("Attempt to use already assigned screen name: {}", screenname);
req.setAttribute("screennameUsed", true);
req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
+ } catch (IllegalStateException ex) {
+ LOG.warn("Exception message is: {}", ex.getMessage());
+
+ if ("BirthMonthNotSet".equals(ex.getMessage())) {
+ req.setAttribute("BirthMonthNotSet", true);
+ }
+
+ // Birth year is still at the default. Infants probably are not users
+ else if ("BirthYearNotSet".equals(ex.getMessage())) {
+ req.setAttribute("BirthYearNotSet", true);
+ }
+
+ // Set the on-screen error message numonic. Code in the
+ // register.jsp page will map the numonic to a language sting and
+ // display that to the user
+ else if ("SponsorEmail".equals(ex.getMessage())) {
+ req.setAttribute("sponsorEmailMalformed", true);
+ }
+
+ else // User email issue
+ {
+ req.setAttribute("emailMalformed", true);
+ }
+ req.getRequestDispatcher("WEB-INF/servlet/register/register.jsp").forward(req, resp);
+ }
+ }
+
+ private String getMonthFromDate(String date) {
+ String[] dates = date.split("/");
+ int month = 0;
+
+ try {
+ if (dates.length == 2) {
+ month = Integer.parseInt(dates[0]);
+ }
+ }
+ catch (NumberFormatException ex) {
+ month = 0;
+ }
+
+ return Integer.toString(month);
+ }
+
+ private String getYearFromDate(String date) {
+ String[] dates = date.split("/");
+ int year = 0;
+
+ try {
+ if (dates.length == 2) {
+ year = Integer.parseInt(dates[1]);
+ }
+ }
+ catch (NumberFormatException ex) {
+ year = 0;
}
+
+ return Integer.toString(year);
}
}
diff --git a/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties b/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties
index e371b14c..fc0adec9 100644
--- a/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties
+++ b/src/main/resources/com/parallax/server/blocklyprop/internationalization/translations.properties
@@ -6,9 +6,6 @@ logout = Logout
cancel = Cancel
back = Back
-password.complexity = The password should be at least 8 characters long
-password.complexity.error = Password is not complex enough
-
error.generic = A problem occurred
error.unknownemail = Unknown email
@@ -21,6 +18,7 @@ menu.help = Help
menu.newproject.title = New project
menu.newproject.spin = Scribbler Robot
menu.newproject.c = Propeller C
+menu.privacy = Privacy Policy
footer.licenselink = License
footer.changelog = Change log
@@ -29,8 +27,8 @@ footer.clientdownloadlink = BlocklyProp-client
# Application version numbers.
application.major = 0
-application.minor = 96
-application.build = 410
+application.minor = 97
+application.build = 419
html.content_missing = Content missing
@@ -39,6 +37,8 @@ clientdownload.showall = Show clients for all operating systems
clientdownload.client.macos.installer = MacOS client installer
clientdownload.client.windows32.installer = Windows 7/8/8.1/10 (32-bit) client installer
clientdownload.client.windows64.installer = Windows 7/8/8.1/10 (64-bit) client installer
+clientdownload.client.chromeos.installer = Add to Chrome
+clientdownload.client.chromeos.alreadyinstalled = BlocklyProp Launcher is already installed. Make sure it is open and running.
help.title = Help
help.not-found = Help file not found
@@ -58,9 +58,9 @@ home.spin_project.title = S3 Robot Project
home.spin_project.newlink = New
oauth.new-user = New user
-oauth.new-user.screenname = Screenname
+oauth.new-user.screenname = Screen Name
oauth.new-user.do.submit = Save
-oauth.new-user.error.screenname = Screenname already in use
+oauth.new-user.error.screenname = Screen Name already in use
oauth.success = User logged in
not_loggedin.title = You are not logged in
@@ -70,16 +70,16 @@ not_loggedin.try.trylink = Try editor
not_loggedin.try.viewprojectlink = View project
not_loggedin.login.title = Log in
not_loggedin.login.loginlink = Login
-not_loggedin.login.registerlink = Register
+not_loggedin.login.registerlink = Register a new account
my_project.list.title = My projects
project.list.title = Community projects
project.title = Project
project.details_title = Project details
-project.name = Name
-project.user = User
-project.board = Board
+project.name = Project Name
+project.user = User Screen Name
+project.board = Board Type
project.created = Created On
project.modified = Last Modified
project.description = Description
@@ -87,12 +87,13 @@ project.share-link = Share project using link
project.sharing = Sharing
project.sharing.private = Private
project.sharing.shared = Shared
-# TODO change translation to "Friends" once implemented
-project.sharing.friends = Default
+project.sharing.tooltip.shared = Make project visible to other users
+project.sharing.tooltip.private = Hide project from other users
#project.openlink = Open project
project.viewcode = View project code
project.clonelink = Clone
project.savelink = Save
+project.saveaslink = Save As
project.deletelink = Delete
project.delete.confirm.title = Confirm delete
project.delete.confirm = Are you sure you want to delete this project?
@@ -105,7 +106,7 @@ project.changed = Project changes have been saved
project.created = Created
project.modified = Modified
-project.create.title = Your project
+project.create.title = New project
project.create.basic = Basic info
project.create.basic.title = Basic project info
project.create.project_name = Project name
@@ -128,14 +129,19 @@ confirm.request.title = Email confirm request
confirm.request.email = Email:
confirm.request.submit = Request
confirm.requested = Please check your email
+
confirm.do.title = Please confirm
confirm.do.error.invalid_combination = Invalid token/email combination
confirm.do.email = Email:
confirm.do.token = Token:
confirm.do.submit = Confirm
+
confirm.error.requested_too_often = Confirm requested too often
confirm.error.wrong_authentication_source = Email confirm is not for users using third party authentication sources.
+password.complexity = The password should be at least 8 characters long
+password.complexity.error = Password is not complex enough
+
password_reset.request.title = Request password reset
password_reset.request.email = Email:
password_reset.request.submit = Confirm
@@ -151,17 +157,35 @@ password_reset.error.requested_too_often = Reset requested too often
password_reset.error.wrong_authentication_source = Password reset is not for users using third party authentication sources.
register.do.title = Register
-register.do.screenname = Screenname:
+register.do.screenname = Screen name:
register.do.email = Email:
register.do.password = Password:
register.do.confirm_password = Confirm password:
+register.do.birth.month = Birth Month:
+register.do.birth.year = Birth Year:
+register.do.sponsor.email = Parent (or Teacher) email:
+register.do.sponsor.emailtype = Select one:
+register.do.coppa.msg0 = Why are we asking?
+register.do.coppa.msg1 = To protect your privacy, especially if you are one of our younger users, US COPPA regulations require that we determine your age. More information is available
+register.do.coppa.msg2 = here
register.do.submit = Register
register.done.title = Registration successful
register.done.text = Please check your email to confirm your email account.
+
+register.error.generic = Something has gone wrong while registering your account. Support has been notified.
+register.error.birth_month_not_selected = Please select the month of your birthday
+register.error.birth_year_not_selected = Please select the year of your birthday
+register.error.user_email_empty = Please enter your email address
register.error.email_already_used = Email already used
+register.error.email_format_error = The email address is not formatted correctly
+register.error.sponsor_email_empty = Please enter a sponsor email address. Ask a parent or teacher if you can use their email address
+register.error.sponsor_email_format_error = The sponsor email address is not formatted correctly
+register.error.password_empty = Please provide a password to secure your account
+register.error.password_confirm_empty = Please enter your password twice to ensure it matches
register.error.passwords_dont_match = Passwords don't match
register.error.missing_fields = All fields must be completed
-register.error.screenname_used = Screenname already in use
+register.error.screenname_empty = Please enter a screen name
+register.error.screenname_used = Screen name already in use
profile.title = Profile
profile.unlock.error = Could not unlock your profile
@@ -188,7 +212,7 @@ public-profile.nav.projects = Projects
public-profile.friends = Friends
public-profile.projects = Projects
-login.registerlink = Register now!
+login.registerlink = Register a new account
login.title = Please Log in
login.email = Email:
login.password = Password:
@@ -217,11 +241,13 @@ editor.project = Project
editor.edit-details = Edit Project details
editor.save = Save
editor.save-as = Save project as
+editor.save-check = Save project reminder
+editor.save-check.warning = It has been 20 minutes since you last saved your project. Save now?
editor.client.title = BlocklyPropClient
-editor.client.checking = Looking for BlocklyPropClient
-editor.client.available = Select the correct port, then click or .
-editor.client.available.short = Select the correct port, then click .
-editor.client.not-available = BlocklyPropClient is not running
+editor.client.checking = \u2139 Looking for BlocklyPropClient
+editor.client.available = Select the correct port, then click or .
+editor.client.available.short = Select the correct port, then click .
+editor.client.not-available = \u26a0 BlocklyPropClient is not running
editor.download = Download blocks file
editor.upload = Upload blocks file
editor.upload.selectfile = Select File
diff --git a/src/main/resources/documents/client-instructions.textile b/src/main/resources/documents/client-instructions.textile
index fe152933..77b0c047 100644
--- a/src/main/resources/documents/client-instructions.textile
+++ b/src/main/resources/documents/client-instructions.textile
@@ -1,4 +1,4 @@
To download code to your Propeller, BlocklyProp requires that the BlocklyPropClient software is installed and connected on your computer.
* If BlocklyPropClient is already installed, run it and click the "Connect" button, then return to this editor
-* If BlocklyPropClient is not installed, download and install it using the link for your computer type below
\ No newline at end of file
+* If BlocklyPropClient is not installed, download and install it using the link for your computer below
\ No newline at end of file
diff --git a/src/main/resources/documents/confirm/already-confirmed.textile b/src/main/resources/documents/confirm/already-confirmed.textile
index 47d74be8..393a1930 100644
--- a/src/main/resources/documents/confirm/already-confirmed.textile
+++ b/src/main/resources/documents/confirm/already-confirmed.textile
@@ -1,3 +1,4 @@
-h2. Email already confirmed
+h3. Email already confirmed
+This account has already been activated.
"Go to home":/blockly/index
diff --git a/src/main/resources/documents/confirm/confirm-requested.textile b/src/main/resources/documents/confirm/confirm-requested.textile
index c256160e..6ae60e6e 100644
--- a/src/main/resources/documents/confirm/confirm-requested.textile
+++ b/src/main/resources/documents/confirm/confirm-requested.textile
@@ -1,3 +1,3 @@
-h2. Email confirm requested
+h3. Email confirm requested
"Go to home":/blockly/index
diff --git a/src/main/resources/documents/confirm/confirmed.textile b/src/main/resources/documents/confirm/confirmed.textile
index f67f82b7..4a66b225 100644
--- a/src/main/resources/documents/confirm/confirmed.textile
+++ b/src/main/resources/documents/confirm/confirmed.textile
@@ -1,3 +1,5 @@
-h2. Email confirmed
+h3. Registration Complete
+Congratulations! your account has been activated. You are ready to build
+your own projects.
"Go to home":/blockly/index
diff --git a/src/main/resources/shiro.ini b/src/main/resources/shiro.ini
index 7cf8c1b8..0ffd72b6 100644
--- a/src/main/resources/shiro.ini
+++ b/src/main/resources/shiro.ini
@@ -29,6 +29,9 @@ shiro.loginUrl = /login.jsp
[urls]
+#
+# A list of accessable URLs
+#
# CDN (data, local during development) (maybe add a hotlink protection?)
/cdn/** = anon
@@ -55,6 +58,7 @@ shiro.loginUrl = /login.jsp
/demo/** = anon, ssl
/frame/** = anon, ssl
/projectlink = anon, ssl
+/privacy-policy = anon, ssl
# REST api and api documentation
/apidoc = anon
diff --git a/src/main/webapp/WEB-INF/includes/pageparts/clientdownload.jsp b/src/main/webapp/WEB-INF/includes/pageparts/clientdownload.jsp
index 81c0a9a5..81f5728f 100644
--- a/src/main/webapp/WEB-INF/includes/pageparts/clientdownload.jsp
+++ b/src/main/webapp/WEB-INF/includes/pageparts/clientdownload.jsp
@@ -34,6 +34,13 @@