Index: modules/profile.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/profile.module,v
retrieving revision 1.90.2.2
diff -u -F^f -r1.90.2.2 profile.module
--- modules/profile.module	1 Jun 2005 04:30:07 -0000	1.90.2.2
+++ modules/profile.module	10 Jun 2005 07:39:25 -0000
@@ -9,7 +9,8 @@
 /**
  * Flags to define the visibility of a profile field.
  */
-define('PROFILE_PRIVATE', 1);
+// The functionality of PROFILE_PRIVATE is superceded by role based field permissions.
+//define('PROFILE_PRIVATE', 1);
 define('PROFILE_PUBLIC', 2);
 define('PROFILE_PUBLIC_LISTINGS', 3);
 
@@ -26,6 +27,22 @@ function profile_help($section) {
 }
 
 /**
+ * Implementation of hook_perm().
+ */
+function profile_perm() {
+    // Compile the permissions for each individual profile field.
+    $perms = array();
+    $result = db_query('SELECT name FROM {profile_fields}');
+    while ($record = db_fetch_object($result)) {
+      $perms[] = 'access ' .$record->name;
+      $perms[] = 'access own ' .$record->name;
+      $perms[] = 'edit own ' .$record->name;
+    }
+  
+  return $perms;
+}
+
+/**
  * Implementation of hook_menu().
  */
 function profile_menu($may_cache) {
@@ -68,8 +85,8 @@ function profile_browse() {
   $field = db_fetch_object(db_query("SELECT DISTINCT(fid), type, title, page, visibility FROM {profile_fields} WHERE name = '%s'", $name));
 
   if ($name && $field->fid) {
-    // Do not allow browsing of private fields by non-admins
-    if (!user_access('administer users') && $field->visibility == PROFILE_PRIVATE) {
+    // Do not allow browsing of fields by users who can't access the fields content
+    if (!_profile_perm_access_field($field)) {
        drupal_access_denied();
        return;
     }
@@ -78,7 +95,9 @@ function profile_browse() {
     $fields = array();
     $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE fid != %d AND visibility = %d ORDER BY weight', $field->fid, PROFILE_PUBLIC_LISTINGS);
     while ($record = db_fetch_object($result)) {
-      $fields[] = $record;
+      if (_profile_perm_access_field($record)) {
+        $fields[] = $record;
+      }
     }
 
     // Determine what query to use:
@@ -125,7 +144,9 @@ function profile_browse() {
     $fields = array();
     $result = db_query('SELECT name, title, type FROM {profile_fields} WHERE visibility = %d', PROFILE_PUBLIC_LISTINGS);
     while ($record = db_fetch_object($result)) {
-      $fields[] = $record;
+      if (_profile_perm_access_field($record)) {
+        $fields[] = $record;
+      }
     }
 
     // Extract the affected users:
@@ -161,21 +182,23 @@ function profile_save_profile(&$edit, &$
     // We use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues.
   }
   while ($field = db_fetch_object($result)) {
-    if (_profile_field_serialize($field->type)) {
-       $edit[$field->name] = serialize($edit[$field->name]);
+    if (isset($edit[$field->name])) {
+      if (_profile_field_serialize($field->type)) {
+         $edit[$field->name] = serialize($edit[$field->name]);
+      }
+      db_query("DELETE FROM {profile_values} WHERE fid = %d AND uid = %d", $field->fid, $user->uid);
+      db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]);
+      // Mark field as handled (prevents saving to user->data).
+      $edit[$field->name] = null;
     }
-    db_query("DELETE FROM {profile_values} WHERE fid = %d AND uid = %d", $field->fid, $user->uid);
-    db_query("INSERT INTO {profile_values} (fid, uid, value) VALUES (%d, %d, '%s')", $field->fid, $user->uid, $edit[$field->name]);
-    // Mark field as handled (prevents saving to user->data).
-    $edit[$field->name] = null;
   }
 }
 
 function profile_view_field($user, $field) {
-  // Only allow browsing of private fields for admins
-  $browse = user_access('administer users') || $field->visibility != PROFILE_PRIVATE;
+  // Only allow browsing of  fields by users who can access the fields content for all users
+  $browse = _profile_perm_access_field($field);
 
-  if ($value = $user->{$field->name}) {
+  if (($value = $user->{$field->name}) && _profile_perm_access_field($field, $user)) {
     switch ($field->type) {
       case 'textfield':
         return check_plain($value);
@@ -215,17 +238,11 @@ function profile_view_profile($user) {
 
   profile_load_profile($user);
 
-  // Show private fields to administrators and people viewing their own account.
-  if (user_access('administer users') || $GLOBALS['user']->uid == $user->uid) {
-    $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
-  }
-  else {
-    $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY category, weight', PROFILE_PRIVATE);
-  }
+  $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight');
 
   while ($field = db_fetch_object($result)) {
     if ($value = profile_view_field($user, $field)) {
-      $description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
+      //$description = ($field->visibility == PROFILE_PRIVATE) ? t('The content of this field is private and only visible to yourself.') : '';
       $title = ($field->type != 'checkbox') ? check_plain($field->title) : '';
       $fields[$field->category] .= form_item($title, $value, $description);
     }
@@ -241,9 +258,10 @@ function _profile_form_explanation($fiel
     $output .= ' '. t('Put each item on a separate line or separate them by commas.  No HTML allowed.');
   }
 
-  if ($field->visibility == PROFILE_PRIVATE) {
-    $output .= ' '. t('The content of this field is kept private and will not be shown publicly.');
-  }
+  //  We could read the roles with permission to access this field and print them here.
+  //if ($field->visibility == PROFILE_PRIVATE) {
+  //  $output .= ' '. t('The content of this field is kept private and will not be shown publicly.');
+  //}
 
   return $output;
 }
@@ -260,35 +278,37 @@ function profile_form_profile($edit, $us
 
   $fields = array();
   while ($field = db_fetch_object($result)) {
-    $category = $field->category;
-    switch ($field->type) {
-      case 'textfield':
-      case 'url':
-        $fields[$category] .= form_textfield(check_plain($field->title), $field->name, $edit[$field->name], 70, 255, _profile_form_explanation($field), NULL, $field->required);
-        break;
-      case 'textarea':
-        $fields[$category] .= form_textarea(check_plain($field->title), $field->name, $edit[$field->name], 70, 5, _profile_form_explanation($field), NULL, $field->required);
-        break;
-      case 'list':
-        $fields[$category] .= form_textarea(check_plain($field->title), $field->name, $edit[$field->name], 70, 5, _profile_form_explanation($field), NULL, $field->required);
-        break;
-      case 'checkbox':
-        $fields[$category] .= form_checkbox(check_plain($field->title), $field->name, 1, $edit[$field->name], _profile_form_explanation($field), NULL, $field->required);
-        break;
-      case 'selection':
-        $options = array('--');
-        $lines = split("[,\n\r]", $field->options);
-        foreach ($lines as $line) {
-          if ($line = trim($line)) {
-            $options[$line] = $line;
+    if (_profile_perm_edit_field($field, $user)) {
+      $category = $field->category;
+      switch ($field->type) {
+        case 'textfield':
+        case 'url':
+          $fields[$category] .= form_textfield(check_plain($field->title), $field->name, $edit[$field->name], 70, 255, _profile_form_explanation($field), NULL, $field->required);
+          break;
+        case 'textarea':
+          $fields[$category] .= form_textarea(check_plain($field->title), $field->name, $edit[$field->name], 70, 5, _profile_form_explanation($field), NULL, $field->required);
+          break;
+        case 'list':
+          $fields[$category] .= form_textarea(check_plain($field->title), $field->name, $edit[$field->name], 70, 5, _profile_form_explanation($field), NULL, $field->required);
+          break;
+        case 'checkbox':
+          $fields[$category] .= form_checkbox(check_plain($field->title), $field->name, 1, $edit[$field->name], _profile_form_explanation($field), NULL, $field->required);
+          break;
+        case 'selection':
+          $options = array('--');
+          $lines = split("[,\n\r]", $field->options);
+          foreach ($lines as $line) {
+            if ($line = trim($line)) {
+              $options[$line] = $line;
+            }
           }
-        }
 
-        $fields[$category] .= form_select(check_plain($field->title), $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required);
-        break;
-      case 'date':
-        $fields[$category] .= _profile_date_field($field, $edit);
-        break;
+          $fields[$category] .= form_select(check_plain($field->title), $field->name, $edit[$field->name], $options, _profile_form_explanation($field), 0, 0, $field->required);
+          break;
+        case 'date':
+          $fields[$category] .= _profile_date_field($field, $edit);
+          break;
+      }
     }
   }
 
@@ -348,7 +368,7 @@ function _profile_map_month($month) {
   return format_date(gmmktime(0, 0, 0, $month, 2, 1970), 'custom', 'M', 0);
 }
 
-function profile_validate_profile($edit, $category) {
+function profile_validate_profile($edit, $user, $category) {
 
   if (($_GET['q'] == 'user/register') ? 1 : 0) {
     $result = db_query('SELECT * FROM {profile_fields} WHERE register = 1 ORDER BY category, weight');
@@ -360,13 +380,17 @@ function profile_validate_profile($edit,
 
   while ($field = db_fetch_object($result)) {
     if ($edit[$field->name]) {
+      if (!_profile_perm_edit_field($field, $user)) {
+        form_set_error($field->name, t('You do not have permission to edit %field.', array('%field' => theme('placeholder', $field->title))));      
+      }
+      
       if ($field->type == 'url') {
         if (!valid_url($edit[$field->name], true)) {
           form_set_error($field->name, t('The value provided for %field is not a valid URL.', array('%field' => theme('placeholder', $field->title))));
         }
       }
     }
-    else if ($field->required && !user_access('administer users')) {
+    else if ($field->required && !user_access('administer users') && _profile_perm_edit_field($field, $user)) {
       form_set_error($field->name, t('The field %field is required.', array('%field' => theme('placeholder', $field->title))));
     }
   }
@@ -399,7 +423,7 @@ function profile_user($type, &$edit, &$u
     case 'form':
       return profile_form_profile($edit, $user, $category);
     case 'validate':
-      return profile_validate_profile($edit, $category);
+      return profile_validate_profile($edit, $user, $category);
     case 'categories':
       return profile_categories();
   }
@@ -515,7 +539,7 @@ function _profile_field_form($type, $edi
     $group .= form_textarea(t('Selection options'), 'options', $edit['options'], 70, 8, t('A list of all options.  Put each option on a separate line.  Example options are "red", "blue", "green", etc.'));
   }
   $group .= form_weight(t('Weight'), 'weight', $edit['weight'], 5, t('The weights define the order in which the form fields are shown.  Lighter fields "float up" towards the top of the category.'));
-  $group .= form_radios(t('Visibility'), 'visibility', $edit['visibility'], array(PROFILE_PRIVATE => t('Private field, content only available to privileged users.'), PROFILE_PUBLIC => t('Public field, content shown on profile page but not used on member list pages.'), PROFILE_PUBLIC_LISTINGS => t('Public field, content shown on profile page and on member list pages.')));
+  $group .= form_radios(t('Visibility'), 'visibility', $edit['visibility'], array(PROFILE_PUBLIC => t('Public field, content shown on profile page but not used on member list pages.'), PROFILE_PUBLIC_LISTINGS => t('Public field, content shown on profile page and on member list pages.')));
   if ($type == 'selection' || $type == 'list') {
     $group .= form_textfield(t('Page title'), 'page', $edit['page'], 70, 128, t('The title of the page showing all users with the specified field.  The word <code>%value</code> will be substituted with the corresponding value.  An example page title is "People whose favorite color is %value".  Only applicable if the field is configured to be shown on member list pages.'));
   }
@@ -590,4 +614,17 @@ function _profile_field_serialize($type 
   return $type == 'date';
 }
 
+/*
+ *  Returns true if the logged in user has permission to access/view the $field for $user.
+ */
+function _profile_perm_access_field($field, $user = NULL) {
+  return user_access('administer users') || user_access('access ' .$field->name) || (($user != NULL) && ($GLOBALS['user']->uid == $user->uid) && user_access('access own ' .$field->name));
+}
+
+/*
+ *  Returns true if the logged in user has permission to edit the $field for $user.
+ */
+function _profile_perm_edit_field($field, $user) {
+  return user_access('administer users') || user_access('edit own ' .$field->name);
+}
 ?>
