This is the custom field widget I made, if anyone else needs a temporary fix:
<?php
namespace Drupal\custom_module\Plugin\Field\FieldWidget;
use Drupal\commerce_order\Adjustment;
use Drupal\commerce_price\Price;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of 'commerce_adjustment_custom'.
*
* @FieldWidget(
* id = "commerce_adjustment_custom",
* label = @Translation("Adjustment (Custom)"),
* field_types = {
* "commerce_adjustment"
* }
* )
*/
class CustomAdjustmentWidget extends WidgetBase {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
/** @var \Drupal\commerce_order\Adjustment $adjustment */
$adjustment = $items[$delta]->value;
$element['#type'] = 'container';
$element['#attributes']['class'][] = 'form--inline';
$element['#attached']['library'][] = 'commerce_price/admin';
/** @var \Drupal\Component\Plugin\PluginManagerInterface $plugin_manager */
$plugin_manager = \Drupal::service('plugin.manager.commerce_adjustment_type');
$types = [
'_none' => $this->t('- Select -'),
];
foreach ($plugin_manager->getDefinitions() as $id => $definition) {
if (!empty($definition['has_ui'])) {
$types[$id] = $definition['label'];
}
}
$element['type'] = [
'#type' => 'select',
'#title' => $this->t('Type'),
'#options' => $types,
'#weight' => 1,
'#default_value' => ($adjustment) ? $adjustment->getType() : '_none',
];
// If this is being added through the UI, the source ID should be empty,
// and we will want to default it to custom.
$source_id = ($adjustment) ? $adjustment->getSourceId() : NULL;
$element['source_id'] = [
'#type' => 'value',
'#value' => empty($source_id) ? 'custom' : $source_id,
];
// If this is being added through the UI, the adjustment should be locked.
// UI added adjustments need to be locked to persist after an order refresh.
$element['locked'] = [
'#type' => 'value',
'#value' => ($adjustment) ? $adjustment->isLocked() : TRUE,
];
$element['percentage'] = [
'#type' => 'value',
'#value' => ($adjustment) ? $adjustment->getPercentage() : NULL,
];
$states_selector_name = $this->fieldDefinition->getName() . "[$delta][type]";
$element['definition'] = [
'#type' => 'container',
'#weight' => 2,
'#states' => [
'invisible' => [
'select[name="' . $states_selector_name . '"]' => ['value' => '_none'],
],
],
];
$element['definition']['label'] = [
'#type' => 'textfield',
'#title' => $this->t('Label'),
'#size' => 20,
'#default_value' => ($adjustment) ? $adjustment->getLabel() : '',
];
$element['definition']['amount'] = [
'#type' => 'commerce_price',
'#title' => $this->t('Amount'),
'#default_value' => ($adjustment) ? $adjustment->getAmount()->toArray() : NULL,
'#allow_negative' => TRUE,
'#states' => [
'optional' => [
'select[name="' . $states_selector_name . '"]' => ['value' => '_none'],
],
],
'#attributes' => ['class' => ['clearfix']],
];
$element['definition']['included'] = [
'#type' => 'checkbox',
'#title' => $this->t('Included in the base price'),
'#default_value' => ($adjustment) ? $adjustment->isIncluded() : FALSE,
];
return $element;
}
/**
* {@inheritdoc}
*/
public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
foreach ($values as $key => $value) {
if ($value['type'] == '_none') {
continue;
}
// The method can be called with invalid or incomplete data, in
// preparation for validation. Passing such data to the Adjustment
// object would result in an exception.
if (empty($value['definition']['label'])) {
$form_state->setErrorByName('adjustments[' . $key . '][definition][label]', $this->t('The adjustment label field is required.'));
continue;
}
$values[$key] = new Adjustment([
'type' => $value['type'],
'label' => $value['definition']['label'],
'amount' => new Price($value['definition']['amount']['number'], $value['definition']['amount']['currency_code']),
'percentage' => !empty($value['percentage']) ? $value['percentage'] : NULL,
'source_id' => $value['source_id'],
'included' => $value['definition']['included'],
'locked' => $value['locked'],
]);
}
return $values;
}
}
agogo β created an issue.
To clarify the issue:
Normally when using URL objects one can decide wether the URL as a string (toString()) should be absolute or not with the function setAbsolute(). The default seems to be FALSE.
When using multiple languages and language detection is set to domain, the setAbsolute() function makes no difference. The toString() function always returns the URL as an absolute domain.
In function setRouteContextFromRouteMatch, the URL object created usually outputs the URL without protocol and domain when converted to string ($url->toString()). In this case "/news/my-test-news-item". This is sent to getRequestForPath().
However, when using multiple languages and detection is set to domain, the string sent to getRequestForPath() is absolute. This seems to be where the module breaks down and results in a faulty breadcrumb.
Tested again, exact same approach but with Drupal 10.3.2. Same problem. Cannot test Drupal 11 atm because of the DB version requirements.
"Visit JSON" means checking the JSON output which are at the default location and can be viewed through a web browser (easiest way if you're not used to working with JSON). Just replace everything in brackets [protocol]://[domain]/jsonapi/node/article/[uuid] (for example http://local.drupal10/jsonapi/node/article/68a87a7a-e28b-492b-a83c-c734f...)
agogo β created an issue.
Thanks for the patch! Also works with 2.0. (#13)
A small note is that the patch forces all mails to not be sent when not using a/the default mail address by setting setTransportDsn as null. I had to make a copy of the patch and remove that part (+ the setReplyTo setting that is there for some reason).