Problem/Motivation

When switching between Content and Locale in the /admin/tmgmt/sources/content/node you get an ajax error.

Uncaught AjaxError: 
An AJAX HTTP error occurred.
HTTP Result Code: 500
Debugging information follows.
Path: /admin/tmgmt/sources/content/node?ajax_form=1
StatusText: Internal Server Error
ResponseText: {"message":"A fatal error occurred: SQLSTATE[42S22]: Column not found: 1054 Unknown column \u0027e.ajax_form\u0027 in \u0027where clause\u0027: SELECT e.nid AS nid\nFROM \n{node} e\nINNER JOIN {node_field_data} data_table ON data_table.nid = e.nid AND data_table.default_langcode = 1\nWHERE  (e.type IN  (:db_condition_placeholder_0)) AND (e.ajax_form IN  (:db_condition_placeholder_1)) AND (e._wrapper_format IN  (:db_condition_placeholder_2)) AND (data_table.langcode IN  (:db_condition_placeholder_3, :db_condition_placeholder_4, :db_condition_placeholder_5)) \nORDER BY nid DESC\nLIMIT 20 OFFSET 0; Array\n(\n    [:db_condition_placeholder_0] =\u003E translatable_node\n    [:db_condition_placeholder_1] =\u003E 1\n    [:db_condition_placeholder_2] =\u003E drupal_ajax\n    [:db_condition_placeholder_3] =\u003E en\n    [:db_condition_placeholder_4] =\u003E de\n    [:db_condition_placeholder_5] =\u003E fr\n)\n"}

Steps to Recreate:

1.- visit /admin/tmgmt/sources/content/node.
2.- Select from the dropdown Locale.
3.- Wait until the ajax form loads and gives the error.

Proposed resolution

Check the ajax form post and see any errors for locale.

Remaining tasks

User interface changes

Error Demo

API changes

Data model changes

Original report by [username]

Comments

berdir’s picture

We need to whitelist search options in all the places where we dynamically apply conditions:

You can reproduce this in even easier ways:

admin/tmgmt/sources/content/node?whatever=breakit

That will fail. Likely the same for config entities.

Use a whitelist array for all the allowed filter conditions then array_intersect() to limit to those.

juanse254’s picture

Status: Active » Needs review
StatusFileSize
new1.21 KB

this might do it.

berdir’s picture

Status: Needs review » Needs work

Too complicated, just have a hardcoded whitelist, something like protected $conditionFields = [...], add in the dynamic field names while building the form.

juanse254’s picture

Status: Needs work » Needs review
StatusFileSize
new737 bytes

Maybe this will do it, not uploading interdiff because the approach is totally different.

Status: Needs review » Needs work

The last submitted patch, 4: Ajax_error-2540696-4.patch, failed testing.

berdir’s picture

