Account created on 7 October 2017, over 6 years ago
#

Recent comments

The patch #12 did the job on Drupal 10.2.6.

If you explain your use case in a business terms, and in more details (that we be able reproduce it) we will try to help.

You can also use the EVA module (but what is really stable in our life? :)

Could you describe the use case in more details.

The problem still exists. If we run the following command: drush en -y my_module and the hook_requirements returns 'severity' => REQUIREMENT_ERROR then Drush display the error message but still enables the module.

System Configuration
Drupal 10.2.4
Drush 12.5.2.0

The related issue of the Drush project:
1. https://github.com/drush-ops/drush/pull/4874
2. https://github.com/drush-ops/drush/issues/6006

The implementation of the hook_requirements() does not prevent module installation if the module is installed with drush. If the system does not have PostgreSQL and the module is installed using drush, then despite the error message, the module will still be installed. For that case we can additionally hide the "IP Address PostgreSQL" field type from the "Add field" form and display a notification to a user.

Theoreticaly, you can add to the user accout a date field and store in it the date of last content creation. To update this field you can use the module like ECA. After that you can in the view create the relation vith user and display this field in the view. But if you need a solution with a custom code, you may contact me via contact form or mail to wombatbuddy [at] gmail.com

You can just attach a library in your template like this: 

{{ attach_library('your_module/your_library') }}

I hope that you rebuild the caches.
It's seems the 'template' => 'commerce-order-receipt', is redundant. Also, you may try to create the unique template by implementing the hook_theme_suggestions_HOOK().

You may try to implement the hook_theme() in YOUR_MODULE.module file.

dump($node->field_myfield);

or (if the Devel module is installed) 

dpm($node->field_myfield);
select:
  '#type': select
  '#title': Title
  '#options':
    english: English
    russian: Russian
  '#default_value': english

It seems, that a more modern solution would be to convert images into Media images. The core's "Media library" module provides a convenient widget. Also, as a media is an entity you will be able to add fields to it. See
1. Media Management in Drupal (2024) .
2. THE ULTIMATE GUIDE TO THE MEDIA MANAGER IN DRUPAL 9.

Add the information about using subqueries in conditions.
The reference:  https://www.drupal.org/node/2770421 .

<?php

declare(strict_types=1);

namespace Drupal\my_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Provides a non submitted webforms block.
 *
 * Fetch all node IDs for which attached webforms have not yet been submitted.
 * Also, fetch all node IDs for which attached webforms were submitted more than
 * a year ago and whose sender is the current user. After that render links to
 * thous nodes and show the message that a user should to submit the
 * corresponding webforms. The solution works both for the case when a user is
 * allowed to create only one submission per webform/source entity and for the
 * case when there is no such restriction. This is provided by the "groupBy" and
 * "having" operators. We find the last timestamp when a submission was changed
 * and compare it to last year's timestamp.
 *
 * @Block(
 *   id = "non_submitted_webforms",
 *   admin_label = @Translation("Non Submitted Webforms"),
 *   category = @Translation("Custom"),
 * )
 */
final class NonSubmittedWebformsBlock extends BlockBase implements ContainerFactoryPluginInterface {

