### Eclipse Workspace Patch 1.0 #P drupal Index: modules/profile/profile.install =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.install,v retrieving revision 1.8 diff -u -r1.8 profile.install --- modules/profile/profile.install 28 Nov 2006 14:37:44 -0000 1.8 +++ modules/profile/profile.install 4 May 2007 17:25:15 -0000 @@ -8,12 +8,21 @@ switch ($GLOBALS['db_type']) { case 'mysql': case 'mysqli': + db_query("CREATE TABLE {profile_categories} ( + cid int NOT NULL auto_increment, + title varchar(255) default NULL, + name varchar(128) default NULL, + explanation text, + weight tinyint(4) DEFAULT '0' NOT NULL, + PRIMARY KEY (cid) + ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); + db_query("CREATE TABLE {profile_fields} ( fid int NOT NULL auto_increment, + cid int NOT NULL DEFAULT '0', title varchar(255) default NULL, name varchar(128) default NULL, explanation TEXT, - category varchar(255) default NULL, page varchar(255) default NULL, type varchar(128) default NULL, weight tinyint DEFAULT '0' NOT NULL, @@ -22,7 +31,7 @@ visibility tinyint DEFAULT '0' NOT NULL, autocomplete tinyint DEFAULT '0' NOT NULL, options text, - KEY category (category), + KEY(cid), UNIQUE KEY name (name), PRIMARY KEY (fid) ) /*!40100 DEFAULT CHARACTER SET UTF8 */ "); @@ -71,6 +80,7 @@ * Implementation of hook_uninstall(). */ function profile_uninstall() { + db_query('DROP TABLE {profile_categories}'); db_query('DROP TABLE {profile_fields}'); db_query('DROP TABLE {profile_values}'); variable_del('profile_block_author_fields'); Index: modules/profile/profile.module =================================================================== RCS file: /cvs/drupal/drupal/modules/profile/profile.module,v retrieving revision 1.200 diff -u -r1.200 profile.module --- modules/profile/profile.module 30 Apr 2007 17:03:27 -0000 1.200 +++ modules/profile/profile.module 4 May 2007 17:25:16 -0000 @@ -79,24 +79,44 @@ 'description' => 'Create customizable fields for your users.', 'page callback' => 'profile_admin_overview', ); - $items['admin/user/profile/add'] = array( + $items['admin/user/profile/list'] = array( + 'title' => 'List', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items['admin/user/profile/add/category'] = array( + 'title' => 'Add category', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('profile_category_form'), + 'type' => MENU_LOCAL_TASK, + 'parent' => 'admin/user/profile', + ); + $items['admin/user/profile/add/field'] = array( 'title' => 'Add field', + 'page callback' => 'profile_admin_add_field_page', + 'access callback' => '_profile_add_field_access', + 'type' => MENU_LOCAL_TASK, + 'parent' => 'admin/user/profile', + ); + $items['admin/user/profile/edit/category'] = array( + 'title' => 'Edit category', 'page callback' => 'drupal_get_form', - 'page arguments' => array('profile_field_form'), + 'page arguments' => array('profile_category_form'), 'type' => MENU_CALLBACK, ); - $items['admin/user/profile/autocomplete'] = array( - 'title' => 'Profile category autocomplete', - 'page callback' => 'profile_admin_settings_autocomplete', + $items['admin/user/profile/delete/category'] = array( + 'title' => 'Delete category', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('profile_category_delete'), 'type' => MENU_CALLBACK, ); - $items['admin/user/profile/edit'] = array( + $items['admin/user/profile/edit/field'] = array( 'title' => 'Edit field', 'page callback' => 'drupal_get_form', 'page arguments' => array('profile_field_form'), 'type' => MENU_CALLBACK, ); - $items['admin/user/profile/delete'] = array( + $items['admin/user/profile/delete/field'] = array( 'title' => 'Delete field', 'page callback' => 'drupal_get_form', 'page arguments' => array('profile_field_delete'), @@ -111,6 +131,14 @@ return $items; } +function _profile_add_field_access() { + // Access is allowed if there are existing categories + if ( _profile_categories()) { + return TRUE; + } else { + return FALSE; + } +} /** * Implementation of hook_block(). */ @@ -205,6 +233,120 @@ } /** + * Menu callback: Generate a form to add/edit a user profile category. + */ +function profile_category_form($arg = NULL) { + if (arg(3) == 'edit') { + if (is_numeric($arg)) { + $fid = $arg; + + $edit = db_fetch_array(db_query('SELECT * FROM {profile_categories} WHERE cid = %d', $fid)); + + if (!$edit) { + drupal_not_found(); + return; + } + drupal_set_title(t('edit %title', array('%title' => $edit['title']))); + $form['cid'] = array('#type' => 'value', + '#value' => $fid, + ); + } + else { + drupal_not_found(); + return; + } + } else { + $edit = array(); + } + $edit += array( + 'name' => '', + 'title' => '', + 'explanation' => '', + 'weight' => '', + ); + $form['fields'] = array('#type' => 'fieldset', + '#title' => t('Category settings'), + ); + $form['fields']['title'] = array('#type' => 'textfield', + '#title' => t('Title'), + '#default_value' => $edit['title'], + '#description' => t('The title of the new category. The title will be shown to the user. An example title is "Personal information".'), + '#required' => TRUE, + ); + $form['fields']['name'] = array('#type' => 'textfield', + '#title' => t('Form name'), + '#default_value' => $edit['name'], + '#description' => t('The name of the category. The form name is not shown to the user but used internally in the HTML code and URLs. +Unless you know what you are doing, it is highly recommended that you prefix the form name with profile_ to avoid name clashes with other categories. Spaces or any other special characters except dash (-) and underscore (_) are not allowed. An example name is "profile_favorite_color" or perhaps just "profile_color".'), + '#required' => TRUE, + ); + $form['fields']['explanation'] = array('#type' => 'textarea', + '#title' => t('Explanation'), + '#default_value' => $edit['explanation'], + '#description' => t('An optional explanation to go with the new category. The explanation will be shown to the user.'), + ); + $form['fields']['weight'] = array('#type' => 'weight', + '#title' => t('Weight'), + '#default_value' => $edit['weight'], + '#delta' => 5, + '#description' => t('The weights define the order in which the categories are shown. Lighter categories are shown first.'), + ); + $form['submit'] = array('#type' => 'submit', + '#value' => t('Save category'), + ); + return $form; +} + +/** + * Validate profile_category_form submissions. + */ +function profile_category_form_validate($form_id, $form_values) { + // Validate the 'field name': + if (preg_match('/[^a-zA-Z0-9_-]/', $form_values['name'])) { + form_set_error('name', t('The specified form name contains one or more illegal characters. Spaces or any other special characters except dash (-) and underscore (_) are not allowed.')); + } + // Validate the category name + if ($form_values['name'] == 'account' || in_array($form_values['name'], user_fields())) { + form_set_error('name', t('The specified form name is reserved for use by Drupal.')); + } + + $args1 = array($form_values['title']); + $args2 = array($form_values['name']); + $query_suffix = ''; + + if (isset($form_values['cid'])) { + $args1[] = $args2[] = $form_values['cid']; + $query_suffix = ' AND cid != %d'; + } + + if (db_result(db_query("SELECT cid FROM {profile_categories} WHERE title = '%s'". $query_suffix, $args1))) { + form_set_error('title', t('The specified title is already in use.')); + } + if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'". $query_suffix, $args2))) { + form_set_error('name', t('The specified name is already in use.')); + } +} +/** + * Process profile_category_form submissions. + */ +function profile_category_form_submit($form_id, $form_values) { + if (!isset($form_values['cid'])) { + db_query("INSERT INTO {profile_categories} (title, name, explanation, weight) VALUES ('%s', '%s', '%s', %d)", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['weight']); + + drupal_set_message(t('The category has been created.')); + watchdog('profile', 'Profile category %title added.', array('%title' => $form_values['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/user/profile')); + } + else { + db_query("UPDATE {profile_categories} SET title = '%s', name = '%s', explanation = '%s', weight = %d WHERE cid = %d", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['weight'], $form_values['cid']); + + drupal_set_message(t('The category has been updated.')); + } + cache_clear_all(); + menu_rebuild(); + + return 'admin/user/profile'; +} +/** * Menu callback: Generate a form to add/edit a user profile field. */ function profile_field_form($arg = NULL) { @@ -230,18 +372,13 @@ } } else { - $types = _profile_field_types(); - if (!isset($types[$arg])) { - drupal_not_found(); - return; - } $type = $arg; - drupal_set_title(t('add new %type', array('%type' => $types[$type]))); + drupal_set_title(t('add new %type', array('%type' => _profile_field_types($type)))); $edit = array('name' => 'profile_'); $form['type'] = array('#type' => 'value', '#value' => $type); } $edit += array( - 'category' => '', + 'cid' => '', 'title' => '', 'explanation' => '', 'weight' => '', @@ -253,10 +390,10 @@ $form['fields'] = array('#type' => 'fieldset', '#title' => t('Field settings'), ); - $form['fields']['category'] = array('#type' => 'textfield', + $form['fields']['cid'] = array('#type' => 'select', '#title' => t('Category'), - '#default_value' => $edit['category'], - '#autocomplete_path' => 'admin/user/profile/autocomplete', + '#default_value' => $edit['cid'], + '#options' => _profile_categories(), '#description' => t('The category the new field should be part of. Categories are used to group fields logically. An example category is "Personal information".'), '#required' => TRUE, ); @@ -340,14 +477,8 @@ if (in_array($form_values['name'], user_fields())) { form_set_error('name', t('The specified form name is reserved for use by Drupal.')); } - // Validate the category: - if (!$form_values['category']) { - form_set_error('category', t('You must enter a category.')); - } - if ($form_values['category'] == 'account') { - form_set_error('category', t('The specified category name is reserved for use by Drupal.')); - } - $args1 = array($form_values['title'], $form_values['category']); + + $args1 = array($form_values['title'], $form_values['cid']); $args2 = array($form_values['name']); $query_suffix = ''; @@ -356,7 +487,7 @@ $query_suffix = ' AND fid != %d'; } - if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s' AND category = '%s'". $query_suffix, $args1))) { + if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE title = '%s' AND cid = %d". $query_suffix, $args1))) { form_set_error('title', t('The specified title is already in use.')); } if (db_result(db_query("SELECT fid FROM {profile_fields} WHERE name = '%s'". $query_suffix, $args2))) { @@ -369,13 +500,13 @@ */ function profile_field_form_submit($form_id, $form_values) { if (!isset($form_values['fid'])) { - db_query("INSERT INTO {profile_fields} (title, name, explanation, category, type, weight, required, register, visibility, autocomplete, options, page) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d, %d, %d, '%s', '%s')", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['category'], $form_values['type'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['autocomplete'], $form_values['options'], $form_values['page']); + db_query("INSERT INTO {profile_fields} (title, name, explanation, cid, type, weight, required, register, visibility, autocomplete, options, page) VALUES ('%s', '%s', '%s', %d, '%s', %d, %d, %d, %d, %d, '%s', '%s')", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['cid'], $form_values['type'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['autocomplete'], $form_values['options'], $form_values['page']); drupal_set_message(t('The field has been created.')); - watchdog('profile', 'Profile field %field added under category %category.', array('%field' => $form_values['title'], '%category' => $form_values['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/user/profile')); + watchdog('profile', 'Profile field %field added under category %category.', array('%field' => $form_values['title'], '%category' => _profile_categories($form_values['category'])), WATCHDOG_NOTICE, l(t('view'), 'admin/user/profile')); } else { - db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', category = '%s', weight = %d, required = %d, register = %d, visibility = %d, autocomplete = %d, options = '%s', page = '%s' WHERE fid = %d", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['category'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['autocomplete'], $form_values['options'], $form_values['page'], $form_values['fid']); + db_query("UPDATE {profile_fields} SET title = '%s', name = '%s', explanation = '%s', cid = %d, weight = %d, required = %d, register = %d, visibility = %d, autocomplete = %d, options = '%s', page = '%s' WHERE fid = %d", $form_values['title'], $form_values['name'], $form_values['explanation'], $form_values['cid'], $form_values['weight'], $form_values['required'], $form_values['register'], $form_values['visibility'], $form_values['autocomplete'], $form_values['options'], $form_values['page'], $form_values['fid']); drupal_set_message(t('The field has been updated.')); } @@ -386,6 +517,44 @@ } /** + * Menu callback; deletes a category and all related fields from all user profiles. + */ +function profile_category_delete($cid) { + $category = db_fetch_object(db_query("SELECT title FROM {profile_categories} WHERE cid = %d", $cid)); + if (!$category) { + drupal_not_found(); + return; + } + $form['cid'] = array('#type' => 'value', '#value' => $cid); + $form['title'] = array('#type' => 'value', '#value' => $category->title); + + return confirm_form($form, + t('Are you sure you want to delete the category %category and all the fields in this category?', array('%category' => $category->title)), 'admin/user/profile', + t('This action cannot be undone. If users have entered values into this category\'s fields in their profile, these entries will also be deleted.'), + t('Delete'), t('Cancel')); +} + +/** + * Process a category delete form submission. + */ +function profile_category_delete_submit($form_id, $form_values) { + // Delete all fields for this category first + $result = db_query('SELECT * FROM {profile_fields} WHERE cid = %d', $form_values['cid']); + while ($field = db_fetch_object($result)) { + db_query('DELETE FROM {profile_values} WHERE fid = %d', $field->fid); + drupal_set_message(t('The field %field has been deleted.', array('%field' => $field->title))); + } + db_query('DELETE FROM {profile_fields} WHERE cid = %d', $form_values['cid']); + db_query('DELETE FROM {profile_categories} WHERE cid = %d', $form_values['cid']); + cache_clear_all(); + + drupal_set_message(t('The category %category has been deleted.', array('%category' => $form_values['title']))); + watchdog('profile', 'Profile category %category deleted.', array('%category' => $form_values['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/user/profile')); + + return 'admin/user/profile'; +} + +/** * Menu callback; deletes a field from all user profiles. */ function profile_field_delete($fid) { @@ -422,30 +591,58 @@ * Menu callback; display a listing of all editable profile fields. */ function profile_admin_overview() { - - $result = db_query('SELECT * FROM {profile_fields} ORDER BY category, weight'); + // List categories + $output = '

