Update Composer, update everything

This commit is contained in:
Oliver Davies 2018-11-23 12:29:20 +00:00
parent ea3e94409f
commit dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions

View file

@ -0,0 +1,46 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\CKEditorPlugin;
use Drupal\ckeditor\CKEditorPluginBase;
use Drupal\editor\Entity\Editor;
/**
* Defines the "{{ plugin_label }}" plugin.
*
* @CKEditorPlugin(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* module = "{{ machine_name }}"
* )
*/
class {{ class }} extends CKEditorPluginBase {
/**
* {@inheritdoc}
*/
public function getFile() {
return drupal_get_path('module', '{{ machine_name }}') . '/js/plugins/{{ short_plugin_id }}/plugin.js';
}
/**
* {@inheritdoc}
*/
public function getConfig(Editor $editor) {
return [];
}
/**
* {@inheritdoc}
*/
public function getButtons() {
$module_path = drupal_get_path('module', '{{ machine_name }}');
return [
'{{ short_plugin_id }}' => [
'label' => $this->t('{{ plugin_label }}'),
'image' => $module_path . '/js/plugins/{{ short_plugin_id }}/icons/{{ short_plugin_id }}.png',
],
];
}
}

View file

@ -0,0 +1,70 @@
/**
* @file
* Defines dialog for {{ plugin_label }} CKEditor plugin.
*/
(function (Drupal) {
'use strict';
// Dialog definition.
CKEDITOR.dialog.add('{{ command_name }}Dialog', function(editor) {
return {
// Basic properties of the dialog window: title, minimum size.
title: Drupal.t('Abbreviation properties'),
minWidth: 400,
minHeight: 150,
// Dialog window content definition.
contents: [
{
// Definition of the settings dialog tab.
id: 'tab-settings',
label: 'Settings',
// The tab content.
elements: [
{
// Text input field for the abbreviation text.
type: 'text',
id: 'abbr',
label: Drupal.t('Abbreviation'),
// Validation checking whether the field is not empty.
validate: CKEDITOR.dialog.validate.notEmpty(Drupal.t('Abbreviation field cannot be empty.'))
},
{
// Text input field for the abbreviation title (explanation).
type: 'text',
id: 'title',
label: Drupal.t('Explanation'),
validate: CKEDITOR.dialog.validate.notEmpty(Drupal.t('Explanation field cannot be empty.'))
}
]
}
],
// This method is invoked once a user clicks the OK button, confirming the
// dialog.
onOk: function() {
// The context of this function is the dialog object itself.
// See http://docs.ckeditor.com/#!/api/CKEDITOR.dialog.
var dialog = this;
// Create a new <abbr> element.
var abbr = editor.document.createElement('abbr');
// Set element attribute and text by getting the defined field values.
abbr.setAttribute('title', dialog.getValueOf('tab-settings', 'title'));
abbr.setText( dialog.getValueOf('tab-settings', 'abbr'));
// Finally, insert the element into the editor at the caret position.
editor.insertElement(abbr);
}
};
});
} (Drupal));

Binary file not shown.

After

Width:  |  Height:  |  Size: 597 B

View file

@ -0,0 +1,44 @@
/**
* @file
* {{ plugin_label }} CKEditor plugin.
*
* Basic plugin inserting abbreviation elements into the CKEditor editing area.
*
* @DCG The code is based on an example from CKEditor Plugin SDK tutorial.
*
* @see http://docs.ckeditor.com/#!/guide/plugin_sdk_sample_1
*/
(function (Drupal) {
'use strict';
CKEDITOR.plugins.add('{{ plugin_id }}', {
// Register the icons.
icons: '{{ short_plugin_id }}',
// The plugin initialization logic goes inside this method.
init: function(editor) {
// Define an editor command that opens our dialog window.
editor.addCommand('{{ command_name }}', new CKEDITOR.dialogCommand('{{ command_name }}Dialog'));
// Create a toolbar button that executes the above command.
editor.ui.addButton('{{ short_plugin_id }}', {
// The text part of the button (if available) and the tooltip.
label: Drupal.t('Insert abbreviation'),
// The command to execute on click.
command: '{{ command_name }}',
// The button placement in the toolbar (toolbar group name).
toolbar: 'insert'
});
// Register our dialog file, this.path is the plugin folder path.
CKEDITOR.dialog.add('{{ command_name }}Dialog', this.path + 'dialogs/{{ short_plugin_id }}.js');
}
});
} (Drupal));

