diff --git a/core/includes/ajax.inc b/core/includes/ajax.inc index 45bb706..1edd871 100644 --- a/core/includes/ajax.inc +++ b/core/includes/ajax.inc @@ -631,7 +631,7 @@ function ajax_pre_render_element($element) { // If the element is a (non-image) button, its name may not identify it // uniquely, in which case a match on value is also needed. // @see _form_button_was_clicked() - if (isset($element['#button_type']) && empty($element['#has_garbage_value'])) { + if (!empty($element['#is_button']) && empty($element['#has_garbage_value'])) { $settings['submit']['_triggering_element_value'] = $element['#value']; } } diff --git a/core/includes/form.inc b/core/includes/form.inc index 226d4a6..ea5c4c6 100644 --- a/core/includes/form.inc +++ b/core/includes/form.inc @@ -1169,7 +1169,7 @@ function drupal_validate_form($form_id, &$form, &$form_state) { // allow the value of the clicked button to be retained in its normal // $form_state['values'] locations, even if these locations are not included // in #limit_validation_errors. - if (isset($form_state['triggering_element']['#button_type'])) { + if (!empty($form_state['triggering_element']['#is_button'])) { $button_value = $form_state['triggering_element']['#value']; // Like all input controls, the button value may be in the location @@ -1947,7 +1947,7 @@ function form_builder($form_id, &$element, &$form_state) { } // Special processing if the triggering element is a button. - if (isset($form_state['triggering_element']['#button_type'])) { + if (!empty($form_state['triggering_element']['#is_button'])) { // Because there are several ways in which the triggering element could // have been determined (including from input variables set by JavaScript // or fallback behavior implemented for IE), and because buttons often @@ -2083,7 +2083,7 @@ function _form_builder_handle_input_element($form_id, &$element, &$form_state) { // If the form was submitted by the browser rather than via Ajax, then it // can only have been triggered by a button, and we need to determine which // button within the constraints of how browsers provide this information. - if (isset($element['#button_type'])) { + if (!empty($element['#is_button'])) { // All buttons in the form need to be tracked for // form_state_values_clean() and for the form_builder() code that handles // a form submission containing no button information in $_POST. @@ -3161,6 +3161,12 @@ function form_pre_render_conditional_form_element($element) { * Processes a form button element. */ function form_process_button($element, $form_state) { + // Automatically take over the button's array key as #button_type, unless + // #button_type has been manually specified. + // @see theme_button() + if (!isset($element['#button_type'])) { + $element['#button_type'] = end($element['#array_parents']); + } // If this is a button intentionally allowing incomplete form submission // (e.g., a "Previous" or "Add another item" button), then also skip // client-side validation. @@ -3250,6 +3256,13 @@ function form_process_checkboxes($element) { */ function form_process_actions($element, &$form_state) { $element['#attributes']['class'][] = 'form-actions'; + // The primary actions in a form may be denoted by setting #primary to TRUE, + // in order to facilitate specialized button styling in themes. + // @todo Consider to remove actions containers for alternate form actions or + // introduce a suitable replacement container for them. + if (!empty($element['#primary'])) { + $element['#attributes']['class'][] = 'primary'; + } return $element; } @@ -3936,7 +3949,13 @@ function theme_button($variables) { $element['#attributes']['type'] = 'submit'; element_set_attributes($element, array('id', 'name', 'value')); - $element['#attributes']['class'][] = 'form-' . $element['#button_type']; + $element['#attributes']['class'][] = 'button'; + if (!empty($element['#button_type'])) { + $element['#attributes']['class'][] = 'button-' . $element['#button_type']; + } + // @todo Various JavaScript depends on this button class. + $element['#attributes']['class'][] = 'form-submit'; + if (!empty($element['#attributes']['disabled'])) { $element['#attributes']['class'][] = 'form-button-disabled'; } @@ -3965,7 +3984,13 @@ function theme_image_button($variables) { $element['#attributes']['title'] = $element['#title']; } - $element['#attributes']['class'][] = 'form-' . $element['#button_type']; + $element['#attributes']['class'][] = 'button'; + if (!empty($element['#button_type'])) { + $element['#attributes']['class'][] = 'button-' . $element['#button_type']; + } + // @todo Various JavaScript depends on this button class. + $element['#attributes']['class'][] = 'form-submit'; + if (!empty($element['#attributes']['disabled'])) { $element['#attributes']['class'][] = 'form-button-disabled'; } diff --git a/core/lib/Drupal/Core/Entity/EntityFormController.php b/core/lib/Drupal/Core/Entity/EntityFormController.php index 2e9daac..d348ef1 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormController.php +++ b/core/lib/Drupal/Core/Entity/EntityFormController.php @@ -88,17 +88,13 @@ public function form(array $form, array &$form_state, EntityInterface $entity) { */ protected function actionsElement(array $form, array &$form_state) { $element = $this->actions($form, $form_state); + $element['#type'] = 'actions'; + // Indicate that these are the primary actions of the form. + $element['#primary'] = TRUE; // We cannot delete an entity that has not been created yet. if ($this->getEntity($form_state)->isNew()) { - unset($element['delete']); - } - elseif (isset($element['delete'])) { - // Move the delete action as last one, unless weights are explicitly - // provided. - $delete = $element['delete']; - unset($element['delete']); - $element['delete'] = $delete; + $element['delete']['#access'] = FALSE; } $count = 0; @@ -109,10 +105,6 @@ protected function actionsElement(array $form, array &$form_state) { ); } - if (!empty($element)) { - $element['#type'] = 'actions'; - } - return $element; } @@ -134,6 +126,7 @@ protected function actions(array $form, array &$form_state) { ), 'delete' => array( '#value' => t('Delete'), + '#weight' => 50, // No need to validate the form when deleting the entity. '#submit' => array( array($this, 'delete'), diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/ElementTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/ElementTest.php index 621578c..bec18fb 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Form/ElementTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Form/ElementTest.php @@ -92,4 +92,19 @@ function testOptions() { ))); } } + + /** + * Tests button classes. + */ + function testButtonClasses() { + $this->drupalGet('form-test/button-class'); + // Just contains(@class, "button") won't do because then + // "button-foo" would contain "button". Instead, check + // " button ". Make sure it matches in the beginning and the end too + // by adding a space before and after. + $this->assertEqual(2, count($this->xpath('//*[contains(concat(" ", @class, " "), " button ")]'))); + $this->assertEqual(1, count($this->xpath('//*[contains(concat(" ", @class, " "), " button-foo ")]'))); + $this->assertEqual(1, count($this->xpath('//*[contains(concat(" ", @class, " "), " button-delete ")]'))); + } + } diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 843ed78..f8ae55d 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -300,7 +300,7 @@ function system_element_info() { $types['submit'] = array( '#input' => TRUE, '#name' => 'op', - '#button_type' => 'submit', + '#is_button' => TRUE, '#executes_submit_callback' => TRUE, '#limit_validation_errors' => FALSE, '#process' => array('form_process_button', 'ajax_process_form'), @@ -309,7 +309,7 @@ function system_element_info() { $types['button'] = array( '#input' => TRUE, '#name' => 'op', - '#button_type' => 'submit', + '#is_button' => TRUE, '#executes_submit_callback' => FALSE, '#limit_validation_errors' => FALSE, '#process' => array('form_process_button', 'ajax_process_form'), @@ -317,7 +317,7 @@ function system_element_info() { ); $types['image_button'] = array( '#input' => TRUE, - '#button_type' => 'submit', + '#is_button' => TRUE, '#executes_submit_callback' => TRUE, '#limit_validation_errors' => FALSE, '#process' => array('form_process_button', 'ajax_process_form'), @@ -551,6 +551,7 @@ function system_element_info() { $types['actions'] = array( '#theme_wrappers' => array('container'), '#process' => array('form_process_actions', 'form_process_container'), + '#primary' => FALSE, '#weight' => 100, ); diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module index 03b7cb8..db0eb8c 100644 --- a/core/modules/system/tests/modules/form_test/form_test.module +++ b/core/modules/system/tests/modules/form_test/form_test.module @@ -307,6 +307,12 @@ function form_test_menu() { 'page arguments' => array('form_test_required_attribute'), 'access callback' => TRUE, ); + $items['form-test/button-class'] = array( + 'title' => 'Button class testing', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('form_test_button_class'), + 'access callback' => TRUE, + ); return $items; } @@ -2310,3 +2316,19 @@ function form_test_html_id($form, &$form_state) { ); return $form; } + +/** + * Builds a simple form to test form button classes. + */ +function form_test_button_class($form, &$form_state) { + $form['button'] = array( + '#type' => 'button', + '#value' => 'test', + '#button_type' => 'foo', + ); + $form['delete'] = array( + '#type' => 'button', + '#value' => 'Delete', + ); + return $form; +} diff --git a/core/modules/user/lib/Drupal/user/ProfileFormController.php b/core/modules/user/lib/Drupal/user/ProfileFormController.php index d2a7e81..5584e63 100644 --- a/core/modules/user/lib/Drupal/user/ProfileFormController.php +++ b/core/modules/user/lib/Drupal/user/ProfileFormController.php @@ -21,16 +21,10 @@ protected function actions(array $form, array &$form_state) { $element = parent::actions($form, $form_state); $account = $this->getEntity($form_state); - // @todo Actually the cancel action can be assimilated to the delete one: we - // should alter it instead of providing a new one. - unset($element['delete']); - - $element['cancel'] = array( - '#type' => 'submit', - '#value' => t('Cancel account'), - '#submit' => array('user_edit_cancel_submit'), - '#access' => $account->uid > 1 && (($account->uid == $GLOBALS['user']->uid && user_access('cancel account')) || user_access('administer users')), - ); + $element['delete']['#type'] = 'submit'; + $element['delete']['#value'] = t('Cancel account'); + $element['delete']['#submit'] = array('user_edit_cancel_submit'); + $element['delete']['#access'] = $account->uid > 1 && (($account->uid == $GLOBALS['user']->uid && user_access('cancel account')) || user_access('administer users')); return $element; } diff --git a/core/modules/views/views_ui/admin.inc b/core/modules/views/views_ui/admin.inc index 5b0ae41..a37297f 100644 --- a/core/modules/views/views_ui/admin.inc +++ b/core/modules/views/views_ui/admin.inc @@ -2303,7 +2303,7 @@ function views_fetch_fields($base, $type, $grouping = FALSE, $sub_type = NULL) { */ function views_ui_form_button_was_clicked($element, &$form_state) { $process_input = empty($element['#disabled']) && ($form_state['programmed'] || ($form_state['process_input'] && (!isset($element['#access']) || $element['#access']))); - if ($process_input && !isset($form_state['triggering_element']) && isset($element['#button_type']) && isset($form_state['input'][$element['#name']]) && isset($element['#values']) && in_array($form_state['input'][$element['#name']], $element['#values'], TRUE)) { + if ($process_input && !isset($form_state['triggering_element']) && !empty($element['#is_button']) && isset($form_state['input'][$element['#name']]) && isset($element['#values']) && in_array($form_state['input'][$element['#name']], $element['#values'], TRUE)) { $form_state['triggering_element'] = $element; } return $element; diff --git a/core/themes/bartik/css/style-rtl.css b/core/themes/bartik/css/style-rtl.css index d95d66b..3b52583 100644 --- a/core/themes/bartik/css/style-rtl.css +++ b/core/themes/bartik/css/style-rtl.css @@ -146,8 +146,7 @@ ul.tips { /* ----------------- Buttons ------------------ */ -input.form-submit, -a.button { +.button { margin-right: 0; margin-left: 0.6em; } diff --git a/core/themes/bartik/css/style.css b/core/themes/bartik/css/style.css index 94ac775..2a70405 100644 --- a/core/themes/bartik/css/style.css +++ b/core/themes/bartik/css/style.css @@ -1096,8 +1096,7 @@ div.password-confirm { /* ---------------- Buttons ---------------- */ -input.form-submit, -a.button { +.button { background: #fff url(../images/buttons.png) 0 0 repeat-x; border: 1px solid #e4e4e4; border-bottom: 1px solid #b4b4b4; @@ -1113,11 +1112,11 @@ a.button { padding: 4px 17px; border-radius: 15px; } -a.button:link, -a.button:visited, -a.button:hover, -a.button:focus, -a.button:active { +.button:link, +.button:visited, +.button:hover, +.button:focus, +.button:active { text-decoration: none; color: #5a5a5a; } diff --git a/core/themes/seven/images/buttons.png b/core/themes/seven/images/buttons.png deleted file mode 100644 index 0a89f98..0000000 --- a/core/themes/seven/images/buttons.png +++ /dev/null @@ -1,3 +0,0 @@ -PNG - - IHDRxS3IDATx^u E"B1D_וּ)@-@a aX@D00, c7d@a0, c?" 2 °]Dod+@aaX@>DOd#@aaX@."vDwdaX@." [2 °0, c" 9@add@s2 °=0, ð 2 °=0, c" @a;0, ð 2 0, ð 2 ° 2 2 0, ð 2 0, ð 2v ° 2 @ad{daX@DaX@aaX@7 ZIENDB` \ No newline at end of file diff --git a/core/themes/seven/style-rtl.css b/core/themes/seven/style-rtl.css index 3276ce8..f039008 100644 --- a/core/themes/seven/style-rtl.css +++ b/core/themes/seven/style-rtl.css @@ -127,8 +127,7 @@ body div.form-type-checkbox div.description { margin-left: 0; margin-right: 1.5em; } -input.form-submit, -a.button { +.button { margin-left: 1em; margin-right: 0; } diff --git a/core/themes/seven/style.css b/core/themes/seven/style.css index 575c91b..bea0931 100644 --- a/core/themes/seven/style.css +++ b/core/themes/seven/style.css @@ -615,8 +615,7 @@ body div.form-type-radio div.description, body div.form-type-checkbox div.description { margin-left: 1.5em; /* LTR */ } -input.form-submit, -a.button { +.button { cursor: pointer; padding: 4px 17px; margin-bottom: 1em; @@ -630,29 +629,26 @@ a.button { border-bottom: 1px solid #b4b4b4; border-left-color: #d2d2d2; border-right-color: #d2d2d2; - background: url(images/buttons.png) 0 0 repeat-x; + background-color: #e4e4e4; border-radius: 20px; } -a.button:link, -a.button:visited, -a.button:hover, -a.button:active { +.button:link, +.button:visited, +.button:hover, +.button:active { text-decoration: none; } -input.form-submit:hover, -input.form-submit:focus, -a.button:hover, -a.button:focus { - background-position: 0 -40px; +.button:focus, +.button:hover { + background-color: #c0c0c0; border: 1px solid #bebebe; border-left-color: #afafaf; border-right-color: #afafaf; border-bottom-color: #9b9b9b; color: #2e2e2e; } -input.form-submit:active, -a.button:active { - background-position: 0 -80px; +.button:active { + background-color: #565656; border: 1px solid #333; border-left-color: #222; border-right-color: #222; @@ -660,9 +656,26 @@ a.button:active { color: #fff; text-shadow: #222 0 -1px 0; } +.form-actions.primary .button:first-child { + background-color: #9dcae7; + border: 1px solid #8eB7cd; + border-bottom-color: #7691a2; + color: #133B54; +} +.form-actions.primary .button:first-child:focus, +.form-actions.primary .button:first-child:hover { + background-color: #73b3dd; + border: 1px solid #6ea3bf; + border-bottom-color: #4680a0; +} +.form-actions.primary .button:first-child:active { + background-color: #3981b1; + border: 1px solid #36647c; + border-bottom-color: #284657; +} input.form-button-disabled, input.form-button-disabled:active { - background: #eee none; + background-color: #eee; border-color: #eee; text-shadow: none; color: #999; @@ -795,6 +808,11 @@ div.filter-options select { padding: 0; } +/* Form actions */ +.form-actions { + margin-top: 1em; +} + /** * System. */