+++ b/sources/content/src/ContentEntitySourcePluginUi.php
@@ -214,7 +214,7 @@ class ContentEntitySourcePluginUi extends SourcePluginUiBase {
       // Skip exclude params, and those that have empty values, as these would
       // make it into query condition instead of being ignored.
-      if (in_array($key, $exclude_params) || $value === '') {
+      if (!(in_array($key, $exclude_params) || $value == '')) {
         continue;
       }
       $search_property_params[$key] = $value;

I don't think this is doing what it should, but you likely did find the correct place.

However, based on what we found, we need to turn this pattern around. So instead of an exclude list, we need to have an include list/whitelist with allowed params and make sure the keys are in that.

juanse254’s picture

StatusFileSize
new1.47 KB
new1.38 KB

okay then something like this should do it then.

juanse254’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 7: Ajax_error-2540696-7.patch, failed testing.

berdir’s picture

  1. +++ b/sources/content/src/ContentEntitySourcePluginUi.php
    @@ -208,20 +208,22 @@ class ContentEntitySourcePluginUi extends SourcePluginUiBase {
    +    // Load search property params which will be passed into.
    +    $whitelist = array(
    +      'title',
    

    Some of those fields are dynamic, specifically the title and bundle filter. it's title for node but name for terms, and it's type for nodes (missing here) but vid for terms.

    That's why the tests are failing. See the code in the search form, you need the same logic here. (getKey()).

  2. +++ b/sources/content/src/ContentEntitySourcePluginUi.php
    @@ -208,20 +208,22 @@ class ContentEntitySourcePluginUi extends SourcePluginUiBase {
         $search_property_params = array();
    -    $exclude_params = array('q', 'page');
         foreach ($_GET as $key => $value) {
    -      // Skip exclude params, and those that have empty values, as these would
    -      // make it into query condition instead of being ignored.
    -      if (in_array($key, $exclude_params) || $value === '') {
    +      if ($value == '') {
             continue;
           }
           $search_property_params[$key] = $value;
         }
    -
    +    $search_property_params = array_intersect_key($search_property_params, array_flip($whitelist));
    

    This can actually be simplified into $search_property_params = array_filter($_GET). While touching this, let's switch to \Drupal::request()->query->all().

berdir’s picture

Issue tags: +Needs tests

Oh and... needs tests :)

Just simulate an ajax request by mixing a valid filter argument with a non-valid one and make sure it has been filtered correctly.

juanse254’s picture

Status: Needs work » Needs review
StatusFileSize
new2.83 KB
new1.31 KB
new2.36 KB

Added tests and also updated patch following suggestions.

The last submitted patch, 12: Ajax_error-2540696-12.patch, failed testing.

Status: Needs review » Needs work

The last submitted patch, 12: Ajax_error-2540696-TEST-ONLY-12.patch, failed testing.

juanse254’s picture

Status: Needs work » Needs review
StatusFileSize
new2.91 KB
new1 KB

Fixed wrong elements in whitelist. Test remains the same.

juanse254’s picture

StatusFileSize
new2.91 KB
new945 bytes

Ups, minor changes in coding standards.

LKS90’s picture

Status: Needs review » Needs work
  1. +++ b/sources/content/src/ContentEntitySourcePluginUi.php
    @@ -208,18 +208,13 @@ class ContentEntitySourcePluginUi extends SourcePluginUiBase {
    +    array_push($whitelist, $entity_type->getKey('bundle'));
    +    array_push($whitelist, $entity_type->getKey('label'));
    

    $whitelist[] = $entity->getKey('ENTITY_KEY') is easier to read and does exactly the same in this situation. If you don"t need the return value of array_push (it returns the amount of elements in the array after the push), always use the $array[] way. [See also: http://php.net/manual/en/function.array-push.php#83388]

  2. +++ b/sources/content/src/Tests/ContentEntitySourceListTest.php
    @@ -292,4 +292,21 @@ class ContentEntitySourceListTest extends EntityTestBase {
    +    $this->drupalGet('/admin/tmgmt/sources/content/node',[], $edit);
    

    Isn't this switched around? The third parameter is headers, the second is options. The options are used to build the Url.

juanse254’s picture

Status: Needs work » Needs review
StatusFileSize
new1.32 KB
new2.89 KB
new2.22 KB

Right on, fixed :). Some standards as well..

The last submitted patch, 18: Ajax_error-2540696-TEST-ONLY-18.patch, failed testing.

berdir’s picture

Status: Needs review » Needs work
  1. +++ b/sources/content/src/ContentEntitySourcePluginUi.php
    @@ -208,18 +208,13 @@ class ContentEntitySourcePluginUi extends SourcePluginUiBase {
    +    // Load search property params which will be passed into.
    +    $entity_type = \Drupal::entityManager()->getDefinition($type);
    +    $whitelist = array('langcode', 'target_language', 'target_status');
    +    $whitelist[] = $entity_type->getKey('bundle');
    +    $whitelist[] = $entity_type->getKey('label');
    +    $search_property_params = array_filter(\Drupal::request()->query->all());
    +    $search_property_params = array_intersect_key($search_property_params, array_flip($whitelist));
    

    This looks pretty good now, just some minor improvements:

    a) The documentation isn't really clear to me. It was weird before and it's worse now. Suggestion:

    // Build a list of allowed search conditions and get their values from the request.

    Also, I still think we need the same check config entities or do they work differently?

  2. +++ b/sources/content/src/Tests/ContentEntitySourceListTest.php
    @@ -292,4 +292,21 @@ class ContentEntitySourceListTest extends EntityTestBase {
    +
    +  /**
    +   * Tests that the term bundle filter works.
    +   */
    +  public function testAjaxFunctions() {
    

    I'd recommend to put this into one of the existing methods. The list tests are already very slow.

    The use of $term in the test method is also very confusing, $term is usually a taxonomy term. Just hardcode them into the string, should actually be easier to understand.

juanse254’s picture

Status: Needs work » Needs review
StatusFileSize
new2.85 KB
new2.35 KB
new1.25 KB

Changes applied.

P.D: config entities works in a different way.

Status: Needs review » Needs work

The last submitted patch, 21: Ajax_error-2540696-21-TEST_ONLY.patch, failed testing.

juanse254’s picture

Status: Needs work » Needs review
berdir’s picture

Status: Needs review » Fixed
Issue tags: -Needs tests

Yes, better now. Committed.

  • Berdir committed 44813bd on 8.x-1.x authored by juanse254
    Issue #2540696 by juanse254: AJAX error when switching between Content...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.