/[drupal]/contributions/modules/coppa/coppa.module
ViewVC logotype

Diff of /contributions/modules/coppa/coppa.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph | View Patch Patch

revision 1.4, Tue Jul 29 19:50:40 2008 UTC revision 1.5, Mon Feb 9 19:12:26 2009 UTC
# Line 1  Line 1 
1  <?php  <?php
2  // $Id: coppa.module,v 1.3 2008/07/23 07:02:10 jgraham Exp $  // $Id: coppa.module,v 1.4 2008/07/29 19:50:40 jgraham Exp $
3    
4  define('COPPA_AGE', 13);  define('COPPA_AGE', 13);
5  define('COPPA_ADD_ELEMENTS', 20);  define('COPPA_ADD_ELEMENTS', 20);
6    
7  /**  /**
8   * Implementation of hook_help   * Implementation of hook_help().
  *  
9   */   */
10  function coppa_help($section) {  function coppa_help($path, $arg) {
11    global $user;    global $user;
12    switch ($section) {    switch ($path) {
13      case 'admin/settings/coppa':      case 'admin/settings/coppa':
14        $output =  '<p>'. t('Settings for COPPA') .'</p>';        $output =  '<p>'. t('Settings for COPPA') .'</p>';
15        return $output;        return $output;
# Line 19  function coppa_help($section) { Line 18  function coppa_help($section) {
18  }  }
19    
20  /**  /**
21   * Implementation of hook_menu   * Implementation of hook_menu().
  *  
  */  
 function coppa_menu($may_cache) {  
   global $user;  
   $items = array();  
   $admin_access = user_access('administer users');  
   
   if ($may_cache) {  
     $items[] = array('path' => 'admin/settings/coppa',  
                      'title' => t('COPPA Settings'),  
                      'description' => t('Settings for COPPA compliance.'),  
                      'callback' => 'drupal_get_form',  
                      'callback arguments' => array('coppa_admin_settings'),  
                      'access' => $admin_access,  
                      'type' => MENU_NORMAL_ITEM);  
   
     $items[] = array('path' => 'coppa/required',  
                      'title' => t('COPPA Requirements'),  
                      'callback' => 'coppa_info',  
                      'type' => MENU_CALLBACK,  
                      'access' => 1,  
                 );  
     $items[] = array('path' => 'admin/user/coppa',  
                     'title' => t('Manage COPPA Relationships'),  
                     'description' => t('Manage and add COPPA Relationships'),  
                     'callback' => 'coppa_manage_page',  
                 );  
     $items[] = array('path' => 'admin/user/coppa/add',  
                      'title' => t('COPPA Add Relationships'),  
                      'access' => $admin_access,  
                      'type' => MENU_LOCAL_TASK,  
                 );  
    $items[] = array('path' => 'admin/user/coppa/manage',  
                     'title' => t('COPPA Manage Relationships'),  
                     'access' => $admin_access,  
                     'type' => MENU_DEFAULT_LOCAL_TASK,  
                 );  
   }  
   else {  
     // only add coppa/sign to the menu if the user is a parent/guardian  
     $result = db_query('SELECT * FROM {coppa} WHERE pid = %d', $user->uid);  
     if (db_num_rows($result)) {  
       $items[] = array('path' => 'coppa/sign',  
                        'title' => t('Sign COPPA compliance'),  
                        'callback' => 'drupal_get_form',  
                        'callback arguments' => array('coppa_sign'),  
                        'type' => MENU_NORMAL_ITEM,  
                        'access' => 1,  
                   );  
     }  
   }  
   return $items;  
 }  
   
 function coppa_manage_page($path = NULL) {  
   $bcrumb = drupal_get_breadcrumb();  
   $bcrumb[] = l(t('COPPA'), 'admin/user/coppa/add');  
   switch($path) {  
     case 'add':  
       $output = drupal_get_form('coppa_set_parents');  
       $bcrumb[] = l(t('Add'), 'admin/user/coppa/add');  
       break;  
     case 'manage':  
     default:  
       $manage = coppa_manage_relationships();  
       $bcrumb[] = l(t('Manage'), 'admin/user/coppa/manage');  
       if (!empty($manage)) {  
         $output = drupal_get_form('coppa_manage_relationships');  
       }  
       else {  
         $output = t('No existing COPPA relationships.');  
       }  
       break;  
   }  
   
   drupal_set_title(t('COPPA'));  
   drupal_set_breadcrumb($bcrumb);  
   return $output;  
 }  
   
 /**  
  * Informational page to prompt user about COPPA requirements  
22   */   */
23  function coppa_info() {  function coppa_menu() {
24    $content = '<p>'. t('COPPA compliance is required on this site. Please remind your learning coach to sign your COPPA forms.') .'</p>';    $items['admin/settings/coppa'] = array(
25    return $content;      'title' => 'COPPA Settings',
26  }      'description' => 'Settings for COPPA compliance.',
27        'page callback' => 'drupal_get_form',
28  /**      'page arguments' => array('coppa_admin_settings'),
29   * menu callback for coppa/sign      'access arguments' => array('administer users'),
30   */      'file' => 'coppa.admin.inc',
 function coppa_sign() {  
   global $user;  
   $result = db_query("SELECT DISTINCT u.uid, u.name, c.status, pv.value, pv.fid  
                       FROM {users} u, {coppa} c, {profile_fields} pf, profile_values pv  
                       WHERE c.cid=u.uid AND pv.uid = u.uid AND pf.fid = pv.fid AND pf.name = 'profile_dob' AND c.pid = %d", $user->uid);  
   
   $form['children'] = array('#tree' => true);  
   
   $weight = 1;  
   while ($r = db_fetch_object($result)) {  
     $dob = unserialize($r->value);  
   
     $coppa_needed = coppa_needed($r->uid);  
     $coppa_comply = coppa_is_compliant($r->uid);  
   
     $form['children'][$r->uid]['tracker'] = array('#type' => 'markup', '#value' => l($r->name, 'tracker/'. $r->uid));  
     $form['children'][$r->uid]['dob'] = array('#type' => 'markup', '#value' => $dob['month'] .'/'. $dob['day'] .'/'. $dob['year']);  
     if ($coppa_needed && !$coppa_comply) {  
       // we need to render the field unchecked  
       $checked = 0;  
       $disabled = 0;  
     }  
     elseif ($coppa_needed && $coppa_comply) {  
       // we need to render the field checked and disabled  
       $checked = 1;  
       $disabled = 1;  
     }  
     else {  
       // unchecked disabled  
       $checked = 1;  
       $disabled = 1;  
     }  
     $form['children'][$r->uid]['status'] = array('#type' => 'checkbox',  
                                                 '#default_value' => $checked,  
                                                 '#disabled' => $disabled);  
   }  
   $form['submit'] = array('#type' => 'submit', '#value' => t('Confirm COPPA'));  
   
   if (count($form['children']) == 1) {  
     return array('message' => array('#type' => 'message', '#value' => t('You do not have any children associated to you.')));  
   }  
   else {  
     return $form;  
   }  
 }  
   
 function coppa_sign_submit($form_id, $edit) {  
   global $user;  
   foreach ($edit['children'] as $cid => $status) {  
     if ($status['status'] == 1) {  
       $result = db_query("SELECT status FROM {coppa} WHERE pid= %d AND cid= %d", $user->pid, $cid);  
       $r = db_fetch_object($result);  
       if ($r->status == 0) {  
         $result = db_query("UPDATE {coppa} SET status = %d WHERE pid = %d AND cid = %d", time(), $user->uid, $cid);  
       }  
     }  
   }  
 }  
   
 function theme_coppa_sign($edit) {  
   $rows = array();  
   if (!isset($edit['children'])) {  
     $output = $edit['message']['#value'];  
   }  
   else {  
     $header = array(t('User History'), t('Date of Birth'), t('COPPA Status'));  
   
     foreach (element_children($edit['children']) as $uid) {  
       unset($row);  
       $row[] = drupal_render($edit['children'][$uid]['tracker']);  
       $row[] = drupal_render($edit['children'][$uid]['dob']);  
       unset($edit['children'][$uid]['status']['#title']);  
       $row[] = drupal_render($edit['children'][$uid]['status']);  
       $rows[] = $row;  
     }  
   
     $output = theme('table', $header, $rows, array('id' => 'coppa-sign'));  
     $output .= '<br />';  
     $output .= drupal_render($edit['submit']);  
   
     $output .= drupal_render($edit['form_id']);  
   
     $output .= drupal_render($edit['form_token']);  
   }  
   return $output;  
   
 }  
   
 /**  
  * Form for COPPA admin settings  
  */  
 function coppa_admin_settings() {  
   $roles = user_roles(TRUE);  
   
   $selected = variable_get('coppa_roles', 0);  
   $selected = unserialize($selected);  
   
   $form['coppa_roles'] = array(  
     '#type' => 'select',  
     '#multiple' => true,  
     '#title' => t('Roles that will require COPPA compliance'),  
     '#default_value' => $selected,  
     '#size' => min(12, count($roles)),  
     '#options' => $roles,  
     '#description' => t('Please be aware that setting this for unintended roles can lock users out of the system.'),  
31    );    );
32    
33    return system_settings_form($form);    $items['coppa/required'] = array(
34  }      'title' => 'COPPA Requirements',
35        'page callback' => 'coppa_info',
36  /**      'type' => MENU_CALLBACK,
37   * Form submission handler for coppa_admin_settings      'access arguments' => array('access content'),
38   */      'file' => 'coppa.admin.inc',
39  function coppa_admin_settings_submit($form_id, $edit) {    );
   $coppa_roles = $edit['coppa_roles'];  
   variable_set('coppa_roles', serialize($coppa_roles));  
 }  
   
 /**  
  * Form for admin/coppa/set-parents  
  */  
 function coppa_set_parents() {  
   $form['rows'] = array('#tree' => true);  
   
   for ($i = 0; $i < COPPA_ADD_ELEMENTS; $i++) {  
     $form['rows'][$i]['parent'] = array(  
       '#type' => 'textfield',  
       '#title' => t('Parent'),  
       '#maxlength' => 60,  
       '#size' => 32,  
       '#autocomplete_path' => 'user/autocomplete',  
       '#default_value' => ''  
     );  
     $form['rows'][$i]['child'] = array(  
       '#type' => 'textfield',  
       '#title' => t('Child'),  
       '#maxlength' => 60,  
       '#size' => 32,  
       '#autocomplete_path' => 'user/autocomplete',  
       '#default_value' => ''  
     );  
   }  
   $form['submit'] = array('#type' => 'submit', '#value' => t('Set Parent to Child Relationships'));  
   return $form;  
 }  
   
 /**  
  * Form validation for coppa_set_parents_submit  
  */  
 function coppa_set_parents_validate($form_id, $form) {  
   foreach ($form['rows'] as $rid => $row) {  
     if (!empty($row['parent'])) {  
       if (!empty($row['child'])) {  
         // make sure both are valid usernames (is there a better way than user_load?)  
         $parent = user_load(array('name' => $row['parent']));  
         $child = user_load(array('name' => $row['child']));  
   
         if (empty($parent->uid)) {  
           form_set_error("$rid][parent", t('Parent name %parentname is invalid', array('%parentname' => $row['parent'])));  
         }  
         else {  
           if (empty($child->uid)) {  
             form_set_error("$rid][child", t('Child name %childname is invalid', array('%childname' => $row['child'])));  
           }  
           else {  
             if ($parent->uid == $child->uid) {  
               form_set_error($rid, t('Parent and Child (%name) cannot be the same person', array('%name' => $child->name)));  
             }  
             else {  
               // both are valid users let's see if we have an entry in coppa  
               $result = db_query('SELECT cid, pid, status FROM {coppa} WHERE cid = %d AND pid = %d', $child->uid, $parent->uid);  
               $coppa = db_fetch_object($result);  
               if (isset($coppa->cid) && isset($coppa->pid)) {  
                 form_set_error($rid, t('Relationship %parent to %child already exists', array('%parent' => $parent->name, '%child' => $child->name)));  
               }  
               else {  
                 // parent and child are valid users and no existing record in coppa  
                 // validation is good  
               }  
             }  
           }  
         }  
       }  
       else {  
         form_set_error("$rid][child", t('Parent (%name) and Child must both be completed', array('%name' => $row['parent'])));  
       }  
     }  
     else {  // parent empty  
       if (!empty($row['child'])) {  
         form_set_error("$rid][child", t('Parent and Child (%name) fields must both be completed', array('%name' => $row['child'])));  
       }  
       else {  
         // empty row... do nothing  
       }  
     }  
   }  
 }  
   
 /**  
  * Form submission handler for coppa/set-parents  
  */  
 function coppa_set_parents_submit($form_id, $form) {  
   
   $inserted = array();  // keep track of what we've added to avoid double inserts (we didn't check for that in validate)  
   
   // form has gone through validation  
   foreach ($form['rows'] as $rid => $row) {  
     if (!empty($row['child']) && !empty($row['parent'])) {  
       $child = user_load(array('name' => $row['child']));  
       $parent = user_load(array('name' => $row['parent']));  
   
       if (!isset($inserted[$parent->uid])) {  
         $inserted[$parent->uid] = array();  
       }  
   
       if (!in_array($child->uid, $inserted[$parent->uid])) {  
         db_query('INSERT INTO {coppa} (cid, pid, status) values(%d, %d, %d)', $child->uid, $parent->uid, 0);  
         drupal_set_message(t('Added %cname as child of %pname', array('%cname' => $child->name, '%pname' => $parent->name)));  
         $inserted[$parent->uid][] = $child->uid;  
       }  
       else {  
         // duplicate entry no big thing... just don't try to double insert  
       }  
     }  
   }  
 }  
   
 /**  
  * Theme for coppa_set_parents to display in tabular format  
  */  
 function theme_coppa_set_parents($edit) {  
   $rows = array();  
   
   $header = array(t('Parent'), t('Child'));  
   
   foreach (element_children($edit['rows']) as $r) {  
     unset($row);  
   
     unset($edit['rows'][$r]['parent']['#title']);  
     $row[] = drupal_render($edit['rows'][$r]['parent']);  
   
     unset($edit['rows'][$r]['child']['#title']);  
     $row[] = drupal_render($edit['rows'][$r]['child']);  
   
     $rows[] = $row;  
   }  
   
   $output = theme('table', $header, $rows, array('id' => 'coppa-set-parents'));  
   $output .= '<br />';  
   $output .= drupal_render($edit['submit']);  
   
   $output .= drupal_render($edit['form_id']);  
   
   $output .= drupal_render($edit['form_token']);  
   
   return $output;  
 }  
   
 /**  
  * form handler for coppa/manage-relationships  
  */  
 function coppa_manage_relationships() {  
   $form = array();  
   $date_format = 'n j Y'; // m/d/y  
   $result = db_query("SELECT cu.uid as cid, cu.name as cname, pu.uid as pid, pu.name as pname, pv.value as dob, c.status  
                       FROM {users} cu  
                       JOIN {coppa} c ON cu.uid = c.cid  
                       JOIN {users} pu ON pu.uid = c.pid  
                       LEFT JOIN profile_values pv ON pv.uid = c.cid  
                       LEFT JOIN profile_fields pf ON pv.fid = pf.fid");  
   
   
   while ($r = db_fetch_object($result)) {  
     $r->dob = unserialize($r->dob);  
   
     if (!isset($form[$r->pid])) {  
       $form[$r->pid] = array('#tree' => true);  
     }  
     if (!isset($form[$r->pid][$r->cid])) {  
       $form[$r->pid][$r->cid] = array('#tree' => true);  
     }  
   
     $form[$r->pid][$r->cid]['parent'] = array('#type' => 'markup', '#value' => l($r->pname, 'user/'. $r->pid));  
     $form[$r->pid][$r->cid]['child'] = array('#type' => 'markup', '#value' => l($r->cname, 'user/'. $r->cid));  
   
     if (!empty($r->dob)) {  
       $form[$r->pid][$r->cid]['dob'] = array('#type' => 'markup', '#value' => $r->dob['month'] .'/'. $r->dob['day'] .'/'. $r->dob['year']);  
     }  
     else {  
       $form[$r->pid][$r->cid]['dob'] = array('#type' => 'markup', '#value' => t('No DOB on record'));  
     }  
   
     switch ($r->status) {  
       case 0:  
         if (coppa_needed($r->cid)) {  
           $message = t('COPPA Needed');  
         }  
         else {  
           $message = t('COPPA Unnecessary');  
         }  
         $form[$r->pid][$r->cid]['status'] = array('#type' => 'markup', '#value' => $message);  
         break;  
       case 1:  
         $form[$r->pid][$r->cid]['status'] = array('#type' => 'markup', '#value' => t('N/A'));  
         break;  
       default:  
         $form[$r->pid][$r->cid]['status'] = array('#type' => 'markup', '#value' => t('Compliant on: %date', array('%date' => date($date_format, $r->status))));  
         break;  
     }  
   
     $form[$r->pid][$r->cid]['remove'] = array('#type' => 'checkbox', '#default_value' => 0);  
   }  
   
   if (!empty($form)) {  
     $form['submit'] = array('#type' => 'submit', '#value' => t('Remove Selected Parent to Child Relationships'));  
   }  
   return $form;  
 }  
40    
41  /**    $items['admin/user/coppa'] = array(
42   * form submission routine to remove user relationships      'title' => 'Manage COPPA Relationships',
43   */      'description' => 'Manage and add COPPA Relationships',
44  function coppa_manage_relationships_submit($form_id, $form) {      'page callback' => 'coppa_manage_page',
45        'page arguments' => array('manage'),
46    foreach ($form as $pid => $child) {      'access arguments' => array('administer users'),
47      if (is_numeric($pid)) {      'file' => 'coppa.admin.inc',
48        foreach ($child as $cid => $remove) {    );
         if ($remove['remove']) {  
           db_query('DELETE FROM {coppa} WHERE pid = %d AND cid = %d', $pid, $cid);  
         }  
       }  
     }  
   }  
 }  
49    
50  /**    $items['admin/user/coppa/add'] = array(
51   * theming function for coppa_manage_relationships      'title' => 'COPPA Add Relationships',
52  */      'access arguments' => array('administer users'),
53  function theme_coppa_manage_relationships($edit) {      'page callback' => 'coppa_manage_page',
54    $rows = array();      'page arguments' => array('add'),
55        'type' => MENU_LOCAL_TASK,
56    $header = array(t('Parent'), t('Child'), t('Date of Birth'), t('COPPA Status'), t('Delete Relationship'));      'file' => 'coppa.admin.inc',
57      );
   foreach (element_children($edit) as $parent) {  
     foreach (element_children($edit[$parent]) as $child) {  
       unset($row);  
       $row[] = drupal_render($edit[$parent][$child]['parent']);  
       $row[] = drupal_render($edit[$parent][$child]['child']);  
       $row[] = drupal_render($edit[$parent][$child]['dob']);  
       $row[] = drupal_render($edit[$parent][$child]['status']);  
       $row[] = drupal_render($edit[$parent][$child]['remove']);  
       $rows[] = $row;  
     }  
   }  
58    
59    $output = theme('table', $header, $rows, array('id' => 'coppa-manage-relationships'));    $items['admin/user/coppa/manage'] = array(
60    $output .= '<br />';      'title' => 'COPPA Manage Relationships',
61    $output .= drupal_render($edit['submit']);      'access arguments' => array('administer users'),
62        'page callback' => 'coppa_manage_page',
63        'page arguments' => array('manage'),
64        'type' => MENU_DEFAULT_LOCAL_TASK,
65        'file' => 'coppa.admin.inc',
66      );
67    
68    $output .= drupal_render($edit['form_id']);    $items['coppa/sign'] = array(
69        'title' => 'Sign COPPA compliance',
70        'page callback' => 'drupal_get_form',
71        'page arguments' => array('coppa_sign'),
72        'access arguments' => array('access content'),
73        'file' => 'coppa.admin.inc',
74      );
75    
76    $output .= drupal_render($edit['form_token']);    return $items;
   return $output;  
77  }  }
78    
79  /**  /**
80   * Implementation of hook_user   * Implementation of hook_user().
81   *   *
82   */   */
83  function coppa_user($op, &$edit, &$account, $category = NULL) {  function coppa_user($op, &$edit, &$account, $category = NULL) {
# Line 596  function coppa_is_compliant($uid) { Line 196  function coppa_is_compliant($uid) {
196  /**  /**
197   * given a uid this returns an array with month, day, and year populated for the particular uid   * given a uid this returns an array with month, day, and year populated for the particular uid
198   * if a profile_dob entry is not found for this user then an empty array is returned   * if a profile_dob entry is not found for this user then an empty array is returned
199  */   */
200  function coppa_get_dob($uid) {  function coppa_get_dob($uid) {
201    $result = db_query("SELECT pv.value FROM {profile_values} pv, profile_fields pf, users u WHERE pf.name = 'profile_dob' AND pf.fid = pv.fid AND pv.uid = u.uid AND u.uid = %d", $uid);    $result = db_query("SELECT pv.value FROM {profile_values} pv, profile_fields pf, users u WHERE pf.name = 'profile_dob' AND pf.fid = pv.fid AND pv.uid = u.uid AND u.uid = %d", $uid);
202    $r = db_fetch_array($result);    $r = db_fetch_array($result);
# Line 607  function coppa_get_dob($uid) { Line 207  function coppa_get_dob($uid) {
207      return array();      return array();
208    }    }
209  }  }
210    
211    /**
212     * Implementation of hook_theme().
213     */
214    function coppa_theme() {
215      return array(
216        'coppa_sign' => array(
217          'arguments' => array('edit' => NULL),
218          'file' => 'coppa.admin.inc',
219        ),
220        'coppa_set_parents' => array(
221          'arguments' => array('edit' => NULL),
222          'file' => 'coppa.admin.inc',
223        ),
224      );
225    }
226    
227    /**
228     * Pull age from the user registration form.
229     *
230     * @return integer
231     *   Unix timestamp representing the DOB on the form.
232     */
233    function _coppa_get_dob_form($form, $form_state) {
234      $field = variable_get('coppa_dob_field', 'profile_dob');
235      $field  = 'field_dob';
236      $type = variable_get('coppa_dob_field_type', 'profile');
237      $type = 'content_profile';
238    
239      switch ($type) {
240        case 'profile':
241          // @todo
242          break;
243    
244        case 'content_profile':
245          $dob = $form_state['values'][$field][0]['value'];
246          return strtotime($dob);
247      }
248    }
249    
250    /**
251     * Determine age based on DOB.
252     */
253    function coppa_age($dob, $now = NULL) {
254      if (!$now) {
255        $now = time();
256      }
257    
258    
259      return 12;
260    }

Legend:
Removed from v.1.4  
changed lines
  Added in v.1.5

  ViewVC Help
Powered by ViewVC 1.1.2