From eba2b8f788e7679250eff5aad73b4ee874e14c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?CZ=C3=96VEK=20Andr=C3=A1s?= Date: Tue, 7 Feb 2017 16:37:23 +0100 Subject: [PATCH 01/17] Profile selection --- modules/order/src/Element/ProfileSelect.php | 163 ++++++++++++++++- .../commerce_order_test.routing.yml | 7 + .../src/Form/ProfileSelectTestForm.php | 48 +++++ .../ProfileSelectTest.php | 173 ++++++++++++++++++ 4 files changed, 386 insertions(+), 5 deletions(-) create mode 100644 modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml create mode 100644 modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php create mode 100644 modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index d530915637..c96f802330 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -3,10 +3,15 @@ namespace Drupal\commerce_order\Element; use Drupal\commerce\Element\CommerceElementTrait; +use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Render\Element; use Drupal\Core\Render\Element\RenderElement; use Drupal\profile\Entity\ProfileInterface; +use Drupal\profile\Entity\Profile; +use Symfony\Component\HttpFoundation\Request; /** * Provides a form element for selecting a customer profile. @@ -87,8 +92,72 @@ public static function processForm(array $element, FormStateInterface $form_stat } $element['#profile'] = $element['#default_value']; - $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); - $form_display->buildForm($element['#profile'], $element, $form_state); + $bundle = $element['#profile']->bundle(); + $default_profile_id = $element['#profile']->id(); + $default_profile = $element['#profile']; + // Fetch all profiles of the user for an addressbook functionality. + $profile_uid = $element['#profile']->getOwnerId(); + // Anonymous users don't get an addressbook. + if ($profile_uid) { + $profile_ids = \Drupal::service('entity.query') + ->get('profile') + ->condition('uid', $profile_uid) + ->condition('type', $bundle) + ->condition('status', TRUE) + ->sort('profile_id', 'DESC') + ->execute(); + $profiles = Profile::loadMultiple($profile_ids); + $profile_options = []; + /** @var \Drupal\profile\Entity\Profile $profile_option */ + foreach ($profiles as $profile_option) { + if (empty($default_profile_id)) { + $default_profile_id = $profile_option->id(); + $default_profile = $profile_option; + } + $profile_options[$profile_option->id()] = $profile_option->label(); + } + $profile_options['new_profile'] = t('+ Enter a new profile'); + } + $mode = 'view'; + $triggering_element = $form_state->getTriggeringElement(); + if (!empty($triggering_element)) { + $triggering_element_parents = $triggering_element['#parents']; + $last_parent = array_pop($triggering_element_parents); + if ($triggering_element_parents == $element['#parents']) { + if ($last_parent == 'edit_button') { + $mode = 'edit'; + } + elseif ($last_parent == 'cancel_button') { + $mode = 'view'; + } + elseif ($last_parent == 'profile_selection' && $triggering_element['#value'] == 'new_profile') { + $mode = 'new'; + } + } + } + else { + if (empty($profile_ids)) { + $mode = 'new'; + } + } + + $ajax_wrapper_id = Html::getUniqueId('profile-select-ajax-wrapper'); + // Prefix and suffix used for Ajax replacement. + $element['#prefix'] = '
'; + $element['#suffix'] = '
'; + + // No profiles found or user wants to create a new one. + if ($mode == 'new') { + $values = [ + 'type' => $element['#profile']->bundle(), + 'uid' => $element['#profile']->getOwnerId(), + ]; + $default_profile = Profile::create($values); + $default_profile_id = NULL; + } + + $form_display = EntityFormDisplay::collectRenderDisplay($default_profile, 'default'); + $form_display->buildForm($default_profile, $element, $form_state); if (!empty($element['address']['widget'][0])) { $widget_element = &$element['address']['widget'][0]; // Remove the details wrapper from the address widget. @@ -103,6 +172,68 @@ public static function processForm(array $element, FormStateInterface $form_stat } } + $called_class = get_called_class(); + if (!empty($profile_uid) && $mode != 'edit' && !empty($profile_options) && count($profile_options) > 1) { + $element['profile_selection'] = [ + '#title' => t('Select a profile'), + '#options' => $profile_options, + '#type' => 'select', + '#weight' => -5, + '#default_value' => $default_profile_id, + '#ajax' => [ + 'callback' => [$called_class, 'profileSelectAjax'], + 'wrapper' => $ajax_wrapper_id, + ], + ]; + } + + $name = array_shift($element['#parents']); + $element['cancel_button'] = [ + '#type' => 'button', + '#value' => t('Return to profile selection'), + '#limit_validation_errors' => [], + '#name' => $name . '[' . implode('][', $element['#parents']) . '][cancel_button]', + '#ajax' => [ + 'callback' => [$called_class, 'profileSelectAjax'], + 'wrapper' => $ajax_wrapper_id, + ], + '#weight' => 99, + ]; + array_unshift($element['#parents'], $name); + + // Viewing a profile. + if ($mode == 'view' ) { + $view_builder = \Drupal::entityTypeManager() + ->getViewBuilder('profile'); + $content = $view_builder->view($default_profile, 'default'); + + $element['rendered_profile'] = [ + $content, + ]; + + $name = array_shift($element['#parents']); + $element['edit_button'] = [ + '#type' => 'button', + '#value' => t('Edit'), + '#name' => $name . '[' . implode('][', $element['#parents']) . '][edit_button]', + '#limit_validation_errors' => [], + '#ajax' => [ + 'callback' => [$called_class, 'profileSelectAjax'], + 'wrapper' => $ajax_wrapper_id, + ], + ]; + array_unshift($element['#parents'], $name); + + foreach (Element::children($element) as $key) { + if (!in_array($key, ['edit_button', 'rendered_profile', 'profile_selection'])) { + hide($element[$key]); + } + } + } + if (empty($profiles) || $mode != 'edit') { + hide($element['cancel_button']); + } + return $element; } @@ -119,9 +250,13 @@ public static function processForm(array $element, FormStateInterface $form_stat * form, as a protection against buggy behavior. */ public static function validateForm(array &$element, FormStateInterface $form_state) { - $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); - $form_display->extractFormValues($element['#profile'], $element, $form_state); - $form_display->validateFormValues($element['#profile'], $element, $form_state); + $triggering_parents = $form_state->getTriggeringElement()['#parents']; + $triggering_last_parent = array_pop($triggering_parents); + if (!in_array($triggering_last_parent, ['edit_button', 'cancel_button', 'profile_selection', 'country_code'])) { + $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); + $form_display->extractFormValues($element['#profile'], $element, $form_state); + $form_display->validateFormValues($element['#profile'], $element, $form_state); + } } /** @@ -138,4 +273,22 @@ public static function submitForm(array &$element, FormStateInterface $form_stat $element['#profile']->save(); } + /** + * Profile form AJAX callback. + * + * @param array $form + * The complete form array. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Symfony\Component\HttpFoundation\Request $request + * The Request object. + * + * @return array + * The form element replace the wrapper with. + */ + public static function profileSelectAjax(array &$form, FormStateInterface $form_state, Request $request) { + $triggering_parents = $form_state->getTriggeringElement()['#parents']; + array_pop($triggering_parents); + return NestedArray::getValue($form, $triggering_parents); + } } diff --git a/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml b/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml new file mode 100644 index 0000000000..e0f444a442 --- /dev/null +++ b/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml @@ -0,0 +1,7 @@ +commerce_order_test.profile_select_form: + path: '/commerce_order_test/profile_select_form' + defaults: + _form: '\Drupal\commerce_order_test\Form\ProfileSelectTestForm' + _title: 'Profile select test form' + requirements: + _access: 'TRUE' diff --git a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php new file mode 100644 index 0000000000..4dfd2bd48b --- /dev/null +++ b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php @@ -0,0 +1,48 @@ +get('entity_type.manager')->getStorage('profile'); + + $profile = $profile_storage->create([ + 'type' => 'customer', + 'uid' => \Drupal::currentUser()->id(), + ]); + + $form['profile'] = [ + '#type' => 'commerce_profile_select', + '#title' => $this->t('Profile'), + '#default_value' => $profile, + ]; + $form['submit'] = [ + '#type' => 'submit', + '#value' => $this->t('Submit'), + ]; + + return $form; + } + + /** + * {@inheritdoc} + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + drupal_set_message(t('Profile saved".')); + } + +} diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php new file mode 100644 index 0000000000..ae93fa8b2b --- /dev/null +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -0,0 +1,173 @@ + 'HU', + 'given_name' => 'Gustav', + 'family_name' => 'Mahler', + 'address_line1' => 'Teréz körút 7', + 'locality' => 'Budapest', + 'postal_code' => '1067', + ]; + + /** + * Profile address values. + * + * @var array + */ + protected $address2 = [ + 'country_code' => 'DE', + 'given_name' => 'Johann Sebastian', + 'family_name' => 'Bach', + 'address_line1' => 'Thomaskirchhof 15', + 'locality' => 'Leipzig', + 'postal_code' => '04109', + ]; + + /** + * Modules to enable. + * + * @var array + */ + public static $modules = [ + 'commerce_order_test', + ]; + + /** + * Tests the profile select form element for anonymous user. + */ + public function testProfileSelectAnonymous() { + $this->useProfileForm(0, $this->address1); + } + + /** + * Tests the profile select form element for authenticated user. + */ + public function testProfileSelectAuthenticated() { + $account = $this->createUser(); + $this->drupalLogin($account); + $this->useProfileForm($account->id(), $this->address1); + // Create one more profile to test how the form behaves with two existing profiles. + $this->useProfileForm($account->id(), $this->address2, 1); + + $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->fieldExists('profile_selection'); + // The last created profile should be selected by default. + $this->assertSession()->pageTextContains($this->address2['locality']); + + $this->getSession()->getPage()->fillField('profile[profile_selection]', 1); + $this->waitForAjaxToFinish(); + $this->assertSession()->pageTextContains($this->address1['locality']); + $this->submitForm([], 'Submit'); + $profile = \Drupal::entityTypeManager()->getStorage('profile')->load(1); + // Assert that field values have not changed. + foreach ($this->address1 as $key => $value) { + $this->assertEquals($this->address1[$key], $profile->address->getValue()[0][$key], t('@key of address has not changed.', ['@key' => $key])); + } + + // Edit a profile. + $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->fieldExists('profile[profile_selection]'); + // The last created profile should be selected by default. + $this->assertSession()->elementTextContains('css', '.locality', $this->address2['locality']); + $this->assertSession()->fieldNotExists('profile[cancel]'); + + $this->drupalPostForm(NULL, [], ['profile[edit]']); + foreach ($this->address1 as $key => $value) { + $this->assertSession()->fieldValueEquals('profile[address][0][address][' . $key . ']', $value); + } + $this->assertSession()->fieldExists('profile[cancel]'); + $this->assertSession()->fieldNotExists('profile[profile_selection]'); + $edit = [ + 'profile[address][0][address][address_line1]' => 'Andrássy út 22', + ]; + $this->submitForm([], 'Submit'); + $profile = \Drupal::entityTypeManager()->getStorage('profile')->resetCache([1]); + $profile = \Drupal::entityTypeManager()->getStorage('profile')->load([1]); + // Assert that only address_line1 has changed. + foreach ($this->address1 as $key => $value) { + if ($key == 'address_line1') { + $this->assertEquals($edit['profile[address][0][address][address_line1]'], $profile->address->getValue()[0][$key], t('@key of address has changed.', ['@key' => $key])); + } + else { + $this->assertEquals($this->address1[$key], $profile->address->getValue()[0][$key], t('@key of address has not changed.', ['@key' => $key])); + } + } + $profile_ids = \Drupal::service('entity.query') + ->get('profile') + ->condition('uid', $account->id()) + ->execute(); + $this->Equals(2, count($profile_ids), t('No new profile has been created after editing an existing one.') ); + } + + /** + * Submits the test profile select form. + * + * @param int $uid + * The user uid using the form. + * @param array $address_fields + * An associative array of address fields to submit. + * @param number $initial_profile_count + * The number of profiles before the form submission. + */ + protected function useProfileForm($uid, $address_fields, $initial_profile_count = 0) { + $profile_ids = \Drupal::service('entity.query') + ->get('profile') + ->condition('uid', $uid) + ->execute(); + $this->assertEmpty($profile_ids, t('The number of profiles before submitting the profile form is @count.', ['@count' => $initial_profile_count])); + + $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->assertSession()->statusCodeEquals(200); + + if (0 == $initial_profile_count || 0 == $uid) { + $this->assertSession()->fieldNotExists('profile[profile_selection]'); + } + else { + $this->assertSession()->fieldExists('profile[profile_selection]'); + $edit = [ + 'profile[profile_selection]', 'new_profile', + ]; + $this->drupalPostForm(NULL, $edit, ['profile[profile_selection]']); + $this->waitForAjaxToFinish(); + } + $this->assertSession()->fieldNotExists('profile[address][0][address][administrative_area]'); + + $this->getSession()->getPage()->fillField('profile[address][0][address][country_code]', $address_fields['country_code']); + $this->waitForAjaxToFinish(); + + $edit = []; + foreach ($address_fields as $key => $value) { + $edit['profile[address][0][address][' . $key . ']'] = $value; + } + + $this->submitForm($edit, 'Submit'); + + $profile_ids = \Drupal::service('entity.query') + ->get('profile') + ->condition('uid', $uid) + ->execute(); + $this->Equals($initial_profile_count + 1, count($profile_ids), t('There are @count profiles after creating a new one.', ['@count' => $initial_profile_count + 1])); + } + +} From 5ff60956f64b917f78503553aea433f9db8eadc4 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Mon, 5 Jun 2017 14:31:21 -0500 Subject: [PATCH 02/17] Fix phpcs issues --- modules/order/src/Element/ProfileSelect.php | 8 +++----- .../tests/src/FunctionalJavascript/ProfileSelectTest.php | 6 +++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index c96f802330..1a0882e4b3 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -11,7 +11,6 @@ use Drupal\Core\Render\Element\RenderElement; use Drupal\profile\Entity\ProfileInterface; use Drupal\profile\Entity\Profile; -use Symfony\Component\HttpFoundation\Request; /** * Provides a form element for selecting a customer profile. @@ -202,7 +201,7 @@ public static function processForm(array $element, FormStateInterface $form_stat array_unshift($element['#parents'], $name); // Viewing a profile. - if ($mode == 'view' ) { + if ($mode == 'view') { $view_builder = \Drupal::entityTypeManager() ->getViewBuilder('profile'); $content = $view_builder->view($default_profile, 'default'); @@ -280,15 +279,14 @@ public static function submitForm(array &$element, FormStateInterface $form_stat * The complete form array. * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. - * @param \Symfony\Component\HttpFoundation\Request $request - * The Request object. * * @return array * The form element replace the wrapper with. */ - public static function profileSelectAjax(array &$form, FormStateInterface $form_state, Request $request) { + public static function profileSelectAjax(array &$form, FormStateInterface $form_state) { $triggering_parents = $form_state->getTriggeringElement()['#parents']; array_pop($triggering_parents); return NestedArray::getValue($form, $triggering_parents); } + } diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index ae93fa8b2b..709104885a 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -117,7 +117,7 @@ public function testProfileSelectAuthenticated() { ->get('profile') ->condition('uid', $account->id()) ->execute(); - $this->Equals(2, count($profile_ids), t('No new profile has been created after editing an existing one.') ); + $this->Equals(2, count($profile_ids), t('No new profile has been created after editing an existing one.')); } /** @@ -127,10 +127,10 @@ public function testProfileSelectAuthenticated() { * The user uid using the form. * @param array $address_fields * An associative array of address fields to submit. - * @param number $initial_profile_count + * @param int $initial_profile_count * The number of profiles before the form submission. */ - protected function useProfileForm($uid, $address_fields, $initial_profile_count = 0) { + protected function useProfileForm($uid, array $address_fields, $initial_profile_count = 0) { $profile_ids = \Drupal::service('entity.query') ->get('profile') ->condition('uid', $uid) From 09da776715c528c0475d69fb4a696740ec35fb20 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Mon, 5 Jun 2017 15:36:28 -0500 Subject: [PATCH 03/17] Fix first test, country_code reset element mode --- modules/order/src/Element/ProfileSelect.php | 82 +++++++++++-------- .../src/Form/ProfileSelectTestForm.php | 2 +- .../ProfileSelectTest.php | 50 ++++++----- .../JavascriptTestTrait.php | 23 ++++++ 4 files changed, 99 insertions(+), 58 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 1a0882e4b3..2e8866aaca 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -10,7 +10,6 @@ use Drupal\Core\Render\Element; use Drupal\Core\Render\Element\RenderElement; use Drupal\profile\Entity\ProfileInterface; -use Drupal\profile\Entity\Profile; /** * Provides a form element for selecting a customer profile. @@ -83,40 +82,18 @@ public static function processForm(array $element, FormStateInterface $form_stat if (empty($element['#default_value'])) { throw new \InvalidArgumentException('The commerce_profile_select element requires the #default_value property.'); } - elseif (isset($element['#default_value']) && !($element['#default_value'] instanceof ProfileInterface)) { + if (!($element['#default_value'] instanceof ProfileInterface)) { throw new \InvalidArgumentException('The commerce_profile_select #default_value property must be a profile entity.'); } if (!is_array($element['#available_countries'])) { throw new \InvalidArgumentException('The commerce_profile_select #available_countries property must be an array.'); } + $storage = \Drupal::entityTypeManager()->getStorage('profile'); + $element['#profile'] = $element['#default_value']; - $bundle = $element['#profile']->bundle(); $default_profile_id = $element['#profile']->id(); $default_profile = $element['#profile']; - // Fetch all profiles of the user for an addressbook functionality. - $profile_uid = $element['#profile']->getOwnerId(); - // Anonymous users don't get an addressbook. - if ($profile_uid) { - $profile_ids = \Drupal::service('entity.query') - ->get('profile') - ->condition('uid', $profile_uid) - ->condition('type', $bundle) - ->condition('status', TRUE) - ->sort('profile_id', 'DESC') - ->execute(); - $profiles = Profile::loadMultiple($profile_ids); - $profile_options = []; - /** @var \Drupal\profile\Entity\Profile $profile_option */ - foreach ($profiles as $profile_option) { - if (empty($default_profile_id)) { - $default_profile_id = $profile_option->id(); - $default_profile = $profile_option; - } - $profile_options[$profile_option->id()] = $profile_option->label(); - } - $profile_options['new_profile'] = t('+ Enter a new profile'); - } $mode = 'view'; $triggering_element = $form_state->getTriggeringElement(); if (!empty($triggering_element)) { @@ -133,6 +110,10 @@ public static function processForm(array $element, FormStateInterface $form_stat $mode = 'new'; } } + // If first element parent matches, assume field within Profile did AJAX. + elseif ($triggering_element_parents[0] == $element['#parents'][0]) { + $mode = 'edit'; + } } else { if (empty($profile_ids)) { @@ -151,7 +132,7 @@ public static function processForm(array $element, FormStateInterface $form_stat 'type' => $element['#profile']->bundle(), 'uid' => $element['#profile']->getOwnerId(), ]; - $default_profile = Profile::create($values); + $default_profile = $storage->create($values); $default_profile_id = NULL; } @@ -171,7 +152,29 @@ public static function processForm(array $element, FormStateInterface $form_stat } } - $called_class = get_called_class(); + // Fetch all profiles of the user for an addressbook functionality. + $profile_uid = $element['#profile']->getOwnerId(); + // Anonymous users don't get an addressbook. + if ($profile_uid) { + $profile_ids = $storage->getQuery() + ->condition('uid', $profile_uid) + ->condition('type', $element['#profile']->bundle()) + ->condition('status', TRUE) + ->sort('profile_id', 'DESC') + ->execute(); + /** @var \Drupal\profile\Entity\Profile[] $profiles */ + $profiles = $storage->loadMultiple($profile_ids); + $profile_options = []; + foreach ($profiles as $profile_option) { + if (empty($default_profile_id) && $profile_option->isDefault()) { + $default_profile_id = $profile_option->id(); + $default_profile = $profile_option; + } + $profile_options[$profile_option->id()] = $profile_option->label(); + } + $profile_options['new_profile'] = t('+ Enter a new profile'); + } + if (!empty($profile_uid) && $mode != 'edit' && !empty($profile_options) && count($profile_options) > 1) { $element['profile_selection'] = [ '#title' => t('Select a profile'), @@ -180,7 +183,7 @@ public static function processForm(array $element, FormStateInterface $form_stat '#weight' => -5, '#default_value' => $default_profile_id, '#ajax' => [ - 'callback' => [$called_class, 'profileSelectAjax'], + 'callback' => [get_called_class(), 'profileSelectAjax'], 'wrapper' => $ajax_wrapper_id, ], ]; @@ -193,7 +196,7 @@ public static function processForm(array $element, FormStateInterface $form_stat '#limit_validation_errors' => [], '#name' => $name . '[' . implode('][', $element['#parents']) . '][cancel_button]', '#ajax' => [ - 'callback' => [$called_class, 'profileSelectAjax'], + 'callback' => [get_called_class(), 'profileSelectAjax'], 'wrapper' => $ajax_wrapper_id, ], '#weight' => 99, @@ -217,20 +220,24 @@ public static function processForm(array $element, FormStateInterface $form_stat '#name' => $name . '[' . implode('][', $element['#parents']) . '][edit_button]', '#limit_validation_errors' => [], '#ajax' => [ - 'callback' => [$called_class, 'profileSelectAjax'], + 'callback' => [get_called_class(), 'profileSelectAjax'], 'wrapper' => $ajax_wrapper_id, ], ]; array_unshift($element['#parents'], $name); foreach (Element::children($element) as $key) { - if (!in_array($key, ['edit_button', 'rendered_profile', 'profile_selection'])) { - hide($element[$key]); + if (!in_array($key, [ + 'edit_button', + 'rendered_profile', + 'profile_selection', + ])) { + $element[$key]['#access'] = FALSE; } } } if (empty($profiles) || $mode != 'edit') { - hide($element['cancel_button']); + $element['cancel_button']['#access'] = FALSE; } return $element; @@ -251,7 +258,12 @@ public static function processForm(array $element, FormStateInterface $form_stat public static function validateForm(array &$element, FormStateInterface $form_state) { $triggering_parents = $form_state->getTriggeringElement()['#parents']; $triggering_last_parent = array_pop($triggering_parents); - if (!in_array($triggering_last_parent, ['edit_button', 'cancel_button', 'profile_selection', 'country_code'])) { + if (!in_array($triggering_last_parent, [ + 'edit_button', + 'cancel_button', + 'profile_selection', + 'country_code', + ])) { $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); $form_display->extractFormValues($element['#profile'], $element, $form_state); $form_display->validateFormValues($element['#profile'], $element, $form_state); diff --git a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php index 4dfd2bd48b..bc4b905b51 100644 --- a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php +++ b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php @@ -42,7 +42,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - drupal_set_message(t('Profile saved".')); + drupal_set_message('Profile saved.'); } } diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 709104885a..e8cca2c8e8 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -2,15 +2,16 @@ namespace Drupal\Tests\commerce_order\FunctionalJavascript; +use Drupal\Core\Url; +use Drupal\Tests\commerce\Functional\CommerceBrowserTestBase; use Drupal\Tests\commerce\FunctionalJavascript\JavascriptTestTrait; -use Drupal\FunctionalJavascriptTests\JavascriptTestBase; /** * Tests the ProfileSelect form element. * * @group commerce */ -class ProfileSelectTest extends JavascriptTestBase { +class ProfileSelectTest extends CommerceBrowserTestBase { use JavascriptTestTrait; @@ -42,6 +43,11 @@ class ProfileSelectTest extends JavascriptTestBase { 'postal_code' => '04109', ]; + /** + * @var \Drupal\profile\ProfileStorageInterface + */ + protected $profileStorage; + /** * Modules to enable. * @@ -51,6 +57,14 @@ class ProfileSelectTest extends JavascriptTestBase { 'commerce_order_test', ]; + /** + * @inheritDoc + */ + protected function setUp() { + parent::setUp(); + $this->profileStorage = $this->container->get('entity_type.manager')->getStorage('profile'); + } + /** * Tests the profile select form element for anonymous user. */ @@ -131,43 +145,35 @@ public function testProfileSelectAuthenticated() { * The number of profiles before the form submission. */ protected function useProfileForm($uid, array $address_fields, $initial_profile_count = 0) { - $profile_ids = \Drupal::service('entity.query') - ->get('profile') - ->condition('uid', $uid) - ->execute(); - $this->assertEmpty($profile_ids, t('The number of profiles before submitting the profile form is @count.', ['@count' => $initial_profile_count])); + $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $uid)->execute(); + $this->assertEmpty($profile_ids); - $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); if (0 == $initial_profile_count || 0 == $uid) { - $this->assertSession()->fieldNotExists('profile[profile_selection]'); + $this->assertSession()->fieldNotExists('Select a profile'); } else { - $this->assertSession()->fieldExists('profile[profile_selection]'); - $edit = [ - 'profile[profile_selection]', 'new_profile', - ]; - $this->drupalPostForm(NULL, $edit, ['profile[profile_selection]']); + $this->assertSession()->fieldExists('Select a profile'); + $this->getSession()->getPage()->fillField('Select a profile', 'new_profile'); $this->waitForAjaxToFinish(); } - $this->assertSession()->fieldNotExists('profile[address][0][address][administrative_area]'); - - $this->getSession()->getPage()->fillField('profile[address][0][address][country_code]', $address_fields['country_code']); + $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); $this->waitForAjaxToFinish(); + $this->createScreenshot(); + $edit = []; + unset($address_fields['country_code']); foreach ($address_fields as $key => $value) { $edit['profile[address][0][address][' . $key . ']'] = $value; } $this->submitForm($edit, 'Submit'); - $profile_ids = \Drupal::service('entity.query') - ->get('profile') - ->condition('uid', $uid) - ->execute(); - $this->Equals($initial_profile_count + 1, count($profile_ids), t('There are @count profiles after creating a new one.', ['@count' => $initial_profile_count + 1])); + $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $uid)->execute(); + $this->assertEquals($initial_profile_count + 1, count($profile_ids)); } } diff --git a/tests/src/FunctionalJavascript/JavascriptTestTrait.php b/tests/src/FunctionalJavascript/JavascriptTestTrait.php index ac92303811..8fbf479959 100644 --- a/tests/src/FunctionalJavascript/JavascriptTestTrait.php +++ b/tests/src/FunctionalJavascript/JavascriptTestTrait.php @@ -55,4 +55,27 @@ protected function waitForAjaxToFinish() { $this->assertJsCondition($condition, 10000); } + /** + * Creates a screenshot. + * + * @param bool $set_background_color + * (optional) By default this method will set the background color to white. + * Set to FALSE to override this behaviour. + * + * @throws \Behat\Mink\Exception\UnsupportedDriverActionException + * When operation not supported by the driver. + * @throws \Behat\Mink\Exception\DriverException + * When the operation cannot be done. + */ + protected function createScreenshot($set_background_color = TRUE) { + $jpg_output_filename = $this->htmlOutputClassName . '-' . $this->htmlOutputCounter . '-' . $this->htmlOutputTestId . '.jpg'; + $session = $this->getSession(); + if ($set_background_color) { + $session->executeScript("document.body.style.backgroundColor = 'white';"); + } + $image = $session->getScreenshot(); + file_put_contents($this->htmlOutputDirectory . '/' . $jpg_output_filename, $image); + $this->htmlOutputCounter++; + } + } From 727e392e3604a58be83852d7517c3e55092d8071 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Mon, 5 Jun 2017 17:09:12 -0500 Subject: [PATCH 04/17] Fix changing profile and rendering --- modules/order/src/Element/ProfileSelect.php | 158 ++++++++---------- .../ProfileSelectTest.php | 25 ++- 2 files changed, 83 insertions(+), 100 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 2e8866aaca..40765731b0 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -3,11 +3,9 @@ namespace Drupal\commerce_order\Element; use Drupal\commerce\Element\CommerceElementTrait; -use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element; use Drupal\Core\Render\Element\RenderElement; use Drupal\profile\Entity\ProfileInterface; @@ -89,15 +87,44 @@ public static function processForm(array $element, FormStateInterface $form_stat throw new \InvalidArgumentException('The commerce_profile_select #available_countries property must be an array.'); } + // Prefix and suffix used for Ajax replacement. + $ajax_wrapper_id = 'profile-select-ajax-wrapper'; + $element['#prefix'] = '
'; + $element['#suffix'] = '
'; + + /** @var \Drupal\profile\ProfileStorageInterface $storage */ $storage = \Drupal::entityTypeManager()->getStorage('profile'); $element['#profile'] = $element['#default_value']; $default_profile_id = $element['#profile']->id(); $default_profile = $element['#profile']; + + // Fetch all profiles of the user for an addressbook functionality. + $profile_uid = $element['#profile']->getOwnerId(); + // Anonymous users don't get an addressbook. + if ($profile_uid) { + /** @var \Drupal\profile\Entity\Profile[] $profiles */ + $profiles = $storage->loadMultipleByUser($element['#profile']->getOwner(), $element['#profile']->bundle(), TRUE); + $profile_options = []; + foreach ($profiles as $profile_option) { + if (empty($default_profile_id) && $profile_option->isDefault()) { + $default_profile_id = $profile_option->id(); + $default_profile = $profile_option; + } + $profile_options[$profile_option->id()] = $profile_option->label(); + } + $profile_options['new_profile'] = t('+ Enter a new profile'); + } + else { + $profiles = []; + $profile_options = []; + $default_profile_id = 'new_profile'; + } + $mode = 'view'; $triggering_element = $form_state->getTriggeringElement(); if (!empty($triggering_element)) { - $triggering_element_parents = $triggering_element['#parents']; + $triggering_element_parents = $triggering_element['#array_parents']; $last_parent = array_pop($triggering_element_parents); if ($triggering_element_parents == $element['#parents']) { if ($last_parent == 'edit_button') { @@ -109,6 +136,11 @@ public static function processForm(array $element, FormStateInterface $form_stat elseif ($last_parent == 'profile_selection' && $triggering_element['#value'] == 'new_profile') { $mode = 'new'; } + elseif ($last_parent == 'profile_selection') { + $default_profile_id = $triggering_element['#value']; + $default_profile = $storage->load($default_profile_id); + $mode = 'view'; + } } // If first element parent matches, assume field within Profile did AJAX. elseif ($triggering_element_parents[0] == $element['#parents'][0]) { @@ -116,16 +148,11 @@ public static function processForm(array $element, FormStateInterface $form_stat } } else { - if (empty($profile_ids)) { + if (empty($profiles)) { $mode = 'new'; } } - $ajax_wrapper_id = Html::getUniqueId('profile-select-ajax-wrapper'); - // Prefix and suffix used for Ajax replacement. - $element['#prefix'] = '
'; - $element['#suffix'] = '
'; - // No profiles found or user wants to create a new one. if ($mode == 'new') { $values = [ @@ -133,75 +160,21 @@ public static function processForm(array $element, FormStateInterface $form_stat 'uid' => $element['#profile']->getOwnerId(), ]; $default_profile = $storage->create($values); - $default_profile_id = NULL; - } - - $form_display = EntityFormDisplay::collectRenderDisplay($default_profile, 'default'); - $form_display->buildForm($default_profile, $element, $form_state); - if (!empty($element['address']['widget'][0])) { - $widget_element = &$element['address']['widget'][0]; - // Remove the details wrapper from the address widget. - $widget_element['#type'] = 'container'; - // Provide a default country. - if (!empty($element['#default_country']) && empty($widget_element['address']['#default_value']['country_code'])) { - $widget_element['address']['#default_value']['country_code'] = $element['#default_country']; - } - // Limit the available countries. - if (!empty($element['#available_countries'])) { - $widget_element['address']['#available_countries'] = $element['#available_countries']; - } - } - - // Fetch all profiles of the user for an addressbook functionality. - $profile_uid = $element['#profile']->getOwnerId(); - // Anonymous users don't get an addressbook. - if ($profile_uid) { - $profile_ids = $storage->getQuery() - ->condition('uid', $profile_uid) - ->condition('type', $element['#profile']->bundle()) - ->condition('status', TRUE) - ->sort('profile_id', 'DESC') - ->execute(); - /** @var \Drupal\profile\Entity\Profile[] $profiles */ - $profiles = $storage->loadMultiple($profile_ids); - $profile_options = []; - foreach ($profiles as $profile_option) { - if (empty($default_profile_id) && $profile_option->isDefault()) { - $default_profile_id = $profile_option->id(); - $default_profile = $profile_option; - } - $profile_options[$profile_option->id()] = $profile_option->label(); - } - $profile_options['new_profile'] = t('+ Enter a new profile'); + $default_profile_id = 'new_profile'; } - if (!empty($profile_uid) && $mode != 'edit' && !empty($profile_options) && count($profile_options) > 1) { - $element['profile_selection'] = [ - '#title' => t('Select a profile'), - '#options' => $profile_options, - '#type' => 'select', - '#weight' => -5, - '#default_value' => $default_profile_id, - '#ajax' => [ - 'callback' => [get_called_class(), 'profileSelectAjax'], - 'wrapper' => $ajax_wrapper_id, - ], - ]; - } - - $name = array_shift($element['#parents']); - $element['cancel_button'] = [ - '#type' => 'button', - '#value' => t('Return to profile selection'), - '#limit_validation_errors' => [], - '#name' => $name . '[' . implode('][', $element['#parents']) . '][cancel_button]', + $element['profile_selection'] = [ + '#title' => t('Select a profile'), + '#options' => $profile_options, + '#type' => 'select', + '#weight' => -5, + '#default_value' => $default_profile_id, '#ajax' => [ 'callback' => [get_called_class(), 'profileSelectAjax'], 'wrapper' => $ajax_wrapper_id, ], - '#weight' => 99, + '#access' => count($profile_options) > 1, ]; - array_unshift($element['#parents'], $name); // Viewing a profile. if ($mode == 'view') { @@ -213,31 +186,44 @@ public static function processForm(array $element, FormStateInterface $form_stat $content, ]; - $name = array_shift($element['#parents']); $element['edit_button'] = [ '#type' => 'button', '#value' => t('Edit'), - '#name' => $name . '[' . implode('][', $element['#parents']) . '][edit_button]', '#limit_validation_errors' => [], '#ajax' => [ 'callback' => [get_called_class(), 'profileSelectAjax'], 'wrapper' => $ajax_wrapper_id, ], + '#access' => $mode != 'edit', ]; - array_unshift($element['#parents'], $name); - - foreach (Element::children($element) as $key) { - if (!in_array($key, [ - 'edit_button', - 'rendered_profile', - 'profile_selection', - ])) { - $element[$key]['#access'] = FALSE; + } + else { + $form_display = EntityFormDisplay::collectRenderDisplay($default_profile, 'default'); + $form_display->buildForm($default_profile, $element, $form_state); + if (!empty($element['address']['widget'][0])) { + $widget_element = &$element['address']['widget'][0]; + // Remove the details wrapper from the address widget. + $widget_element['#type'] = 'container'; + // Provide a default country. + if (!empty($element['#default_country']) && empty($widget_element['address']['#default_value']['country_code'])) { + $widget_element['address']['#default_value']['country_code'] = $element['#default_country']; + } + // Limit the available countries. + if (!empty($element['#available_countries'])) { + $widget_element['address']['#available_countries'] = $element['#available_countries']; } } - } - if (empty($profiles) || $mode != 'edit') { - $element['cancel_button']['#access'] = FALSE; + $element['cancel_button'] = [ + '#type' => 'button', + '#value' => t('Return to profile selection'), + '#limit_validation_errors' => [], + '#ajax' => [ + 'callback' => [get_called_class(), 'profileSelectAjax'], + 'wrapper' => $ajax_wrapper_id, + ], + '#access' => $mode == 'edit', + '#weight' => 99, + ]; } return $element; @@ -296,7 +282,7 @@ public static function submitForm(array &$element, FormStateInterface $form_stat * The form element replace the wrapper with. */ public static function profileSelectAjax(array &$form, FormStateInterface $form_state) { - $triggering_parents = $form_state->getTriggeringElement()['#parents']; + $triggering_parents = $form_state->getTriggeringElement()['#array_parents']; array_pop($triggering_parents); return NestedArray::getValue($form, $triggering_parents); } diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index e8cca2c8e8..4e6ec671c9 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -74,15 +74,16 @@ public function testProfileSelectAnonymous() { /** * Tests the profile select form element for authenticated user. + * + * @group debug */ public function testProfileSelectAuthenticated() { $account = $this->createUser(); $this->drupalLogin($account); $this->useProfileForm($account->id(), $this->address1); - // Create one more profile to test how the form behaves with two existing profiles. $this->useProfileForm($account->id(), $this->address2, 1); - $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->fieldExists('profile_selection'); // The last created profile should be selected by default. @@ -99,14 +100,14 @@ public function testProfileSelectAuthenticated() { } // Edit a profile. - $this->drupalGet('/commerce_order_test/profile_select_form'); + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); $this->assertSession()->fieldExists('profile[profile_selection]'); // The last created profile should be selected by default. $this->assertSession()->elementTextContains('css', '.locality', $this->address2['locality']); $this->assertSession()->fieldNotExists('profile[cancel]'); - $this->drupalPostForm(NULL, [], ['profile[edit]']); + $this->submitForm([], 'profile[edit]'); foreach ($this->address1 as $key => $value) { $this->assertSession()->fieldValueEquals('profile[address][0][address][' . $key . ']', $value); } @@ -116,8 +117,8 @@ public function testProfileSelectAuthenticated() { 'profile[address][0][address][address_line1]' => 'Andrássy út 22', ]; $this->submitForm([], 'Submit'); - $profile = \Drupal::entityTypeManager()->getStorage('profile')->resetCache([1]); - $profile = \Drupal::entityTypeManager()->getStorage('profile')->load([1]); + $this->profileStorage->resetCache([1]); + $profile = $this->profileStorage->load([1]); // Assert that only address_line1 has changed. foreach ($this->address1 as $key => $value) { if ($key == 'address_line1') { @@ -127,11 +128,8 @@ public function testProfileSelectAuthenticated() { $this->assertEquals($this->address1[$key], $profile->address->getValue()[0][$key], t('@key of address has not changed.', ['@key' => $key])); } } - $profile_ids = \Drupal::service('entity.query') - ->get('profile') - ->condition('uid', $account->id()) - ->execute(); - $this->Equals(2, count($profile_ids), t('No new profile has been created after editing an existing one.')); + $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $account->id())->execute(); + $this->assertEquals(2, $profile_ids, t('No new profile has been created after editing an existing one.')); } /** @@ -146,7 +144,7 @@ public function testProfileSelectAuthenticated() { */ protected function useProfileForm($uid, array $address_fields, $initial_profile_count = 0) { $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $uid)->execute(); - $this->assertEmpty($profile_ids); + $this->assertEquals($initial_profile_count, $profile_ids); $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); @@ -159,11 +157,10 @@ protected function useProfileForm($uid, array $address_fields, $initial_profile_ $this->getSession()->getPage()->fillField('Select a profile', 'new_profile'); $this->waitForAjaxToFinish(); } + $this->createScreenshot(); $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); $this->waitForAjaxToFinish(); - $this->createScreenshot(); - $edit = []; unset($address_fields['country_code']); foreach ($address_fields as $key => $value) { From 4c88c9dbadbf9563620e9789d34d827623134468 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 6 Jun 2017 15:00:27 -0500 Subject: [PATCH 05/17] Update the element and refactor some items --- modules/order/src/Element/ProfileSelect.php | 274 +++++++++--------- .../commerce_order_test.routing.yml | 2 + .../src/Form/ProfileSelectTestForm.php | 15 +- 3 files changed, 151 insertions(+), 140 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 40765731b0..a3fb4b5f8f 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -3,11 +3,11 @@ namespace Drupal\commerce_order\Element; use Drupal\commerce\Element\CommerceElementTrait; +use Drupal\Component\Utility\Html; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Form\FormStateInterface; -use Drupal\Core\Render\Element\RenderElement; -use Drupal\profile\Entity\ProfileInterface; +use Drupal\Core\Render\Element\FormElement; /** * Provides a form element for selecting a customer profile. @@ -25,9 +25,9 @@ * $form['billing_profile']['#profile']. Due to Drupal core limitations the * profile can't be accessed via $form_state->getValue('billing_profile'). * - * @RenderElement("commerce_profile_select") + * @FormElement("commerce_profile_select") */ -class ProfileSelect extends RenderElement { +class ProfileSelect extends FormElement { use CommerceElementTrait; @@ -43,7 +43,9 @@ public function getInfo() { '#available_countries' => [], // The profile entity operated on. Required. - '#default_value' => NULL, + '#default_value' => '_new', + '#owner_uid' => 0, + '#profile_type' => NULL, '#process' => [ [$class, 'attachElementSubmit'], [$class, 'processForm'], @@ -59,6 +61,16 @@ public function getInfo() { ]; } + /** + * @inheritDoc + */ + public static function valueCallback(&$element, $input, FormStateInterface $form_state) { + if (!empty($input)) { + return $input['profile_selection']; + } + return parent::valueCallback($element, $input, $form_state); + } + /** * Builds the element form. * @@ -77,129 +89,105 @@ public function getInfo() { * The processed form element. */ public static function processForm(array $element, FormStateInterface $form_state, array &$complete_form) { - if (empty($element['#default_value'])) { - throw new \InvalidArgumentException('The commerce_profile_select element requires the #default_value property.'); - } - if (!($element['#default_value'] instanceof ProfileInterface)) { - throw new \InvalidArgumentException('The commerce_profile_select #default_value property must be a profile entity.'); - } if (!is_array($element['#available_countries'])) { throw new \InvalidArgumentException('The commerce_profile_select #available_countries property must be an array.'); } - - // Prefix and suffix used for Ajax replacement. - $ajax_wrapper_id = 'profile-select-ajax-wrapper'; - $element['#prefix'] = '
'; - $element['#suffix'] = '
'; - - /** @var \Drupal\profile\ProfileStorageInterface $storage */ - $storage = \Drupal::entityTypeManager()->getStorage('profile'); - - $element['#profile'] = $element['#default_value']; - $default_profile_id = $element['#profile']->id(); - $default_profile = $element['#profile']; - - // Fetch all profiles of the user for an addressbook functionality. - $profile_uid = $element['#profile']->getOwnerId(); - // Anonymous users don't get an addressbook. - if ($profile_uid) { - /** @var \Drupal\profile\Entity\Profile[] $profiles */ - $profiles = $storage->loadMultipleByUser($element['#profile']->getOwner(), $element['#profile']->bundle(), TRUE); - $profile_options = []; - foreach ($profiles as $profile_option) { - if (empty($default_profile_id) && $profile_option->isDefault()) { - $default_profile_id = $profile_option->id(); - $default_profile = $profile_option; - } - $profile_options[$profile_option->id()] = $profile_option->label(); - } - $profile_options['new_profile'] = t('+ Enter a new profile'); - } - else { - $profiles = []; - $profile_options = []; - $default_profile_id = 'new_profile'; + if (empty($element['#profile_type'])) { + throw new \InvalidArgumentException('The commerce_profile_select #profile_type property must be provided.'); } + $entity_type_manager = \Drupal::entityTypeManager(); + /** @var \Drupal\profile\ProfileStorageInterface $profile_storage */ + $profile_storage = $entity_type_manager->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileTypeInterface $profile_type */ + $profile_type = $entity_type_manager->getStorage('profile_type')->load($element['#profile_type']); - $mode = 'view'; - $triggering_element = $form_state->getTriggeringElement(); - if (!empty($triggering_element)) { - $triggering_element_parents = $triggering_element['#array_parents']; - $last_parent = array_pop($triggering_element_parents); - if ($triggering_element_parents == $element['#parents']) { - if ($last_parent == 'edit_button') { - $mode = 'edit'; - } - elseif ($last_parent == 'cancel_button') { - $mode = 'view'; - } - elseif ($last_parent == 'profile_selection' && $triggering_element['#value'] == 'new_profile') { - $mode = 'new'; - } - elseif ($last_parent == 'profile_selection') { - $default_profile_id = $triggering_element['#value']; - $default_profile = $storage->load($default_profile_id); - $mode = 'view'; + $user_profiles = []; + /** @var \Drupal\user\UserInterface $user */ + $user = $entity_type_manager->getStorage('user')->load($element['#owner_uid']); + if (!$user->isAnonymous()) { + // If the user exists, attempt to load other profiles for selection. + foreach ($profile_storage->loadMultipleByUser($user, $profile_type->id(), TRUE) as $existing_profile) { + $user_profiles[$existing_profile->id()] = $existing_profile->label(); + + // If there is no default value, and the user has a default profile, + // use that as the current value. + if (empty($element['#value']) && $existing_profile->isDefault()) { + $element['#value'] = $existing_profile->id(); } } - // If first element parent matches, assume field within Profile did AJAX. - elseif ($triggering_element_parents[0] == $element['#parents'][0]) { - $mode = 'edit'; - } } else { - if (empty($profiles)) { - $mode = 'new'; - } - } - - // No profiles found or user wants to create a new one. - if ($mode == 'new') { - $values = [ - 'type' => $element['#profile']->bundle(), - 'uid' => $element['#profile']->getOwnerId(), - ]; - $default_profile = $storage->create($values); - $default_profile_id = 'new_profile'; + $element['#value'] = '_new'; } + $id_prefix = implode('-', $element['#parents']); + $wrapper_id = Html::getUniqueId($id_prefix . '-ajax-wrapper'); + $element = [ + '#tree' => TRUE, + '#prefix' => '
', + '#suffix' => '
', + // Pass the id along to other methods. + '#wrapper_id' => $wrapper_id, + ] + $element; $element['profile_selection'] = [ '#title' => t('Select a profile'), - '#options' => $profile_options, + '#options' => $user_profiles + ['_new' => t('+ Create new :label', [':label' => $profile_type->label()])], '#type' => 'select', '#weight' => -5, - '#default_value' => $default_profile_id, + '#default_value' => $element['#value'], '#ajax' => [ - 'callback' => [get_called_class(), 'profileSelectAjax'], - 'wrapper' => $ajax_wrapper_id, + 'callback' => [get_called_class(), 'ajaxRefresh'], + 'wrapper' => $wrapper_id, ], - '#access' => count($profile_options) > 1, + '#element_mode' => 'view', + '#access' => !empty($user_profiles), ]; - // Viewing a profile. - if ($mode == 'view') { - $view_builder = \Drupal::entityTypeManager() - ->getViewBuilder('profile'); - $content = $view_builder->view($default_profile, 'default'); + /** @var \Drupal\profile\Entity\ProfileInterface $element_profile */ + if ($element['#value'] == '_new') { + $element_profile = $profile_storage->create([ + 'type' => $profile_type->id(), + 'uid' => $user->id(), + ]); + $element['#element_mode'] = 'create'; + } + else { + $element_profile = $profile_storage->load($element['#value']); + $element['#element_mode'] = 'view'; - $element['rendered_profile'] = [ - $content, - ]; + $triggering_element = $form_state->getTriggeringElement(); + if ($triggering_element) { + $element['#element_mode'] = $triggering_element['#element_mode']; + } + } + + // Viewing a profile. + if ($element['#element_mode'] == 'view') { + $view_builder = $entity_type_manager->getViewBuilder('profile'); + $element['rendered_profile'] = $view_builder->view($element_profile, 'default'); $element['edit_button'] = [ '#type' => 'button', '#value' => t('Edit'), - '#limit_validation_errors' => [], + '#limit_validation_errors' => [ + $element['#parents'], + ], '#ajax' => [ - 'callback' => [get_called_class(), 'profileSelectAjax'], - 'wrapper' => $ajax_wrapper_id, + 'callback' => [get_called_class(), 'ajaxRefresh'], + 'wrapper' => $wrapper_id, ], - '#access' => $mode != 'edit', + '#name' => 'edit_profile', + '#element_mode' => 'edit', + // @todo Allow editing. + // '#access' => $element['#element_mode'] == 'view', + '#access' => FALSE, ]; } else { - $form_display = EntityFormDisplay::collectRenderDisplay($default_profile, 'default'); - $form_display->buildForm($default_profile, $element, $form_state); + $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); + $form_display->buildForm($element_profile, $element, $form_state); + + // @todo Loop over all possible address fields. if (!empty($element['address']['widget'][0])) { $widget_element = &$element['address']['widget'][0]; // Remove the details wrapper from the address widget. @@ -216,13 +204,16 @@ public static function processForm(array $element, FormStateInterface $form_stat $element['cancel_button'] = [ '#type' => 'button', '#value' => t('Return to profile selection'), - '#limit_validation_errors' => [], + '#limit_validation_errors' => [ + $element['#parents'], + ], '#ajax' => [ - 'callback' => [get_called_class(), 'profileSelectAjax'], - 'wrapper' => $ajax_wrapper_id, + 'callback' => [get_called_class(), 'ajaxRefresh'], + 'wrapper' => $wrapper_id, ], - '#access' => $mode == 'edit', - '#weight' => 99, + '#name' => 'cancel_edit_profile', + '#element_mode' => 'view', + '#access' => $element['#element_mode'] == 'edit', ]; } @@ -242,17 +233,26 @@ public static function processForm(array $element, FormStateInterface $form_stat * form, as a protection against buggy behavior. */ public static function validateForm(array &$element, FormStateInterface $form_state) { - $triggering_parents = $form_state->getTriggeringElement()['#parents']; - $triggering_last_parent = array_pop($triggering_parents); - if (!in_array($triggering_last_parent, [ - 'edit_button', - 'cancel_button', - 'profile_selection', - 'country_code', - ])) { - $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); - $form_display->extractFormValues($element['#profile'], $element, $form_state); - $form_display->validateFormValues($element['#profile'], $element, $form_state); + $value = $form_state->getValue($element['#parents']); + + $entity_type_manager = \Drupal::entityTypeManager(); + /** @var \Drupal\profile\ProfileStorageInterface $profile_storage */ + $profile_storage = $entity_type_manager->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileInterface $element_profile */ + if ($value['profile_selection'] == '_new') { + $element_profile = $profile_storage->create([ + 'type' => $element['#profile_type'], + 'uid' => $element['#owner_uid'], + ]); + } + else { + $element_profile = $profile_storage->load($value['profile_selection']); + } + + if ($element['#element_mode'] != 'view') { + $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); + $form_display->extractFormValues($element_profile, $element, $form_state); + $form_display->validateFormValues($element_profile, $element, $form_state); } } @@ -265,26 +265,38 @@ public static function validateForm(array &$element, FormStateInterface $form_st * The current state of the form. */ public static function submitForm(array &$element, FormStateInterface $form_state) { - $form_display = EntityFormDisplay::collectRenderDisplay($element['#profile'], 'default'); - $form_display->extractFormValues($element['#profile'], $element, $form_state); - $element['#profile']->save(); + $value = $form_state->getValue($element['#parents']); + + $entity_type_manager = \Drupal::entityTypeManager(); + /** @var \Drupal\profile\ProfileStorageInterface $profile_storage */ + $profile_storage = $entity_type_manager->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileInterface $element_profile */ + if ($value['profile_selection'] == '_new') { + $element_profile = $profile_storage->create([ + 'type' => $element['#profile_type'], + 'uid' => $element['#owner_uid'], + ]); + } + else { + $element_profile = $profile_storage->load($value['profile_selection']); + } + + if ($element['#element_mode'] != 'view') { + $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); + $form_display->extractFormValues($element_profile, $element, $form_state); + $element_profile->save(); + } + + $form_state->setValueForElement($element, $element_profile); } /** - * Profile form AJAX callback. - * - * @param array $form - * The complete form array. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * - * @return array - * The form element replace the wrapper with. + * Ajax callback. */ - public static function profileSelectAjax(array &$form, FormStateInterface $form_state) { - $triggering_parents = $form_state->getTriggeringElement()['#array_parents']; - array_pop($triggering_parents); - return NestedArray::getValue($form, $triggering_parents); + public static function ajaxRefresh(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + $element = NestedArray::getValue($form, array_slice($triggering_element['#array_parents'], 0, -1)); + return $element; } } diff --git a/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml b/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml index e0f444a442..7d62a7662f 100644 --- a/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml +++ b/modules/order/tests/modules/commerce_order_test/commerce_order_test.routing.yml @@ -5,3 +5,5 @@ commerce_order_test.profile_select_form: _title: 'Profile select test form' requirements: _access: 'TRUE' + options: + no_cache: TRUE diff --git a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php index bc4b905b51..f9a1da10cf 100644 --- a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php +++ b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php @@ -18,17 +18,13 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - $profile_storage = \Drupal::getContainer()->get('entity_type.manager')->getStorage('profile'); - - $profile = $profile_storage->create([ - 'type' => 'customer', - 'uid' => \Drupal::currentUser()->id(), - ]); - $form['profile'] = [ '#type' => 'commerce_profile_select', '#title' => $this->t('Profile'), - '#default_value' => $profile, + '#default_value' => NULL, + '#profile_type' => 'customer', + '#owner_uid' => \Drupal::currentUser()->id(), + '#available_countries' => ['HU', 'FR', 'US', 'RS'], ]; $form['submit'] = [ '#type' => 'submit', @@ -42,7 +38,8 @@ public function buildForm(array $form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { - drupal_set_message('Profile saved.'); + $profile = $form_state->getValue('profile'); + drupal_set_message($this->t('Profile saved: :label', [':label' => $profile->label()])); } } From 325ec82dddb8ff52d7229f659cbf7646f9859392 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 12:45:27 -0500 Subject: [PATCH 06/17] Get anonymous to pass again --- modules/order/src/Element/ProfileSelect.php | 11 +++--- .../ProfileSelectTest.php | 34 +++++++++++++++++-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index a3fb4b5f8f..f02efce794 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -62,13 +62,13 @@ public function getInfo() { } /** - * @inheritDoc + * {@inheritdoc} */ public static function valueCallback(&$element, $input, FormStateInterface $form_state) { if (!empty($input)) { return $input['profile_selection']; } - return parent::valueCallback($element, $input, $form_state); + return '_new'; } /** @@ -120,6 +120,9 @@ public static function processForm(array $element, FormStateInterface $form_stat $element['#value'] = '_new'; } + $current_value = $element['#value']; + $stop = NULL; + $id_prefix = implode('-', $element['#parents']); $wrapper_id = Html::getUniqueId($id_prefix . '-ajax-wrapper'); $element = [ @@ -153,8 +156,6 @@ public static function processForm(array $element, FormStateInterface $form_stat } else { $element_profile = $profile_storage->load($element['#value']); - $element['#element_mode'] = 'view'; - $triggering_element = $form_state->getTriggeringElement(); if ($triggering_element) { $element['#element_mode'] = $triggering_element['#element_mode']; @@ -162,7 +163,7 @@ public static function processForm(array $element, FormStateInterface $form_stat } // Viewing a profile. - if ($element['#element_mode'] == 'view') { + if (!$element_profile->isNew() && $element['#element_mode'] == 'view') { $view_builder = $entity_type_manager->getViewBuilder('profile'); $element['rendered_profile'] = $view_builder->view($element_profile, 'default'); diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 4e6ec671c9..8830ae7ce5 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -67,15 +67,43 @@ protected function setUp() { /** * Tests the profile select form element for anonymous user. + * + * @group debug */ public function testProfileSelectAnonymous() { - $this->useProfileForm(0, $this->address1); + $this->drupalLogout(); + $address_fields = $this->address1; + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); + $this->assertSession()->statusCodeEquals(200); + + $this->assertSession()->fieldNotExists('Select a profile'); + $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); + $this->waitForAjaxToFinish(); + + $edit = []; + foreach ($address_fields as $key => $value) { + if ($key == 'country_code') { + continue; + } + $edit['profile[address][0][address][' . $key . ']'] = $value; + } + + $this->submitForm($edit, 'Submit'); + + /** @var \Drupal\profile\Entity\ProfileInterface $profile */ + $profile = $this->profileStorage->load(1); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ + $address = $profile->get('address')->first(); + $this->assertEquals($address_fields['country_code'], $address->getCountryCode()); + $this->assertEquals($address_fields['given_name'], $address->getGivenName()); + $this->assertEquals($address_fields['family_name'], $address->getFamilyName()); + $this->assertEquals($address_fields['address_line1'], $address->getAddressLine1()); + $this->assertEquals($address_fields['locality'], $address->getLocality()); + $this->assertEquals($address_fields['postal_code'], $address->getPostalCode()); } /** * Tests the profile select form element for authenticated user. - * - * @group debug */ public function testProfileSelectAuthenticated() { $account = $this->createUser(); From 9f882ba8130ab65af8eb61e5145f5c6d79cf95cc Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 13:03:04 -0500 Subject: [PATCH 07/17] Test anon, auth without existing. --- modules/order/src/Element/ProfileSelect.php | 5 +-- .../ProfileSelectTest.php | 41 +++++++++++++++++-- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index f02efce794..b9b8994b88 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -104,6 +104,8 @@ public static function processForm(array $element, FormStateInterface $form_stat $user_profiles = []; /** @var \Drupal\user\UserInterface $user */ $user = $entity_type_manager->getStorage('user')->load($element['#owner_uid']); + + $element['#value'] = '_new'; if (!$user->isAnonymous()) { // If the user exists, attempt to load other profiles for selection. foreach ($profile_storage->loadMultipleByUser($user, $profile_type->id(), TRUE) as $existing_profile) { @@ -116,9 +118,6 @@ public static function processForm(array $element, FormStateInterface $form_stat } } } - else { - $element['#value'] = '_new'; - } $current_value = $element['#value']; $stop = NULL; diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 8830ae7ce5..344c3fef0c 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -67,10 +67,8 @@ protected function setUp() { /** * Tests the profile select form element for anonymous user. - * - * @group debug */ - public function testProfileSelectAnonymous() { + public function testAnonymous() { $this->drupalLogout(); $address_fields = $this->address1; $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); @@ -102,6 +100,43 @@ public function testProfileSelectAnonymous() { $this->assertEquals($address_fields['postal_code'], $address->getPostalCode()); } + /** + * Tests the profile select form element for anonymous user. + */ + public function testAuthenticatedNoExistingProfiles() { + $account = $this->createUser(); + $this->drupalLogin($account); + + $address_fields = $this->address1; + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); + $this->assertSession()->statusCodeEquals(200); + + $this->assertSession()->fieldNotExists('Select a profile'); + $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); + $this->waitForAjaxToFinish(); + + $edit = []; + foreach ($address_fields as $key => $value) { + if ($key == 'country_code') { + continue; + } + $edit['profile[address][0][address][' . $key . ']'] = $value; + } + + $this->submitForm($edit, 'Submit'); + + /** @var \Drupal\profile\Entity\ProfileInterface $profile */ + $profile = $this->profileStorage->load(1); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ + $address = $profile->get('address')->first(); + $this->assertEquals($address_fields['country_code'], $address->getCountryCode()); + $this->assertEquals($address_fields['given_name'], $address->getGivenName()); + $this->assertEquals($address_fields['family_name'], $address->getFamilyName()); + $this->assertEquals($address_fields['address_line1'], $address->getAddressLine1()); + $this->assertEquals($address_fields['locality'], $address->getLocality()); + $this->assertEquals($address_fields['postal_code'], $address->getPostalCode()); + } + /** * Tests the profile select form element for authenticated user. */ From 0aad4c591fbe3c5ec93bdf654eecaa4f1b56575d Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 14:35:49 -0500 Subject: [PATCH 08/17] Split out auth select/edit test. --- modules/order/src/Element/ProfileSelect.php | 48 +++---- .../src/Form/ProfileSelectTestForm.php | 2 +- .../ProfileSelectTest.php | 119 ++++++++++-------- 3 files changed, 96 insertions(+), 73 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index b9b8994b88..0d547e9b2a 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -65,7 +65,7 @@ public function getInfo() { * {@inheritdoc} */ public static function valueCallback(&$element, $input, FormStateInterface $form_state) { - if (!empty($input)) { + if (!empty($input['profile_selection'])) { return $input['profile_selection']; } return '_new'; @@ -105,23 +105,19 @@ public static function processForm(array $element, FormStateInterface $form_stat /** @var \Drupal\user\UserInterface $user */ $user = $entity_type_manager->getStorage('user')->load($element['#owner_uid']); - $element['#value'] = '_new'; if (!$user->isAnonymous()) { // If the user exists, attempt to load other profiles for selection. foreach ($profile_storage->loadMultipleByUser($user, $profile_type->id(), TRUE) as $existing_profile) { $user_profiles[$existing_profile->id()] = $existing_profile->label(); - // If there is no default value, and the user has a default profile, - // use that as the current value. - if (empty($element['#value']) && $existing_profile->isDefault()) { + // If this is the first form build, set the element's value based on + // the user's default profile. + if (!$form_state->isRebuilding() && $existing_profile->isDefault()) { $element['#value'] = $existing_profile->id(); } } } - $current_value = $element['#value']; - $stop = NULL; - $id_prefix = implode('-', $element['#parents']); $wrapper_id = Html::getUniqueId($id_prefix . '-ajax-wrapper'); $element = [ @@ -130,20 +126,30 @@ public static function processForm(array $element, FormStateInterface $form_stat '#suffix' => '', // Pass the id along to other methods. '#wrapper_id' => $wrapper_id, - ] + $element; - $element['profile_selection'] = [ - '#title' => t('Select a profile'), - '#options' => $user_profiles + ['_new' => t('+ Create new :label', [':label' => $profile_type->label()])], - '#type' => 'select', - '#weight' => -5, - '#default_value' => $element['#value'], - '#ajax' => [ - 'callback' => [get_called_class(), 'ajaxRefresh'], - 'wrapper' => $wrapper_id, - ], '#element_mode' => 'view', - '#access' => !empty($user_profiles), - ]; + ] + $element; + + if (!empty($user_profiles)) { + $element['profile_selection'] = [ + '#title' => t('Select a profile'), + '#options' => $user_profiles + ['_new' => t('+ Create new :label', [':label' => $profile_type->label()])], + '#type' => 'select', + '#weight' => -5, + '#default_value' => $element['#value'], + '#ajax' => [ + 'callback' => [get_called_class(), 'ajaxRefresh'], + 'wrapper' => $wrapper_id, + ], + '#element_mode' => 'view', + ]; + } + else { + $element['profile_selection'] = [ + '#type' => 'value', + '#value' => '_new', + '#element_mode' => 'create', + ]; + } /** @var \Drupal\profile\Entity\ProfileInterface $element_profile */ if ($element['#value'] == '_new') { diff --git a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php index f9a1da10cf..4cf6224b1c 100644 --- a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php +++ b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php @@ -39,7 +39,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { */ public function submitForm(array &$form, FormStateInterface $form_state) { $profile = $form_state->getValue('profile'); - drupal_set_message($this->t('Profile saved: :label', [':label' => $profile->label()])); + drupal_set_message($this->t('Profile selected: :label', [':label' => $profile->label()])); } } diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 344c3fef0c..97366a1a1f 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -2,6 +2,7 @@ namespace Drupal\Tests\commerce_order\FunctionalJavascript; +use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Url; use Drupal\Tests\commerce\Functional\CommerceBrowserTestBase; use Drupal\Tests\commerce\FunctionalJavascript\JavascriptTestTrait; @@ -90,6 +91,9 @@ public function testAnonymous() { /** @var \Drupal\profile\Entity\ProfileInterface $profile */ $profile = $this->profileStorage->load(1); + + $this->assertSession()->responseContains(new FormattableMarkup('Profile selected: :label', [':label' => $profile->label()])); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ $address = $profile->get('address')->first(); $this->assertEquals($address_fields['country_code'], $address->getCountryCode()); @@ -127,6 +131,9 @@ public function testAuthenticatedNoExistingProfiles() { /** @var \Drupal\profile\Entity\ProfileInterface $profile */ $profile = $this->profileStorage->load(1); + + $this->assertSession()->responseContains(new FormattableMarkup('Profile selected: :label', [':label' => $profile->label()])); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ $address = $profile->get('address')->first(); $this->assertEquals($address_fields['country_code'], $address->getCountryCode()); @@ -142,30 +149,81 @@ public function testAuthenticatedNoExistingProfiles() { */ public function testProfileSelectAuthenticated() { $account = $this->createUser(); - $this->drupalLogin($account); - $this->useProfileForm($account->id(), $this->address1); - $this->useProfileForm($account->id(), $this->address2, 1); + $profile_storage = $this->container->get('entity_type.manager')->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileInterface $profile_address1 */ + $profile_address1 = $profile_storage->create([ + 'type' => 'customer', + 'uid' => $account->id(), + 'address' => $this->address1, + ]); + $profile_address1->save(); + /** @var \Drupal\profile\Entity\ProfileInterface $profile_address2 */ + $profile_address2 = $profile_storage->create([ + 'type' => 'customer', + 'uid' => $account->id(), + 'address' => $this->address2, + ]); + $profile_address2->setDefault(TRUE); + $profile_address2->save(); + + $this->drupalLogin($account); $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); - $this->assertSession()->fieldExists('profile_selection'); + $this->assertSession()->fieldExists('Select a profile'); // The last created profile should be selected by default. $this->assertSession()->pageTextContains($this->address2['locality']); - $this->getSession()->getPage()->fillField('profile[profile_selection]', 1); + $this->getSession()->getPage()->fillField('Select a profile', $profile_address1->id()); $this->waitForAjaxToFinish(); $this->assertSession()->pageTextContains($this->address1['locality']); $this->submitForm([], 'Submit'); - $profile = \Drupal::entityTypeManager()->getStorage('profile')->load(1); + $this->assertSession()->responseContains(new FormattableMarkup('Profile selected: :label', [':label' => $profile_address1->label()])); + + $profile_storage->resetCache([$profile_address1->id()]); + $profile_address1 = $profile_storage->load($profile_address1->id()); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ + $address = $profile_address1->get('address')->first(); // Assert that field values have not changed. - foreach ($this->address1 as $key => $value) { - $this->assertEquals($this->address1[$key], $profile->address->getValue()[0][$key], t('@key of address has not changed.', ['@key' => $key])); - } + $this->assertEquals($this->address1['country_code'], $address->getCountryCode()); + $this->assertEquals($this->address1['given_name'], $address->getGivenName()); + $this->assertEquals($this->address1['family_name'], $address->getFamilyName()); + $this->assertEquals($this->address1['address_line1'], $address->getAddressLine1()); + $this->assertEquals($this->address1['locality'], $address->getLocality()); + $this->assertEquals($this->address1['postal_code'], $address->getPostalCode()); + } + + /** + * Tests the profile select form element for authenticated user. + */ + public function testProfileSelectAuthenticatedEdit() { + $account = $this->createUser(); + + $profile_storage = $this->container->get('entity_type.manager')->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileInterface $profile_address1 */ + $profile_address1 = $profile_storage->create([ + 'type' => 'customer', + 'uid' => $account->id(), + 'address' => $this->address1, + ]); + $profile_address1->save(); + /** @var \Drupal\profile\Entity\ProfileInterface $profile_address2 */ + $profile_address2 = $profile_storage->create([ + 'type' => 'customer', + 'uid' => $account->id(), + 'address' => $this->address2, + ]); + $profile_address2->setDefault(TRUE); + $profile_address2->save(); + + $this->drupalLogin($account); + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); + $this->assertSession()->statusCodeEquals(200); // Edit a profile. $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); $this->assertSession()->statusCodeEquals(200); - $this->assertSession()->fieldExists('profile[profile_selection]'); + $this->assertSession()->fieldExists('Select a profile'); // The last created profile should be selected by default. $this->assertSession()->elementTextContains('css', '.locality', $this->address2['locality']); $this->assertSession()->fieldNotExists('profile[cancel]'); @@ -195,45 +253,4 @@ public function testProfileSelectAuthenticated() { $this->assertEquals(2, $profile_ids, t('No new profile has been created after editing an existing one.')); } - /** - * Submits the test profile select form. - * - * @param int $uid - * The user uid using the form. - * @param array $address_fields - * An associative array of address fields to submit. - * @param int $initial_profile_count - * The number of profiles before the form submission. - */ - protected function useProfileForm($uid, array $address_fields, $initial_profile_count = 0) { - $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $uid)->execute(); - $this->assertEquals($initial_profile_count, $profile_ids); - - $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); - $this->assertSession()->statusCodeEquals(200); - - if (0 == $initial_profile_count || 0 == $uid) { - $this->assertSession()->fieldNotExists('Select a profile'); - } - else { - $this->assertSession()->fieldExists('Select a profile'); - $this->getSession()->getPage()->fillField('Select a profile', 'new_profile'); - $this->waitForAjaxToFinish(); - } - $this->createScreenshot(); - $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); - $this->waitForAjaxToFinish(); - - $edit = []; - unset($address_fields['country_code']); - foreach ($address_fields as $key => $value) { - $edit['profile[address][0][address][' . $key . ']'] = $value; - } - - $this->submitForm($edit, 'Submit'); - - $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $uid)->execute(); - $this->assertEquals($initial_profile_count + 1, count($profile_ids)); - } - } From 375f9360b1b1240a83dfd305aca61c25929bc379 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 15:41:53 -0500 Subject: [PATCH 09/17] More changes --- .../src/Form/ProfileSelectTestForm.php | 2 +- .../ProfileSelectTest.php | 54 +++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php index 4cf6224b1c..30b0555f8f 100644 --- a/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php +++ b/modules/order/tests/modules/commerce_order_test/src/Form/ProfileSelectTestForm.php @@ -24,7 +24,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#default_value' => NULL, '#profile_type' => 'customer', '#owner_uid' => \Drupal::currentUser()->id(), - '#available_countries' => ['HU', 'FR', 'US', 'RS'], + '#available_countries' => ['HU', 'FR', 'US', 'RS', 'DE'], ]; $form['submit'] = [ '#type' => 'submit', diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 97366a1a1f..24164c3ae7 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -193,6 +193,60 @@ public function testProfileSelectAuthenticated() { $this->assertEquals($this->address1['postal_code'], $address->getPostalCode()); } + /** + * Tests the profile select form element for authenticated user. + * + * @group debug + */ + public function testProfileSelectAuthenticatedCreateNew() { + $account = $this->createUser(); + $address_fields = $this->address2; + $profile_storage = $this->container->get('entity_type.manager')->getStorage('profile'); + /** @var \Drupal\profile\Entity\ProfileInterface $profile_address1 */ + $profile_address1 = $profile_storage->create([ + 'type' => 'customer', + 'uid' => $account->id(), + 'address' => $this->address1, + ]); + $profile_address1->save(); + + $this->drupalLogin($account); + $this->drupalGet(Url::fromRoute('commerce_order_test.profile_select_form')); + $this->assertSession()->statusCodeEquals(200); + $this->assertSession()->fieldExists('Select a profile'); + // The last created profile should be selected by default. + $this->assertSession()->pageTextContains($this->address1['locality']); + + $this->getSession()->getPage()->fillField('Select a profile', '_new'); + $this->waitForAjaxToFinish(); + $this->assertSession()->waitForElementVisible('named', ['select', 'Country']); + $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); + $this->waitForAjaxToFinish(); + $this->createScreenshot(); + $edit = []; + foreach ($address_fields as $key => $value) { + if ($key == 'country_code') { + continue; + } + $edit['profile[address][0][address][' . $key . ']'] = $value; + } + + $this->submitForm($edit, 'Submit'); + + $new_profile = $profile_storage->load(2); + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ + $address = $new_profile->get('address')->first(); + + $this->assertSession()->responseContains(new FormattableMarkup('Profile selected: :label', [':label' => $new_profile->label()])); + // Assert that field values have not changed. + $this->assertEquals($this->address2['country_code'], $address->getCountryCode()); + $this->assertEquals($this->address2['given_name'], $address->getGivenName()); + $this->assertEquals($this->address2['family_name'], $address->getFamilyName()); + $this->assertEquals($this->address2['address_line1'], $address->getAddressLine1()); + $this->assertEquals($this->address2['locality'], $address->getLocality()); + $this->assertEquals($this->address2['postal_code'], $address->getPostalCode()); + } + /** * Tests the profile select form element for authenticated user. */ From ad5aeacce9159b5401fef11ff4f7e00f85bbac0d Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 16:23:17 -0500 Subject: [PATCH 10/17] Fix testProfileSelectAuthenticatedCreateNew --- modules/order/src/Element/ProfileSelect.php | 9 ++++++--- .../tests/src/FunctionalJavascript/ProfileSelectTest.php | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 0d547e9b2a..8cc6aedfc6 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -112,7 +112,7 @@ public static function processForm(array $element, FormStateInterface $form_stat // If this is the first form build, set the element's value based on // the user's default profile. - if (!$form_state->isRebuilding() && $existing_profile->isDefault()) { + if (!$form_state->isProcessingInput() && $existing_profile->isDefault()) { $element['#value'] = $existing_profile->id(); } } @@ -255,7 +255,10 @@ public static function validateForm(array &$element, FormStateInterface $form_st $element_profile = $profile_storage->load($value['profile_selection']); } - if ($element['#element_mode'] != 'view') { + if ($element['#element_mode'] != 'view' && $form_state->isSubmitted()) { + $triggering_element = $form_state->getTriggeringElement(); + $is_processing = $form_state->isProcessingInput(); + $is_submitted = $form_state->isSubmitted(); $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); $form_display->extractFormValues($element_profile, $element, $form_state); $form_display->validateFormValues($element_profile, $element, $form_state); @@ -287,7 +290,7 @@ public static function submitForm(array &$element, FormStateInterface $form_stat $element_profile = $profile_storage->load($value['profile_selection']); } - if ($element['#element_mode'] != 'view') { + if ($element['#element_mode'] != 'view' && $form_state->isSubmitted()) { $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); $form_display->extractFormValues($element_profile, $element, $form_state); $element_profile->save(); diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 24164c3ae7..9ad9df7469 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -219,7 +219,6 @@ public function testProfileSelectAuthenticatedCreateNew() { $this->getSession()->getPage()->fillField('Select a profile', '_new'); $this->waitForAjaxToFinish(); - $this->assertSession()->waitForElementVisible('named', ['select', 'Country']); $this->getSession()->getPage()->fillField('Country', $address_fields['country_code']); $this->waitForAjaxToFinish(); $this->createScreenshot(); From 9ec2a2e628736ad1ec97bd71add3e8ae10270998 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 17:18:27 -0500 Subject: [PATCH 11/17] Try to fix other tests, and edit. --- .../CheckoutPane/BillingInformation.php | 2 + modules/order/src/Element/ProfileSelect.php | 29 ++-------- .../FieldWidget/BillingProfileWidget.php | 2 + .../ProfileSelectTest.php | 57 +++++++++---------- .../CheckoutPane/PaymentInformation.php | 2 + .../src/PluginForm/PaymentMethodAddForm.php | 2 + 6 files changed, 39 insertions(+), 55 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php index e53b0f92aa..921b84e0b8 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php @@ -47,6 +47,8 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#default_value' => $billing_profile, '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), + '#profile_type' => 'customer', + '#owner_uid' => $this->order->getCustomerId(), ]; return $pane_form; diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 8cc6aedfc6..0b89e65251 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -19,6 +19,8 @@ * '#default_value' => $profile, * '#default_country' => 'FR', * '#available_countries' => ['US', 'FR'], + * '#profile_type' => 'customer', + * '#owner_uid' => \Drupal::currentUser()->id(), * ]; * @endcode * To access the profile in validation or submission callbacks, use @@ -45,7 +47,8 @@ public function getInfo() { // The profile entity operated on. Required. '#default_value' => '_new', '#owner_uid' => 0, - '#profile_type' => NULL, + // Provide default to not break contrib which have outdated elements. + '#profile_type' => 'customer', '#process' => [ [$class, 'attachElementSubmit'], [$class, 'processForm'], @@ -175,18 +178,13 @@ public static function processForm(array $element, FormStateInterface $form_stat $element['edit_button'] = [ '#type' => 'button', '#value' => t('Edit'), - '#limit_validation_errors' => [ - $element['#parents'], - ], + '#limit_validation_errors' => [], '#ajax' => [ 'callback' => [get_called_class(), 'ajaxRefresh'], 'wrapper' => $wrapper_id, ], '#name' => 'edit_profile', '#element_mode' => 'edit', - // @todo Allow editing. - // '#access' => $element['#element_mode'] == 'view', - '#access' => FALSE, ]; } else { @@ -207,20 +205,6 @@ public static function processForm(array $element, FormStateInterface $form_stat $widget_element['address']['#available_countries'] = $element['#available_countries']; } } - $element['cancel_button'] = [ - '#type' => 'button', - '#value' => t('Return to profile selection'), - '#limit_validation_errors' => [ - $element['#parents'], - ], - '#ajax' => [ - 'callback' => [get_called_class(), 'ajaxRefresh'], - 'wrapper' => $wrapper_id, - ], - '#name' => 'cancel_edit_profile', - '#element_mode' => 'view', - '#access' => $element['#element_mode'] == 'edit', - ]; } return $element; @@ -256,9 +240,6 @@ public static function validateForm(array &$element, FormStateInterface $form_st } if ($element['#element_mode'] != 'view' && $form_state->isSubmitted()) { - $triggering_element = $form_state->getTriggeringElement(); - $is_processing = $form_state->isProcessingInput(); - $is_submitted = $form_state->isSubmitted(); $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); $form_display->extractFormValues($element_profile, $element, $form_state); $form_display->validateFormValues($element_profile, $element, $form_state); diff --git a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php index 2d0987c942..f04bb4306c 100644 --- a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php +++ b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php @@ -91,6 +91,8 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen '#default_value' => $profile, '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), + '#profile_type' => 'customer', + '#owner_uid' => $order->getCustomerId(), ]; // Workaround for massageFormValues() not getting $element. $element['array_parents'] = [ diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index 9ad9df7469..b952c73067 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -195,15 +195,12 @@ public function testProfileSelectAuthenticated() { /** * Tests the profile select form element for authenticated user. - * - * @group debug */ public function testProfileSelectAuthenticatedCreateNew() { $account = $this->createUser(); $address_fields = $this->address2; - $profile_storage = $this->container->get('entity_type.manager')->getStorage('profile'); /** @var \Drupal\profile\Entity\ProfileInterface $profile_address1 */ - $profile_address1 = $profile_storage->create([ + $profile_address1 = $this->profileStorage->create([ 'type' => 'customer', 'uid' => $account->id(), 'address' => $this->address1, @@ -232,7 +229,7 @@ public function testProfileSelectAuthenticatedCreateNew() { $this->submitForm($edit, 'Submit'); - $new_profile = $profile_storage->load(2); + $new_profile = $this->profileStorage->load(2); /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ $address = $new_profile->get('address')->first(); @@ -248,20 +245,20 @@ public function testProfileSelectAuthenticatedCreateNew() { /** * Tests the profile select form element for authenticated user. + * + * @group debug */ public function testProfileSelectAuthenticatedEdit() { $account = $this->createUser(); - - $profile_storage = $this->container->get('entity_type.manager')->getStorage('profile'); /** @var \Drupal\profile\Entity\ProfileInterface $profile_address1 */ - $profile_address1 = $profile_storage->create([ + $profile_address1 = $this->profileStorage->create([ 'type' => 'customer', 'uid' => $account->id(), 'address' => $this->address1, ]); $profile_address1->save(); /** @var \Drupal\profile\Entity\ProfileInterface $profile_address2 */ - $profile_address2 = $profile_storage->create([ + $profile_address2 = $this->profileStorage->create([ 'type' => 'customer', 'uid' => $account->id(), 'address' => $this->address2, @@ -278,32 +275,30 @@ public function testProfileSelectAuthenticatedEdit() { $this->assertSession()->statusCodeEquals(200); $this->assertSession()->fieldExists('Select a profile'); // The last created profile should be selected by default. - $this->assertSession()->elementTextContains('css', '.locality', $this->address2['locality']); - $this->assertSession()->fieldNotExists('profile[cancel]'); + $this->assertSession()->pageTextContains($this->address2['locality']); + $this->getSession()->getPage()->pressButton('Edit'); + $this->waitForAjaxToFinish(); - $this->submitForm([], 'profile[edit]'); - foreach ($this->address1 as $key => $value) { + foreach ($this->address2 as $key => $value) { $this->assertSession()->fieldValueEquals('profile[address][0][address][' . $key . ']', $value); } - $this->assertSession()->fieldExists('profile[cancel]'); - $this->assertSession()->fieldNotExists('profile[profile_selection]'); - $edit = [ - 'profile[address][0][address][address_line1]' => 'Andrássy út 22', - ]; + $this->getSession()->getPage()->fillField('Street address', 'Andrássy út 22'); $this->submitForm([], 'Submit'); - $this->profileStorage->resetCache([1]); - $profile = $this->profileStorage->load([1]); - // Assert that only address_line1 has changed. - foreach ($this->address1 as $key => $value) { - if ($key == 'address_line1') { - $this->assertEquals($edit['profile[address][0][address][address_line1]'], $profile->address->getValue()[0][$key], t('@key of address has changed.', ['@key' => $key])); - } - else { - $this->assertEquals($this->address1[$key], $profile->address->getValue()[0][$key], t('@key of address has not changed.', ['@key' => $key])); - } - } - $profile_ids = $this->profileStorage->getQuery()->count()->condition('uid', $account->id())->execute(); - $this->assertEquals(2, $profile_ids, t('No new profile has been created after editing an existing one.')); + + $this->profileStorage->resetCache([$profile_address2->id()]); + $profile_address2 = $this->profileStorage->load($profile_address2->id()); + + /** @var \Drupal\address\Plugin\Field\FieldType\AddressItem $address */ + $address = $profile_address2->get('address')->first(); + + $this->assertSession()->responseContains(new FormattableMarkup('Profile selected: :label', [':label' => $profile_address2->label()])); + // Assert that field values have not changed. + $this->assertEquals($this->address2['country_code'], $address->getCountryCode()); + $this->assertEquals($this->address2['given_name'], $address->getGivenName()); + $this->assertEquals($this->address2['family_name'], $address->getFamilyName()); + $this->assertEquals($this->address2['address_line1'], 'Andrássy út 22'); + $this->assertEquals($this->address2['locality'], $address->getLocality()); + $this->assertEquals($this->address2['postal_code'], $address->getPostalCode()); } } diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index e0121a344b..4960b1dfac 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -148,6 +148,8 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, '#default_value' => $billing_profile, '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), + '#profile_type' => 'customer', + '#owner_uid' => $this->order->getCustomerId(), ]; } diff --git a/modules/payment/src/PluginForm/PaymentMethodAddForm.php b/modules/payment/src/PluginForm/PaymentMethodAddForm.php index 0a3ece9eba..8475c6e45e 100644 --- a/modules/payment/src/PluginForm/PaymentMethodAddForm.php +++ b/modules/payment/src/PluginForm/PaymentMethodAddForm.php @@ -74,6 +74,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta '#default_value' => $billing_profile, '#default_country' => $store ? $store->getAddress()->getCountryCode() : NULL, '#available_countries' => $store ? $store->getBillingCountries() : [], + '#profile_type' => 'customer', + '#owner_uid' => $payment_method->getOwnerId(), ]; return $form; From 5b4ba6f39f83c4ea37d8714ec88409e6a8ff101d Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 17:51:38 -0500 Subject: [PATCH 12/17] Edit now works. On to integrations. --- modules/order/src/Element/ProfileSelect.php | 18 ++++++++++++------ .../FunctionalJavascript/ProfileSelectTest.php | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 0b89e65251..7f7e523f90 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -129,7 +129,7 @@ public static function processForm(array $element, FormStateInterface $form_stat '#suffix' => '', // Pass the id along to other methods. '#wrapper_id' => $wrapper_id, - '#element_mode' => 'view', + '#element_mode' => $form_state->get('element_mode') ?: 'view', ] + $element; if (!empty($user_profiles)) { @@ -164,10 +164,6 @@ public static function processForm(array $element, FormStateInterface $form_stat } else { $element_profile = $profile_storage->load($element['#value']); - $triggering_element = $form_state->getTriggeringElement(); - if ($triggering_element) { - $element['#element_mode'] = $triggering_element['#element_mode']; - } } // Viewing a profile. @@ -176,13 +172,14 @@ public static function processForm(array $element, FormStateInterface $form_stat $element['rendered_profile'] = $view_builder->view($element_profile, 'default'); $element['edit_button'] = [ - '#type' => 'button', + '#type' => 'submit', '#value' => t('Edit'), '#limit_validation_errors' => [], '#ajax' => [ 'callback' => [get_called_class(), 'ajaxRefresh'], 'wrapper' => $wrapper_id, ], + '#submit' => [[get_called_class(), 'ajaxSubmit']], '#name' => 'edit_profile', '#element_mode' => 'edit', ]; @@ -289,4 +286,13 @@ public static function ajaxRefresh(array &$form, FormStateInterface $form_state) return $element; } + /** + * Ajax submit callback. + */ + public static function ajaxSubmit(array &$form, FormStateInterface $form_state) { + $triggering_element = $form_state->getTriggeringElement(); + $form_state->set('element_mode', $triggering_element['#element_mode']); + $form_state->setRebuild(); + } + } diff --git a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php index b952c73067..3d86511601 100644 --- a/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php +++ b/modules/order/tests/src/FunctionalJavascript/ProfileSelectTest.php @@ -296,7 +296,7 @@ public function testProfileSelectAuthenticatedEdit() { $this->assertEquals($this->address2['country_code'], $address->getCountryCode()); $this->assertEquals($this->address2['given_name'], $address->getGivenName()); $this->assertEquals($this->address2['family_name'], $address->getFamilyName()); - $this->assertEquals($this->address2['address_line1'], 'Andrássy út 22'); + $this->assertEquals('Andrássy út 22', $address->getAddressLine1()); $this->assertEquals($this->address2['locality'], $address->getLocality()); $this->assertEquals($this->address2['postal_code'], $address->getPostalCode()); } From 085a7e2ba498ae00982c35d0b9e4d97d30e67d6e Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Tue, 13 Jun 2017 18:49:50 -0500 Subject: [PATCH 13/17] Fix admin order test --- .../CheckoutPane/BillingInformation.php | 2 +- modules/order/src/Element/ProfileSelect.php | 18 +++--------------- .../Field/FieldWidget/BillingProfileWidget.php | 4 +--- .../CheckoutPane/PaymentInformation.php | 2 +- .../src/PluginForm/PaymentMethodAddForm.php | 2 +- 5 files changed, 7 insertions(+), 21 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php index 921b84e0b8..034ed9403d 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php @@ -58,7 +58,7 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, * {@inheritdoc} */ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { - $this->order->setBillingProfile($pane_form['profile']['#profile']); + $this->order->setBillingProfile($pane_form['profile']['#value']); } } diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 7f7e523f90..28589c58c7 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -241,6 +241,8 @@ public static function validateForm(array &$element, FormStateInterface $form_st $form_display->extractFormValues($element_profile, $element, $form_state); $form_display->validateFormValues($element_profile, $element, $form_state); } + + $form_state->setValueForElement($element, $element_profile); } /** @@ -252,21 +254,7 @@ public static function validateForm(array &$element, FormStateInterface $form_st * The current state of the form. */ public static function submitForm(array &$element, FormStateInterface $form_state) { - $value = $form_state->getValue($element['#parents']); - - $entity_type_manager = \Drupal::entityTypeManager(); - /** @var \Drupal\profile\ProfileStorageInterface $profile_storage */ - $profile_storage = $entity_type_manager->getStorage('profile'); - /** @var \Drupal\profile\Entity\ProfileInterface $element_profile */ - if ($value['profile_selection'] == '_new') { - $element_profile = $profile_storage->create([ - 'type' => $element['#profile_type'], - 'uid' => $element['#owner_uid'], - ]); - } - else { - $element_profile = $profile_storage->load($value['profile_selection']); - } + $element_profile = $form_state->getValue($element['#parents']); if ($element['#element_mode'] != 'view' && $form_state->isSubmitted()) { $form_display = EntityFormDisplay::collectRenderDisplay($element_profile, 'default'); diff --git a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php index f04bb4306c..40ced14449 100644 --- a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php +++ b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php @@ -2,7 +2,6 @@ namespace Drupal\commerce_order\Plugin\Field\FieldWidget; -use Drupal\Component\Utility\NestedArray; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldItemListInterface; @@ -109,8 +108,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen public function massageFormValues(array $values, array $form, FormStateInterface $form_state) { $new_values = []; foreach ($values as $delta => $value) { - $element = NestedArray::getValue($form, $value['array_parents']); - $new_values[$delta]['entity'] = $element['profile']['#profile']; + $new_values[$delta]['entity'] = $value['profile']; } return $new_values; } diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 4960b1dfac..0ef7160a73 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -348,7 +348,7 @@ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state else { $this->order->set('payment_gateway', $payment_gateway); $this->order->set('payment_method', NULL); - $this->order->setBillingProfile($pane_form['billing_information']['#profile']); + $this->order->setBillingProfile($pane_form['billing_information']['#value']); } } diff --git a/modules/payment/src/PluginForm/PaymentMethodAddForm.php b/modules/payment/src/PluginForm/PaymentMethodAddForm.php index 8475c6e45e..256f477e30 100644 --- a/modules/payment/src/PluginForm/PaymentMethodAddForm.php +++ b/modules/payment/src/PluginForm/PaymentMethodAddForm.php @@ -111,7 +111,7 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s } /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */ $payment_method = $this->entity; - $payment_method->setBillingProfile($form['billing_information']['#profile']); + $payment_method->setBillingProfile($form['billing_information']['#value']); $values = $form_state->getValue($form['#parents']); /** @var \Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface $payment_gateway_plugin */ From 08f9792959ef45f0eab049fa11dbd151e124be89 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Wed, 14 Jun 2017 09:50:44 -0500 Subject: [PATCH 14/17] Fix PaymentMethodAdd --- modules/payment/src/PluginForm/PaymentMethodAddForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/payment/src/PluginForm/PaymentMethodAddForm.php b/modules/payment/src/PluginForm/PaymentMethodAddForm.php index 256f477e30..a4da228e82 100644 --- a/modules/payment/src/PluginForm/PaymentMethodAddForm.php +++ b/modules/payment/src/PluginForm/PaymentMethodAddForm.php @@ -111,9 +111,9 @@ public function submitConfigurationForm(array &$form, FormStateInterface $form_s } /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */ $payment_method = $this->entity; - $payment_method->setBillingProfile($form['billing_information']['#value']); $values = $form_state->getValue($form['#parents']); + $payment_method->setBillingProfile($values['billing_information']); /** @var \Drupal\commerce_payment\Plugin\Commerce\PaymentGateway\SupportsStoredPaymentMethodsInterface $payment_gateway_plugin */ $payment_gateway_plugin = $this->plugin; // The payment method form is customer facing. For security reasons From edb7e86b76988761326e65c4e0496e4bc6320e86 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Wed, 14 Jun 2017 10:48:05 -0500 Subject: [PATCH 15/17] Fix usage with default value --- .../Commerce/CheckoutPane/BillingInformation.php | 14 +++----------- modules/order/src/Element/ProfileSelect.php | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php index 034ed9403d..c9d3d4fc1c 100644 --- a/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php +++ b/modules/checkout/src/Plugin/Commerce/CheckoutPane/BillingInformation.php @@ -33,18 +33,9 @@ public function buildPaneSummary() { */ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, array &$complete_form) { $store = $this->order->getStore(); - $billing_profile = $this->order->getBillingProfile(); - if (!$billing_profile) { - $profile_storage = $this->entityTypeManager->getStorage('profile'); - $billing_profile = $profile_storage->create([ - 'type' => 'customer', - 'uid' => $this->order->getCustomerId(), - ]); - } - $pane_form['profile'] = [ '#type' => 'commerce_profile_select', - '#default_value' => $billing_profile, + '#default_value' => $this->order->getBillingProfile(), '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), '#profile_type' => 'customer', @@ -58,7 +49,8 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, * {@inheritdoc} */ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state, array &$complete_form) { - $this->order->setBillingProfile($pane_form['profile']['#value']); + $values = $form_state->getValue($pane_form['#parents']); + $this->order->setBillingProfile($values['profile']); } } diff --git a/modules/order/src/Element/ProfileSelect.php b/modules/order/src/Element/ProfileSelect.php index 28589c58c7..ae05474a11 100644 --- a/modules/order/src/Element/ProfileSelect.php +++ b/modules/order/src/Element/ProfileSelect.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\FormElement; +use Drupal\profile\Entity\ProfileInterface; /** * Provides a form element for selecting a customer profile. @@ -69,9 +70,19 @@ public function getInfo() { */ public static function valueCallback(&$element, $input, FormStateInterface $form_state) { if (!empty($input['profile_selection'])) { - return $input['profile_selection']; + $value = $input['profile_selection']; } - return '_new'; + elseif ($element['#default_value'] instanceof ProfileInterface) { + $value = $element['#default_value']->id(); + } + elseif (!empty($element['#default_value'])) { + $value = $element['#default_value']; + } + else { + $value = '_new'; + } + + return $value; } /** From bcd7e32716b9d03847bf9bf81f7fabb653e2d486 Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Wed, 14 Jun 2017 11:41:31 -0500 Subject: [PATCH 16/17] Fix more defaults --- .../Field/FieldWidget/BillingProfileWidget.php | 12 +----------- .../Commerce/CheckoutPane/PaymentInformation.php | 10 +--------- .../payment/src/PluginForm/PaymentMethodAddForm.php | 7 ------- 3 files changed, 2 insertions(+), 27 deletions(-) diff --git a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php index 40ced14449..aec9aa21ae 100644 --- a/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php +++ b/modules/order/src/Plugin/Field/FieldWidget/BillingProfileWidget.php @@ -74,20 +74,10 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen $order = $items[$delta]->getEntity(); $store = $order->getStore(); - if (!$items[$delta]->isEmpty()) { - $profile = $items[$delta]->entity; - } - else { - $profile = $this->entityTypeManager->getStorage('profile')->create([ - 'type' => 'customer', - 'uid' => $order->getCustomerId(), - ]); - } - $element['#type'] = 'fieldset'; $element['profile'] = [ '#type' => 'commerce_profile_select', - '#default_value' => $profile, + '#default_value' => $profile = $items[$delta]->entity, '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), '#profile_type' => 'customer', diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 0ef7160a73..26655f21ca 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -135,17 +135,9 @@ public function buildPaneForm(array $pane_form, FormStateInterface $form_state, } else { $store = $this->order->getStore(); - $billing_profile = $this->order->getBillingProfile(); - if (!$billing_profile) { - $billing_profile = $this->entityTypeManager->getStorage('profile')->create([ - 'uid' => $this->order->getCustomerId(), - 'type' => 'customer', - ]); - } - $pane_form['billing_information'] = [ '#type' => 'commerce_profile_select', - '#default_value' => $billing_profile, + '#default_value' => $this->order->getBillingProfile(), '#default_country' => $store->getAddress()->getCountryCode(), '#available_countries' => $store->getBillingCountries(), '#profile_type' => 'customer', diff --git a/modules/payment/src/PluginForm/PaymentMethodAddForm.php b/modules/payment/src/PluginForm/PaymentMethodAddForm.php index a4da228e82..56f65dfdab 100644 --- a/modules/payment/src/PluginForm/PaymentMethodAddForm.php +++ b/modules/payment/src/PluginForm/PaymentMethodAddForm.php @@ -6,7 +6,6 @@ use Drupal\commerce_payment\Exception\DeclineException; use Drupal\commerce_payment\Exception\PaymentGatewayException; use Drupal\Core\Form\FormStateInterface; -use Drupal\profile\Entity\Profile; class PaymentMethodAddForm extends PaymentGatewayFormBase { @@ -54,11 +53,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta /** @var \Drupal\commerce_payment\Entity\PaymentMethodInterface $payment_method */ $payment_method = $this->entity; - /** @var \Drupal\profile\Entity\ProfileInterface $billing_profile */ - $billing_profile = Profile::create([ - 'type' => 'customer', - 'uid' => $payment_method->getOwnerId(), - ]); if ($order = $this->routeMatch->getParameter('commerce_order')) { $store = $order->getStore(); } @@ -71,7 +65,6 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta $form['billing_information'] = [ '#parents' => array_merge($form['#parents'], ['billing_information']), '#type' => 'commerce_profile_select', - '#default_value' => $billing_profile, '#default_country' => $store ? $store->getAddress()->getCountryCode() : NULL, '#available_countries' => $store ? $store->getBillingCountries() : [], '#profile_type' => 'customer', From 76aa6950c8876849132a9c97c1a2ebcba62e6aee Mon Sep 17 00:00:00 2001 From: Matt Glaman Date: Wed, 14 Jun 2017 13:41:48 -0500 Subject: [PATCH 17/17] Fix tests --- .../Commerce/CheckoutPane/PaymentInformation.php | 2 +- .../src/FunctionalJavascript/PaymentCheckoutTest.php | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php index 26655f21ca..da6895a41a 100644 --- a/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php @@ -340,7 +340,7 @@ public function submitPaneForm(array &$pane_form, FormStateInterface $form_state else { $this->order->set('payment_gateway', $payment_gateway); $this->order->set('payment_method', NULL); - $this->order->setBillingProfile($pane_form['billing_information']['#value']); + $this->order->setBillingProfile($values['billing_information']); } } diff --git a/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php b/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php index bebd9b80c1..03d2d5c148 100644 --- a/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php +++ b/modules/payment/tests/src/FunctionalJavascript/PaymentCheckoutTest.php @@ -204,7 +204,9 @@ public function testPaymentInformation() { $this->drupalGet('checkout/1'); $radio_button = $page->findField('Example'); $this->assertNull($radio_button); - $this->assertSession()->fieldExists('payment_information[billing_information][address][0][address][postal_code]'); + $this->createScreenshot(); + $this->assertSession()->fieldExists('Select a profile'); + $this->assertSession()->pageTextContains('Pabst Blue Ribbon Dr'); } /** @@ -337,7 +339,8 @@ public function testCheckoutWithOffsiteRedirectPost() { $radio_button = $this->getSession()->getPage()->findField('Example'); $radio_button->click(); $this->waitForAjaxToFinish(); - + $this->getSession()->getPage()->selectFieldOption('Select a profile', '_new'); + $this->waitForAjaxToFinish(); $this->submitForm([ 'payment_information[billing_information][address][0][address][given_name]' => 'Johnny', 'payment_information[billing_information][address][0][address][family_name]' => 'Appleseed', @@ -381,7 +384,8 @@ public function testCheckoutWithOffsiteRedirectGet() { $this->drupalGet($this->product->toUrl()->toString()); $this->submitForm([], 'Add to cart'); $this->drupalGet('checkout/1'); - + $this->getSession()->getPage()->selectFieldOption('Select a profile', '_new'); + $this->waitForAjaxToFinish(); $this->submitForm([ 'payment_information[billing_information][address][0][address][given_name]' => 'Johnny', 'payment_information[billing_information][address][0][address][family_name]' => 'Appleseed',