@hockey2112: Yes, that's the intended purpose of this module. Unfortunately, the project stalled internally.
I am not sure if a lot can be done inside the ICO module to handle this case better, since it doesn't impact how configuration files get discovered by Core. It appears that Core caches only a single path for each config, so solving this problem might require a custom InstallStorage
implementation that changes the way that getAllFolders()
builds its cache under the hood.
As a workaround, an update hook could write the desired configuration directly into configuration storage using a utility method like this:
/**
* Import a config from an explicit file path into database storage.
*
* @param string $config_name
* The name of the config item to import from the config folder.
* @param string $config_path
* The path to the config folder.
*
* @return bool
* TRUE on success, or FALSE if the config could not be installed.
*/
function my_module_import_single_config(string $config_name, string $config_path): bool {
$source = new FileStorage($config_path);
$config_storage = \Drupal::service('config.storage');
assert($config_storage instanceof StorageInterface);
return $config_storage->write($config_name, $source->read($config_name));
}
This is risky, since the config does NOT get validated. A safer version could look like this:
/**
* Import a config from an explicit file path by its entity type and ID.
*
* @param string $config_path
* The path to the config folder.
* @param string $entity_type
* The machine name of the configuration entity being imported.
* @param string $id
* The ID of the configuration being imported.
*/
function my_module_import_single_config_safer(string $config_path, string $entity_type, string $id): void {
$source = new FileStorage($config_path);
try {
$config_full_name = _arkayo_am_get_full_config_name($entity_type, $id);
$config_storage = \Drupal::entityTypeManager()->getStorage($entity_type);
// _arkayo_am_get_full_config_name() should have thrown an exception on the
// entity type by now if this assertion does not hold.
assert($config_storage instanceof ConfigEntityStorageInterface);
$config_data = $source->read($config_full_name);
$entity = $config_storage->createFromStorageRecord($config_data);
$entity->save();
}
catch (PluginNotFoundException | InvalidPluginDefinitionException $ex) {
throw new InvalidArgumentException(
sprintf(
'Invalid configuration entity type ("%s"): %s',
$entity_type,
$ex->getMessage()
)
);
}
catch (EntityStorageException $ex) {
throw new InvalidArgumentException(
sprintf(
'Failed to import configuration to "%s": %s',
$config_full_name ?? sprintf('%s.%s', $entity_type, $id),
$ex->getMessage()
));
}
}
I rewrote the description of this issue because I wasn't 100% sure what the original description was alluding to and I just ran into this issue when reverting a config as part of an existing module that uses install-time config overrides.
@dineshkumarbollu: I appreciate that you were trying to move this issue forward, but unfortunately you did not submit working code, so I cannot give you credit for the fix. For future MRs, I would recommend testing code thoroughly locally before submitting it for review. I would have been happy to grant credit if the code was functional.
Instead, I ended up having to start over with my own fix in the issue fork.
GuyPaddock → made their first commit to this issue’s fork.
Looks like the original issue is no longer reproducible even without a patch.
GuyPaddock → created an issue.
No longer cleanly applies on 1.0.0-alpha5. Setting to NW.
GuyPaddock → made their first commit to this issue’s fork.
No credit for reviewers, eh? Oh well. :/
Updated for 11.x. Ready for re-review.
GuyPaddock → changed the visibility of the branch 2796341-entity-reference-field to hidden.
Updated for 10.1.x.
Just updated the branch for 9.5. My next update will update this for 10.1.x.
Linking related issues.
I reviewed the MR feedback (thank you, @ameymudras). I've ticketed
📌
Replace \Drupal calls with proper IoC in form elements
Active
under the
#2729597: [meta] Replace \Drupal with injected services where appropriate in core →
meta issue. That issue should be appropriately scoped to rework form elements like the Entity Auto-complete text field to use DI. I am moving both \Drupal::service('plugin.manager.entity_reference_selection')
calls in the class into a utility method and adding a todo that references #3423053. I believe that to be the most prudent way to avoid bloating the scope of this issue.
Regarding your feedback, @liquidcms, I agree with what I believe @dqd is saying -- the scope of this issue is to fix what value appears in the autocomplete field when a view is being used to drive selections. The question of whether an ID should be suffixed or not is outside the scope of this issue. The issue you are looking for is 🐛 UX: Hide entity ID in autocomplete widget Needs work and until that issue lands, the implementation in this ticket is consistent with both the way that the ID appears during initial selection with a view as well as autocomplete fields that are not driven by a view.
As for your other question, @dqd, it seems like you found a solution to your problem with IEF. Regardless, I have cleaned up the issue description to clarify that this issue is specific to autocomplete text fields. Please let me know if that addresses your issue, since IEF is completely separate.
GuyPaddock → created an issue.
Taking on additional rework.
Tests added. This is finally ready for review!
I was just trying to write a functional test that asserts that a log message emitted by my SUT is correct and ran smack into this issue. I gave the patch a try and have not had much success. I might be in the minority here, but I found the description and instructions on the trait to be unclear:
/**
* Sets test expectations for generated log messages.
*
* A test class using this trait should:
* - ensure that AssertableLogger::log() is called when logs are generated,
* - provide a getAssertableLogger() method that returns that logger, and
* - call LoggingTrait::assertLogExpectationsMet(), typically in its
* assertPostConditions() method.
*
* In order to assert that a test does or does not generate logs, the test
* must call LoggingTrait::expectLog() or
* LoggingTrait::expectNoLogsAsSevereAs(); it may also call
* LoggingTrait::allowLogsAsSevereAs().
*/
Here's what is unclear:
- "ensure that AssertableLogger::log() is called when logs are generated" -- wouldn't the SUT be the one logging errors, not the test? Or is this a method I am supposed to call before or after the code I am testing? I do not understand what this means.
- "provide a getAssertableLogger() method that returns that logger" -- by that logger, what logger are we talking about?
- "In order to assert that a test does or does not generate logs, the test must call LoggingTrait::expectLog() or LoggingTrait::expectNoLogsAsSevereAs(); it may also call LoggingTrait::allowLogsAsSevereAs()." -- when must these be called? Before the code being tested? After it?
GuyPaddock → changed the visibility of the branch 3228797-fix-eof-on-empty-stream to hidden.
GuyPaddock → created an issue.
GuyPaddock → created an issue.
So far, I've been able to fix the concurrency issues and improve performance by reworking the methods as follows:
/**
* Installs Drupal into the Simpletest site.
*/
public function installDrupal() {
if (getenv('BROWSERTEST_CACHE_DB')) {
$this->initDumpFile();
$lock_file = $this->dumpFile . '.lock';
$this->runWithLockFile($lock_file, function () use (&$install_from_dump) {
if (file_exists($this->dumpFile)) {
$install_from_dump = TRUE;
}
else {
$this->installDrupalFromProfile();
$this->dumpDatabase();
$install_from_dump = FALSE;
}
});
// The check for the dump file happens inside the critical section to
// ensure it blocks while a dump is being created, but the installation
// happens outside the critical section so that multiple tests can use the
// same dump file concurrently.
if ($install_from_dump) {
$this->installDrupalFromDump();
}
}
else {
$this->installDrupalFromProfile();
}
}
/**
* Determines a proper file name for SQL dump.
*/
protected function initDumpFile() {
$class = get_class($this);
$modules = [];
while ($class) {
if (property_exists($class, 'modules')) {
$modules = array_merge($modules, $class::$modules);
}
$class = get_parent_class($class);
}
sort($modules);
array_unique($modules);
$temp_dir = sys_get_temp_dir();
$cache_dir = getenv('BROWSERTEST_CACHE_DIR') ?: $temp_dir . '/test_dumps/' . \Drupal::VERSION;
$this->dumpFile = $cache_dir . '/' . md5(implode('-', $modules)) . '.sql';
// Create a folder to contain the test database dumps, if it does not
// already exist. Only one test is allowed to create this at a time.
$lock_file = sprintf("%s/test_dumps-%s.lock", $temp_dir, \Drupal::VERSION);
$this->runWithLockFile($lock_file, function () use ($cache_dir) {
if (!is_dir($cache_dir)) {
mkdir($cache_dir, 0777, TRUE);
}
});
}
/**
* Acquires an exclusive lock on the specified file and runs the given code.
*
* This is used to ensure that only one test is executing the same code at the
* same time on the same runner. The lock is released after the critical code
* returns.
*
* @param string $lock_file_path
* The path to the lock file.
* @param callable $critical_code
* The code to invoke only once the lock is obtained.
*/
private function runWithLockFile(string $lock_file_path, callable $critical_code) {
$locked = FALSE;
$lock_handle = fopen($lock_file_path, 'w');
try {
if (($lock_handle !== FALSE) && flock($lock_handle, LOCK_EX)) {
$locked = TRUE;
$critical_code();
}
else {
$this->fail('Failed to acquire lock: ' . $lock_file_path);
}
}
finally {
// Delete the lock file before unlocking it to ensure we don't delete a
// lock created by another process.
if (file_exists($lock_file_path)) {
@unlink($lock_file_path);
}
if ($locked) {
@flock($lock_handle, LOCK_UN);
}
if ($lock_handle !== FALSE) {
@fclose($lock_handle);
}
}
}
An interdiff (against #48) is attached for the changes above, but I was curious about feedback on this approach before I apply them to the MR.
@ahmadhalah: "Needs review" is the status for if you have proposed code changes. I still believe your issue lies outside this module and that it would be better for you to work with the Azure API team instead, but I will keep this open for now.
@vegardjo This wrapper module doesn't do a whole lot on its own, so it should be easy for me to bump versions to pull it in.
Updating issue metadata to aid in discoverability.
@mkolar We're going to give this snippet a spin here for an internal API integration that needs to fetch role information from sites. One thing I noticed -- right now the snippet you provided doesn't confirm that the entity type is a user, correct? So, theoretically any roles
field in any entity would be impacted.
I'm thankful for the snippet, but just wanted to point out for others using the snippet that they might want some additional checks in their hook implementation.
Alternatively, what if there was a separate permission for "access reports overview"? It's a similar use case to wanting to give users access to the node overview page without granting them the ability to edit all nodes.
Our use case is that we are adding some additional reports that we want some of our content editors to use but we don't want to give them the permission to view all the developer-facing reports that Drupal ships with.
Actually, Nathan, I've pushed the latest copy of what we have been using in-house to the new 1.x dev release. Ideally there would be more cleanup that my team would want to do with this to make the widget easier to maintain and to address stylelint issues. But, we've been using this code in production since 2020, so I figure it's time we share it.
Happy holidays!
@Nathan: Not sure if this will be in time for your project, but we did implement this a few years back in our proprietary code base and I intend to extract it and publish it here within 30 days.
shiva369 and @adstokes: Is there any chance you've got a firewall that's limiting outbound traffic?
@Dave Reid: Release cut! Please let me know if that works for you.
@adstokes: Since you mentioned being blocked by the Guzzle dependency, can you try with the latest dev? I just merged 📌 Automated Drupal 10 compatibility fixes Fixed .
Resolved by 📌 Automated Drupal 10 compatibility fixes Fixed .
LGTM
Fix cringe-worthy formatting of the YAML in the multiple tags example.
Cleaned-up examples:
- Clarified documentation on what each value does.
- Added "default_values" and "values" to the example of "all possible configuration parameters", since these were missing. (There may be other values that are missing from this example as well, so perhaps we should not call it the list of all possible parameters.)
- Fixed bad documentation about what "default_values" does in the node case.
- Added an additional example of looking-up or creating a user.
Can someone confirm that there are tests for this behavior in 10.x? If not, I can at least reroll for the added test coverage.
+1 for this issue solving an area of frustrating DX.
Repro:
- I created a paragraphs type.
- I created a content type that has a paragraphs field that can reference items of the paragraphs type I created in step 1.
- I created a new view of paragraphs.
- I added a required relationship to the view that links paragraphs to content that references the paragraph.
- I added a content type filer to the view that uses the relationship added in step 4.
- I deleted the relationship that I had added in step 4.
At this point, the Views UI stopped responding -- I couldn't even get the dialog that I was using to edit/delete the relationship to close. Upon refreshing the edit form for the View, I got a WSOD with these stack traces:
The website encountered an unexpected error. Please try again later.
InvalidArgumentException: A valid cache entry key is required. Use getAll() to get all table data. in Drupal\views\ViewsData->get() (line 140 of core/modules/views/src/ViewsData.php).
Drupal\views\Plugin\views\HandlerBase->getEntityType() (Line: 93)
Drupal\views\Plugin\views\filter\Bundle->init(Object, Object, Array) (Line: 904)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('filter') (Line: 2532)
Drupal\views\Plugin\views\display\DisplayPluginBase->validate() (Line: 2127)
Drupal\views\ViewExecutable->validate() (Line: 128)
Drupal\views_ui\ViewFormBase->getDisplayTabs(Object) (Line: 49)
Drupal\views_ui\ViewFormBase->prepareEntity() (Line: 125)
Drupal\Core\Entity\EntityForm->init(Object) (Line: 26)
Drupal\views_ui\ViewFormBase->init(Object) (Line: 96)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object) (Line: 41)
Drupal\views_ui\ViewFormBase->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 534)
Drupal\Core\Form\FormBuilder->retrieveForm('view_edit_form', Object) (Line: 281)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object, 'edit', Array) (Line: 230)
Drupal\views_ui\Controller\ViewsUIController->edit(Object, NULL)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 163)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 74)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 681)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
Warning: Undefined array key "reverse__node__field_poll_choices" in Drupal\views\Plugin\views\HandlerBase->getEntityType() (line 720 of core/modules/views/src/Plugin/views/HandlerBase.php).
Drupal\views\Plugin\views\HandlerBase->getEntityType() (Line: 93)
Drupal\views\Plugin\views\filter\Bundle->init(Object, Object, Array) (Line: 904)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('filter') (Line: 2532)
Drupal\views\Plugin\views\display\DisplayPluginBase->validate() (Line: 2127)
Drupal\views\ViewExecutable->validate() (Line: 128)
Drupal\views_ui\ViewFormBase->getDisplayTabs(Object) (Line: 49)
Drupal\views_ui\ViewFormBase->prepareEntity() (Line: 125)
Drupal\Core\Entity\EntityForm->init(Object) (Line: 26)
Drupal\views_ui\ViewFormBase->init(Object) (Line: 96)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object) (Line: 41)
Drupal\views_ui\ViewFormBase->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 534)
Drupal\Core\Form\FormBuilder->retrieveForm('view_edit_form', Object) (Line: 281)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object, 'edit', Array) (Line: 230)
Drupal\views_ui\Controller\ViewsUIController->edit(Object, NULL)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 163)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 74)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 681)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
Warning: Trying to access array offset on value of type null in Drupal\views\Plugin\views\HandlerBase->getEntityType() (line 721 of core/modules/views/src/Plugin/views/HandlerBase.php).
Drupal\views\Plugin\views\HandlerBase->getEntityType() (Line: 93)
Drupal\views\Plugin\views\filter\Bundle->init(Object, Object, Array) (Line: 904)
Drupal\views\Plugin\views\display\DisplayPluginBase->getHandlers('filter') (Line: 2532)
Drupal\views\Plugin\views\display\DisplayPluginBase->validate() (Line: 2127)
Drupal\views\ViewExecutable->validate() (Line: 128)
Drupal\views_ui\ViewFormBase->getDisplayTabs(Object) (Line: 49)
Drupal\views_ui\ViewFormBase->prepareEntity() (Line: 125)
Drupal\Core\Entity\EntityForm->init(Object) (Line: 26)
Drupal\views_ui\ViewFormBase->init(Object) (Line: 96)
Drupal\Core\Entity\EntityForm->buildForm(Array, Object) (Line: 41)
Drupal\views_ui\ViewFormBase->buildForm(Array, Object)
call_user_func_array(Array, Array) (Line: 534)
Drupal\Core\Form\FormBuilder->retrieveForm('view_edit_form', Object) (Line: 281)
Drupal\Core\Form\FormBuilder->buildForm(Object, Object) (Line: 48)
Drupal\Core\Entity\EntityFormBuilder->getForm(Object, 'edit', Array) (Line: 230)
Drupal\views_ui\Controller\ViewsUIController->edit(Object, NULL)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 580)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 163)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 74)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 58)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 48)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 51)
Drupal\Core\StackMiddleware\StackedHttpKernel->handle(Object, 1, 1) (Line: 681)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
W00t! Test passed for D7.