View file

@ -0,0 +1,7 @@
action.configuration.{{ plugin_id }}:
type: mapping
label: '{{ plugin_label }} configuration'
mapping:
title:
type: string
label: Title

View file

@ -0,0 +1,80 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Action;
{% if configurable %}
use Drupal\Core\Action\ConfigurableActionBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
{% else %}
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Session\AccountInterface;
{% endif %}
/**
* Provides a {{ plugin_label|article }} action.
*
* @Action(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* type = "node",
* category = @Translation("{{ category }}")
* )
*
* @DCG
* For simple updating entity fields consider extending FieldUpdateActionBase.
*/
class {{ class }} extends {{ configurable ? 'ConfigurableActionBase' : 'ActionBase' }} {
{% if configurable %}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return ['title' => ''];
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['title'] = [
'#title' => t('New title'),
'#type' => 'textfield',
'#required' => TRUE,
'#default_value' => $this->configuration['title'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['title'] = $form_state->getValue('title');
}
{% endif %}
/**
* {@inheritdoc}
*/
public function access($node, AccountInterface $account = NULL, $return_as_object = FALSE) {
/** @var \Drupal\node\NodeInterface $node */
$access = $node->access('update', $account, TRUE)
->andIf($node->title->access('edit', $account, TRUE));
return $return_as_object ? $access : $access->isAllowed();
}
/**
* {@inheritdoc}
*/
public function execute($node = NULL) {
/** @var \Drupal\node\NodeInterface $node */
{% if configurable %}
$node->setTitle($this->configuration['title'])->save();
{% else %}
$node->setTitle(t('New title'))->save();
{% endif %}
}
}

View file

@ -0,0 +1,7 @@
block.settings.{{ plugin_id }}:
type: block_settings
label: '{{ plugin_label }} block'
mapping:
foo:
type: string
label: Foo

View file

@ -0,0 +1,119 @@
{% import 'lib/di.twig' as di %}
<?php
namespace Drupal\{{ machine_name }}\Plugin\Block;
{% sort %}
{% if access %}
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Session\AccountInterface;
{% endif %}
use Drupal\Core\Block\BlockBase;
{% if configurable %}
use Drupal\Core\Form\FormStateInterface;
{% endif %}
{% if services %}
{{ di.use(services) }}
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
{% endif %}
{% endsort %}
/**
* Provides {{ plugin_label|article|lower }} block.
*
* @Block(
* id = "{{ plugin_id }}",
* admin_label = @Translation("{{ plugin_label }}"),
* category = @Translation("{{ category }}")
* )
*/
class {{ class }} extends BlockBase {% if services %}implements ContainerFactoryPluginInterface {% endif %}{
{% if services %}
{{ di.properties(services) }}
/**
* Constructs a new {{ class }} instance.
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
{{ di.annotation(services) }}
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, {{ di.signature(services) }}) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
{{ di.assignment(services) }}
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
{{ di.container(services) }}
);
}
{% endif %}
{% if configurable %}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'foo' => $this->t('Hello world!'),
];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form['foo'] = [
'#type' => 'textarea',
'#title' => $this->t('Foo'),
'#default_value' => $this->configuration['foo'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['foo'] = $form_state->getValue('foo');
}
{% endif %}
{% if access %}
/**
* {@inheritdoc}
*/
protected function blockAccess(AccountInterface $account) {
// @DCG Evaluate the access condition here.
$condition = TRUE;
return AccessResult::allowedIf($condition);
}
{% endif %}
/**
* {@inheritdoc}
*/
public function build() {
$build['content'] = [
'#markup' => $this->t('It works!'),
];
return $build;
}
}

View file

@ -0,0 +1,7 @@
condition.plugin.{{ plugin_id }}:
type: condition.plugin
label: '{{ plugin_label }} condition'
mapping:
age:
type: integer
label: Age

View file

@ -0,0 +1,129 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Condition;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Condition\ConditionPluginBase;
use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a '{{ plugin_label }}' condition.
*
* @Condition(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* context = {
* "node" = @ContextDefinition(
* "entity:node",
* label = @Translation("Node")
* )
* }
* )
*/
class {{ class }} extends ConditionPluginBase implements ContainerFactoryPluginInterface {
/**
* The date formatter.
*
* @var \Drupal\Core\Datetime\DateFormatterInterface
*/
protected $dateFormatter;
/**
* The time service.
*
* @var \Drupal\Component\Datetime\TimeInterface
*/
protected $time;
/**
* Creates a new {{ class }} instance.
*
* @param array $configuration
* The plugin configuration, i.e. an array with configuration values keyed
* by configuration option name. The special key 'context' may be used to
* initialize the defined contexts by setting it to an array of context
* values keyed by context names.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Datetime\DateFormatterInterface $date_formatter
* The date formatter.
* @param \Drupal\Component\Datetime\TimeInterface $time
* The time service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, DateFormatterInterface $date_formatter, TimeInterface $time) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->dateFormatter = $date_formatter;
$this->time = $time;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('date.formatter'),
$container->get('datetime.time')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return ['age' => NULL] + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form['age'] = [
'#title' => $this->t('Node age, sec'),
'#type' => 'number',
'#min' => 0,
'#default_value' => $this->configuration['age'],
];
return parent::buildConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
$this->configuration['age'] = $form_state->getValue('age');
parent::submitConfigurationForm($form, $form_state);
}
/**
* {@inheritdoc}
*/
public function summary() {
return $this->t(
'Node age: @age',
['@age' => $this->dateFormatter->formatInterval($this->configuration['age'])]
);
}
/**
* {@inheritdoc}
*/
public function evaluate() {
if (!$this->configuration['age'] && !$this->isNegated()) {
return TRUE;
}
$age = $this->time->getRequestTime() - $this->getContextValue('node')->getCreatedTime();
return $age < $this->configuration['age'];
}
}

View file

@ -0,0 +1,62 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
/**
* Validates the {{ plugin_label }} constraint.
*/
class {{ class }}Validator extends ConstraintValidator {
/**
* {@inheritdoc}
*/
{% if input_type == 'entity' %}
public function validate($entity, Constraint $constraint) {
// @DCG Validate the entity here.
if ($entity->label() == 'foo') {
$this->context->buildViolation($constraint->errorMessage)
// @DCG The path depends on entity type. It can be title, name, etc.
->atPath('title')
->addViolation();
}
}
{% elseif input_type == 'item_list' %}
public function validate($items, Constraint $constraint) {
foreach ($items as $delta => $item) {
// @DCG Validate the item here.
if ($item->value == 'foo') {
$this->context->buildViolation($constraint->errorMessage)
->atPath($delta)
->addViolation();
}
}
}
{% elseif input_type == 'item' %}
public function validate($item, Constraint $constraint) {
$value = $item->getValue()['value'];
// @DCG Validate the value here.
if ($value == 'foo') {
$this->context->addViolation($constraint->errorMessage);
}
}
{% else %}
public function validate($value, Constraint $constraint) {
// @DCG Validate the value here.
if ($value == 'foo') {
$this->context->addViolation($constraint->errorMessage);
}
}
{% endif %}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Validation\Constraint;
use Symfony\Component\Validator\Constraint;
/**
* Provides {{ plugin_label|article }} constraint.
*
* @Constraint(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}", context = "Validation"),
* )
{% if input_type == 'entity' %}
*
* @DCG
* To apply this constraint on a particular field implement
* hook_entity_type_build().
{% elseif input_type == 'item_list' %}
*
* @DCG
* To apply this constraint on third party entity types implement either
* hook_entity_base_field_info_alter() or hook_entity_bundle_field_info_alter().
{% elseif input_type == 'item' %}
*
* @DCG
* To apply this constraint on third party field types. Implement
* hook_field_info_alter().
{% endif %}
*/
class {{ class }} extends Constraint {
public $errorMessage = 'The error message.';
}

View file

@ -0,0 +1,10 @@
entity_reference_selection.{{ plugin_id }}:
{# User selection plugin provides has some additional options. #}
type: entity_reference_selection.default{{ entity_type == 'user' ? ':user' }}
label: '{{ plugin_label }} handler settings'
{% if configurable %}
mapping:
foo:
type: string
label: Foo
{% endif %}

View file

@ -0,0 +1,68 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\EntityReferenceSelection;
{% sort %}
{% if configurable %}
use Drupal\Core\Form\FormStateInterface;
{% endif %}
use {{ base_class_full }};
{% endsort %}
/**
* Plugin description.
*
* @EntityReferenceSelection(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* group = "{{ plugin_id }}",
* entity_types = {"{{ entity_type }}"},
* weight = 0
* )
*/
class {{ class }} extends {{ base_class }} {
{% if configurable %}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
$default_configuration = [
'foo' => 'bar',
];
return $default_configuration + parent::defaultConfiguration();
}
/**
* {@inheritdoc}
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$form['foo'] = [
'#type' => 'textfield',
'#title' => $this->t('Foo'),
'#default_value' => $this->configuration['foo'],
];
return $form;
}
{% endif %}
/**
* {@inheritdoc}
*/
protected function buildEntityQuery($match = NULL, $match_operator = 'CONTAINS') {
$query = parent::buildEntityQuery($match, $match_operator);
// @DCG
// Here you can apply addition conditions, sorting, etc to the query.
// Also see self::entityQueryAlter().
$query->condition('field_example', 123);
return $query;
}
}

View file

@ -0,0 +1,7 @@
field.formatter.settings.{{ plugin_id }}:
type: mapping
label: {{ plugin_label }} formatter settings
mapping:
foo:
type: string
label: Foo

View file

@ -0,0 +1,73 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
{% if configurable %}
use Drupal\Core\Form\FormStateInterface;
{% endif %}
/**
* Plugin implementation of the '{{ plugin_label }}' formatter.
*
* @FieldFormatter(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* field_types = {
* "string"
* }
* )
*/
class {{ class }} extends FormatterBase {
{% if configurable %}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'foo' => 'bar',
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$elements['foo'] = [
'#type' => 'textfield',
'#title' => $this->t('Foo'),
'#default_value' => $this->getSetting('foo'),
];
return $elements;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary[] = $this->t('Foo: @foo', ['@foo' => $this->getSetting('foo')]);
return $summary;
}
{% endif %}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$element = [];
foreach ($items as $delta => $item) {
$element[$delta] = [
'#type' => 'item',
'#markup' => $item->value,
];
}
return $element;
}
}

