Skip to content
Snippets Groups Projects
Commit a3badee0 authored by Kelly's avatar Kelly Committed by Bob McDonald
Browse files

Issue #3443802 by ultrabob, kelly.m.jacobs, wylbur: Add Gitlab CI

parent 96e0a4db
Branches
Tags
1 merge request!13Resolve #3443802 "Add gitlab ci"
Pipeline #187492 passed
antibot
Beissert's
\ No newline at end of file
################
# GitLabCI template for Drupal projects.
#
# This template is designed to give any Contrib maintainer everything they need to test, without requiring modification.
# It is also designed to keep up to date with Core Development automatically through the use of include files that can be centrally maintained.
# As long as you include the project, ref and three files below, any future updates added by the Drupal Association will be used in your
# pipelines automatically. However, you can modify this template if you have additional needs for your project.
# The full documentation is on https://project.pages.drupalcode.org/gitlab_templates/
################
# For information on alternative values for 'ref' see https://project.pages.drupalcode.org/gitlab_templates/info/templates-version/
# To test a Drupal 7 project, change the first include filename from .main.yml to .main-d7.yml
include:
- project: $_GITLAB_TEMPLATES_REPO
ref: $_GITLAB_TEMPLATES_REF
file:
- "/includes/include.drupalci.main.yml"
- "/includes/include.drupalci.variables.yml"
- "/includes/include.drupalci.workflows.yml"
################
# Pipeline configuration variables are defined with default values and descriptions in the file
# https://git.drupalcode.org/project/gitlab_templates/-/blob/main/includes/include.drupalci.variables.yml
# Uncomment the lines below if you want to override any of the variables. The following is just an example.
################
# variables:
# SKIP_ESLINT: '1'
# OPT_IN_TEST_NEXT_MAJOR: '1'
# _CURL_TEMPLATES_REF: 'main'
<!-- writeme -->
Workflow Buttons Workflow Buttons
================ ================
Provide workflow buttons for content moderation instead of a select dropdown of states. Button labels are taken from the transition names. Provide workflow buttons for content moderation instead of a select
dropdown of states. Button labels are taken from the transition names.
* https://www.drupal.org/project/workflow_buttons * https://www.drupal.org/project/workflow_buttons
* Issues: https://www.drupal.org/project/issues/workflow_buttons * Issues: https://www.drupal.org/project/issues/workflow_buttons
* Source code: https://git.drupalcode.org/project/workflow_buttons.git * Source code: https://git.drupalcode.org/project/workflow_buttons.git
* Keywords: drupal, editor, workflow, buttons, form, content, content moderation * Keywords:
* drupal
* editor
* workflow
* buttons
* form
* content
* content moderation
* Package name: drupal/workflow_buttons * Package name: drupal/workflow_buttons
...@@ -20,12 +27,11 @@ No dependencies. ...@@ -20,12 +27,11 @@ No dependencies.
GPL-2.0+ GPL-2.0+
<!-- endwriteme -->
### Setup instructions ### Setup instructions
Set the form widget of Moderation state pseudofield to Workflow buttons. Set the form widget of Moderation state pseudo-field to Workflow buttons.
### Translation ### Translation
To translate buttons, translate the workflow transition labels of the core Workflows module at `/admin/config/regional/config-translation/workflow` To translate buttons, translate the workflow transition labels of the core
Workflows module at `/admin/config/regional/config-translation/workflow`
...@@ -7,19 +7,19 @@ ...@@ -7,19 +7,19 @@
* https://agaric.gitlab.io/raw-notes/notes/2019-10-31-change-drupal-s-submit-action-buttons-to-simple-workflow-state-based-buttons-including-replacing-delete-link-with-delete-to-trash-state-button/ * https://agaric.gitlab.io/raw-notes/notes/2019-10-31-change-drupal-s-submit-action-buttons-to-simple-workflow-state-based-buttons-including-replacing-delete-link-with-delete-to-trash-state-button/
*/ */
.submit-trash.action-link::before { .submit-trash.action-link::before {
margin: 0;
z-index: 100; z-index: 100;
margin: 0;
} }
.submit-trash.action-link--icon-trash { .submit-trash.action-link--icon-trash {
position: relative; position: relative;
left: 1.5em; left: 1.5em;
padding-left: 0;
padding-right: 0; padding-right: 0;
padding-left: 0;
} }
.submit-trash.button { .submit-trash.button {
padding-left: 3em;
background-color: unset;
color: unset; /* Frustratingly no amount of unsetting will get this to use the .action-link--danger red, even though unchecking the overriding colors in browser inspector does work. */
position: relative; position: relative;
left: -1.5em; left: -1.5em;
padding-left: 3em;
color: unset; /* Frustratingly no amount of unsetting will get this to use the .action-link--danger red, even though unchecking the overriding colors in browser inspector does work. */
background-color: unset;
} }
uuid: 5a86886e-9b82-46c2-a8ff-08d6f594a14f
langcode: en
status: true
dependencies:
config:
- node.type.announcement
- node.type.event
- node.type.group_comment
- node.type.page
- node.type.resource
module:
- content_moderation
_core:
default_config_hash: C0jZewM2mDrvz1X6CgI-Z0c5suz-odCZu1SKLKhotjo
id: workflow_buttons_trash_publishing
label: "Publishing (with draft and soft delete)"
type: content_moderation
type_settings:
states:
draft:
label: Draft
published: false
default_revision: false
weight: -2
published:
label: Published
published: true
default_revision: true
weight: 0
trash:
published: false
default_revision: true
label: Trash
weight: 1
unpublished:
published: false
default_revision: true
label: Unpublished
weight: 2
transitions:
create_new_draft:
label: Save
to: draft
weight: -5
from:
- draft
delete:
label: Delete
from:
- draft
- published
- unpublished
to: trash
weight: 1
publish:
label: Publish
to: published
weight: -1
from:
- draft
- unpublished
restore_draft:
label: "Restore to Draft"
from:
- trash
to: draft
weight: 2
restore_publish:
label: "Restore and Publish"
from:
- trash
to: published
weight: 3
save_draft_leave_current_published:
label: "Create draft (leave current version published)"
from:
- published
to: draft
weight: -2
save_unpublished:
label: Save
from:
- unpublished
to: unpublished
weight: -4
unpublish:
label: Unpublish
from:
- published
to: unpublished
weight: 0
update:
label: Update
from:
- published
to: published
weight: -3
default_moderation_state: draft
entity_types:
node:
- announcement
- event
- group_comment
- page
- resource
...@@ -13,6 +13,7 @@ use Drupal\Core\Form\FormStateInterface; ...@@ -13,6 +13,7 @@ use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountInterface; use Drupal\Core\Session\AccountInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\TempStore\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
/** /**
...@@ -57,6 +58,13 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact ...@@ -57,6 +58,13 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact
*/ */
protected $validator; protected $validator;
/**
* Private tempstore factory.
*
* @var \Drupal\Core\TempStore\PrivateTempStoreFactory
*/
protected $tempstoreFactory;
/** /**
* Constructs a new ModerationStateWidget object. * Constructs a new ModerationStateWidget object.
* *
...@@ -78,13 +86,16 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact ...@@ -78,13 +86,16 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact
* Moderation information service. * Moderation information service.
* @param \Drupal\content_moderation\StateTransitionValidation $validator * @param \Drupal\content_moderation\StateTransitionValidation $validator
* Moderation state transition validation service. * Moderation state transition validation service.
* @param \Drupal\Core\TempStore\PrivateTempStoreFactory $tempstore_factory
* Private tempstore factory.
*/ */
public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, ModerationInformation $moderation_information, StateTransitionValidation $validator) { public function __construct($plugin_id, $plugin_definition, FieldDefinitionInterface $field_definition, array $settings, array $third_party_settings, AccountInterface $current_user, EntityTypeManagerInterface $entity_type_manager, ModerationInformation $moderation_information, StateTransitionValidation $validator, PrivateTempStoreFactory $tempstore_factory) {
parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings); parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
$this->entityTypeManager = $entity_type_manager; $this->entityTypeManager = $entity_type_manager;
$this->currentUser = $current_user; $this->currentUser = $current_user;
$this->moderationInformation = $moderation_information; $this->moderationInformation = $moderation_information;
$this->validator = $validator; $this->validator = $validator;
$this->tempstoreFactory = $tempstore_factory;
} }
/** /**
...@@ -100,7 +111,8 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact ...@@ -100,7 +111,8 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact
$container->get('current_user'), $container->get('current_user'),
$container->get('entity_type.manager'), $container->get('entity_type.manager'),
$container->get('content_moderation.moderation_information'), $container->get('content_moderation.moderation_information'),
$container->get('content_moderation.state_transition_validation') $container->get('content_moderation.state_transition_validation'),
$container->get('tempstore.private')
); );
} }
...@@ -172,7 +184,7 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact ...@@ -172,7 +184,7 @@ class WorkflowButtonsWidget extends OptionsSelectWidget implements ContainerFact
'transition_machine_name' => $transition->id(), 'transition_machine_name' => $transition->id(),
]; ];
} }
$tempstore = \Drupal::service('tempstore.private')->get('workflow_buttons'); $tempstore = $this->tempstoreFactory->get('workflow_buttons');
$form_id = $form_state->getBuildInfo()['form_id']; $form_id = $form_state->getBuildInfo()['form_id'];
$tempstore->set($form_id . '_transition_data', $transition_data); $tempstore->set($form_id . '_transition_data', $transition_data);
......
This diff is collapsed.
<?php
namespace Drupal\Tests\workflow_buttons\Functional;
use Drupal\Tests\node\Functional\AssertButtonsTrait;
use Drupal\Tests\node\Functional\NodeTestBase;
use Drupal\Core\StringTranslation\StringTranslationTrait;
/**
* Tests all the different buttons on the node form.
*
* @group workflow_buttons
*/
class NodeFormButtonsTest extends NodeTestBase {
use StringTranslationTrait;
use AssertButtonsTrait;
/**
* {@inheritDoc}
*/
protected $defaultTheme = 'stark';
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = [
'workflow_buttons',
];
/**
* A normal logged in user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* A user with permission to bypass access content.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritDoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a user that has no access to change the state of the node.
$this->webUser = $this->drupalCreateUser(['create article content', 'edit own article content']);
// Create a user that has access to change the state of the node.
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'bypass node access']);
}
/**
* Tests that the right buttons are displayed for saving nodes.
*/
public function testNodeFormButtons() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
// Log in as administrative user.
$this->drupalLogin($this->adminUser);
// Verify the buttons on a node add form.
$this->drupalGet('node/add/article');
$this->assertButtons([$this->t('Save and publish'), $this->t('Save as unpublished')]);
// Save the node and assert it's published after clicking
// 'Save and publish'.
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalGet('node/add/article');
$this->submitForm($edit, $this->t('Save and publish'));
// Get the node.
$node_1 = $node_storage->load(1);
$this->assertTrue($node_1->isPublished(), 'Node is published');
// Verify the buttons on a node edit form.
$this->drupalGet('node/' . $node_1->id() . '/edit');
$this->assertButtons([$this->t('Save and keep published'), $this->t('Save and unpublish')]);
// Save the node and verify it's still published after clicking
// 'Save and keep published'.
$this->submitForm($edit, $this->t('Save and keep published'));
$node_storage->resetCache([1]);
$node_1 = $node_storage->load(1);
$this->assertTrue($node_1->isPublished(), 'Node is published');
$this->drupalGet('node/' . $node_1->id() . '/edit');
// Save the node and verify it's unpublished after clicking
// 'Save and unpublish'.
$this->submitForm($edit, $this->t('Save and unpublish'));
$node_storage->resetCache([1]);
$node_1 = $node_storage->load(1);
$this->assertFalse($node_1->isPublished(), 'Node is unpublished');
// Verify the buttons on an unpublished node edit screen.
$this->drupalGet('node/' . $node_1->id() . '/edit');
$this->assertButtons([$this->t('Save and keep unpublished'), $this->t('Save and publish')]);
// Create a node as a normal user.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
// Verify the buttons for a normal user.
$this->drupalGet('node/add/article');
$this->assertButtons([$this->t('Save')], FALSE);
// Create the node.
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalGet('node/add/article');
$this->submitForm($edit, $this->t('Save'));
$node_2 = $node_storage->load(2);
$this->assertTrue($node_2->isPublished(), 'Node is published');
// Log in as an administrator and unpublish the node that just
// was created by the normal user.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
$this->drupalGet('node/' . $node_2->id() . '/edit');
$this->submitForm([], $this->t('Save and unpublish'));
$node_storage->resetCache([2]);
$node_2 = $node_storage->load(2);
$this->assertFalse($node_2->isPublished(), 'Node is unpublished');
// Log in again as the normal user, save the node and verify
// it's still unpublished.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
$this->drupalGet('node/' . $node_2->id() . '/edit');
$this->submitForm([], $this->t('Save'));
$node_storage->resetCache([2]);
$node_2 = $node_storage->load(2);
$this->assertFalse($node_2->isPublished(), 'Node is still unpublished');
$this->drupalLogout();
// Set article content type default to unpublished. This will change the
// the initial order of buttons and/or status of the node when creating
// a node.
$fields = \Drupal::service('entity_field.manager')->getFieldDefinitions('node', 'article');
$fields['status']->getConfig('article')
->setDefaultValue(FALSE)
->save();
// Verify the buttons on a node add form for an administrator.
$this->drupalLogin($this->adminUser);
$this->drupalGet('node/add/article');
$this->assertButtons([$this->t('Save as unpublished'), $this->t('Save and publish')]);
// Verify the node is unpublished by default for a normal user.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalGet('node/add/article');
$this->submitForm($edit, $this->t('Save'));
$node_3 = $node_storage->load(3);
$this->assertFalse($node_3->isPublished(), 'Node is unpublished');
}
}
...@@ -12,6 +12,7 @@ use Drupal\Core\Entity\ContentEntityType; ...@@ -12,6 +12,7 @@ use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormBuilder;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\Routing\RouteMatchInterface;
...@@ -31,22 +32,22 @@ function workflow_buttons_form_alter(&$form, FormStateInterface $form_state, $fo ...@@ -31,22 +32,22 @@ function workflow_buttons_form_alter(&$form, FormStateInterface $form_state, $fo
return; return;
} }
$form['#entity_builders']['update_status'] = '\Drupal\workflow_buttons\Plugin\Field\FieldWidget\WorkflowButtonsWidget::updateStatus'; $form['#entity_builders']['update_status'] = '\Drupal\workflow_buttons\Plugin\Field\FieldWidget\WorkflowButtonsWidget::updateStatus';
// Only display the workflow buttons when rendering the pseudofield. // Only display the workflow buttons when rendering the pseudo-field.
if ($form_state->get('workflow_buttons')) { if ($form_state->get('workflow_buttons')) {
$form_elements_keep_list = [ $form_elements_keep_list = [
'actions', 'actions',
'moderation_state', 'moderation_state',
'form_token', 'form_token',
'form_id', 'form_id',
'antibot_key', 'antibot_key',
'antibot_no_js', 'antibot_no_js',
'honeypot_time' 'honeypot_time',
]; ];
foreach ($form as $key => $value) { foreach ($form as $key => $value) {
if (!str_starts_with($key, '#') && !in_array($key, $form_elements_keep_list)) { if (!str_starts_with($key, '#') && !in_array($key, $form_elements_keep_list)) {
unset($form[$key]); unset($form[$key]);
} }
} }
} }
} }
...@@ -70,8 +71,8 @@ function workflow_buttons_entity_extra_field_info() { ...@@ -70,8 +71,8 @@ function workflow_buttons_entity_extra_field_info() {
$extra = []; $extra = [];
$content_entity_types = []; $content_entity_types = [];
$entity_type_definitions = \Drupal::entityTypeManager()->getDefinitions(); $entity_type_definitions = \Drupal::entityTypeManager()->getDefinitions();
/* @var $definition EntityTypeInterface */
foreach ($entity_type_definitions as $definition) { foreach ($entity_type_definitions as $definition) {
assert($definition instanceof EntityTypeInterface);
if ($definition instanceof ContentEntityType) { if ($definition instanceof ContentEntityType) {
$content_entity_types[] = $definition->get('id'); $content_entity_types[] = $definition->get('id');
} }
...@@ -103,12 +104,12 @@ function workflow_buttons_entity_view(array &$build, EntityInterface $entity, En ...@@ -103,12 +104,12 @@ function workflow_buttons_entity_view(array &$build, EntityInterface $entity, En
$form_state_additions = [ $form_state_additions = [
'workflow_buttons' => TRUE, 'workflow_buttons' => TRUE,
]; ];
/* @var \Drupal\Core\Form\FormBuilder $form_builder */
$form_builder = \Drupal::service('entity.form_builder'); $form_builder = \Drupal::service('entity.form_builder');
assert($form_builder instanceof FormBuilder);
try { try {
$form = $form_builder->getForm($entity, 'default', $form_state_additions); $form = $form_builder->getForm($entity, 'default', $form_state_additions);
} }
// Not all custom entities are likely to have a default form, use edit if they don't. // Not all custom entities have a default form, use edit if they don't.
catch (InvalidPluginDefinitionException $e) { catch (InvalidPluginDefinitionException $e) {
$form = $form_builder->getForm($entity, 'edit', $form_state_additions); $form = $form_builder->getForm($entity, 'edit', $form_state_additions);
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment