COPPA support added
authorDan
Thu, 28 Jun 2007 13:49:40 -0400
changeset 30 7e8fd44b36b0
parent 29 e5484a9e0818
child 31 dc8741857bde
COPPA support added
includes/clientside/static/ajax.js
includes/common.php
includes/paths.php
includes/sessions.php
includes/template.php
plugins/SpecialAdmin.php
plugins/SpecialGroups.php
plugins/SpecialUserFuncs.php
schema.sql
upgrade.sql
--- a/includes/clientside/static/ajax.js	Thu Jun 28 11:13:39 2007 -0400
+++ b/includes/clientside/static/ajax.js	Thu Jun 28 13:49:40 2007 -0400
@@ -582,7 +582,7 @@
       }
     });
   
-  return true;
+  return false;
   
 }
 
--- a/includes/common.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/includes/common.php	Thu Jun 28 13:49:40 2007 -0400
@@ -209,7 +209,7 @@
 define('ENANO_MAINSTREAM', '');
 
 // If the site is disabled, bail out, unless we're trying to log in or administer the site
-if(getConfig('site_disabled') == '1')
+if(getConfig('site_disabled') == '1' && $session->user_level < USER_LEVEL_ADMIN)
 {
   if ( $paths->namespace == 'Admin' || ( $paths->namespace == 'Special' && ( $paths->cpage['urlname_nons'] == 'CSS' || $paths->cpage['urlname_nons'] == 'Administration' || $paths->cpage['urlname_nons'] == 'Login' ) ) )
   {
@@ -230,6 +230,10 @@
     die_semicritical('Site disabled', $text);
   }
 }
+else if(getConfig('site_disabled') == '1' && $session->user_level >= USER_LEVEL_ADMIN)
+{
+  $template->site_disabled = true;
+}
 
 $code = $plugins->setHook('session_started');
 foreach ( $code as $cmd )
--- a/includes/paths.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/includes/paths.php	Thu Jun 28 13:49:40 2007 -0400
@@ -84,6 +84,7 @@
     $this->addAdminNode('Appearance', 'Manage themes', 'ThemeManager');
     $this->addAdminNode('Users', 'Manage users', 'UserManager');
     $this->addAdminNode('Users', 'Edit groups', 'GroupManager');
+    $this->addAdminNode('Users', 'COPPA support', 'COPPA');
     $this->addAdminNode('Users', 'Ban control', 'BanControl');
     $this->addAdminNode('Users', 'Mass e-mail', 'MassEmail');
     
--- a/includes/sessions.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/includes/sessions.php	Thu Jun 28 13:49:40 2007 -0400
@@ -1296,9 +1296,10 @@
    * @param string $password This should be unencrypted.
    * @param string $email
    * @param string $real_name Optional, defaults to ''.
+   * @param bool   $coppa     Optional. If true, the account is not activated initially and an admin activation request is sent. The caller is responsible for sending the address info and notice.
    */
    
-  function create_user($username, $password, $email, $real_name = '')
+  function create_user($username, $password, $email, $real_name = '', $coppa = false)
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
     
@@ -1341,30 +1342,42 @@
         $active = '0';
         break;
     }
+    if ( $coppa )
+      $active = '0';
+    
+    $coppa_col = ( $coppa ) ? '1' : '0';
     
     // Generate a totally random activation key
     $actkey = sha1 ( microtime() . mt_rand() );
 
-    // We good, create the user    
-    $this->sql('INSERT INTO '.table_prefix.'users ( username, password, email, real_name, theme, style, reg_time, account_active, activation_key, user_level ) VALUES ( \''.$username.'\', \''.$password.'\', \''.$email.'\', \''.$real_name.'\', \''.$template->default_theme.'\', \''.$template->default_style.'\', '.time().', '.$active.', \''.$actkey.'\', '.USER_LEVEL_CHPREF.' )');
+    // We good, create the user
+    $this->sql('INSERT INTO '.table_prefix.'users ( username, password, email, real_name, theme, style, reg_time, account_active, activation_key, user_level, user_coppa ) VALUES ( \''.$username.'\', \''.$password.'\', \''.$email.'\', \''.$real_name.'\', \''.$template->default_theme.'\', \''.$template->default_style.'\', '.time().', '.$active.', \''.$actkey.'\', '.USER_LEVEL_CHPREF.', ' . $coppa_col . ' );');
     
     // Require the account to be activated?
-    switch(getConfig('account_activation'))
+    if ( $coppa )
+    {
+      $this->admin_activation_request($username);
+      $this->send_coppa_mail($username,$email);
+    }
+    else
     {
-      case 'none':
-      default:
-        break;
-      case 'user':
-        $a = $this->send_activation_mail($username);
-        if(!$a)
-        {
+      switch(getConfig('account_activation'))
+      {
+        case 'none':
+        default:
+          break;
+        case 'user':
+          $a = $this->send_activation_mail($username);
+          if(!$a)
+          {
+            $this->admin_activation_request($username);
+            return 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you. ' . $a;
+          }
+          break;
+        case 'admin':
           $this->admin_activation_request($username);
-          return 'The activation e-mail could not be sent due to an internal error. This could possibly be due to an incorrect SMTP configuration. A request has been sent to the administrator to activate your account for you. ' . $a;
-        }
-        break;
-      case 'admin':
-        $this->admin_activation_request($username);
-        break;
+          break;
+      }
     }
     
     // Leave some data behind for the hook