'. t('Category overview') .'

'; + $header = array(t('Title'), t('Name'), array('data' => t('Operations'), 'colspan' => '2')); $rows = array(); - while ($field = db_fetch_object($result)) { - $rows[] = array(check_plain($field->title), $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/user/profile/edit/$field->fid"), l(t('delete'), "admin/user/profile/delete/$field->fid")); - } - if (count($rows) == 0) { - $rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6')); - } - - $header = array(t('Title'), t('Name'), t('Type'), t('Category'), array('data' => t('Operations'), 'colspan' => '2')); - - $output = theme('table', $header, $rows); - $output .= '

'. t('Add new field') .'

'; - $output .= ''; - + $output .= theme('table', $header, $rows); return $output; } /** + * Menu callback; add field page + */ +function profile_admin_add_field_page($arg = NULL) { + $types = _profile_field_types(); + if ($arg && isset($types[$arg])) { + return drupal_get_form('profile_field_form', $arg); + } else { + $output = ''; + return $output; + } +} +/** * Menu callback; display a list of user information. */ function profile_browse() { @@ -611,15 +808,15 @@ } function profile_view_profile($user) { - + $categories = _profile_categories(); 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} WHERE visibility != %d ORDER BY category, weight', PROFILE_HIDDEN); + $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d ORDER BY cid, weight', PROFILE_HIDDEN); } else { - $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND visibility != %d ORDER BY category, weight', PROFILE_PRIVATE, PROFILE_HIDDEN); + $result = db_query('SELECT * FROM {profile_fields} WHERE visibility != %d AND visibility != %d ORDER BY cid, weight', PROFILE_PRIVATE, PROFILE_HIDDEN); } $fields = array(); @@ -630,7 +827,7 @@ 'value' => $value, 'class' => $field->name, ); - $fields[$field->category][$field->name] = $item; + $fields[$categories[$field->cid]][$field->name] = $item; } } return $fields; @@ -655,14 +852,14 @@ $w = 0; $fields = array(); while ($field = db_fetch_object($result)) { - $category = $field->category; - if (!isset($fields[$category])) { - $fields[$category] = array('#type' => 'fieldset', '#title' => $category, '#weight' => $w++); + $category = db_fetch_object(db_query("SELECT * FROM {profile_categories} WHERE name = '%s'", $category)); + if (!isset($fields[$category->name])) { + $fields[$category->name] = array('#type' => 'fieldset', '#title' => $category->title, '#weight' => $w++, '#description' => $category->explanation); } switch ($field->type) { case 'textfield': case 'url': - $fields[$category][$field->name] = array('#type' => 'textfield', + $fields[$category->name][$field->name] = array('#type' => 'textfield', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#maxlength' => 255, @@ -674,7 +871,7 @@ } break; case 'textarea': - $fields[$category][$field->name] = array('#type' => 'textarea', + $fields[$category->name][$field->name] = array('#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), @@ -682,7 +879,7 @@ ); break; case 'list': - $fields[$category][$field->name] = array('#type' => 'textarea', + $fields[$category->name][$field->name] = array('#type' => 'textarea', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), @@ -690,7 +887,7 @@ ); break; case 'checkbox': - $fields[$category][$field->name] = array('#type' => 'checkbox', + $fields[$category->name][$field->name] = array('#type' => 'checkbox', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), @@ -705,7 +902,7 @@ $options[$line] = $line; } } - $fields[$category][$field->name] = array('#type' => 'select', + $fields[$category->name][$field->name] = array('#type' => 'select', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#options' => $options, @@ -714,7 +911,7 @@ ); break; case 'date': - $fields[$category][$field->name] = array('#type' => 'date', + $fields[$category->name][$field->name] = array('#type' => 'date', '#title' => check_plain($field->title), '#default_value' => $edit[$field->name], '#description' => _profile_form_explanation($field), @@ -771,10 +968,10 @@ } function profile_categories() { - $result = db_query("SELECT DISTINCT(category) FROM {profile_fields}"); + $result = db_query("SELECT * FROM {profile_categories} ORDER BY weight"); $data = array(); - while ($category = db_fetch_object($result)) { - $data[] = array('name' => $category->category, 'title' => $category->category, 'weight' => 3); + while ($category = db_fetch_array($result)) { + $data[] = $category; } return $data; } @@ -814,6 +1011,15 @@ return $output; } +function _profile_categories() { + $categories = array(); + $result = db_query("SELECT * FROM {profile_categories}"); + while($category = db_fetch_object($result)) { + $categories[$category->cid] = $category->title; + } + return $categories; +} + function _profile_field_types($type = NULL) { $types = array('textfield' => t('single-line textfield'), 'textarea' => t('multi-line textfield'), @@ -831,34 +1037,19 @@ function _profile_get_fields($category, $register = FALSE) { $args = array(); - $sql = 'SELECT * FROM {profile_fields} WHERE '; - $filters = array(); + $filters = $joins = array(); if ($register) { - $filters[] = 'register = 1'; + $filters[] = 'f.register = 1'; } else { - // Use LOWER('%s') instead of PHP's strtolower() to avoid UTF-8 conversion issues. - $filters[] = "LOWER(category) = LOWER('%s')"; + $joins[] = ' INNER JOIN {profile_categories} c ON f.cid = c.cid'; + $filters[] = "c.name = '%s'"; $args[] = $category; } if (!user_access('administer users')) { $filters[] = 'visibility != %d'; $args[] = PROFILE_HIDDEN; } - $sql .= implode(' AND ', $filters); - $sql .= ' ORDER BY category, weight'; + $sql = 'SELECT f.* FROM {profile_fields} f '. implode(' ', $joins). ' WHERE '. implode(' AND ', $filters).' ORDER BY cid, weight'; return db_query($sql, $args); } - -/** - * Retrieve a pipe delimited string of autocomplete suggestions for profile categories - */ -function profile_admin_settings_autocomplete($string) { - $matches = array(); - $result = db_query_range("SELECT category FROM {profile_fields} WHERE LOWER(category) LIKE LOWER('%s%%')", $string, 0, 10); - while ($data = db_fetch_object($result)) { - $matches[$data->category] = check_plain($data->category); - } - print drupal_to_js($matches); - exit(); -}