? Pending

User tests: Successful: Unsuccessful:

avatar roland-d
roland-d
23 Jun 2018

Summary of Changes

As I was testing 3.8.9 RC I found this issue popping up when clicking to install the sample data:

image

Notice the \' in the text. I tracked this down and it happened because in the process of rendering the page we call the function onBeforeCompileHead(). I have a plugin installed that listens to this and in the plugin at some point the setHeadData() of HtmlDocument class is called. This method calls the JText::script() again. It calls it with the language key and language value but the second argument is not the value but if the text should be considered JavaScript safe. Supplying a text string as second argument tells the method that it should always treat the string as JavaScript safe. Since the language strings was originally already set to be JavaScript safe it now gets 2 backslashes. If I were to have 3 more plugins calling setHeadData() I would end up with 4 backslashes.

I tracked the implementation of this code back to this commit 9f692f7 which shows the code has never changed since it's original implementation in May 2013. Interesting enough either this never showed up or nobody noticed :)

Testing Instructions

  1. Open the file plugins/system/updatenotification/updatenotification.php (assuming you have the update notification plugin enabled)
  2. After line 419 add this code and save
public function onBeforeCompileHead(){
		$doc = JFactory::getDocument();
		$head = $doc->getHeadData();
		$doc->setHeadData($head);
	}
  1. Create a sample data module in the backend
  2. Enable the Sample Data Blog plugin
  3. Click on the Blog Sample Data link
    image
  4. Notice the text with the \' in it.
  5. Apply the patch
  6. Refresh the page
  7. Click on the Blog Sample Data link again
  8. Notice the word is now can't as expected.

Expected result

The word can't

Actual result

The word can\'t

Documentation Changes Required

None

avatar roland-d roland-d - open - 23 Jun 2018
avatar roland-d roland-d - change - 23 Jun 2018
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 23 Jun 2018
Category Libraries
avatar infograf768 infograf768 - test_item - 23 Jun 2018 - Tested successfully
avatar infograf768
infograf768 - comment - 23 Jun 2018

I have tested this item successfully on 6d69334


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/20843.

avatar ggppdk
ggppdk - comment - 23 Jun 2018

so you called

***Document->getHeadData();

to get the current (head) data

... which also includes (unfortunately ... ??)

$data['scriptText']  = \JText::getScriptStrings();

you customize / manipulate $data, and then you call ...

***Document->setHeadData($data);

but now you will be calling 2nd (or 3rd or 4th time if you call setHeadData multiple times)
JText::script on the same language strings
this is ok to do , but the problem is a little more complex ... (i think)

how do you know if the original call to JText::script($string, $jsSafe ...) was using false or was using true for $jsSafe, you don't know right ?

avatar ggppdk
ggppdk - comment - 23 Jun 2018

Also you don't know what the setting for $interpretBackSlashes was during the original calls

public static function script($string = null, $jsSafe = false, $interpretBackSlashes = true)
avatar roland-d
roland-d - comment - 23 Jun 2018

@ggppdk

this is ok to do , but the problem is a little more complex ... (i think)

I realized that as well but doesn't warrant incorrect use of a function. The real solution would be to keep track of which string wants to be jsSafe. A refactoring not to be done in this PR.

how do you know if the original call to JText::script($string, $jsSafe ...) was using false or was using true for $jsSafe, you don't know right ?

Nope, you do not know.

Also you don't know what the setting for $interpretBackSlashes was during the original calls

Yep, you don't know that either. There is more weird stuff going in here because the argument claims to be a boolean but in the function we check if it is an array :|

avatar ggppdk
ggppdk - comment - 23 Jun 2018

Also i see a logic / design problem here

$data['scriptText']
is supposed to have
$key => $already_manipulate_string

why the above ? , because that is what is given by

$data['scriptText']  = \JText::getScriptStrings();

you are not supposed to add or "re-add" them using ... Text::script aka remanipulating according to $jsSafe, and $interpretBackSlashes

instead you should some how
-- merge them with existing static::strings inside the Text class

$new_strings_array = array_merge(\JText::getScriptStrings(), $data['scriptText']);

// and then call ...
Factory::getDocument()->addScriptOptions('joomla.jtext', $new_strings_array, false);
  • but there is no method in the API of Text class to do the above

also "fixing" this bogus behavior now may be considered a B/C break ...

avatar ggppdk
ggppdk - comment - 23 Jun 2018

@roland-d

i agree that this PR is better than what is happening now

i think making a full / proper fix is for J4

avatar ggppdk
ggppdk - comment - 23 Jun 2018

@roland-d

question,

why not call php \JText::getScriptStrings();
(to get already keys which already have been added via Text::script)

and then loop through $data['scriptText'] and only call Text::script only on new keys ?

avatar roland-d
roland-d - comment - 23 Jun 2018

@ggppdk

i think making a full / proper fix is for J4

I also believe that is the place to really refactor this.

and then loop through $data['scriptText'] and only call Text::script only on new keys ?

Not sure if this is enough because when I look at the original commit it was for cached data. Javascript translations are not available if caching is enabled. So from this I gather that when you load a cached page the JS has no language strings, so you need to run through all of them to be made available.

avatar ggppdk
ggppdk - comment - 23 Jun 2018

@roland-d

right,
missed this one about caching
but we can still do the check that i suggested and not re-add the keys already added ... to the static::$strings of Text class

Just , we need to guarantee that at least 1 call to Text:script is made inside setHeadData()
So after the loop we can do

Text::script('__DUMMY__');

that way you will get Text class to call

HTMLHelper::_('behavior.core');

Factory::getDocument()->addScriptOptions('joomla.jtext', static::$strings, false);
avatar roland-d
roland-d - comment - 23 Jun 2018

@ggppdk
Adding that DUMMY string doesn't sit right with me either as it feels like a workaround. Perhaps we should just focus on a better solution on Joomla 4.

avatar ggppdk
ggppdk - comment - 23 Jun 2018

Adding that DUMMY string doesn't sit right with me either as it feels like a workaround. Perhaps we should just focus on a better solution on Joomla 4.

i see,
i hope i contributed something here for a more complete fix in J4

avatar roland-d roland-d - change - 10 Mar 2019
Status Pending Closed
Closed_Date 0000-00-00 00:00:00 2019-03-10 20:21:11
Closed_By roland-d
avatar roland-d roland-d - close - 10 Mar 2019

Add a Comment

Login with GitHub to post a comment