View file

@ -0,0 +1,27 @@
{% if configurable_storage %}
field.storage_settings.{{ plugin_id }}:
type: mapping
label: {{ plugin_label }} storage settings
mapping:
foo:
type: string
label: Foo
{% endif %}
{% if configurable_instance %}
field.field_settings.{{ plugin_id }}:
type: mapping
label: {{ plugin_label }} field settings
mapping:
bar:
type: string
label: Bar
{% endif %}
field.value.{{ plugin_id }}:
type: mapping
label: Default value
mapping:
value:
type: label
label: Value

View file

@ -0,0 +1,154 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Field\FieldType;
use Drupal\Component\Utility\Random;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
{% if configurable_storage or configurable_instance %}
use Drupal\Core\Form\FormStateInterface;
{% endif %}
use Drupal\Core\TypedData\DataDefinition;
/**
* Defines the '{{ plugin_id }}' field type.
*
* @FieldType(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* category = @Translation("General"),
* default_widget = "string_textfield",
* default_formatter = "string"
* )
*
* @DCG
* If you are implementing a single value field type you may want to inherit
* this class form some of the field type classes provided by Drupal core.
* Check out /core/lib/Drupal/Core/Field/Plugin/Field/FieldType directory for a
* list of available field type implementations.
*/
class {{ class }} extends FieldItemBase {
{% if configurable_storage %}
/**
* {@inheritdoc}
*/
public static function defaultStorageSettings() {
$settings = ['foo' => 'wine'];
return $settings + parent::defaultStorageSettings();
}
/**
* {@inheritdoc}
*/
public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) {
$element['foo'] = [
'#type' => 'textfield',
'#title' => $this->t('Foo'),
'#default_value' => $this->getSetting('foo'),
'#disabled' => $has_data,
];
return $element;
}
{% endif %}
{% if configurable_instance %}
/**
* {@inheritdoc}
*/
public static function defaultFieldSettings() {
$settings = ['bar' => 'beer'];
return $settings + parent::defaultFieldSettings();
}
/**
* {@inheritdoc}
*/
public function fieldSettingsForm(array $form, FormStateInterface $form_state) {
$element['bar'] = [
'#type' => 'textfield',
'#title' => t('Bar'),
'#default_value' => $this->getSetting('bar'),
];
return $element;
}
{% endif %}
/**
* {@inheritdoc}
*/
public function isEmpty() {
$value = $this->get('value')->getValue();
return $value === NULL || $value === '';
}
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
// @DCG
// See /core/lib/Drupal/Core/TypedData/Plugin/DataType directory for
// available data types.
$properties['value'] = DataDefinition::create('string')
->setLabel(t('Text value'))
->setRequired(TRUE);
return $properties;
}
/**
* {@inheritdoc}
*/
public function getConstraints() {
$constraints = parent::getConstraints();
$constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager();
// @DCG Suppose our value must not be longer than 10 characters.
$options['value']['Length']['max'] = 10;
// @DCG
// See /core/lib/Drupal/Core/Validation/Plugin/Validation/Constraint
// directory for available constraints.
$constraints[] = $constraint_manager->create('ComplexData', $options);
return $constraints;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$columns = [
'value' => [
'type' => 'varchar',
'not null' => FALSE,
'description' => 'Column description.',
'length' => 255,
],
];
$schema = [
'columns' => $columns,
// @DCG Add indexes here if necessary.
];
return $schema;
}
/**
* {@inheritdoc}
*/
public static function generateSampleValue(FieldDefinitionInterface $field_definition) {
$random = new Random();
$values['value'] = $random->word(mt_rand(1, 50));
return $values;
}
}

