Updated: Comment #3

Problem/Motivation

When more than one language is configured and path-based language negotiation is enabled, node hit data collection was broken but fixed in #2364157: Replace most existing _url calls with Url objects. The underlying cause was that the data collection callback, a simple PHP script located at the root directory of the Statistics module, was run through url() before being added to drupalSettings. When that URL was hit asynchronously for languages with a configured language prefix, no data is collected and a 404 results.

This problem was also introduced in Drupal 7.25 and, as such, needs backport.

There is no tests for this to prevent it happening again in the future.

Proposed resolution

Add test to test node hit counter when path-based language negotiation is used

Remaining tasks

Review, commit

User interface changes

n/a

API changes

n/a

Comments

iamEAP’s picture

This issue depends on #2172859: Statistics JS and settings are not included on node pages, because the Statistics module is completely broken, regardless of the multilingual bug.

iamEAP’s picture

Here's a patch for D7, with tests. D8 is still dependent on #2172859: Statistics JS and settings are not included on node pages, but the relevant test modifications are below:

  1. +++ b/modules/statistics/statistics.module
    @@ -123,7 +123,7 @@ function statistics_node_view($node, $view_mode) {
           );
    -      $settings = array('data' => array('nid' => $node->nid), 'url' => url(drupal_get_path('module', 'statistics') . '/statistics.php'));
    +      $settings = array('data' => array('nid' => $node->nid), 'url' => base_path() . drupal_get_path('module', 'statistics') . '/statistics.php');
           $node->content['#attached']['js'][] = array(
    

    The actual fix, very straightforward.

  2. +++ b/modules/statistics/statistics.test
    @@ -61,7 +61,7 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
       function setUp() {
    -    parent::setUp('statistics');
    +    parent::setUp('statistics', 'locale');
     
         $this->auth_user = $this->drupalCreateUser(array('access content', 'create page content', 'edit own page content'));
     
    @@ -75,9 +75,25 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
    
    @@ -75,9 +75,25 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
         variable_set('statistics_enable_access_log', 1);
         variable_set('statistics_count_content_views', 1);
     
    +    // User to add and remove language.
    +    $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages'));
    +
    +    // Add custom language.
    +    $this->drupalLogin($admin_user);
    +    $this->language = array(
    +      'langcode' => 'xx',
    +      'name' => $this->randomName(16),
    +      'native' => $this->randomName(16),
    +      'prefix' => 'xx',
    +      'direction' => '0',
    +    );
    +    $this->drupalPost('admin/config/regional/language/add', $this->language, t('Add custom language'));
    +    $this->drupalPost('admin/config/regional/language/configure', array('language[enabled][locale-url]' => 1), t('Save settings'));
    +    $this->drupalLogout();
    +
         // Clear the logs.
    -    db_truncate('accesslog');
    -    db_truncate('node_counter');
    +    db_truncate('accesslog')->execute();
    +    db_truncate('node_counter')->execute();
       }
    

    Modifications necessary to enable path prefix based language negotiation.

  3. +++ b/modules/statistics/statistics.test
    @@ -158,6 +174,34 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
    +
    +    // Verify the same when loading the site in a non-default language.
    +    $this->drupalGet($this->language['prefix'] . '/node/' . $this->node->nid);
    +    $this->assertRaw($expected_script, 'Statistics JS found on valid node page in non-default language.');
    +    $this->assertRaw($expected_settings, 'Statistics settings found on valid node page in non-default language.');
    +    variable_set('statistics_count_content_views_ajax', 0);
    

    The only additional tests needed for D8. The rest are backports of D8 tests for D7.

iamEAP’s picture

Issue summary: View changes
Status: Active » Needs review
Issue tags: +D8MI
StatusFileSize
new5.21 KB
new6.23 KB

Patch attached. As illustrated above, the issue is that url() takes path-based language negotiation into account when generating aliases, which we don't want (because /core/modules/statistics/statistics.js will always be at the Drupal root, not at a language path-prefixed location).

In addition to the fix and tests for that change, I'm being more explicit in the check for the script/settings on-page by:

  • Encoding the expected settings JSON rather than faking it in plain-text,
  • Searching for a regex that includes <script src="[/path/to/statistics.js]">

Also tagging D8MI to put it on the multilingual initiative's radar.

Status: Needs review » Needs work

The last submitted patch, 3: drupal8-ajax_node_counter_lang_path_prefix-2172871-3.patch, failed testing.

iamEAP’s picture

iamEAP’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 3: drupal8-ajax_node_counter_lang_path_prefix-2172871-3.patch, failed testing.

anybody’s picture

Confirming this major problem in our multilingual D7.25 environment.

iamEAP’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 3: drupal8-ajax_node_counter_lang_path_prefix-2172871-3.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review
StatusFileSize
new6.24 KB

Rebasing #3...

Status: Needs review » Needs work

The last submitted patch, 13: drupal8-ajax_node_counter_lang_path_prefix-2172871-13.patch, failed testing.

iamEAP’s picture

Ah. $base_url includes the path. Just need to use $base_root in the test.

Status: Needs review » Needs work

The last submitted patch, 15: drupal8-ajax_node_counter_lang_path_prefix-2172871-15.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 15: drupal8-ajax_node_counter_lang_path_prefix-2172871-15.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review

Status: Needs review » Needs work

The last submitted patch, 15: drupal8-ajax_node_counter_lang_path_prefix-2172871-15.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review
iamEAP’s picture

^^ Head was broken. Looking good now.

Status: Needs review » Needs work

The last submitted patch, 15: drupal8-ajax_node_counter_lang_path_prefix-2172871-15.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review
Issue tags: -
StatusFileSize
new6.19 KB

Re-roll of #15.

Status: Needs review » Needs work

The last submitted patch, 25: drupal8-ajax_node_counter_lang_path_prefix-2172871-25.patch, failed testing.

anybody’s picture

How can we get this issue forward? It exists on every multilang Drupal 7 website using statistics and a current Drupal 7 version, which is represended by major state.

Does someone know where the error in the last patch comes from?
The test did not complete due to a fatal error. Completion check StatisticsLoggingTest.php 85 Drupal\statistics\Tests\StatisticsLoggingTest->testLogging()

David_Rothstein’s picture

Issue tags: +Needs backport to D7

#2236165: Ajax POST to statistics.php fails when using i18n with path prefix language code is a possible duplicate (which also suggests that the issue is already fixed in Drupal 8).

effulgentsia’s picture

Status: Needs work » Needs review
StatusFileSize
new1.03 KB

Yes, it was fixed for D8 as a side effect of #2364157: Replace most existing _url calls with Url objects. However, I don't think test coverage was added there. Posting this patch to revert the side-effect fix and see if any test catches it. If not, we should reroll #25's test coverage and get that in.

iamEAP’s picture

iamEAP’s picture

Status: Needs review » Needs work

Per #29, this needs work... Will be back shortly with two patches:

  • Tests only (based on #25), which should pass
  • Tests + #29, which should fail
iamEAP’s picture

Status: Needs work » Needs review
StatusFileSize
new5.01 KB
new6.04 KB

iamEAP’s picture

StatusFileSize
new864 bytes
new5.14 KB
iamEAP’s picture

StatusFileSize
new6.17 KB

#34 with regression from #29.

The last submitted patch, 34: 2172871-34.patch, failed testing.

effulgentsia’s picture

StatusFileSize
new6.21 KB
new956 bytes

Core moves fast. _url() is gone. So here's #35 with a different regression. We still need #34 to pass.

Status: Needs review » Needs work

The last submitted patch, 37: 2172871-34-with-regression-37.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review
StatusFileSize
new1.77 KB
new5.46 KB

Thanks @effulgentsia.

I had to add a new #cache key of cache_context.language to the build array to get the drupalSettings to actually reflect the language. Without it, the drupalSettings attached to the node were being stored in render cache globally for all languages and thus, hook_node_view was never being triggered after initial load.

That behavior seems like a bug to me, but I don't really have time personally to investigate whether or not that's true, nor whether or not an issue already exists for that bug.

In any event, here's a patch with a working test that should pass. I'll follow-up with an updated regression.

iamEAP’s picture

StatusFileSize
new6.58 KB

And here's #39 + a tweaked regression based on #37. It should fail. #39 should be reviewed/committed.

iamEAP’s picture

Hiding regression from issue summary...

Status: Needs review » Needs work

The last submitted patch, 40: 2172871-39-with-regression-37.patch, failed testing.

iamEAP’s picture

Status: Needs work » Needs review

See #39.

timmillwood’s picture

Status: Needs review » Reviewed & tested by the community

#39 looks good.

alexpott’s picture

Status: Reviewed & tested by the community » Needs work
+++ b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php
@@ -69,32 +97,47 @@ protected function setUp() {
+    $expected_settings = Json::encode(array(
+      'statistics' => array(
+        'data' => array(
+          'nid' => $this->node->id(),
+        ),
+        'url' => $stats_path,
+      ),
+    ));
+
+    // Per drupalSettings' nested structure, strip surrounding brackets.
+    $expected_settings = trim($expected_settings, '{}');
...
     $this->assertRaw($expected_settings, 'Found statistics settings on node page.');
...
+    $this->assertRaw($expected_settings, 'Found statistics settings on valid node page in a non-default language.');

This is unnecessary - you can just make an assertion on $this->getDrupalSettings() - then you don't have to muck around with json encoding and trimming.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new5.46 KB

Just a reroll of #39 still need to add suggestions from #45.

Status: Needs review » Needs work

The last submitted patch, 46: async_node_hit_counter-2172871-46.patch, failed testing.

timmillwood’s picture

Status: Needs work » Needs review
StatusFileSize
new3.37 KB
new5.55 KB

Fixed issue in #46 and used getDrupalSettings() as suggested in #45.

iamEAP’s picture

Status: Needs review » Reviewed & tested by the community

Looks good! #45 is addressed.

wim leers’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs issue summary update
+++ b/core/modules/statistics/src/Tests/StatisticsLoggingTest.php
@@ -34,6 +34,13 @@ class StatisticsLoggingTest extends WebTestBase {
+   * Associative array representing a hypothetical Drupal language.

An associative array representing a language?

That sounds a bit mysterious. Can't this add an @see to a place that explains this format?


More importantly: this adds a test, without a fix. That's … quite confusing. Was it fixed in another issue in the mean time?

timmillwood’s picture

Title: Async node hit counter data collection is broken when path-based language negotiation is used » Test node hit counter when path-based language negotiation is used
Issue summary: View changes
Status: Needs work » Reviewed & tested by the community
Issue tags: -Needs issue summary update

@wim - As mentioned in #29, "Yes, it was fixed for D8 as a side effect of #2364157: Replace most existing _url calls with Url objects"

Also the $language array is "posted" to admin/config/regional/language/add, does this not make it obvious what it's doing and the format needed?

wim leers’s picture

Thanks, all much clearer now!

alexpott’s picture

Version: 8.0.x-dev » 7.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

Moar test coverage. Nice one eveyone. Committed 525d8e6 and pushed to 8.0.x. Thanks!

  • alexpott committed 525d8e6 on 8.0.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...
iamEAP’s picture

Title: Test node hit counter when path-based language negotiation is used » Async node hit counter data collection is broken when path-based language negotiation is used
Status: Patch (to be ported) » Needs review
StatusFileSize
new5.86 KB

Reverting name change so this makes more sense for D7.

Here's #2 verbatim, but renamed so that it will be tested.

Should probably be updated to use DrupalWebTestCase::drupalGetSettings(), per #45.

Status: Needs review » Needs work

The last submitted patch, 55: drupal-ajax_node_counter_lang_path_prefix-2172871-55.patch, failed testing.

digitaldonkey’s picture

A simple workaround for people who can't change core to fix this:

/**
 * Implements hook_url_outbound_alter().
 *
 * Fix statistics path when i18n path prefix enabled
 * see: https://www.drupal.org/node/2172871
 */
function YOUR_THEME_OR MODULE_url_outbound_alter(&$path, &$options, $original_path) {
  if ($original_path === 'modules/statistics/statistics.php' && isset($options['prefix'])) {
    $options['prefix'] = '';
  }
}
leofishman’s picture

#57 simple work around from @digitaldonkey works fine!

  • alexpott committed 525d8e6 on 8.1.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...
frodeste’s picture

Were are we on this issue for Drupal7?

  • alexpott committed 525d8e6 on 8.3.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...

  • alexpott committed 525d8e6 on 8.3.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...

  • alexpott committed 525d8e6 on 8.4.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...

  • alexpott committed 525d8e6 on 8.4.x
    Issue #2172871 by iamEAP, timmillwood, effulgentsia: Test node hit...
anybody’s picture

Status: Needs work » Patch (to be ported)
alexpott’s picture

If someone does decide to port this patch note that the backport policy has changed (https://www.drupal.org/core/backport-policy). A new issue should be created and once it is - this issue should be then set to closed (fixed) with a link to the D7 issue.

anybody’s picture

Thanks @alexpott, in that case I'd like to revive the Drupal 7 issue containing a working patch, see #2236165: Ajax POST to statistics.php fails when using i18n with path prefix language code which is then the new follow-up. It would be nice if someone could have a look at this quite old issue then.

berdir’s picture

Status: Patch (to be ported) » Fixed

Closing this then.

Status: Fixed » Closed (fixed)

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