cms/drupal/modules/profile/profile.admin.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * Administrative page callbacks for the profile module.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Form builder to display a listing of all editable profile fields.
       
    10  *
       
    11  * @ingroup forms
       
    12  * @see profile_admin_overview_submit()
       
    13  */
       
    14 function profile_admin_overview($form) {
       
    15   $result = db_query('SELECT title, name, type, category, fid, weight FROM {profile_field} ORDER BY category, weight');
       
    16 
       
    17   $categories = array();
       
    18   foreach ($result as $field) {
       
    19     // Collect all category information
       
    20     $categories[] = $field->category;
       
    21 
       
    22     // Save all field information
       
    23     $form[$field->fid]['name'] = array('#markup' => check_plain($field->name));
       
    24     $form[$field->fid]['title'] = array('#markup' => check_plain($field->title));
       
    25     $form[$field->fid]['type'] = array('#markup' => $field->type);
       
    26     $form[$field->fid]['category'] = array(
       
    27       '#type' => 'select',
       
    28       '#title' => t('Category for @title', array('@title' => $field->title)),
       
    29       '#title_display' => 'invisible',
       
    30       '#default_value' => $field->category,
       
    31       '#options' => array(),
       
    32     );
       
    33     $form[$field->fid]['weight'] = array(
       
    34       '#type' => 'weight',
       
    35       '#title' => t('Weight for @title', array('@title' => $field->title)),
       
    36       '#title_display' => 'invisible',
       
    37       '#default_value' => $field->weight,
       
    38     );
       
    39     $form[$field->fid]['edit'] = array('#type' => 'link', '#title' => t('edit'), '#href' => "admin/config/people/profile/edit/$field->fid");
       
    40     $form[$field->fid]['delete'] = array('#type' => 'link', '#title' => t('delete'), '#href' => "admin/config/people/profile/delete/$field->fid");
       
    41   }
       
    42 
       
    43   // Add the category combo boxes
       
    44   $categories = array_unique($categories);
       
    45   foreach ($form as $fid => $field) {
       
    46     foreach ($categories as $cat => $category) {
       
    47       $form[$fid]['category']['#options'][$category] = $category;
       
    48     }
       
    49   }
       
    50 
       
    51   // Display the submit button only when there's more than one field
       
    52   if (count($form) > 1) {
       
    53     $form['actions'] = array('#type' => 'actions');
       
    54     $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration'));
       
    55   }
       
    56   else {
       
    57     // Disable combo boxes when there isn't a submit button
       
    58     foreach ($form as $fid => $field) {
       
    59       unset($form[$fid]['weight']);
       
    60       $form[$fid]['category']['#type'] = 'value';
       
    61     }
       
    62   }
       
    63   $form['#tree'] = TRUE;
       
    64 
       
    65   // @todo: Any reason this isn't done using an element with #theme = 'links'?
       
    66   $addnewfields = '<h2>' . t('Add new field') . '</h2>';
       
    67   $addnewfields .= '<ul>';
       
    68   foreach (_profile_field_types() as $key => $value) {
       
    69     $addnewfields .= '<li>' . l($value, "admin/config/people/profile/add/$key") . '</li>';
       
    70   }
       
    71   $addnewfields .= '</ul>';
       
    72   $form['addnewfields'] = array('#markup' => $addnewfields);
       
    73 
       
    74   return $form;
       
    75 }
       
    76 
       
    77 /**
       
    78  * Submit handler to update changed profile field weights and categories.
       
    79  *
       
    80  * @see profile_admin_overview()
       
    81  */
       
    82 function profile_admin_overview_submit($form, &$form_state) {
       
    83   foreach (element_children($form_state['values']) as $fid) {
       
    84     if (is_numeric($fid)) {
       
    85       $weight = $form_state['values'][$fid]['weight'];
       
    86       $category = $form_state['values'][$fid]['category'];
       
    87       if ($weight != $form[$fid]['weight']['#default_value'] || $category != $form[$fid]['category']['#default_value']) {
       
    88         db_update('profile_field')
       
    89           ->fields(array(
       
    90             'weight' => $weight,
       
    91             'category' => $category,
       
    92           ))
       
    93           ->condition('fid', $fid)
       
    94           ->execute();
       
    95       }
       
    96     }
       
    97   }
       
    98 
       
    99   drupal_set_message(t('Profile fields have been updated.'));
       
   100   cache_clear_all();
       
   101   menu_rebuild();
       
   102 }
       
   103 
       
   104 /**
       
   105  * Returns HTML for the profile field overview form into a drag and drop enabled table.
       
   106  *
       
   107  * @param $variables
       
   108  *   An associative array containing:
       
   109  *   - form: A render element representing the form.
       
   110  *
       
   111  * @see profile_admin_overview()
       
   112  * @ingroup themeable
       
   113  */
       
   114 function theme_profile_admin_overview($variables) {
       
   115   $form = $variables['form'];
       
   116 
       
   117   drupal_add_css(drupal_get_path('module', 'profile') . '/profile.css');
       
   118   // Add javascript if there's more than one field.
       
   119   if (isset($form['actions'])) {
       
   120     drupal_add_js(drupal_get_path('module', 'profile') . '/profile.js');
       
   121   }
       
   122 
       
   123   $rows = array();
       
   124   $categories = array();
       
   125   $category_number = 0;
       
   126   foreach (element_children($form) as $key) {
       
   127     // Don't take form control structures.
       
   128     if (isset($form[$key]['category'])) {
       
   129       $field = &$form[$key];
       
   130       $category = $field['category']['#default_value'];
       
   131 
       
   132       if (!isset($categories[$category])) {
       
   133         // Category classes are given numeric IDs because there's no guarantee
       
   134         // class names won't contain invalid characters.
       
   135         $categories[$category] = $category_number;
       
   136         $category_field['#attributes']['class'] = array('profile-category', 'profile-category-' . $category_number);
       
   137         $rows[] = array(array('data' => check_plain($category), 'colspan' => 7, 'class' => array('category')));
       
   138         $rows[] = array('data' => array(array('data' => '<em>' . t('No fields in this category. If this category remains empty when saved, it will be removed.') . '</em>', 'colspan' => 7)), 'class' => array('category-' . $category_number . '-message', 'category-message', 'category-populated'));
       
   139 
       
   140         // Make it draggable only if there is more than one field
       
   141         if (isset($form['actions'])) {
       
   142           drupal_add_tabledrag('profile-fields', 'order', 'sibling', 'profile-weight', 'profile-weight-' . $category_number);
       
   143           drupal_add_tabledrag('profile-fields', 'match', 'sibling', 'profile-category', 'profile-category-' . $category_number);
       
   144         }
       
   145         $category_number++;
       
   146       }
       
   147 
       
   148       // Add special drag and drop classes that group fields together.
       
   149       $field['weight']['#attributes']['class'] = array('profile-weight', 'profile-weight-' . $categories[$category]);
       
   150       $field['category']['#attributes']['class'] = array('profile-category', 'profile-category-' . $categories[$category]);
       
   151 
       
   152       // Add the row
       
   153       $row = array();
       
   154       $row[] = drupal_render($field['title']);
       
   155       $row[] = drupal_render($field['name']);
       
   156       $row[] = drupal_render($field['type']);
       
   157       if (isset($form['actions'])) {
       
   158         $row[] = drupal_render($field['category']);
       
   159         $row[] = drupal_render($field['weight']);
       
   160       }
       
   161       $row[] = drupal_render($field['edit']);
       
   162       $row[] = drupal_render($field['delete']);
       
   163       $rows[] = array('data' => $row, 'class' => array('draggable'));
       
   164     }
       
   165   }
       
   166 
       
   167   $header = array(t('Title'), t('Name'), t('Type'));
       
   168   if (isset($form['actions'])) {
       
   169     $header[] = t('Category');
       
   170     $header[] = t('Weight');
       
   171   }
       
   172   $header[] = array('data' => t('Operations'), 'colspan' => 2);
       
   173 
       
   174   $output = theme('table', array('header' => $header, 'rows' => $rows, 'empty' => t('No fields available.'), 'attributes' => array('id' => 'profile-fields')));
       
   175   $output .= drupal_render_children($form);
       
   176 
       
   177   return $output;
       
   178 }
       
   179 
       
   180 /**
       
   181  * Menu callback: Generate a form to add/edit a user profile field.
       
   182  *
       
   183  * @ingroup forms
       
   184  * @see profile_field_form_validate()
       
   185  * @see profile_field_form_submit()
       
   186  */
       
   187 function profile_field_form($form, &$form_state, $arg = NULL) {
       
   188   if (arg(4) == 'edit') {
       
   189     if (is_numeric($arg)) {
       
   190       $fid = $arg;
       
   191 
       
   192       $edit = db_query('SELECT * FROM {profile_field} WHERE fid = :fid', array('fid' => $fid))->fetchAssoc();
       
   193 
       
   194       if (!$edit) {
       
   195         drupal_not_found();
       
   196         return;
       
   197       }
       
   198       drupal_set_title(t('Edit %title', array('%title' => $edit['title'])), PASS_THROUGH);
       
   199       $form['fid'] = array('#type' => 'value',
       
   200         '#value' => $fid,
       
   201       );
       
   202       $type = $edit['type'];
       
   203     }
       
   204     else {
       
   205       drupal_not_found();
       
   206       return;
       
   207     }
       
   208   }
       
   209   else {
       
   210     $types = _profile_field_types();
       
   211     if (!isset($types[$arg])) {
       
   212       drupal_not_found();
       
   213       return;
       
   214     }
       
   215     $type = $arg;
       
   216     drupal_set_title(t('Add new %type', array('%type' => $types[$type])), PASS_THROUGH);
       
   217     $edit = array('name' => 'profile_');
       
   218     $form['type'] = array('#type' => 'value', '#value' => $type);
       
   219   }
       
   220   $edit += array(
       
   221     'category' => '',
       
   222     'title' => '',
       
   223     'explanation' => '',
       
   224     'weight' => 0,
       
   225     'page' => '',
       
   226     'autocomplete' => '',
       
   227     'required' => '',
       
   228     'register' => '',
       
   229   );
       
   230   $form['category'] = array('#type' => 'textfield',
       
   231     '#title' => t('Category'),
       
   232     '#default_value' => $edit['category'],
       
   233     '#autocomplete_path' => 'admin/config/people/profile/autocomplete',
       
   234     '#description' => t('The category the new field should be part of. Categories are used to group fields logically. An example category is "Personal information".'),
       
   235     '#required' => TRUE,
       
   236   );
       
   237   $form['title'] = array('#type' => 'textfield',
       
   238     '#title' => t('Title'),
       
   239     '#default_value' => $edit['title'],
       
   240     '#description' => t('The title of the new field. The title will be shown to the user. An example title is "Favorite color".'),
       
   241     '#required' => TRUE,
       
   242   );
       
   243   $form['name'] = array('#type' => 'textfield',
       
   244     '#title' => t('Form name'),
       
   245     '#default_value' => $edit['name'],
       
   246     '#description' => t('The name of the field. The form name is not shown to the user but used internally in the HTML code and URLs.
       
   247 Unless you know what you are doing, it is highly recommended that you prefix the form name with <code>profile_</code> to avoid name clashes with other fields. 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".'),
       
   248     '#required' => TRUE,
       
   249   );
       
   250   $form['explanation'] = array('#type' => 'textarea',
       
   251     '#title' => t('Explanation'),
       
   252     '#default_value' => $edit['explanation'],
       
   253     '#description' => t('An optional explanation to go with the new field. The explanation will be shown to the user.'),
       
   254   );
       
   255   if ($type == 'selection') {
       
   256     $form['fields']['options'] = array('#type' => 'textarea',
       
   257       '#title' => t('Selection options'),
       
   258       '#default_value' => isset($edit['options']) ? $edit['options'] : '',
       
   259       '#description' => t('A list of all options. Put each option on a separate line. Example options are "red", "blue", "green", etc.'),
       
   260     );
       
   261   }
       
   262   $form['visibility'] = array('#type' => 'radios',
       
   263     '#title' => t('Visibility'),
       
   264     '#default_value' => isset($edit['visibility']) ? $edit['visibility'] : PROFILE_PUBLIC,
       
   265     '#options' => array(PROFILE_HIDDEN => t('Hidden profile field, only accessible by administrators, modules and themes.'), 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.')),
       
   266   );
       
   267   if ($type == 'selection' || $type == 'list' || $type == 'textfield') {
       
   268     $form['fields']['page'] = array('#type' => 'textfield',
       
   269       '#title' => t('Page title'),
       
   270       '#default_value' => $edit['page'],
       
   271       '#description' => t('To enable browsing this field by value, enter a title for the resulting page. The word <code>%value</code> will be substituted with the corresponding value. An example page title is "People whose favorite color is %value" . This is only applicable for a public field.'),
       
   272     );
       
   273   }
       
   274   elseif ($type == 'checkbox') {
       
   275     $form['fields']['page'] = array('#type' => 'textfield',
       
   276       '#title' => t('Page title'),
       
   277       '#default_value' => $edit['page'],
       
   278       '#description' => t('To enable browsing this field by value, enter a title for the resulting page. An example page title is "People who are employed" . This is only applicable for a public field.'),
       
   279     );
       
   280   }
       
   281   $form['weight'] = array('#type' => 'weight',
       
   282     '#title' => t('Weight'),
       
   283     '#default_value' => $edit['weight'],
       
   284     '#description' => t('The weights define the order in which the form fields are shown. Lighter fields "float up" towards the top of the category.'),
       
   285   );
       
   286   $form['autocomplete'] = array('#type' => 'checkbox',
       
   287     '#title' => t('Form will auto-complete while user is typing.'),
       
   288     '#default_value' => $edit['autocomplete'],
       
   289     '#description' => t('For security, auto-complete will be disabled if the user does not have access to user profiles.'),
       
   290   );
       
   291   $form['required'] = array('#type' => 'checkbox',
       
   292     '#title' => t('The user must enter a value.'),
       
   293     '#default_value' => $edit['required'],
       
   294   );
       
   295   $form['register'] = array('#type' => 'checkbox',
       
   296     '#title' => t('Visible in user registration form.'),
       
   297     '#default_value' => $edit['register'],
       
   298   );
       
   299 
       
   300   $form['actions'] = array('#type' => 'actions');
       
   301   $form['actions']['submit'] = array('#type' => 'submit',
       
   302     '#value' => t('Save field'),
       
   303   );
       
   304   return $form;
       
   305 }
       
   306 
       
   307 /**
       
   308  * Validate profile_field_form submissions.
       
   309  */
       
   310 function profile_field_form_validate($form, &$form_state) {
       
   311   // Validate the 'field name':
       
   312   if (preg_match('/[^a-zA-Z0-9_-]/', $form_state['values']['name'])) {
       
   313     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.'));
       
   314   }
       
   315 
       
   316   $users_table = drupal_get_schema('users');
       
   317   if (!empty($users_table['fields'][$form_state['values']['name']])) {
       
   318     form_set_error('name', t('The specified form name is reserved for use by Drupal.'));
       
   319   }
       
   320   // Validate the category:
       
   321   if (!$form_state['values']['category']) {
       
   322     form_set_error('category', t('You must enter a category.'));
       
   323   }
       
   324   if (strtolower($form_state['values']['category']) == 'account') {
       
   325     form_set_error('category', t('The specified category name is reserved for use by Drupal.'));
       
   326   }
       
   327   $query = db_select('profile_field');
       
   328   $query->fields('profile_field', array('fid'));
       
   329 
       
   330   if (isset($form_state['values']['fid'])) {
       
   331     $query->condition('fid', $form_state['values']['fid'], '<>');
       
   332   }
       
   333   $query_name = clone $query;
       
   334 
       
   335   $title = $query
       
   336     ->condition('title', $form_state['values']['title'])
       
   337     ->condition('category', $form_state['values']['category'])
       
   338     ->execute()
       
   339     ->fetchField();
       
   340   if ($title) {
       
   341     form_set_error('title', t('The specified title is already in use.'));
       
   342   }
       
   343   $name = $query_name
       
   344     ->condition('name', $form_state['values']['name'])
       
   345     ->execute()
       
   346     ->fetchField();
       
   347   if ($name) {
       
   348     form_set_error('name', t('The specified name is already in use.'));
       
   349   }
       
   350   if ($form_state['values']['visibility'] == PROFILE_HIDDEN) {
       
   351     if ($form_state['values']['required']) {
       
   352       form_set_error('required', t('A hidden field cannot be required.'));
       
   353     }
       
   354     if ($form_state['values']['register']) {
       
   355       form_set_error('register', t('A hidden field cannot be set to visible on the user registration form.'));
       
   356     }
       
   357   }
       
   358 }
       
   359 
       
   360 /**
       
   361  * Process profile_field_form submissions.
       
   362  */
       
   363 function profile_field_form_submit($form, &$form_state) {
       
   364   if (!isset($form_state['values']['options'])) {
       
   365     $form_state['values']['options'] = '';
       
   366   }
       
   367   if (!isset($form_state['values']['page'])) {
       
   368     $form_state['values']['page'] = '';
       
   369   }
       
   370   // Remove all elements that are not profile_field columns.
       
   371   $values = array_intersect_key($form_state['values'], array_flip(array('type', 'category', 'title', 'name', 'explanation', 'visibility', 'page', 'weight', 'autocomplete', 'required', 'register', 'options')));
       
   372   if (!isset($form_state['values']['fid'])) {
       
   373     db_insert('profile_field')
       
   374       ->fields($values)
       
   375       ->execute();
       
   376     drupal_set_message(t('The field has been created.'));
       
   377     watchdog('profile', 'Profile field %field added under category %category.', array('%field' => $form_state['values']['title'], '%category' => $form_state['values']['category']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/people/profile'));
       
   378   }
       
   379   else {
       
   380     db_update('profile_field')
       
   381       ->fields($values)
       
   382       ->condition('fid', $form_state['values']['fid'])
       
   383       ->execute();
       
   384     drupal_set_message(t('The field has been updated.'));
       
   385   }
       
   386   cache_clear_all();
       
   387   menu_rebuild();
       
   388 
       
   389   $form_state['redirect'] = 'admin/config/people/profile';
       
   390   return;
       
   391 }
       
   392 
       
   393 /**
       
   394  * Menu callback; deletes a field from all user profiles.
       
   395  */
       
   396 function profile_field_delete($form, &$form_state, $fid) {
       
   397   $field = db_query("SELECT title FROM {profile_field} WHERE fid = :fid", array(':fid' => $fid))->fetchObject();
       
   398   if (!$field) {
       
   399     drupal_not_found();
       
   400     return;
       
   401   }
       
   402   $form['fid'] = array('#type' => 'value', '#value' => $fid);
       
   403   $form['title'] = array('#type' => 'value', '#value' => $field->title);
       
   404 
       
   405   return confirm_form($form,
       
   406     t('Are you sure you want to delete the field %field?', array('%field' => $field->title)), 'admin/config/people/profile',
       
   407     t('This action cannot be undone. If users have entered values into this field in their profile, these entries will also be deleted. If you want to keep the user-entered data, instead of deleting the field you may wish to <a href="@edit-field">edit this field</a> and change it to a hidden profile field so that it may only be accessed by administrators.', array('@edit-field' => url('admin/config/people/profile/edit/' . $fid))),
       
   408     t('Delete'), t('Cancel'));
       
   409 }
       
   410 
       
   411 /**
       
   412  * Process a field delete form submission.
       
   413  */
       
   414 function profile_field_delete_submit($form, &$form_state) {
       
   415   db_delete('profile_field')
       
   416     ->condition('fid', $form_state['values']['fid'])
       
   417     ->execute();
       
   418   db_delete('profile_value')
       
   419     ->condition('fid', $form_state['values']['fid'])
       
   420     ->execute();
       
   421 
       
   422   cache_clear_all();
       
   423 
       
   424   drupal_set_message(t('The field %field has been deleted.', array('%field' => $form_state['values']['title'])));
       
   425   watchdog('profile', 'Profile field %field deleted.', array('%field' => $form_state['values']['title']), WATCHDOG_NOTICE, l(t('view'), 'admin/config/people/profile'));
       
   426 
       
   427   $form_state['redirect'] = 'admin/config/people/profile';
       
   428   return;
       
   429 }
       
   430 
       
   431 /**
       
   432  * Retrieve a pipe delimited string of autocomplete suggestions for profile categories
       
   433  */
       
   434 function profile_admin_settings_autocomplete($string) {
       
   435   $matches = array();
       
   436   $result = db_select('profile_field')
       
   437     ->fields('profile_field', array('category'))
       
   438     ->condition('category', db_like($string) . '%', 'LIKE')
       
   439     ->range(0, 10)
       
   440     ->execute();
       
   441 
       
   442   foreach ($result as $data) {
       
   443     $matches[$data->category] = check_plain($data->category);
       
   444   }
       
   445   drupal_json_output($matches);
       
   446 }