@@ -1426,6 +1439,90 @@
   }
   
   /**
+   * Attempts to send an e-mail to the specified user's e-mail address on file intended for the parents
+   * @param string $u The usernamd of the user requesting activation
+   * @return bool true on success, false on failure
+   */
+   
+  function send_coppa_mail($u, $actkey = false)
+  {
+    
+    global $db, $session, $paths, $template, $plugins; // Common objects
+    
+    $q = $this->sql('SELECT username,email FROM '.table_prefix.'users WHERE user_id=2 OR user_level=' . USER_LEVEL_ADMIN . ' ORDER BY user_id ASC;');
+    $un = $db->fetchrow();
+    $admin_user = $un['username'];
+    
+    $q = $this->sql('SELECT username,activation_key,account_active,email FROM '.table_prefix.'users WHERE username=\''.$db->escape($u).'\';');
+    $r = $db->fetchrow();
+    if ( empty($r['email']) )
+      $db->_die('BUG: $session->send_activation_mail(): no e-mail address in row');
+      
+    if(isset($_SERVER['HTTPS'])) $prot = 'https';
+    else $prot = 'http';                                                                           
+    if($_SERVER['SERVER_PORT'] == '80') $p = '';
+    else $p = ':'.$_SERVER['SERVER_PORT'];
+    $sidbak = false;
+    if($this->sid_super)
+      $sidbak = $this->sid_super;
+    $this->sid_super = false;
+    if($sidbak)
+      $this->sid_super = $sidbak;
+    unset($sidbak);
+    $link = "$prot://".$_SERVER['HTTP_HOST'].scriptPath;
+    
+    $message = 'Dear parent or legal guardian,
+A child under the username ' . $u . ' recently registered on our website. The child provided your e-mail address as the one of his or her authorized parent or legal guardian, and to comply with the United States Childrens\' Online Privacy Protection act, we ask that all parents of children ages 13 or under please mail us a written form authorizing their child\'s use of our website.
+
+If you wish for your child to be allowed access to our website, please print and fill out the form below, and mail it to this address:
+
+' . getConfig('coppa_address') . '
+
+If you do NOT wish for your child to be allowed access to our site, you do not need to do anything - your child will not be able to access our site as a registered user unless you authorize their account activation.
+
+Authorization form:
+-------------------------------- Cut here --------------------------------
+
+I, _______________________________________, the legal parent or guardian of the child registered on the website "' . getConfig('site_name') . '" as ' . $u . ', hereby give my authorization for the child\'s e-mail address, instant messaging information, location, and real name, to be collected and stored in a database owned and maintained by ' . getConfig('site_name') . ' at the child\'s option, and for the administrators of this website to use this information according to the privacy policy displayed on their website <' . $link . '>.
+
+Child\'s name:               _____________________________________
+
+Child\'s e-mail address:     _____________________________________
+(optional - if you don\'t provide this, we\'ll just send site-related e-mails to your e-mail address)
+
+Signature of parent or guardian:
+
+____________________________________________________
+
+Date (YYYY-MM-DD): ______ / _____ / _____
+
+-------------------------------- Cut here --------------------------------';
+    $message .= "\n\nSincerely yours, \n$admin_user and the ".$_SERVER['HTTP_HOST']." administration team";
+    
+    error_reporting(E_ALL);
+    
+    dc_dump($r, 'session: about to send COPPA e-mail to '.$r['email']);
+    if(getConfig('smtp_enabled') == '1')
+    {
+      $result = smtp_send_email($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), getConfig('contact_email'));
+      if($result == 'success') 
+      {
+        $result = true;
+      }
+      else
+      {
+        echo $result;
+        $result = false;
+      }
+    } 
+    else
+    {
+      $result = mail($r['email'], getConfig('site_name').' website account activation', preg_replace("#(?<!\r)\n#s", "\n", $message), 'From: '.getConfig('contact_email'));
+    }
+    return $result;
+  }
+  
+  /**
    * Sends an e-mail to a user so they can reset their password.
    * @param int $user The user ID, or username if it's a string
    * @return bool true on success, false on failure
--- a/includes/template.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/includes/template.php	Thu Jun 28 13:49:40 2007 -0400
@@ -14,6 +14,15 @@
  
 class template {
   var $tpl_strings, $tpl_bool, $theme, $style, $no_headers, $additional_headers, $sidebar_extra, $sidebar_widgets, $toolbar_menu, $theme_list, $named_theme_list, $default_theme, $default_style, $plugin_blocks, $namespace_string, $style_list, $theme_loaded;
+  
+  /**
+   * Set to true if the site is disabled and thus a message needs to be shown. This should ONLY be changed by common.php.
+   * @var bool
+   * @access private
+   */
+  
+  var $site_disabled = false;
+  
   function __construct()
   {
     global $db, $session, $paths, $template, $plugins; // Common objects
@@ -756,6 +765,13 @@
       echo '<b>Your administrative session has timed out.</b> <a href="' . $login_link . '">Log in again</a>';
       echo '</div>';
     }
