-
Notifications
You must be signed in to change notification settings - Fork 4
by yanniboi: Created new field type for logging status changes. #41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 8.x-1.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| name: Simple CRM Tools | ||
| description: "Tools for building CRM modules." | ||
|
andrewbelcher marked this conversation as resolved.
|
||
| core: 8.x | ||
| type: module | ||
| package: Tool | ||
|
andrewbelcher marked this conversation as resolved.
|
||
|
|
||
| dependencies: | ||
| - decoupled_auth | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| <?php | ||
|
|
||
| /** | ||
| * @file | ||
| * Simple implementation of Decoupled User Authentication Tools for CRM. | ||
|
andrewbelcher marked this conversation as resolved.
|
||
| */ | ||
|
|
||
| use Drupal\Core\Entity\EntityInterface; | ||
| use Drupal\Core\Entity\ContentEntityBase; | ||
|
|
||
| /** | ||
| * Implements hook_entity_presave(). | ||
| * | ||
| * Update status log fields. | ||
| */ | ||
| function decoupled_auth_crm_tools_entity_presave(EntityInterface $entity) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can this not live in the field
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I tried all of the fielditemlist hooks... Unless the field actually is set pre save, nothing on the fielditemlist gets triggered.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (!$entity instanceof ContentEntityBase) { | ||
| return; | ||
| } | ||
|
|
||
| // Check for status log fields. | ||
| foreach ($entity->getFieldDefinitions() as $field_name => $definition) { | ||
| /* @var \Drupal\Core\Field\FieldDefinitionInterface $definition */ | ||
| if ($definition->getType() == 'status_log') { | ||
| $source_field = $definition->getFieldStorageDefinition()->getSetting('source_field'); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's put all of this logic inside the status log field list class. |
||
|
|
||
| $value = $entity->{$source_field}->value; | ||
| $original_value = $entity->original->{$source_field}->value; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we use |
||
| if ($value !== $original_value) { | ||
| $values = [ | ||
| 'value' => $value, | ||
| 'previous' => $original_value, | ||
| 'uid' => \Drupal::currentUser()->id(), | ||
| 'timestamp' => \Drupal::time()->getRequestTime(), | ||
| ]; | ||
| $entity->{$field_name}->appendItem($values); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| <?php | ||
|
|
||
| namespace Drupal\decoupled_auth_crm_tools\Plugin\Field\FieldFormatter; | ||
|
|
||
| use Drupal\Component\Datetime\DateTimePlus; | ||
| use Drupal\Core\Field\FieldDefinitionInterface; | ||
| use Drupal\Core\Field\FieldItemListInterface; | ||
| use Drupal\Core\Field\FormatterBase; | ||
| use Drupal\Core\Plugin\ContainerFactoryPluginInterface; | ||
| use Drupal\user\Entity\User; | ||
| use Symfony\Component\DependencyInjection\ContainerInterface; | ||
|
|
||
| /** | ||
| * Defines the 'status_log_list' field formatter. | ||
| * | ||
| * @FieldFormatter( | ||
| * id = "status_log_list", | ||
| * label = @Translation("Status Log (list)"), | ||
| * field_types = { | ||
| * "status_log" | ||
| * } | ||
| * ) | ||
| */ | ||
| class StatusLogFormatter extends FormatterBase implements ContainerFactoryPluginInterface { | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, $label, $view_mode, array $third_party_settings) { | ||
| parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $label, $view_mode, $third_party_settings); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks these are being overridden with no changes? |
||
| return new static( | ||
| $plugin_id, | ||
| $plugin_definition, | ||
| $configuration['field_definition'], | ||
| $configuration['settings'], | ||
| $configuration['label'], | ||
| $configuration['view_mode'], | ||
| $configuration['third_party_settings'] | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function viewElements(FieldItemListInterface $items, $langcode) { | ||
| // @todo Add human readable labels. | ||
|
andrewbelcher marked this conversation as resolved.
|
||
| $elements = []; | ||
| foreach ($items as $delta => $item) { | ||
| /* @var \Drupal\decoupled_auth_crm_tools\Plugin\Field\FieldType\StatusLog $item */ | ||
| $values = $item->getValue(); | ||
| if (is_array($values) && !empty($values['value'])) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we just use |
||
|
|
||
| $elements[$delta]['value'] = [ | ||
| '#type' => 'html_tag', | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this be better if
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That might mess with other things as I think
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The formatter can say it handles multiple values and then deal with the whole lot as one. |
||
| '#tag' => 'p', | ||
| '#attributes' => ['class' => []], | ||
| '#value' => $this->t('@time: Status was changed from %status_old to %status_new by @username.', [ | ||
| '%status_new' => $values['value'], | ||
| '%status_old' => $values['previous'], | ||
| '@time' => DateTimePlus::createFromTimestamp($values['timestamp'])->format('Y-m-d H:i:s'), | ||
| '@username' => User::load($values['uid'])->label(), | ||
| ]), | ||
| ]; | ||
|
|
||
| } | ||
| } | ||
|
|
||
| return $elements; | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| <?php | ||
|
|
||
| namespace Drupal\decoupled_auth_crm_tools\Plugin\Field\FieldType; | ||
|
|
||
| use Drupal\Component\Utility\Random; | ||
| use Drupal\Core\Field\FieldDefinitionInterface; | ||
| use Drupal\Core\Field\FieldItemBase; | ||
| use Drupal\Core\Field\FieldStorageDefinitionInterface; | ||
| use Drupal\Core\Form\FormStateInterface; | ||
| use Drupal\Core\StringTranslation\TranslatableMarkup; | ||
| use Drupal\Core\TypedData\DataDefinition; | ||
|
|
||
| /** | ||
| * Plugin implementation of the 'status_log' field type. | ||
| * | ||
| * @FieldType( | ||
| * id = "status_log", | ||
| * label = @Translation("Status log"), | ||
| * description = @Translation("Logs changes to a status"), | ||
| * default_widget = "string_textfield", | ||
| * default_formatter = "status_log_list" | ||
| * ) | ||
| */ | ||
| class StatusLog extends FieldItemBase { | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function defaultStorageSettings() { | ||
| return [ | ||
| 'max_length' => 255, | ||
| ] + parent::defaultStorageSettings(); | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) { | ||
| // Prevent early t() calls by using the TranslatableMarkup. | ||
| $properties['value'] = DataDefinition::create('string') | ||
| ->setLabel(new TranslatableMarkup('Status')) | ||
| ->setRequired(TRUE); | ||
|
|
||
| $properties['previous'] = DataDefinition::create('string') | ||
| ->setLabel(new TranslatableMarkup('Previous Status')); | ||
|
|
||
| $properties['timestamp'] = DataDefinition::create('timestamp') | ||
| ->setLabel(new TranslatableMarkup('Changed timestamp')) | ||
| ->setRequired(TRUE); | ||
|
|
||
| $properties['uid'] = DataDefinition::create('integer') | ||
| ->setLabel(new TranslatableMarkup('User responsible for change')); | ||
|
|
||
| return $properties; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function schema(FieldStorageDefinitionInterface $field_definition) { | ||
| return [ | ||
| 'columns' => [ | ||
| 'value' => [ | ||
| 'type' => 'varchar', | ||
| 'length' => (int) $field_definition->getSetting('max_length'), | ||
| ], | ||
| 'previous' => [ | ||
| 'type' => 'varchar', | ||
| 'length' => (int) $field_definition->getSetting('max_length'), | ||
| ], | ||
| 'timestamp' => [ | ||
| 'type' => 'int', | ||
| ], | ||
| 'uid' => [ | ||
| 'type' => 'int', | ||
| ], | ||
| ], | ||
| 'indexes' => [ | ||
| 'value' => ['value'], | ||
| 'previous' => ['previous'], | ||
| ], | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function getConstraints() { | ||
| $constraints = parent::getConstraints(); | ||
|
|
||
| if ($max_length = $this->getSetting('max_length')) { | ||
| $constraint_manager = \Drupal::typedDataManager()->getValidationConstraintManager(); | ||
| $constraints[] = $constraint_manager->create('ComplexData', [ | ||
| 'value' => [ | ||
| 'Length' => [ | ||
| 'max' => $max_length, | ||
| 'maxMessage' => t('%name: may not be longer than @max characters.', [ | ||
| '%name' => $this->getFieldDefinition()->getLabel(), | ||
| '@max' => $max_length | ||
| ]), | ||
| ], | ||
| ], | ||
| ]); | ||
| } | ||
|
|
||
| return $constraints; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function applyDefaultValue($notify = TRUE) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems redundant. |
||
| parent::applyDefaultValue($notify); | ||
| return $this; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public static function generateSampleValue(FieldDefinitionInterface $field_definition) { | ||
| $random = new Random(); | ||
|
|
||
| $values['value'] = $random->word(mt_rand(1, $field_definition->getSetting('max_length'))); | ||
| $values['previous'] = $random->word(mt_rand(1, $field_definition->getSetting('max_length'))); | ||
| $values['uid'] = mt_rand(0, 1); | ||
| $values['timestamp'] = mt_rand(1262055681, 1262055681); | ||
|
|
||
| return $values; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function storageSettingsForm(array &$form, FormStateInterface $form_state, $has_data) { | ||
| $elements = []; | ||
|
|
||
| $elements['max_length'] = [ | ||
| '#type' => 'number', | ||
| '#title' => t('Maximum length'), | ||
| '#default_value' => $this->getSetting('max_length'), | ||
| '#required' => TRUE, | ||
| '#description' => t('The maximum length of the field in characters.'), | ||
| '#min' => 1, | ||
| '#disabled' => $has_data, | ||
| ]; | ||
|
|
||
| // @todo Make this field a select field of existing fields on entity. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When we do this, we need to filter the fields by appropriate ones. My suggestion would be ones where there is a main value and the main value is a string... We may want to put some similar validation somewhere else for programmatic fields, but not exactly sure where that should live... |
||
| $elements['source_field'] = [ | ||
| '#type' => 'textfield', | ||
| '#title' => t('Source Field'), | ||
| '#default_value' => $this->getSetting('source_field'), | ||
| '#required' => TRUE, | ||
| '#description' => t('The field that stores the status being logged.'), | ||
| '#disabled' => $has_data, | ||
| ]; | ||
|
|
||
| return $elements; | ||
| } | ||
|
|
||
| /** | ||
| * {@inheritdoc} | ||
| */ | ||
| public function isEmpty() { | ||
| $value = $this->get('value')->getValue(); | ||
| return $value === NULL || $value === ''; | ||
| } | ||
|
|
||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.