View file

@ -0,0 +1,7 @@
field.widget.settings.{{ plugin_id }}:
type: mapping
label: {{ plugin_label }} widget settings
mapping:
foo:
type: string
label: Foo

View file

@ -0,0 +1,66 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Defines the '{{ plugin_id }}' field widget.
*
* @FieldWidget(
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* field_types = {"string"},
* )
*/
class {{ class }} extends WidgetBase {
{% if configurable %}
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'foo' => 'bar',
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$element['foo'] = [
'#type' => 'textfield',
'#title' => $this->t('Foo'),
'#default_value' => $this->getSetting('foo'),
];
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary[] = $this->t('Foo: @foo', ['@foo' => $this->getSetting('foo')]);
return $summary;
}
{% endif %}
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element['value'] = $element + [
'#type' => 'textfield',
'#default_value' => isset($items[$delta]->value) ? $items[$delta]->value : NULL,
];
return $element;
}
}

View file

@ -0,0 +1,7 @@
filter_settings.{{ plugin_id }}:
type: filter
label: '{{ plugin_label }} filter'
mapping:
example:
type: string
label: Example

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Filter;
use Drupal\Core\Form\FormStateInterface;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
* Provides a '{{ plugin_label }}' filter.
*
* @Filter(
* id = "{{ plugin_id }}",
* title = @Translation("{{ plugin_label }}"),
* type = Drupal\filter\Plugin\FilterInterface::{{ filter_type }},
* settings = {
* "example" = "foo",
* },
* weight = -10
* )
*/
class {{ class }} extends FilterBase {
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form['example'] = [
'#type' => 'textfield',
'#title' => $this->t('Example'),
'#default_value' => $this->settings['example'],
'#description' => $this->t('Description of the setting.'),
];
return $form;
}
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
// @DCG Process text here.
$example = $this->settings['example'];
$text = str_replace($example, "<b>$example</b>", $text);
return new FilterProcessResult($text);
}
/**
* {@inheritdoc}
*/
public function tips($long = FALSE) {
return $this->t('Some filter tips here.');
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\Menu;
use Drupal\Core\Database\Connection;
use Drupal\Core\Menu\MenuLinkDefault;
use Drupal\Core\Menu\StaticMenuLinkOverridesInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* A menu link that displays count of messages.
*/
class {{ class }} extends MenuLinkDefault {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $dbConnection;
/**
* Constructs the plugin object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Menu\StaticMenuLinkOverridesInterface $static_override
* The static override storage.
* @param \Drupal\Core\Database\Connection $db_connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, StaticMenuLinkOverridesInterface $static_override, Connection $db_connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $static_override);
$this->dbConnection = $db_connection;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('menu_link.static.overrides'),
$container->get('database')
);
}
/**
* {@inheritdoc}
*/
public function getTitle() {
$count = $this->dbConnection->query('SELECT COUNT(*) FROM {messages}')->fetchField();
return $this->t('Messages (@count)', ['@count' => $count]);
}
/**
* {@inheritdoc}
*/
public function getRouteName() {
return '{{ machine_name }}.messages';
}
/**
* {@inheritdoc}
*/
public function getCacheTags() {
// @DCG Invalidate this tags when messages are created or removed.
return ['{{ machine_name }}.messages_count'];
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\migrate\process;
use Drupal\Component\Transliteration\TransliterationInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\migrate\MigrateExecutableInterface;
use Drupal\migrate\ProcessPluginBase;
use Drupal\migrate\Row;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides {{ plugin_id|article }} plugin.
*
* Usage:
*
* @code
* process:
* bar:
* plugin: {{ plugin_id }}
* source: foo
* @endcode
*
* @MigrateProcessPlugin(
* id = "{{ plugin_id }}"
* )
*
* @DCG
* ContainerFactoryPluginInterface is optional here. If you have no need for
* external services just remove it and all other stuff except transform()
* method.
*/
class {{ class }} extends ProcessPluginBase implements ContainerFactoryPluginInterface {
/**
* The transliteration service.
*
* @var \Drupal\Component\Transliteration\TransliterationInterface
*/
protected $transliteration;
/**
* Constructs {{ class|article }} plugin.
*
* @param array $configuration
* The plugin configuration.
* @param string $plugin_id
* The plugin ID.
* @param mixed $plugin_definition
* The plugin definition.
* @param \Drupal\Component\Transliteration\TransliterationInterface $transliteration
* The transliteration service.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, TransliterationInterface $transliteration) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->transliteration = $transliteration;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('transliteration')
);
}
/**
* {@inheritdoc}
*/
public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
return $this->transliteration->transliterate($value, LanguageInterface::LANGCODE_DEFAULT);
}
}

View file

@ -0,0 +1,306 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\rest\resource;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Core\Database\Connection;
use Drupal\rest\ModifiedResourceResponse;
use Drupal\rest\Plugin\ResourceBase;
use Drupal\rest\ResourceResponse;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Represents {{ plugin_label }} records as resources.
*
* @RestResource (
* id = "{{ plugin_id }}",
* label = @Translation("{{ plugin_label }}"),
* uri_paths = {
* "canonical" = "/api/{{ plugin_id|u2h }}/{id}",
* "https://www.drupal.org/link-relations/create" = "/api/{{ plugin_id|u2h }}"
* }
* )
*
* @DCG
* This plugin exposes database records as REST resources. In order to enable it
* import the resource configuration into active configuration storage. You may
* find an example of such configuration in the following file:
* core/modules/rest/config/optional/rest.resource.entity.node.yml.
* Alternatively you can make use of REST UI module.
* @see https://www.drupal.org/project/restui
* For accessing Drupal entities through REST interface use
* \Drupal\rest\Plugin\rest\resource\EntityResource plugin.
*/
class {{ class }} extends ResourceBase implements DependentPluginInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $dbConnection;
/**
* Constructs a Drupal\rest\Plugin\rest\resource\EntityResource object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param array $serializer_formats
* The available serialization formats.
* @param \Psr\Log\LoggerInterface $logger
* A logger instance.
* @param \Drupal\Core\Database\Connection $db_connection
* The database connection.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, array $serializer_formats, LoggerInterface $logger, Connection $db_connection) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $serializer_formats, $logger);
$this->dbConnection = $db_connection;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->getParameter('serializer.formats'),
$container->get('logger.factory')->get('rest'),
$container->get('database')
);
}
/**
* Responds to GET requests.
*
* @param int $id
* The ID of the record.
*
* @return \Drupal\rest\ResourceResponse
* The response containing the record.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function get($id) {
return new ResourceResponse($this->loadRecord($id));
}
/**
* Responds to POST requests and saves the new record.
*
* @param mixed $record
* Data to write into the database.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function post($record) {
$this->validate($record);
$id = $this->dbConnection->insert('{{ plugin_id }}')
->fields($record)
->execute();
$this->logger->notice('New {{ plugin_label|lower }} record has been created.');
$created_record = $this->loadRecord($id);
// Return the newly created record in the response body.
return new ModifiedResourceResponse($created_record, 201);
}
/**
* Responds to entity PATCH requests.
*
* @param int $id
* The ID of the record.
* @param mixed $record
* Data to write into the database.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function patch($id, $record) {
$this->validate($record);
return $this->updateRecord($id, $record);
}
/**
* Responds to entity PUT requests.
*
* @param int $id
* The ID of the record.
* @param mixed $record
* Data to write into the database.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
public function put($id, $record) {
$this->validate($record);
// Provide default values to make sure the record is completely replaced.
$record += [
'title' => '',
'description' => '',
'price' => 0,
];
return $this->updateRecord($id, $record);
}
/**
* Responds to entity DELETE requests.
*
* @param int $id
* The ID of the record.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*
* @throws \Symfony\Component\HttpKernel\Exception\HttpException
*/
public function delete($id) {
// Make sure the record still exists.
$this->loadRecord($id);
$this->dbConnection->delete('{{ plugin_id }}')
->condition('id', $id)
->execute();
$this->logger->notice('{{ plugin_label }} record @id has been deleted.', ['@id' => $id]);
// Deleted responses have an empty body.
return new ModifiedResourceResponse(NULL, 204);
}
/**
* {@inheritdoc}
*/
protected function getBaseRoute($canonical_path, $method) {
$route = parent::getBaseRoute($canonical_path, $method);
// Change ID validation pattern.
if ($method != 'POST') {
$route->setRequirement('id', '\d+');
}
return $route;
}
/**
* {@inheritdoc}
*/
public function calculateDependencies() {
return [];
}
/**
* {@inheritdoc}
*/
public function routes() {
$collection = parent::routes();
// ResourceBase class does not support PUT method by some reason.
$definition = $this->getPluginDefinition();
$canonical_path = $definition['uri_paths']['canonical'];
$route = $this->getBaseRoute($canonical_path, 'PUT');
$route->addRequirements(['_content_type_format' => implode('|', $this->serializerFormats)]);
$collection->add('{{ plugin_id }}.PUT', $route);
return $collection;
}
/**
* Validates incoming record.
*
* @param mixed $record
* Data to validate.
*
* @throws \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
*/
protected function validate($record) {
if (!is_array($record) || count($record) == 0) {
throw new BadRequestHttpException('No record content received.');
}
$allowed_fields = [
'title',
'description',
'price',
];
if (count(array_diff(array_keys($record), $allowed_fields)) > 0) {
throw new BadRequestHttpException('Record structure is not correct.');
}
if (empty($record['title'])) {
throw new BadRequestHttpException('Title is required.');
}
elseif (isset($record['title']) && strlen($record['title']) > 255) {
throw new BadRequestHttpException('Title is too big.');
}
// @DCG Add more validation rules here.
}
/**
* Loads record from database.
*
* @param int $id
* The ID of the record.
*
* @return array
* The database record.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*/
protected function loadRecord($id) {
$record = $this->dbConnection->query('SELECT * FROM {{ '{' }}{{ plugin_id }}{{ '}' }} WHERE id = :id', [':id' => $id])->fetchAssoc();
if (!$record) {
throw new NotFoundHttpException('The record was not found.');
}
return $record;
}
/**
* Updates record.
*
* @param int $id
* The ID of the record.
* @param array $record
* The record to validate.
*
* @return \Drupal\rest\ModifiedResourceResponse
* The HTTP response object.
*/
protected function updateRecord($id, array $record) {
// Make sure the record already exists.
$this->loadRecord($id);
$this->validate($record);
$this->dbConnection->update('{{ plugin_id }}')
->fields($record)
->condition('id', $id)
->execute();
$this->logger->notice('{{ plugin_label }} record @id has been updated.', ['@id' => $id]);
// Return the updated record in the response body.
$updated_record = $this->loadRecord($id);
return new ModifiedResourceResponse($updated_record, 200);
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\views\argument_default;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\views\argument_default\ArgumentDefaultPluginBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Example of argument default plugin.
*
* @ViewsArgumentDefault(
* id = "{{ plugin_id }}",
* title = @Translation("{{ plugin_label }}")
* )
*/
class {{ class }} extends ArgumentDefaultPluginBase implements CacheableDependencyInterface {
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Constructs a new {{ class }} instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
// @DCG
// The Route match service is used to extract argument from the current
// route.
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['example_option'] = ['default' => ''];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
$form['example_option'] = [
'#type' => 'textfield',
'#title' => t('Some example option'),
'#default_value' => $this->options['example_option'],
];
}
/**
* {@inheritdoc}
*/
public function getArgument() {
// @DCG
// Here is the place where you should create a default argument for the
// contextual filter. The source of this argument depends on your needs.
// For example, you can extract the value from the URL or fetch it from
// some fields of the current viewed entity.
// For now let's use example option as an argument.
$argument = $this->options['example_option'];
return $argument;
}
/**
* {@inheritdoc}
*/
public function getCacheMaxAge() {
return Cache::PERMANENT;
}
/**
* {@inheritdoc}
*/
public function getCacheContexts() {
return ['url'];
}
}

View file

@ -0,0 +1,52 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\views\field;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\ResultRow;
/**
* Provides {{ plugin_label }} field handler.
*
* @ViewsField("{{ plugin_id }}")
*/
class {{ class }} extends FieldPluginBase {
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['prefix'] = ['default' => ''];
$options['suffix'] = ['default' => ''];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['prefix'] = [
'#type' => 'textfield',
'#title' => $this->t('Prefix'),
'#default_value' => $this->options['prefix'],
];
$form['suffix'] = [
'#type' => 'textfield',
'#title' => $this->t('Suffix'),
'#default_value' => $this->options['suffix'],
];
}
/**
* {@inheritdoc}
*/
public function render(ResultRow $values) {
return $this->options['prefix'] . parent::render($values) . $this->options['suffix'];
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\{{ machine_name }}\Plugin\views\style;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\style\StylePluginBase;
/**
* {{ plugin_label }} style plugin.
*
* @ViewsStyle(
* id = "{{ plugin_id }}",
* title = @Translation("{{ plugin_label }}"),
* help = @Translation("Foo style plugin help."),
* theme = "views_style_{{ plugin_id }}",
* display_types = {"normal"}
* )
*/
class {{ class }} extends StylePluginBase {
/**
* {@inheritdoc}
*/
protected $usesRowPlugin = TRUE;
/**
* {@inheritdoc}
*/
protected $usesRowClass = TRUE;
/**
* {@inheritdoc}
*/
protected function defineOptions() {
$options = parent::defineOptions();
$options['wrapper_class'] = ['default' => 'item-list'];
return $options;
}
/**
* {@inheritdoc}
*/
public function buildOptionsForm(&$form, FormStateInterface $form_state) {
parent::buildOptionsForm($form, $form_state);
$form['wrapper_class'] = [
'#title' => $this->t('Wrapper class'),
'#description' => $this->t('The class to provide on the wrapper, outside rows.'),
'#type' => 'textfield',
'#default_value' => $this->options['wrapper_class'],
];
}
}

View file

@ -0,0 +1,14 @@
/**
* Prepares variables for views-style-{{ plugin_id|u2h }}.html.twig template.
*/
function template_preprocess_views_style_{{ plugin_id }}(&$variables) {
$handler = $variables['view']->style_plugin;
// Fetch wrapper classes from handler options.
if ($handler->options['wrapper_class']) {
$wrapper_class = explode(' ', $handler->options['wrapper_class']);
$variables['attributes']['class'] = array_map('\Drupal\Component\Utility\Html::cleanCssIdentifier', $wrapper_class);
}
template_preprocess_views_view_unformatted($variables);
}

View file

@ -0,0 +1,7 @@
views.style.{{ plugin_id }}:
type: views_style
label: '{{ plugin_label }}'
mapping:
wrapper_class:
type: string
label: Wrapper class

View file

@ -0,0 +1,22 @@
{{ '{#' }}
/**
* @file
* Default theme implementation for a view template to display a list of rows.
*
* Available variables:
* - attributes: HTML attributes for the container.
* - rows: A list of rows.
* - attributes: The row's HTML attributes.
* - content: The row's contents.
* - title: The title of this group of rows. May be empty.
*
* @see template_preprocess_views_style_{{ plugin_id }}()
*/
{{ '#}' }}{% verbatim %}
<div{{ attributes }}>
{% for row in rows %}
<div{{ row.attributes }}>{{ row.content }}</div>
{% endfor %}
</div>{% endverbatim %}