  /**
   * Constructs the plugin instance.
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    private readonly Connection $connection,
    private readonly AccountProxyInterface $currentUser,
    private readonly EntityTypeManagerInterface $entityTypeManager,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): self {
    return new self(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('database'),
      $container->get('current_user'),
      $container->get('entity_type.manager'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function build(): array {
    $date = new DrupalDateTime('now', 'UTC');
    $timestampYearAgo = $date->modify('-1 year')->getTimestamp();

    // The $query1 fetch all node IDs for which attached webforms were submitted
    // more than a year ago and whose sender is the current user. As nodes can
    // be deleted we select node IDs from the "node" table but not from the
    // "webform_submission" to ensure that they still exist.
    $query1 = $this->connection->select('node', 'node');
    $query1->addField('node', 'nid');
    $query1->condition('node.type', 'webform');
    $query1->join('webform_submission', 'ws', 'ws.entity_id = node.nid');
    $query1->condition('ws.uid', $this->currentUser->id());
    $query1->groupBy('node.nid');
    $query1->having('MAX(ws.changed) <= :timestampYearAgo', [':timestampYearAgo' => $timestampYearAgo]);

    // The $query2 fetch all node IDs for which attached webforms have not yet
    // been submitted by a current user, but which have been submitted by other
    // users. The $subquery fetch all node IDs for which attached webforms have
    // been submitted by a current user.
    $subquery = $this->connection->select('node', 'node');
    $subquery->addField('node', 'nid');
    $subquery->condition('node.type', 'webform');
    $subquery->join('webform_submission', 'ws', 'ws.entity_id = node.nid');
    $subquery->condition('ws.uid', $this->currentUser->id());

    $query2 = $this->connection->select('node', 'node');
    $query2->addField('node', 'nid');
    $query2->condition('node.type', 'webform');
    $query2->join('webform_submission', 'ws', 'ws.entity_id = node.nid');
    $query2->condition('node.nid', $subquery, 'NOT IN');

    // The query to fetch all node IDs for which attached webforms have not yet
    // been submitted by any users.
    $query3 = $this->connection->select('node', 'node');
    $query3->addField('node', 'nid');
    $query3->condition('node.type', 'webform');
    $query3->leftJoin('webform_submission', 'ws', 'ws.entity_id = node.nid');
    $query3->isNull('ws.entity_id');

    $query1->union($query2)->union($query3);
    $query1->orderBy('nid', 'ASC');
    $nids = $query1->execute()->fetchCol();

    $build['#cache']['contexts'] = [
      'user',
      'session',
    ];
    $build['#cache']['tags'] = [
      'node_list:webform',
      // If the name of the webform attached to the node is known, then it’s
      // better to add the tag like this 'webform_submission_list:webform_name'.
      'webform_submission_list',
    ];

    if (empty($nids)) {
      $build['message'] = [
        '#type' => 'markup',
        '#markup' => $this->t('<p>All webforms have been submitted.</p>'),
      ];

      return $build;
    }

    $build['message'] = [
      '#type' => 'markup',
      '#markup' => $this->t('<p>You need to visit the following pages and submit the webforms:</p>'),
    ];

    foreach ($nids as $nid) {
      $node = $this->entityTypeManager->getStorage('node')->load($nid);
      $build["link_to_node_{$nid}"] = [
        '#type' => 'link',
        '#title' => $node->getTitle(),
        '#url' => $node->toUrl(),
        '#attributes' => ['target' => '_blank'],
        '#prefix' => '<p>',
        '#sufix' => '</p>',
      ];
    }

    return $build;
  }

}

The option 1
Create a personal view for each webform and include the name of a webfrom to view's path. For example
The path of a view_1 which display a submission of the webform_1:
admin/structure/webform/manage/webform_1/submission/%sid
The path of a view_2 which display a submission of the webform_2
admin/structure/webform/manage/webform_2/submission/%sid

The option 2
1. Create a personal view for each webform.
2. Create personal templates for each webform submission data, like this:
webform-submission--webfrom-1.html.twig
webform-submission--webfrom-2.html.twig
3. Render a related views in templates like was described in the previous answer.

We have two types of pages:

1) The submissions of the webform, which has the path like this: /admin/structure/webform/manage/{WEBFORM_NAME}/results/submissions
This page is the view and you can replace it with your view. See the following video "Webform views integration".

2) The page of one specific submission which can has the path like this: /admin/structure/webform/manage/{WEBFORM_NAME}/submission/{SUBMISSION_ID}
But you can create the view and render it in the following templates:
webform-submission.html.twig
webform-submission-data.html.twig
For example you can copy the "webform-submission.html.twig" template to the "webform-submission--{WEBFROM-NAME}.html.twig" and then render you view in it. You can use the Twig Tweak module for this, see "Twig Tweak and Views" .

Alternatively, you can completely replace the page with your view. Let's say that submission page has the following path: /admin/structure/webform/manage/my_webform/submission/{SUBMISSION_ID)
Now you can create a view to display a submission and set the view's path in to the following value: admin/structure/webform/manage/%webform/submission/%sid
 

If you want to create the exposed filters as the "Content" view used, you can look at the "Content" view by visiting  /admin/structure/views/view/content.

Sorry, but I didn't understand the problem. Could you provide more details about the actions you performed.

I don't see the things you're talking about on (Drupal 10.2.4 and Olivero theme). What theme do you use? Could you share screenshots that illustrate the issue.

You can copy the "webform-submission-data.html.twig" template to the "webform-submission-data--{WEBFORM_NAME}--table.html.twig" and customise the table using the "rowspan" attribute.
Another way is to display submissions with a custom view. In this case the following modules may be useful:
"Webform Views Integration" , "Views Aggregator Plus" , "Views Table Rowspan" .

You can try the following code: 

/**
 * Implements hook_preprocess_HOOK() for field__comment templates.
 *
 * Sort all comments that have a parent in "Newest first" order. The use case:
 * all comments of the first level are questions. They must be sorted in
 * "Oldest first" order. All replies and replies to replies are answers.
 * The answers should be sorted in "Newest First" order.
 * If a user does not have permission to view unpublished comments, then all
 * descendants of the unpublished comment will also not be visible to the user.
 */
function my_module_preprocess_field__comment(&$variables): void {
  // The array of comments keyed by comment ID.
  $comments = [];

  foreach ($variables['comments'] as $key => &$value) {
    if (is_array($value) && array_key_exists('#comment', $value)) {
      $comments[$key] = $value['#comment'];
      // Store the array element so we can reproduce its structure in the
      // future. We can use an element of any comments existing in the array.
      $comment_array_item = $variables['comments'][$key];
      // Remove the existing array of the comment. The arrays of comments will
      // be replaced with sorted version.
      unset($variables['comments'][$key]);
    }
  }
  $cids = [];

  // It is possible that a page will have child comments whose parent will be on
  // the previous page. Such comments appear at the top of the comment list.
  // Let's check for the presence of such comments and save their IDs into the
  // array for sorting in "Newest first" order.
  /** @var \Drupal\comment\CommentInterface $comment */
  foreach ($comments as $cid => $comment) {
    if ($comment->hasParentComment()) {
      $parent_comment = $comment->getParentComment();
      // If the parent comment of the comment is not published, then we do not
      // add the comment to the top of the list.
      if (!$parent_comment->isPublished()) {
        continue;
      }
      $pid = $parent_comment->id();
      // If the parent comment does not exist on the current page.
      if (!array_key_exists($pid, $comments)) {
        $cids[] = $cid;
        $child_cids = my_module_get_childs_recursively($cid);
        $cids = array_merge($cids, $child_cids);
      }
    }
  }
  // Sorts in "Newest First" order the IDs of comments whose parents are on the
  // previous page, as well as IDs of their child comments.
  rsort($cids);

  foreach ($comments as $cid => $comment) {
    if (!$comment->hasParentComment()) {
      $cids[] = $cid;
      $child_cids = my_module_get_childs_recursively($cid);
      rsort($child_cids);
      $cids = array_merge($cids, $child_cids);
    }
  }

  // Due to the "Comments per page" option, all child elements may not fit on
  // the page, so we will reduce the number of elements equal to the number of
  // comments per page.
  $comments_on_oage = count($comments);
  $cids = array_slice($cids, 0, $comments_on_oage);
  $weight = 0;

  foreach ($cids as $cid) {
    $comment = \Drupal::entityTypeManager()->getStorage('comment')->load($cid);
    $comment_array_item['#comment'] = $comment;
    $comment_array_item['#cache']['tags'] = [
      'comment_view',
      "comment:{$cid}",
    ];
    $comment_array_item['#weight'] = $weight++;
    $variables['comments'][$cid] = $comment_array_item;
  }

  // We need that the "pager" was added in the array after comments.
  $pager = $variables['comments']['pager'];
  unset($variables['comments']['pager']);
  $variables['comments']['pager'] = $pager;
}

/**
 * Recursively getting child comments.
 *
 * Getting all child comments and all child comments of child comments.
 *
 * @param string $pid
 *   The ID of the parent comment.
 * @param array $cids
 *   IDs of child comments.
 *
 * @return array
 *   IDs of child comments.
 */
function my_module_get_childs_recursively(string $pid, array &$cids = []) {
  $query = \Drupal::entityQuery('comment')
    ->accessCheck(TRUE)
    ->condition('pid', $pid);

  $current_user = \Drupal::currentUser();
  // Prevent unpublished comments from being shown to users who don't have
  // permission for this.
  if (!$current_user->hasPermission('administer comments')) {
    $query->condition('status', 1);
  }

  $child_cids = $query->execute();
  $child_cids = array_values($child_cids);
  $cids = array_merge($cids, $child_cids);

  foreach ($child_cids as $cid) {
    my_module_get_childs_recursively($cid, $cids);
  }
  return $cids;
}

1. Open /node/698/edit
2. Open the "Promotion options" section and check the "Sticky at top of lists" option.
3. Click on the "Save" button.
4. In the view add the "Sticky at top of lists" sort criteria.
5. In the configuration of the sort criteria check the "Sort descending" option.
6. Rearrange the sort criteria to make the "Sticky at top of lists (desc)" at the top of the list.
7. Click on the "Save" button.

You wrote that "child comments must be in newest first". Is the below the expected result?

Question Comment 1
  Answer Comment 2
    Answer Comment 1-2
    Answer Comment 1-1
  Answer Comment 1
Question Comment 2
.....

Is it possible to display child comments without shifts in еру "flat" mode? For example, like this:
Question Comment 1
  Answer Comment 2
  Answer Comment 1-2
  Answer Comment 1-1
  Answer Comment 1
Question Comment 2
.....
 

Do you consider only first level comments to be parental? What if child comments have their child comments? How do you want to render them them?

The image shared by you is not available.

1. Visit /admin/structure/views/view/content
2. Add the "Authored on" field to the view.
3. Rerange the fields to this field be before the "Operation links" field.
4. Click on the "Settings" link from the "Formant" section.
5. Scroll to the "Authored on" field and check the "Sortable" option for it.
6. Click in the "Save" button.

Add to the "YOUR_THEME.theme" file or to a custom module the code like this: 

/**
 * Implements hook_preprocess_HOOK() for comment templates.
 *
 * Calculate amount of child comments and authors of the child comments and add
 * the values to the preprocess variables.
 */
function my_module_preprocess_comment(&$variables): void {
  $cid = $variables['comment']->id();
  $child_cids = my_module_get_child_comments_recursively($cid);
  $variables['child_comments_count'] = count($child_cids);
  $variables['child_comments_authors_count'] = 0;
  // If child comments exist, count the number of comment authors.
  if ($child_cids) {
    $query = \Drupal::database()->select('comment_field_data', 'cfd');
    $query->addField('cfd', 'uid');
    $authors_count = $query
      ->condition('cid', $child_cids, 'IN')
      ->distinct()
      ->countQuery()
      ->execute()
      ->fetchField();
    $variables['child_comments_authors_count'] = $authors_count;
  }
}

/**
 * Get child comments and child comments of child comments.
 *
 * @param string $pid
 *   The ID of the comment for which we get child comments.
 * @param array $child_cids
 *   Array of child comment IDs.
 *
 * @return array
 *   Array of child comment IDs.
 */
function my_module_get_child_comments_recursively(string $pid, array &$child_cids = []) {
  $query = \Drupal::database()->select('comment_field_data', 'cfd');
  $query->addField('cfd', 'cid');
  $result = $query
    ->condition('pid', $pid)
    ->execute()
    ->fetchCol();
  $child_cids = array_merge($child_cids, $result);

  foreach ($result as $cid) {
    my_module_get_child_comments_recursively($cid, $child_cids);
  }
  return $child_cids;
}

Use new variables in comment templates like "comment.html.twig" or "comment--comment--CONTENT_TYPE.html.twig": 

{{ content }}
{{ 'Child comments:'|t }} {{ child_comments_count }}
{{ 'Authors:'|t }} {{ child_comments_authors_count }}

I don't see a solution without a custom code. If you have the budget for it, you can contact me via the contact form or email: wombatbuddy [at] gmail.com

show the link to the webform in a block with the word 'outstanding' next to it.

Do you mean you want to show a link to a node with a webform attached? 

The are two different entity types: webfrom and webform_submission. Also, a webform can be attached to a node, see "Attaching webforms to nodes"
What is your use case? What data do you need to fetch?

You can export the field storage configuration, and edit it and add allowed values. After that you can import it back.

1. Visit /admin/config/development/configuration/single/export
2. Select the "Field storage" from the "Configuration type" listbox.
3. Selects the configuration name from the "Configuration name".
4. Add allowed values below the "allowed_values" key.
5. Copy the code of the configuration.
6. Visit /admin/config/development/configuration/single/import
7. Select the "Field storage" from the "Configuration type" listbox.
8. Paste the code of the configuration to the "Paste your configuration here" field.
9. Click on the "Import button" button.

I need more info to reproduce the issue.

Dima, I repeat that I have fixed the issue, see the home page of the Views alias filter project.

You can try to apply the "render" filter. But we need more info to reproduce your issue (what is node type, what is field type and field name, what is the template name etc.).

1. Visit /admin/config/development/performance and click the "Clear all caches" button. 
2. Reload the view page. 
If after that the order of the records changes, then the reason is in caching of the view. You can open the "ADVANCED" section on the view settings page and setup the "Caching" (to set "None" or "Time-based"). Also, you can use the  Views Custom Cache Tags module.

In my default Olivero theme all labels have the same font. You can try to look at the class attribute of the label element and then search it in CSS files. For example if the class attribute is "field__label", you can try to search ".field__label" class in CSS files. If you'll find that for this cass set another font, you can change it.

The code should do what you want.

You can use the getPossibleOptions() method. 

/**
 * Implements hook_preprocess_HOOK() for node templates.
 */
function my_module_preprocess_node(&$variables) {
  $current_user = \Drupal::currentUser();
  $node = $variables['node'];
  $variables['cat_type_labels'] = '';
  if ($node->hasfield('field_cat_type') && !$node->get('field_cat_type')->isEmpty()) {
    /** @var \Drupal\options\Plugin\Field\FieldType\ListStringItem $listStringItem */
    foreach ($node->get('field_cat_type') as $listStringItem) {
      $value = $listStringItem->getValue()['value'];
      $possibleOptions = $listStringItem->getPossibleOptions($current_user);
      $label = $possibleOptions[$value];
      $variables['cat_type_labels'] .= $label . ", ";
    }
  }
}

Create the controller and use the following code to get urls of the pages started with "/admin/mydash/". After that use them as values of the "href" attribute of links and render the links on the page. 

$database = \Drupal::database();
$query = $database->query("SELECT path FROM {router} WHERE path LIKE '/admin/mydash/%'");
$result = $query->fetchCol();
foreach ($result as $path) {
  // $path is the value of the "href" attribute of the link to the page.
}

1. Ensure that the "custom_module" is enabled.
2. Ensure that the "getItems.php" is present in the "custom_module/src/Controller" folder.
3. Rebuild the cashes.

Recommendation

1. Use PascalCase notation for class names, see PascalCase except Acronyms/Initialisms.
2. Use "drush generate" commands for generation controller, service and and other things.

If you want to get rid of the "destination" query string you can do it like this:

Forms
since Drupal 10.2 

$form_state->setIgnoreDestination();

For earlier versions 

\Drupal::request()->query->remove('destination');

Controllers, etc. 

\Drupal::request()->query->remove('destination');
\Drupal::destination()->set(NULL);

It looks like it's not possible to create a solution without custom code. But if you have some budget for it, I can create a custom module to solve this task. You can contact me via the contact form or by email: wombatbuddy[at]gmail.com

You can override this service, see
1. Using Symfony Service Decorators in Drupal 8.
2. Altering existing services, providing dynamic services .
If you need assistance and you have a budget for it, you can contact me.

If you need a solution with a custom module, you can contact me by contact form or via email: wombatbuddy[at]gmail.com

Hello,
if you have a budget for it you can contact me by contact form or via email: wombatbuddy[at]gmail.com

Hello,
if you have a budget fot it you can contact me by contact form or via email: wombatbuddy[at]gmail.com

If you have a budget for it you can contact me by contact form or via email: wombatbuddy[at]gmail.com

If you have a budget for it, you can contact me by contact for of via email: wombatbudy[at]gmail.com

One option is to create a custom Views filter. See
1. Custom Views filter plugin in Drupal 9.
2. Creating a custom Views filter in Drupal 8.
3. Creating Custom Exposed View Filters In Drupal 8 For Workflow States.
Also, you can try to create several exposed filters but uncheck the "Expose this filter to visitors, to allow them to change it" option. Then you can create a form and in the submit form handler store the selected value in Private Temporary Store Service. After that you can set filters value programmaticaly using hook_views_pre_view() or  hook_views_pre_build(). Alternatively you can use the Webform module and extract the value in hooks from URL, see How to create a custom Views exposed filter form.

It seems not possible to accomplish it with hooks. Maybe the easy solution is to alter the source files using a text editor's search and replace option. But if you need a solution with a custom code you can contact me.

What is the use case? 
You can look at the following example: https://jsfiddle.net/destan/e9gjwodL

Production build 0.69.0 2024