+    if ( $this->site_disabled && $session->user_level >= USER_LEVEL_ADMIN && ( $paths->page != $paths->nslist['Special'] . 'Administration' ) )
+    {
+      $admin_link = makeUrlNS('Special', 'Administration', 'module=' . $paths->nslist['Admin'] . 'GeneralConfig', true);
+      echo '<div class="usermessage"><b>The site is currently disabled and thus is only accessible to administrators.</b><br />
+            You can re-enable the site through the <a href="' . $admin_link . '">administration panel</a>.
+            </div>';
+    }
   }
   function footer($simple = false)
   {
--- a/plugins/SpecialAdmin.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/plugins/SpecialAdmin.php	Thu Jun 28 13:49:40 2007 -0400
@@ -247,10 +247,10 @@
       <tr><th colspan="2">Global site options</th></tr>
       <tr><th colspan="2" class="subhead">These options control the entire site.</th></tr>
       
-      <tr><td class="row1" style="width: 50%;">Site name:</td>                      <td class="row1" style="width: 50%;"><input name="site_name" size="30" value="<?php echo getConfig('site_name'); ?>" /></td></tr>
-      <tr><td class="row2">Site description:</td>               <td class="row2"><input name="site_desc" size="30" value="<?php echo getConfig('site_desc'); ?>" /></td></tr>
+      <tr><td class="row1" style="width: 50%;">Site name:</td>                      <td class="row1" style="width: 50%;"><input type="text" name="site_name" size="30" value="<?php echo getConfig('site_name'); ?>" /></td></tr>
+      <tr><td class="row2">Site description:</td>               <td class="row2"><input type="text" name="site_desc" size="30" value="<?php echo getConfig('site_desc'); ?>" /></td></tr>
       <tr><td class="row1">Main page:</td>                      <td class="row1"><?php echo $template->pagename_field('main_page', str_replace('_', ' ', getConfig('main_page'))); ?></td></tr>
-      <tr><td class="row2">Copyright notice shown on pages:</td><td class="row2"><input name="copyright" size="30" value="<?php echo getConfig('copyright_notice'); ?>" /></td></tr>
+      <tr><td class="row2">Copyright notice shown on pages:</td><td class="row2"><input type="text" name="copyright" size="30" value="<?php echo getConfig('copyright_notice'); ?>" /></td></tr>
       <tr><td class="row1" colspan="2">Hint: If you're using Windows, you can make a "&copy;" symbol by holding ALT and pressing 0169 on the numeric keypad.</td></tr>
       <tr><td class="row2">Contact e-mail<br /><small>All e-mail sent from this site will appear to have come from the address shown here.</small></td><td class="row2"><input name="contact_email" type="text" size="40" value="<?php echo htmlspecialchars(getConfig('contact_email')); ?>" /></td></tr>
       
@@ -348,7 +348,7 @@
       </tr>
       <tr>
         <td class="row2">
-          <div id="site_disabled_notice">
+          <div id="site_disabled_notice"<?php if(getConfig('site_disabled')!='1') echo(' style="display:none"'); ?>>
             Message to show to users:<br />
             <textarea name="site_disabled_notice" rows="7" cols="30"><?php echo getConfig('site_disabled_notice'); ?></textarea>
           </div>
@@ -958,7 +958,10 @@
         break;
     }
   }
-  $q = $db->sql_query('SELECT log_type, action, time_id, date_string, author, edit_summary FROM '.table_prefix.'logs WHERE log_type=\'admin\' AND action=\'activ_req\' ORDER BY time_id DESC;');
+  $q = $db->sql_query('SELECT l.log_type, l.action, l.time_id, l.date_string, l.author, l.edit_summary, u.user_coppa FROM '.table_prefix.'logs AS l
+                         LEFT JOIN '.table_prefix.'users AS u
+                           ON ( u.username = l.edit_summary OR u.username IS NULL )
+                         WHERE log_type=\'admin\' AND action=\'activ_req\' ORDER BY time_id DESC;');
   if($q)
   {
     if($db->numrows() > 0)
@@ -969,13 +972,14 @@
       echo '<h3>'.$s . ' awaiting account activation</h3>';
       echo '<div class="tblholder">
             <table border="0" cellspacing="1" cellpadding="4" width="100%">
-            <tr><th>Date of request</th><th>Requested by</th><th>Requested for</th><th colspan="3">Actions</th></tr>';
+            <tr><th>Date of request</th><th>Requested by</th><th>Requested for</th><th>COPPA user</th><th colspan="3">Actions</th></tr>';
       $cls = 'row2';
       while($row = $db->fetchrow())
       {
         if($cls == 'row2') $cls = 'row1';
         else $cls = 'row2';
-        echo '<tr><td class="'.$cls.'">'.date('F d, Y h:i a', $row['time_id']).'</td><td class="'.$cls.'">'.$row['author'].'</td><td class="'.$cls.'">'.$row['edit_summary'].'</td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=activate&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Activate now</a></td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=sendemail&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Send activation e-mail</a></td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=deny&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Deny request</a></td></tr>';
+        $coppa = ( $row['user_coppa'] == '1' ) ? '<b>Yes</b>' : 'No';
+        echo '<tr><td class="'.$cls.'">'.date('F d, Y h:i a', $row['time_id']).'</td><td class="'.$cls.'">'.$row['author'].'</td><td class="'.$cls.'">'.$row['edit_summary'].'</td><td style="text-align: center;" class="' . $cls . '">' . $coppa . '</td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=activate&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Activate now</a></td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=sendemail&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Send activation e-mail</a></td><td class="'.$cls.'" style="text-align: center;"><a href="'.makeUrlNS('Special', 'Administration', 'module='.$paths->nslist['Admin'].'UserManager&amp;action=deny&amp;user='.$row['edit_summary'].'&amp;logid='.$row['time_id']).'">Deny request</a></td></tr>';
       }
       echo '</table>';
     }
@@ -1342,6 +1346,79 @@
   echo '</form>';
 }
 
+function page_Admin_COPPA()
+{
+  global $db, $session, $paths, $template, $plugins; // Common objects
+  if ( $session->auth_level < USER_LEVEL_ADMIN || $session->user_level < USER_LEVEL_ADMIN )
+  {
+    echo '<h3>Error: Not authenticated</h3><p>It looks like your administration session is invalid or you are not authorized to access this administration page. Please <a href="' . makeUrlNS('Special', 'Login/' . $paths->nslist['Special'] . 'Administration', 'level=' . USER_LEVEL_ADMIN, true) . '">re-authenticate</a> to continue.</p>';
+    return;
+  }
+  
+  echo '<h2>Background information</h2>';
+  echo '<p>
+          The United States Childrens\' Online Privacy Protection Act (COPPA) was a law passed in 2001 that requires sites oriented towards
+          children under 13 years old or with a significant amount of under-13 children clearly state what information is being collected
+          in a privacy policy and obtain authorization from a parent or legal guardian before allowing children to use the site. Enano 
+          provides an easy way to allow you, as the website administrator, to obtain this authorization.
+        </p>';
+  
+  // Start form
+  
+  if ( isset($_POST['coppa_address']) )
+  {
+    // Saving changes
+    $enable_coppa = ( isset($_POST['enable_coppa']) ) ? '1' : '0';
+    setConfig('enable_coppa', $enable_coppa);
+    
+    $address = $_POST['coppa_address']; // RenderMan::preprocess_text($_POST['coppa_address'], true, false);
+    setConfig('coppa_address', $address);
+    
+    echo '<div class="info-box">Your changes have been saved.</div>';
+  }
+  
+  echo '<form action="'.makeUrl($paths->nslist['Special'].'Administration', (( isset($_GET['sqldbg'])) ? 'sqldbg&amp;' : '') .'module='.$paths->cpage['module']).'" method="post">';
+  
+  echo '<div class="tblholder">';
+  echo '<table border="0" cellspacing="1" cellpadding="4">';
+  echo '<tr>
+          <th colspan="2">
+            COPPA support
+          </th>
+        </tr>';
+        
+  echo '<tr>
+          <td class="row1">
+            Enable COPPA support:
+          </td>
+          <td class="row2">
+            <label><input type="checkbox" name="enable_coppa" ' . ( ( getConfig('enable_coppa') == '1' ) ? 'checked="checked"' : '' ) . ' /> COPPA enabled</label><br />
+            <small>If this is checked, users will be asked if they are under 13 years of age before registering</small>
+          </td>
+        </tr>';
+        
+  echo '<tr>
+          <td class="row1">
+            Your mailing address:<br />
+            <small>This is the address to which parents will send authorization forms.</small>
+          </td>
+          <td class="row2">
+            <textarea name="coppa_address" rows="7" cols="40">' . getConfig('coppa_address') . '</textarea>
+          </td>
+        </tr>';
+        
+  echo '<tr>
+          <th colspan="2" class="subhead">
+            <input type="submit" value="Save changes" />
+          </th>
+        </tr>';
+        
+  echo '</table>';
+  
+  echo '</form>';
+  
+}
+
 function page_Admin_PageManager()
 {
   global $db, $session, $paths, $template, $plugins; // Common objects
--- a/plugins/SpecialGroups.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/plugins/SpecialGroups.php	Thu Jun 28 13:49:40 2007 -0400
@@ -474,12 +474,12 @@
               <th colspan="2">Group membership details</th>
             </tr>
             <tr>
-              <td class="row2" style="text-align: right;">
+              <td class="row2" style="text-align: right; width: 50%;">
                 Current group memberships:
               </td>
-              <td class="row1">';
+              <td class="row1" style="width: 50%;">';
     $taboo = Array('Everyone');
-    if ( sizeof ( $session->groups ) > 0 )
+    if ( sizeof ( $session->groups ) > count($taboo) )
     {
       echo '<select name="group_id">';
       foreach ( $session->groups as $id => $group )
--- a/plugins/SpecialUserFuncs.php	Thu Jun 28 11:13:39 2007 -0400
+++ b/plugins/SpecialUserFuncs.php	Thu Jun 28 13:49:40 2007 -0400
@@ -300,21 +300,38 @@
   $template->footer();
 }
 
-function page_Special_Register() {
+function page_Special_Register()
+{
   global $db, $session, $paths, $template, $plugins; // Common objects
   if(getConfig('account_activation') == 'disable' && ( ( $session->user_level >= USER_LEVEL_ADMIN && !isset($_GET['IWannaPlayToo']) ) || $session->user_level < USER_LEVEL_ADMIN || !$session->user_logged_in ))
   {
     $s = ($session->user_level >= USER_LEVEL_ADMIN) ? '<p>Oops...it seems that you <em>are</em> the administrator...hehe...you can also <a href="'.makeUrl($paths->page, 'IWannaPlayToo', true).'">force account registration to work</a>.</p>' : '';
     die_friendly('Registration disabled', '<p>The administrator has disabled new user registration on this site.</p>' . $s);
   }
-  if(isset($_POST['submit'])) {
+  if(isset($_POST['submit'])) 
+  {
+    $_GET['coppa'] = ( isset($_POST['coppa']) ) ? $_POST['coppa'] : 'x';
+    
     $captcharesult = $session->get_captcha($_POST['captchahash']);
     if($captcharesult != $_POST['captchacode'])
+    {
       $s = 'The confirmation code you entered was incorrect.';
+    }
     else
-      // CAPTCHA code was correct, create the account
-      $s = $session->create_user($_POST['username'], $_POST['password'], $_POST['email'], $_POST['real_name']);
-    if($s == 'success')
+    {
+      if ( getConfig('enable_coppa') == '1' && ( !isset($_POST['coppa']) || ( isset($_POST['coppa']) && !in_array($_POST['coppa'], array('yes', 'no')) ) ) )
+      {
+        $s = 'Invalid COPPA input';
+      }
+      else
+      {
+        $coppa = ( isset($_POST['coppa']) && $_POST['coppa'] == 'yes' );
+        
+        // CAPTCHA code was correct, create the account
+        $s = $session->create_user($_POST['username'], $_POST['password'], $_POST['email'], $_POST['real_name'], $coppa);
+      }
+    }
+    if($s == 'success' && !isset($coppa))
     {
       switch(getConfig('account_activation'))
       {
@@ -331,132 +348,174 @@
       }
       die_friendly('Registration successful', '<p>Thank you for registering, your user account has been created. '.$str.'</p>');
     }
+    else if ( $s == 'success' && $coppa )
+    {
+      $str = 'However, in compliance with the Childrens\' Online Privacy Protection Act, you must have your parent or legal guardian activate your account. Please ask them to check their e-mail for further information.';
+      die_friendly('Registration successful', '<p>Thank you for registering, your user account has been created. '.$str.'</p>');
+    }
   }
   $template->header();
   echo 'A user account enables you to have greater control over your browsing experience.';
-  $session->kill_captcha();
-  $captchacode = $session->make_captcha();
-  ?>
-    <h3>Create a user account</h3>
-    <form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post">
-      <div class="tblholder">
-        <table border="0" width="100%" cellspacing="1" cellpadding="4">
-          <tr><th class="subhead" colspan="3">Please tell us a little bit about yourself.</th></tr>
-          <?php if(isset($_POST['submit'])) echo '<tr><td colspan="3" class="row2" style="color: red;">'.$s.'</td></tr>'; ?>
-          <tr><td class="row1" style="width: 50%;">Preferred username:<span id="e_username"></span></td><td class="row1" style="width: 50%;"><input type="text" name="username" size="30" onkeyup="namegood = false; validateForm();" onblur="checkUsername();" /></td><td class="row1" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_username" /></td></tr>
-          <tr><td class="row3" style="width: 50%;" rowspan="2">Password:<span id="e_password"></span></td><td class="row3" style="width: 50%;"><input type="password" name="password" size="30" onkeyup="validateForm();" /></td><td rowspan="2" class="row3" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_password" /></td></tr>
-          <tr><td class="row3" style="width: 50%;"><input type="password" name="password_confirm" size="30" onkeyup="validateForm();" /> <small>Enter your password again to confirm.</small></td></tr>
-          <tr><td class="row1" style="width: 50%;">E-mail address:<?php if(getConfig('account_activation')=='user') echo '<br /><small>An e-mail with an account activation key will be sent to this address, so please ensure that it is correct.</small></td>'; ?><td class="row1" style="width: 50%;"><input type="text" name="email" size="30" onkeyup="validateForm();" /></td><td class="row1" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_email" /></td></tr>
-          <tr><td class="row3" style="width: 50%;">Real name:<br /><small>Giving your real name is totally optional. If you choose to provide your real name, it will be used to provide attribution for any edits or contributions you may make to this site.</small><td class="row3" style="width: 50%;"><input type="text" name="real_name" size="30" /></td><td class="row3" style="max-width: 24px;"></td></tr>
-          <tr><td class="row1" style="width: 50%;" rowspan="2">Visual confirmation<br /><small>Please enter the code shown in the image to the right into the text box. This process helps to ensure that this registration is not being performed by an automated bot. If the image to the right is illegible, you can <a href="#" onclick="regenCaptcha(); return false;">generate a new image</a>.<br /><br />If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will create an account for you.</small></td><td colspan="2" class="row1"><img id="captchaimg" alt="CAPTCHA image" src="<?php echo makeUrlNS('Special', 'Captcha/'.$captchacode); ?>" /><span id="b_username"></span></td></tr>
-          <tr><td class="row1" colspan="2">Code: <input name="captchacode" type="text" size="10" /><input type="hidden" name="captchahash" value="<?php echo $captchacode; ?>" /></td></tr>
-          <tr><td class="row2" colspan="3" style="text-align: center;"><input type="submit" name="submit" value="Create my account" /></td></tr>
-        </table>
-      </div>
-    </form>
-    <script type="text/javascript">
-      // <![CDATA[
-      var namegood = false;
-      function validateForm()
-      {
-        var frm = document.forms.regform;
-        failed = false;
-        
-        // Username
-        if(!namegood)
+  
+  if ( getConfig('enable_coppa') != '1' || ( isset($_GET['coppa']) && in_array($_GET['coppa'], array('yes', 'no')) ) )
+  {
+    $coppa = ( isset($_GET['coppa']) && $_GET['coppa'] == 'yes' );
+    $session->kill_captcha();
+    $captchacode = $session->make_captcha();
+    ?>
+      <h3>Create a user account</h3>
+      <form name="regform" action="<?php echo makeUrl($paths->page); ?>" method="post">
+        <div class="tblholder">
+          <table border="0" width="100%" cellspacing="1" cellpadding="4">
+            <tr><th class="subhead" colspan="3">Please tell us a little bit about yourself.</th></tr>
+            <?php if(isset($_POST['submit'])) echo '<tr><td colspan="3" class="row2" style="color: red;">'.$s.'</td></tr>'; ?>
+            <tr><td class="row1" style="width: 50%;">Preferred username:<span id="e_username"></span></td><td class="row1" style="width: 50%;"><input type="text" name="username" size="30" onkeyup="namegood = false; validateForm();" onblur="checkUsername();" /></td><td class="row1" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_username" /></td></tr>
+            <tr><td class="row3" style="width: 50%;" rowspan="2">Password:<span id="e_password"></span></td><td class="row3" style="width: 50%;"><input type="password" name="password" size="30" onkeyup="validateForm();" /></td><td rowspan="2" class="row3" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_password" /></td></tr>
+            <tr><td class="row3" style="width: 50%;"><input type="password" name="password_confirm" size="30" onkeyup="validateForm();" /> <small>Enter your password again to confirm.</small></td></tr>
+            <tr><td class="row1" style="width: 50%;"><?php if ( $coppa ) echo 'Your parent or guardian\'s e'; else echo 'E'; ?>-mail address:<?php if(getConfig('account_activation')=='user') echo '<br /><small>An e-mail with an account activation key will be sent to this address, so please ensure that it is correct.</small></td>'; ?><td class="row1" style="width: 50%;"><input type="text" name="email" size="30" onkeyup="validateForm();" /></td><td class="row1" style="max-width: 24px;"><img alt="Good/bad icon" src="<?php echo scriptPath; ?>/images/bad.gif" id="s_email" /></td></tr>
+            <tr><td class="row3" style="width: 50%;">Real name:<br /><small>Giving your real name is totally optional. If you choose to provide your real name, it will be used to provide attribution for any edits or contributions you may make to this site.</small><td class="row3" style="width: 50%;"><input type="text" name="real_name" size="30" /></td><td class="row3" style="max-width: 24px;"></td></tr>
+            <tr><td class="row1" style="width: 50%;" rowspan="2">Visual confirmation<br /><small>Please enter the code shown in the image to the right into the text box. This process helps to ensure that this registration is not being performed by an automated bot. If the image to the right is illegible, you can <a href="#" onclick="regenCaptcha(); return false;">generate a new image</a>.<br /><br />If you are visually impaired or otherwise cannot read the text shown to the right, please contact the site management and they will create an account for you.</small></td><td colspan="2" class="row1"><img id="captchaimg" alt="CAPTCHA image" src="<?php echo makeUrlNS('Special', 'Captcha/'.$captchacode); ?>" /><span id="b_username"></span></td></tr>
+            <tr><td class="row1" colspan="2">Code: <input name="captchacode" type="text" size="10" /><input type="hidden" name="captchahash" value="<?php echo $captchacode; ?>" /></td></tr>
+            <tr><td class="row2" colspan="3" style="text-align: center;"><input type="submit" name="submit" value="Create my account" /></td></tr>
+          </table>
+        </div>
+        <?php
+          $val = ( $coppa ) ? 'yes' : 'no';
+          echo '<input type="hidden" name="coppa" value="' . $val . '" />';
+        ?>
+      </form>
+      <script type="text/javascript">
+        // <![CDATA[
+        var namegood = false;
+        function validateForm()
         {
-          if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
+          var frm = document.forms.regform;
+          failed = false;
+          
+          // Username
+          if(!namegood)
           {
-            document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/unknown.gif';
-            document.getElementById('e_username').innerHTML = ''; // '<br /><small><b>Checking availability...</b></small>';
+            if(frm.username.value.match(/^([A-z0-9 \!@\-\(\)]+){2,}$/ig))
+            {
+              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/unknown.gif';
+              document.getElementById('e_username').innerHTML = ''; // '<br /><small><b>Checking availability...</b></small>';
+            } else {
+              failed = true;
+              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
+              document.getElementById('e_username').innerHTML = '<br /><small>Your username must be at least two characters in length and may contain only alphanumeric characters (A-Z and 0-9), spaces, and the following characters: :, !, @, #, *.</small>';
+            }
+          }
+          document.getElementById('b_username').innerHTML = '';
+          if(hex_md5(frm.real_name.value) == 'fa8e397ae0f6cd5b0f90a3f48178cd7e')
+          {
+            document.getElementById('b_username').innerHTML = '<br /><br />Hey...I know you!<br /><img alt="" src="http://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bill_Gates_2004_cr.jpg/220px-Bill_Gates_2004_cr.jpg" />';
+          }
+          
+          // Password
+          if(frm.password.value.match(/^(.+){6,}$/ig) && frm.password_confirm.value.match(/^(.+){6,}$/ig) && frm.password.value == frm.password_confirm.value)
+          {
+            document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/good.gif';
+            document.getElementById('e_password').innerHTML = '<br /><small>The password you entered is valid.</small>';
           } else {
             failed = true;
-            document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
-            document.getElementById('e_username').innerHTML = '<br /><small>Your username must be at least two characters in length and may contain only alphanumeric characters (A-Z and 0-9), spaces, and the following characters: :, !, @, #, *.</small>';
+            if(frm.password.value.length < 6)
+              document.getElementById('e_password').innerHTML = '<br /><small>Your password must be at least six characters in length.</small>';
+            else if(frm.password.value != frm.password_confirm.value)
+              document.getElementById('e_password').innerHTML = '<br /><small>The passwords you entered do not match.</small>';
+            else
+              document.getElementById('e_password').innerHTML = '';
+            document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/bad.gif';
+          }
+          
+          // E-mail address
+          if(frm.email.value.match(/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/))
+          {
+            document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/good.gif';
+          } else {
+            failed = true;
+            document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/bad.gif';
+          }
+          if(failed)
+          {
+            frm.submit.disabled = 'disabled';
+          } else {
+            frm.submit.disabled = false;
           }
         }
-        document.getElementById('b_username').innerHTML = '';
-        if(hex_md5(frm.real_name.value) == 'fa8e397ae0f6cd5b0f90a3f48178cd7e')
-        {
-          document.getElementById('b_username').innerHTML = '<br /><br />Hey...I know you!<br /><img alt="" src="http://upload.wikimedia.org/wikipedia/commons/thumb/7/7f/Bill_Gates_2004_cr.jpg/220px-Bill_Gates_2004_cr.jpg" />';
-        }
-        
-        // Password
-        if(frm.password.value.match(/^(.+){6,}$/ig) && frm.password_confirm.value.match(/^(.+){6,}$/ig) && frm.password.value == frm.password_confirm.value)
+        function checkUsername()
         {
-          document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/good.gif';
-          document.getElementById('e_password').innerHTML = '<br /><small>The password you entered is valid.</small>';
-        } else {
-          failed = true;
-          if(frm.password.value.length < 6)
-            document.getElementById('e_password').innerHTML = '<br /><small>Your password must be at least six characters in length.</small>';
-          else if(frm.password.value != frm.password_confirm.value)
-            document.getElementById('e_password').innerHTML = '<br /><small>The passwords you entered do not match.</small>';
-          else
-            document.getElementById('e_password').innerHTML = '';
-          document.getElementById('s_password').src='<?php echo scriptPath; ?>/images/bad.gif';
-        }
-        
-        // E-mail address
-        if(frm.email.value.match(/^(?:[\w\d]+\.?)+@(?:(?:[\w\d]\-?)+\.)+\w{2,4}$/))
-        {
-          document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/good.gif';
-        } else {
-          failed = true;
-          document.getElementById('s_email').src='<?php echo scriptPath; ?>/images/bad.gif';
-        }
-        if(failed)
-        {
-          frm.submit.disabled = 'disabled';
-        } else {
-          frm.submit.disabled = false;
+          var frm = document.forms.regform;
+          
+          if(!namegood)
+          {
+            if(frm.username.value.match(/^([A-z0-9 \.:\!@\#\*]+){2,}$/ig))
+            {
+              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/unknown.gif';
+              document.getElementById('e_username').innerHTML = '';
+            } else {
+              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
+              document.getElementById('e_username').innerHTML = '<br /><small>Your username must be at least two characters in length and may contain only alphanumeric characters (A-Z and 0-9), spaces, and the following characters: :, !, @, #, *.</small>';
+              return false;
+            }
+          }
+          
+          document.getElementById('e_username').innerHTML = '<br /><small><b>Checking availability...</b></small>';
+          ajaxGet('<?php echo scriptPath; ?>/ajax.php?title=null&_mode=checkusername&name='+escape(frm.username.value), function() {
+            if(ajax.readyState == 4)
+              if(ajax.responseText == 'good')
+              {
+                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/good.gif';
+                document.getElementById('e_username').innerHTML = '<br /><small><b>This username is available.</b></small>';
+                namegood = true;
+              } else if(ajax.responseText == 'bad') {
+                document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
+                document.getElementById('e_username').innerHTML = '<br /><small><b>Error: that username is already taken.</b></small>';
+                namegood = false;
+              } else {
+                document.getElementById('e_username').innerHTML = ajax.responseText;
+              }
+          });
         }
-      }
-      function checkUsername()
-      {
-        var frm = document.forms.regform;
-        
-        if(!namegood)
+        function regenCaptcha()
         {
-          if(frm.username.value.match(/^([A-z0-9 \.:\!@\#\*]+){2,}$/ig))
-          {
-            document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/unknown.gif';
-            document.getElementById('e_username').innerHTML = '';
-          } else {
-            document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
-            document.getElementById('e_username').innerHTML = '<br /><small>Your username must be at least two characters in length and may contain only alphanumeric characters (A-Z and 0-9), spaces, and the following characters: :, !, @, #, *.</small>';
-            return false;
-          }
+          var frm = document.forms.regform;
+          document.getElementById('captchaimg').src = '<?php echo makeUrlNS("Special", "Captcha/"); ?>'+frm.captchahash.value+'/'+Math.floor(Math.random() * 100000);
+          return false;
         }
-        
-        document.getElementById('e_username').innerHTML = '<br /><small><b>Checking availability...</b></small>';
-        ajaxGet('<?php echo scriptPath; ?>/ajax.php?title=null&_mode=checkusername&name='+escape(frm.username.value), function() {
-          if(ajax.readyState == 4)
-            if(ajax.responseText == 'good')
-            {
-              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/good.gif';
-              document.getElementById('e_username').innerHTML = '<br /><small><b>This username is available.</b></small>';
-              namegood = true;
-            } else if(ajax.responseText == 'bad') {
-              document.getElementById('s_username').src='<?php echo scriptPath; ?>/images/bad.gif';
-              document.getElementById('e_username').innerHTML = '<br /><small><b>Error: that username is already taken.</b></small>';
-              namegood = false;
-            } else {
-              document.getElementById('e_username').innerHTML = ajax.responseText;
-            }
-        });
-      }
-      function regenCaptcha()
-      {
-        var frm = document.forms.regform;
-        document.getElementById('captchaimg').src = '<?php echo makeUrlNS("Special", "Captcha/"); ?>'+frm.captchahash.value+'/'+Math.floor(Math.random() * 100000);
-        return false;
-      }
-      validateForm();
-      setTimeout('checkUsername();', 1000);
-      // ]]>
-    </script>
-  <?php
+        validateForm();
+        setTimeout('checkUsername();', 1000);
+        // ]]>
+      </script>
+    <?php
+  }
+  else
+  {
+    $year = intval( date('Y') );
+    $year = $year - 13;
+    $month = date('F');
+    $day = date('d');
+    
+    $yo13_date = "$month $day, $year";
+    $link_coppa_yes = makeUrlNS('Special', 'Register', 'coppa=yes', true);
+    $link_coppa_no  = makeUrlNS('Special', 'Register', 'coppa=no',  true);
+    
+    // COPPA enabled, ask age
+    echo '<div class="tblholder">';
+    echo '<table border="0" cellspacing="1" cellpadding="4">';
+    echo '<tr>
+            <td class="row1">
+              Before you can register, please tell us your age.
+            </td>
+          </tr>
+          <tr>
+            <td class="row3">
+              <a href="' . $link_coppa_no  . '">I was born <b>on or before</b> ' . $yo13_date . ' and am <b>at least</b> 13 years of age</a><br />
+              <a href="' . $link_coppa_yes . '">I was born <b>after</b> ' . $yo13_date . ' and am <b>less than</b> 13 years of age</a>
+            </td>
+          </tr>';
+    echo '</table>';
+    echo '</div>';
+  }
   $template->footer();
 }
 
--- a/schema.sql	Thu Jun 28 11:13:39 2007 -0400
+++ b/schema.sql	Thu Jun 28 13:49:40 2007 -0400
@@ -103,6 +103,7 @@
   old_encryption tinyint(1) NOT NULL DEFAULT 0,
   temp_password text,
   temp_password_time int(12) NOT NULL DEFAULT 0,
+  user_coppa tinyint(1) NOT NULL DEFAULT 0,
   PRIMARY KEY  (user_id)
 ) CHARACTER SET `utf8` COLLATE `utf8_bin`;
 
--- a/upgrade.sql	Thu Jun 28 11:13:39 2007 -0400
+++ b/upgrade.sql	Thu Jun 28 13:49:40 2007 -0400
@@ -6,7 +6,7 @@
 INSERT INTO {{TABLE_PREFIX}}config (config_name, config_value) VALUES( 'enano_version', '1.0' );
 ---BEGIN 1.0RC3---
 -- Placeholder (all versions need to have at least one query performed)
-UPDATE {{TABLE_PREFIX}}config SET config_value='' WHERE config_name='              ';
+ALTER {{TABLE_PREFIX}}users ADD COLUMN user_coppa tinyint(1) NOT NULL DEFAULT 0;
 ---END 1.0RC3---
 ---BEGIN 1.0RC2---
 -- Add the "Moderators" group