Quantcast
Channel: Issues for Drupal core
Viewing all 291646 articles
Browse latest View live

[META] Hooks via attributes on service methods (hux style)

$
0
0

Problem/Motivation

Currently in Drupal we have the old procedural hook system, we have symfony events, and we have tagged services and service collectors, to allow different modules or components broadcast or respond to "events" (in the broader sense).

The procedural hook system has a number of problems and limitations (some already mentioned in #2237831):

  • Possibility of name clashes / ambiguity, e.g. "field" + "_group_permission" vs "field_group" + "_permission".
  • No dependency injection: Hook implementations have to call \Drupal::service(...) to get their dependencies.
  • Most implementations are in *.module files, which can get crowded, make it hard to organize code, and can easily cause git merge conflicts, if multiple branches attempt to add new hook implementations.
  • There can be only one implementation per module (or the module has to "cheat", and pretend it is implementing on behalf of another module, one that might not even exist).
  • The procedural code feels out of place compared to the rest of Drupal.

Alternatives:

  • symfony events require a dedicated event class per event type instead of named parameters, which "creates additional boilerplate when defining events and additional levels of indirection when subscribing to events." (quote from #2237831). See #2551893: Add events for matching entity hooks
  • tagged services and services collectors typically require a dedicated interface per "event type", and subscribers (= tagged services) to implement that interface.

None of these "alternatives" is a good candidate to fully and generically convert the existing hook system. All past attempts to do so (that I am aware of) only focused on very specific hooks, or families of hooks.

Proposed resolution

The Hux module allows to mark methods on services classes as hook implementation, using php attributes.

Something similar should be added in Drupal core, so that existing hook implementations can be converted to OOP code.

This seems to be the current consensus in #2237831: Allow module services to specify hooks.

class MyService {
  #[Hook('node_insert'), Hook('node_update')]
  public function nodeWrite(NodeInterface $node): void {..}
  #[Alter('form_node_edit_form')]
  public function nodeFormAlter(array $form, FormStateInterface $form_state): void {..}
}

Goals / requirements

Backwards support:

  • The first iteration of this should be fully compatible with existing procedural hook implementations and also on the calling code side.
  • There will be a mix of procedural and non-procedural implementations.
  • The old hook_module_implements_alter() will still work on the list of procedural implementations, but a new hook (or event?) could be added to reorder or alter the collection that includes OOP implementations.
  • The behavior and return value of any call to $module_handler->invoke() or $module_handler->invokeAll() etc...
    • ...must remain unchanged, if no new implementations were added.
    • ...must remain unchanged, if existing implementations are merely converted to the new system.
    • ...must only gradually change, if new implementations are added using the new system.
  • Modules can start implementing existing hooks using the new system, without any change in the module that publishes or invokes the hook.

Basic new features:

  • Methods on service classes can subscribe to hooks using attributes like #[Hook('node_update')].
  • One method can implement more than one hook.
  • Multiple methods in the same module can implement the same hook, resulting in multiple implementations per module.
    This introduces some challenges with return value of single $module_handler->invoke(..), see below.
  • There must be a mechanism to order different implementations. E.g. in hux there is a system of weights / priorities, but in core we might do something different.
  • There must be a mechanism to replace or remove existing implementations.
  • For the two requirements above, it might be necessary to have unique machine names for individual implementations.

Optional or advanced features:

  • There can be a discovery mechanism to register classes with hooks as services. E.g. in hux, this happens if these classes are in a Drupal\\$modulename\\Hooks namespace.
  • Modules can add additional attribute classes, to let the method implement one or more hooks. E.g. #[HookEntityOp(['node', 'taxonomy_term'], ['update', 'insert'])] would implement 4 different hooks.
  • Modules can add other callbacks as hook implementations, that don't rely on php attributes, and that can be added dynamically.
  • We might add additional methods to ModuleHandler, with more control of the results aggregation for single ->invoke(). See "Challenges" below.
  • We might add hooks (or anoother mechanism) to control the default results aggregation per hook name.

Challenges

  • Return value of single $module_handler->invoke():
    • Currently, it returns the return value of the one single implementation. The behavior for this case must not change!
    • With multiple implementations that return arrays, e.g. for hook_schema(), we would expect the results to be merged. Any null values would be skipped, if the "collected data" is already an array.
    • For more than one non-array non-null values, or a mix of array and non-array values, an exception could be thrown, because these cannot be merged, unless an aggregation method is specified somewhere.
  • Design a system to define order of implementations, and to remove or replace implementations. Options:
    • Before/after rules, as proposed for events in #2352351: Add before/after ordering to events
    • Numeric weights/priorities, as currently in hux.
    • A hook or similar to alter the list of implementations, similar to hook_module_implements_alter(), but with support for callbacks.
    • Extending hook_module_implements_alter() in a BC-friendly way. This could be tricky.

Underlying architecture

The module handler ->invoke(), ->invokeAll() etc should operate with a list of callbacks, that can come from different sources.
The methods with attributes will be one such source, but it should be flexible enough to support other sources.

Proposed roadmap / target versions

  • Study and play with the hux module as a proof of concept.
  • Build the new system in latest version of Drupal 10 (10.2.x). Do not change the existing implementations and calling code, yet.
    This allows contrib to start using the new system, while minimizing disruption from upgrading to the new core version.
    Any possible BC conflict will only apply if there is a module that uses the new system.
  • Start converting existing implementations in a future version - 10.? or 11 - depends how disruptive this turns out to be.
  • Add additional changes that are not backwards compatible, consider to deprecate procedural hooks, in Drupal 11.

Remaining tasks

User interface changes

API changes

Methods in service classes can be marked as hook implementations using attributes.
Details are provided above.

Data model changes

Caches for the new kinds of implementations and discovery data.

Release notes snippet


Fix awkward details in hook system

$
0
0

Problem/Motivation

The hook system has some awkward details that make refactoring and improving it harder.
Especially, these force some complex tricks in #3366083: [META] Hooks via attributes on service methods (hux style)#3368812: Hux-style hooks, proof of concept
Some of this can be cleaned up without major BC breaks.

Single module ModuleHandler->invoke()

One should think that this is equivalent to just calling the one implementations from ->invokeAll(), that matches the given module.
With the huxified system, it would mean _any_ implementation that is associated with that module.

However, this is not fully the case:
If the function $module . '_' . $hook exists at the time this is called, then it is always called, even if it is not part of the (cached) list for ->invokeAll(). The latter can be the case if:
- The implementation was removed through hook_module_implements_alter(), OR
- The file that contains the function (e.g. *.install) was not included at the time of discovery, OR
- The module that owns this implementation is not currently enabled - this is the case for hook_requirements().

The hook_module_implements_alter() has limited effects on single ->invoke():

  • Reordering of implementations has no effect, because there is only one function.
  • Removal of implementations has no effect, because the function is still called.
  • Anything that causes a file with the $module . '_' . $hook function to be included, if it would otherwise not be, does have an effect. This generally means that hook_module_implements_alter() has to add a module with include group, e.g. $implementations['mymodule'] = 'mygroup'. This could even be another module that implements the hook on behalf of the module being called.

In fact, most of the hooks that are called with ->invoke() are never called with ->invokeAll().
This means we could consider to handle them completely separately.

Removal of missing functions from the cached list

If a list of implementations is loaded from cache, a function_exists() check is performed on every cached implementation.
If the function does not exist, it is removed from the list, and the updated list is later written to the cache.

The only use case I can think of is during development, if the developer temporarily checks out another git branch, or temporarily removes a function.

The practice of writing the updated list to the cache is questionable.
If you switch back to the main git branch, the function that was accidentally added is now gone from the cache.

Removing this mechanism would make things much easier in #3366083: [META] Hooks via attributes on service methods (hux style).

Removal or adding of modules

Currently it is possible to add or remove modules with ModuleHandler->addModule(), ->addProfile() and ->setModuleList().
This is a bit problematic with #3366083: [META] Hooks via attributes on service methods (hux style), because the services in the container where the ModuleHandler instance comes from is still based on the old module list. So we can add hook implementations for the newly added module, but the respective services won't be available at that time.

Note that this problem is only about adding ore removing modules.
Reordering modules via module_set_weight() is actually fine, because the order of hook implementations _can_ be dynamically changed.

Proposed resolution

Consider to simplify or remove some of these fragile mechanical details.

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

Non-hyperlink anchors (a elements) have hyperlink styling

$
0
0

Problem/Motivation

Steps to reproduce

  • Create a blank site, with Olivero default theme
  • Create a node with a format allowing the <a> element
  • In the body, add something like Foo
  • In the body, add something like Bar
  • Observe (cf. screenshot) they both carry Olivero's hyperlink "underline" styling.
  • The only hint is for those who can use one is the cursor styling not switching to the hand shape

a.name looking like a a.href

This is an accessibility issue: users will believe the a.name is a link and be frustrated to find it is not clickable until they notice the cursor shape.

The same problem exists in Bartik, but not in Claro.

Proposed resolution

Do not style a.name at all unless it is also a a.href. This is an attribute meant for browsers, not for user consumption.

Remaining tasks

Identify the source of this behaviour, and fix it.

User interface changes

Links which are no hyperlinks will no longer look like hyperlinks.

API changes

None.

Data model changes

None.

Release notes snippet

TBD.

ContentEntityType::getRevisionMetadataKeys() doesn't document the revision_default key

Node form with two file fields can cause mystery file deletions.

$
0
0

I am posting this issue a critical core bug because I believe it results in data loss. This issue is very closely related to #2869855: Race condition in file_save_upload causes data loss and that issue may be further along to the point this is simply closed this, but considering the circumstances around these bugs are different, I thought it would make sense to post separately.

Problem/Motivation

We have been dealing with mystery "deleted documents" on our site for a few months now, and have traced the issue back to what we believe is the problem. The cron setting to delete temporary files is ACTUALLY deleting our production files that are in use on active revisions. We believe, and have done some experimentation to prove a scenario that fits this hypothesis, that at some point during the file upload process (node updates), there are 2 file entity entries being created for a single node revision, 1 permanent, and 1 temporary, both with the exact same file URI. Cron comes around to clean up files, finds the temporary entry, and deletes the file at that file location, and you now have an orphaned permanent file entry that does not point to a valid file on the server.

The scenario where we believe this happens...
We have a node with 2 file fields, one for a document (PDF/Word/etc), and one for an image. Consider a node that has both fields filled in.

  1. A content manager goes into the node edit screen, and needs to add a new PDF and remove the old image, a very common practice for our content team.
  2. The CM will click the "Remove" button for the existing PDF, and is then presented with a new file upload field. The user browses to the new file and selects it to be uploaded, which triggers an AJAX request to the server.
  3. The user then clicks the "Remove" button for the existing image. This initiates another AJAX request to the server. If the previous file upload response has not yet been received by the page, the payload in the "remove image" request will ALSO include the file upload field from the previous step.

Step 3 above can cause Drupal to create a 2nd temporary file with the exact same URI as the request in step 2, but with a different fid. When you actually SUBMIT the node edit, only 1 of those fid's is referenced on the form, and only that file gets converted from temporary to permanent, but there is a temporary file left behind.

We are estimating this happens to our content managers maybe 1 out of 40 documents or so uploaded. So it's not every time, but obviously the deletion of any production data is a very serious problem.

We have replicated this problem with by using a delay in a hook_file_validate method to simulate a slower file upload. While our production code has no such delays, we have guessed maybe some kind of network latency, or files coming from shared drives, or various other scenarios could maybe be the culprit, but don't have concrete evidence as to WHAT is causing the delay. Regardless, it seems there should be code put in place to prevent this possibility from happening.

The related issue here seems to take a similar approach as well, using a test with a file unlinked in code to show it's possible. There seem to be some frontend issues in that case, documented in comment #59 over there that may not be considered while fixing that problem. Part of the reason to open this new issue is just to provide considerations for this front-end approach to encounter that error.

Proposed resolution

Would disabling multiple AJAX file field operations from running at the same time be an option?
Should the back-end storage of files be fixed? (which may be done in the linked issue)
Other?

Remaining tasks

N/A

User interface changes

N/A

API changes

N/A

Data model changes

N/A

SDC CSS <link> definition render order

$
0
0

Problem/Motivation

The SDC component CSS <link> definitions are rendering before theme libraries CSS, even where they're keyed as base or theme. I have an example where a button in an SDC has styles that are being overridden by a reset base library that's rendered after the SDC.

Steps to reproduce

Enable SDC, create an SDC component in your default base theme. Create and include SDC component. Create a library with CSS with a base weight. Observe the SDC component.css rendering prior to the theme's reset.css.

style:
  version: VERSION
  css:
    base:
       reset.css: {}

PostCSS layout helpers inconsistency

$
0
0

Problem/Motivation

In the Olivero theme, there is an inconsistency in the use of spacing variables or layout helpers in the PostCSS files. While variables.pcss.css defines spacing variables like --sp1, --sp2, --sp3, etc., based on the variable --sp.

there are instances in some pcss files where values are directly assigned the `calc(2 * var(--sp));` value instead of using the equivalent --sp2. This inconsistency occurs for different versions of the layout helpers across several PCSS files.

I am mentioning --sp2 as an example but the situation is with different version of the layout helpers.

Steps to reproduce

1. Open the PostCSS files in the Olivero theme.
2. Check for occurrences of the full formula instead of the corresponding variable.

Example in file core/themes/olivero/css/components/header-search-narrow.css line 47:

height: calc(3 * var(--sp));

Proposed resolution

To maintain consistency and readability, all instances of `calc([number] * var(--sp));` should be replaced with the equivalent --sp[number]. The same applies to other versions of the layout helpers. This will ensure that the usage of spacing variables is consistent across all PCSS files in the Olivero theme.

Test pages

The changes should be reflected in all pcss files of the Olivero theme.

Before/After screenshots

Screenshots are not applicable for this issue as it's a code consistency issue.

Markup changes

The markup changes involve replacing instances of `calc(2 * var(--sp));` with --sp2 (and similar replacements for other versions of the layout helpers) in the relevant PCSS files.

Cross browser tests

Cross-browser testing is not applicable for this issue as it's a code consistency issue.

No related issues.

Remove dependency of "file_system" service on "logger"

$
0
0

Problem/Motivation

Currently, file_system service depends on three other services:

  file_system:
    class: Drupal\Core\File\FileSystem
    arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file']

Inside of Drupal\Core\File\FileSystem class, instance of logger is used only once in the chmod() method to log error when chmod() PHP function returns FALSE.

Not only this is a side effect, but an instance of logger.channel.file contains references to other services: requests stack, current user object. And from the source of Drupal\Core\Logger\LoggerChannel::log() method, it looks like it can even execute a database query.

Logger makes dependency tree of file_system service unnecessary complicated, and I would expect such a low-level service to have as little side-effects as possible.

Proposed resolution

Solution 1

Remove dependency on logger service and throw ChmodFailedException instead of logging. Handle this exception (or not) in all places in core where chmod() method is called, which is just 3-5 in the whole core.

Solution 2

Silently return FALSE from chmod() method and move logging next to method invocations if it is necessary.

Remaining tasks

Decide which API change is better; and if it is needed at all.

User interface changes

None.

API changes

Change of Drupal\Core\File\FileSystem::__construct() signature. New ChmodFailedException exception class.

Data model changes

None.


Change PathMatcher::matchPath() $patterns param from string to an array of strings

$
0
0

Problem/Motivation

In #2272201: Move drupal_match_path in path.inc to a service we converted the procedural drupal_match_path and kept the implementation exactly the same. However, taking a string of patterns separated by newline isn't the best interface for this as it's tied to how the form values are submitted currently.

Proposed resolution

Convert the $pattern parameter to an array of strings.

Remaining tasks

Wait until #2272201: Move drupal_match_path in path.inc to a service goes in.

User interface changes

API changes

PathMatcher::matchPath() takes an array of strings instead of a string.

Switch Node revision UI to generic UI

$
0
0

Problem/Motivation

From #2350939: Implement a generic revision UI, mentioned in summary/96/103/108/109/110.

To reduce duplicate code and maintenance burden, we should aim to rework Node's revision UI so it extends generic entity UI implemented in #2350939: Implement a generic revision UI

A generic revision UI is being implemented in #2350939: Implement a generic revision UI, this issue exists to deprecate/remove Node's revision UI in favor of the generic solution.

Proposed resolution

Remaining tasks

  • Replace Node' revision UI code, extending generic revision UI.
  • Can we remove Node test coverage which already exists for generic revision UI?
  • Specific enhancements between generic UI and existing Node UI need to be identified, and add tests for this functionality.
  • Decide whether to keep yellow highlight, see #23 and linked comments
  • Move use admin theme out of node and into system. migrate config/schema. update _admin_route setoption in \Drupal\Core\Entity\Routing\RevisionHtmlRouteProvider::getVersionHistoryRoute to use the config value.

User interface changes

Issue aims to replicate existing interface.

API changes

Class names should be maintained, but extend generic classes.
Format of node render arrays and forms will change.

Data model changes

None?

Release notes snippet

-

Add Media revision UI

$
0
0

Problem/Motivation

Media entities are revisionable but it's not possible to:

  • view a revision
  • revert to a revision
  • delete a specific revision
  • see a diff of revisions, if the contrib Diff module is enabled.

Proposed resolution

Introduce the revisions tab, like we have it for node.

Remaining tasks

Also introduce 'view all revisions', 'revert all revisions' and 'delete all revisions' permission.

User interface changes

API changes

Data model changes

Add taxonomy term revision UI

$
0
0

Problem/Motivation

Taxonomy term entities will be revisionable after #2880149: Convert taxonomy terms to be revisionable but it's not possible to revert to a revision or delete a specific revision.

Proposed resolution

Introduce the revisions tab and show the revision fields in the term form, like we have it for node.

Remaining tasks

Wait for #2350939: Implement a generic revision UI, write a patch, etc.

User interface changes

Taxonomy terms will have a revision UI similar to nodes and media items.

API changes

Nope.

Data model changes

TBD.

Nested modals don't work: opening a modal from a modal closes the original

$
0
0

Issue Summary

  • #46 Describes the problem as to why the issue is happening which generally narrows down to hard coding of html ids for modal opening and closing command.
  • #48 Provides a solution so that each open and close command can work with separate modal ids, It also provides a solution for media library form.
  • #73 Extracts the open and close command modifications from #48 so that it can be merged with core first and then for each individual case a separate issue and patch can be added.

Proposed resolution

  • Review and merge #73 or #48 in core.
  • Create individual issues for each possible use case like media library, Ckeditor etc.
  • Add the change records.

Original Post

I am currently looking into running CKEditor in a modal dialog using the Drupal Modal API.

At this moment it is working OK, but there is a problem with the link and image modals which can be triggered from the CKEditor interface. Those also use the Modal API and when triggering one of these functions, the modal containing CKEditor is fully closed.

Is there any way to open de link/image modal without closing the CKEditor modal? It seems like it is possible to define the HTML element to which the modal should append when triggering. When overriding this setting ('appendTo' => '#foo'), even the link/image modals are appended to #foo (instead of the default '#drupal-modal').

Any ideas on how to get this to work?
Thanks for your support.

Entity reference selection handlers do not check the "create" access for autocreated entities

$
0
0

Problem/Motivation

When using an autocompletion element in an entity reference field widget, there is a feature to create entities automatically, in case the typed in labels of the entity aren't found. This is most commonly used to allow users to create new taxonomy terms if no matching ones are found.

While this makes sense for terms, it's a bit awkward for other entity types that can be created using only a label. Since many sites likely rely on this behavior, the fix should take into account existing field widgets and not require sites to grand wide sweeping permissions to what are now "low" or even anonymous uers.

Steps to reproduce

Give a user access to an autocomplete entity reference field widget that references an entity type / bundle they do not have access to, and make sure to enable the autocreate feature. They should be able to create entities automatically by using the widget since the "create" access check isn't being performed.

Proposed resolution

Perform the "create" access check before creating new entities during entity reference autocomplete, without breaking existing sites.

Remaining tasks

Discuss impact on existing sites, write a patch.

User interface changes

None, unless this is opt-in in the field widget config level.

API changes

TBD

Data model changes

TBD

Release notes snippet

Bring back overriding breadcrumb with contextual filter

$
0
0

In Drupal 7, it was possible to override breadcrumb by a contextual filter. That feature is missing in Drupal 8. Maybe in Drupal 9 too, but I am yet to try Drupal 9.

Override breadcrumb in D7

Not sure any resolution happened already on it. I did not get any result so far.

It would be good if we get that feature back.


[ignore] Patch testing issue - ranjith_kumar_k_u

$
0
0

Problem/Motivation

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Release notes snippet

[IGNORE] Patch Testing

$
0
0

Patch Testing issue please ignore.

UI fatal caused by views argument handlers no longer can provide their own default argument handling

$
0
0

Problem/Motivation

Some argument handlers provide their own default argument handling, like the Date argument. The Date argument handler does so by extending the list of of options in the defaultArgumentForm. An exception is however thrown on saving the form.

Steps to reproduce

  • Create a new view /admin/structure/views/add using (node) content
  • Under advanced tab, add new Contextual filter, select Created date (node.created)
  • On "When the filter value is not available", select "Provide default value", on the drop-down select "Current date".
  • Try to submit this form.

Result: A fatal error occurred on dblog: Drupal\Component\Plugin\Exception\PluginNotFoundException: The "date" plugin does not exist.

Proposed resolution

Get the date handlers in line with the other default argument handlers.
Add a 'Current date', a "Current node's creation time" and "Current node's update time" default argument handler.

Remaining tasks

  • Add a follow-up for improving caching - see #114
  • Add a follow-up to make this work for all entity-types using a deriver - see #176
  • Confirm if this works with date time range fields - see #157

User interface changes

API changes

Data model changes

Release notes snippet

Problem

Steps to reproduce:

Proposed resolution

Select's #empty_option gets removed on ajax callback

$
0
0

Problem/Motivation

It was discovered in #2577219: Single item configuration export form config_name does not have "- Select -" as it's first option that select element adds #empty_option in Select::processSelect. However, if a select element is updated (re-rendered?) on an ajax callback, the #empty_option gets removed.

Steps to reproduce

Create a form with the following in buildForm

  $form['ajax_parent_select'] = [
      '#title' => $this->t('Parent select with Ajax callback'),
      '#type' => 'select',
      '#options' => ['one' => 'One', 'two' => 'Two'],
      '#default_value' => 'one',
      '#ajax' => [
        'callback' => '::ajaxCallback',
        'wrapper' => 'child-wrapper',
      ],
    ];
    $form['ajax_child_select'] = [
      '#title' => $this->t('Child select'),
      '#type' => 'select',
      '#options' => ['three' => 'Three'],
      '#empty_option' => $this->t('- Select -'),
      '#prefix' => '<div id="child-wrapper">',
      '#suffix' => '</div>',
    ];

Where ajaxCallback looks something like this

  public function ajaxCallback($form, FormStateInterface $form_state) {
    $options = [
      'one' => ['three' => 'Three'],
      'two' => ['four' => 'Four'],
    ];
    $form['ajax_child_select']['#options'] = $options[$form_state->getValue('ajax_parent_select')];
    return $form['ajax_child_select'];
  }

When ajaxCallback fires, it removes #empty_option during re-rendering of ajax_child_select

This has been demonstrated here

Proposed resolution

Add #empty_option in a #pre_render callback

Remaining tasks

Provide patch to fix the issue.

Write a test to prevent regression.

Code review and validate the used approach.

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

None

Allow plugin derivers to specify cacheability (tags, contexts, max-age) for their definitions

$
0
0

Problem/Motivation

Plugin derivers are essentially a foreach loop for providing plugin definitions.
Typically they iterate over the result of some other service, creating a definition for each item.
If that list of items changes, the plugin definition cache must be cleared so that the deriver can accurately reflect the up-to-date list.

In practice, this has meant that another piece of code, like a hook implementation, will manually clear the plugin definition cache.

For example, \Drupal\system\Plugin\Derivative\SystemMenuBlock loops over all menu entities to provide menu blocks.
\Drupal\system\Entity\Menu::save() and \Drupal\system\Entity\Menu::delete() are then responsible for clearing the block plugin definition cache.

Proposed resolution

Plugin managers already support the use of cache tags for their definitions cache.
Allow the derivers themselves to affect the list of cache tags used.
This will remove the need for external code (like a hook) to clear the cache.

Remaining tasks

User interface changes

API changes

Data model changes

Viewing all 291646 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>