Problem/Motivation

When using the theme_image_formatter() function, the title setting in D7 and D8, and the alt setting in D7, are assumed to exist, but are not documented as required. In addition, the path options array is assumed to exist, and you can't pass the path options array without a path, which prevents a # fragment-only path.

Proposed resolution

Check to see if the title and options are set before using them, and use isset instead of !empty for the path.

Remaining tasks

Review and commit D8 patch, reroll for D7 since #1025796: Rename path to uri in image functions was D8-only.

User interface changes

n/a

API changes

n/a

Original report by Chris Gillis

theme_image_formatter() has the ability to wrap the image in a link. It uses the l() function which takes a second optional argument of 'options'. Problem is that theme_image_formatter() doesn't recognise that 'options' in optional, and treats it as required.

Line 551 of image.field.inc should change from:

$options = $variables['path']['options'];
to:
$options = !empty($variables['path']['options'])?$variables['path']['options']:array();

Sorry no patch.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Chris Gillis’s picture

Status: Active » Needs review
FileSize
800 bytes

Patch Attached

mr.baileys’s picture

Status: Needs review » Needs work

Good catch:

$image = image_load('misc/druplicon.png');
theme('image_formatter', array('image' => $image, 'image_style' => NULL, 'path' => array('path' => '/node')));

...will generate a notice.

+    $options = !empty($variables['path']['options'])?$variables['path']['options']:array();

I think it's preferred to use isset rather than empty in this case. Additionally, Drupal coding guidelines require spaces before and after the operators (see http://drupal.org/coding-standards#operators).

Chris Gillis’s picture

Status: Needs work » Needs review
FileSize
803 bytes

Thanks for the feedback. Patch updated.

tim.plunkett’s picture

Version: 7.x-dev » 8.x-dev
Issue tags: +Needs backport to D7
tim.plunkett’s picture

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-5-combined.patch, failed testing.

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett

I think I need $base_url in there or something.

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-8-debug.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.97 KB

Hm, can't reproduce locally, must be some prefix issue.

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-10-debug.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.73 KB

Ah!

<a href="/checkout/yuZakFd9">
vs
<a href="/yuZakFd9">

Hopefully this works. Leaving the debug in for now.

tim.plunkett’s picture

Okay! Here's those patches again, cleaned up, without debugging info.
Once these are in for D8 I will rewrite them for D7.

tim.plunkett’s picture

Added issue summary.

tim.plunkett’s picture

Title: Link options should be optional in theme_image_formatter » theme_image_formatter() assumes that title, alt, and options are always set
tim.plunkett’s picture

FileSize
3.2 KB

Renamed the class and fixed the assertion messages. Finally ready by my standards.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned
FileSize
3.53 KB

Just to expedite things.

xjm’s picture

Mostly this looks great to me.

+++ b/core/modules/image/image.field.incundefined
@@ -589,7 +589,7 @@ function theme_image_formatter($variables) {
-  if (drupal_strlen($item['title']) == 0) {
+  if (isset($item['title']) && drupal_strlen($item['title']) == 0) {

@@ -601,9 +601,9 @@ function theme_image_formatter($variables) {
-  if (!empty($variables['path']['path'])) {
+  if (isset($variables['path']['path'])) {
...
-    $options = $variables['path']['options'];
+    $options = isset($variables['path']['options']) ? $variables['path']['options'] : array();

Should we add any inline comments in here?

+++ b/core/modules/image/image.testundefined
@@ -1493,3 +1493,63 @@ class ImageFieldDefaultImagesTestCase extends ImageFieldTestCase {
+    $this->assertEqual($expected_result, $rendered_element, 'theme_image_formatter() correctly renders link fragment.');

Can we add an article here? "...correctly renders a link fragment."

tim.plunkett’s picture

FileSize
3.35 KB
1.26 KB

The title hunk already has a comment, and I added one for the path/options section that I think covers it.
And yeah, an article is a good idea there :)

jthorson’s picture

Status: Needs review » Reviewed & tested by the community

Minor nitpick. At first, I read the test comment as

"without 'title, alt, or path' options"

instead of

"without 'title', 'alt', or 'path options'"

Not sure if something like this would be clearer:

"without image title, alt text, or link path 'options' parameters."

But as I said, this is a minor nitpick at most ... so bumping to RTBC.

tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
3.52 KB

Since #1591434: Convert image tests to PSR-0 is about to move all the image tests around, here's a reroll using a PSR-0 test. Can be committed before or after that other issue.

Status: Needs review » Needs work
Issue tags: -Needs backport to D7

The last submitted patch, drupal-1038932-21.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
Issue tags: +Needs backport to D7

#21: drupal-1038932-21.patch queued for re-testing.

tim.plunkett’s picture

The image tests are PSR-0 now, so I rerolled to make sure one last time. I touched up the comment according to #20, and fixed the test class name.

cosmicdreams’s picture

Status: Needs review » Needs work
+++ b/core/modules/image/image.field.inc
@@ -589,7 +589,7 @@ function theme_image_formatter($variables) {
+  if (isset($item['title']) && drupal_strlen($item['title']) == 0) {

why can't empty() be used here?

+++ b/core/modules/image/image.field.inc
@@ -601,9 +601,11 @@ function theme_image_formatter($variables) {
+    $options = isset($variables['path']['options']) ? $variables['path']['options'] : array();

Seems like a good opprotunity to use the ternary operator, ?:

http://www.php.net/manual/en/language.operators.comparison.php#language....

tim.plunkett’s picture

Status: Needs work » Needs review

0 is a valid title, no?

AFAIK, you can't use that ternary with isset(), or else the middle case becomes the result of it. It'd be like saying $options = isset($variables['path']['options']) ? (bool) $variables['path']['options'] : array();

cosmicdreams’s picture

Status: Needs review » Reviewed & tested by the community

Ah you're right. Thanks for helping me see that. RTBC

leanderl’s picture

Hello! I seem to have run into this problem (Drupal 7.14) and found this issue. What branch should I clone into to apply the patch? I tried git clone --recursive --branch 7.x http://git.drupal.org/project/drupal.git. I feel uncertain about how to patch drupal core.

jthorson’s picture

leanderl,

You can try downloading the 'combined' patch in #24 and applying it to your 7.x branch (using git apply) ... but the patch has not made it into the 7.x codebase yet. (The 'reviewed & tested by the community' status indicates that it's waiting for a core committer to add it ... once you see someone post a 'committed' comment here, then it will be included in a new clone.)

leanderl’s picture

Thx for quick and constructive reply jthorson!

webchick’s picture

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

This looks like sensible hardening, and comes with a test. Woo!

Committed and pushed to 8x. This will need a re-roll for D7 thanks to PSR-0.

tim.plunkett’s picture

Status: Patch (to be ported) » Needs review
FileSize
3.32 KB

Rerolled.

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-32.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.66 KB

Ah yes, this is where D7 and D8 differ. (they set 'alt' completely differently)

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-34.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.7 KB

Ah, RDF module is on during D7 tests, and clean URLs are off.

This should finally be good to go.

dags’s picture

Status: Needs review » Reviewed & tested by the community

Applied and tested against an up to date 7.x branch and compared to the D8 version. Looks good.

David_Rothstein’s picture

#36: drupal-1038932-36.patch queued for re-testing.

David_Rothstein’s picture

I'm retesting this because I expect it to fail... the fact that clean URLs are off during D7 tests was a recent bug/regression (it was like that for a while in Drupal 8 too), now fixed in both places due to #1541958: Split setUp() into specific sub-methods).

So the test fails for me locally, and I'm guessing it will fail here too, unfortunately.

Also, we might not want to make any assumptions about clean URLs (one way or another) because I think that depends on the environment the test is running in? Perhaps it would be better to change the tests so that they use Xpath to look for the specific attributes they're asserting, rather than looking to match an entire string of HTML?

tim.plunkett’s picture

Assigned: Unassigned » tim.plunkett

If it fails, please move it back to D8 so I can switch to xpath.

If it doesn't, then COMMIT IT! :D

David_Rothstein’s picture

Version: 7.x-dev » 8.x-dev
Status: Reviewed & tested by the community » Needs work

Well, it didn't fail, but it should have :) The testbot must be running with clean URLs off or something like that.

Because I tried it again locally, and the patch definitely failed for me when I ran it with clean URLs on, but turning clean URLs off and running the test again makes it pass (see test output below).

If this were a critical bug or something I might say let's commit it and worry about it later :) But since it's not, I think we should stabilize the tests first... sorry. Otherwise most people trying to run these locally are going to wind up getting failures, even if the testbot doesn't.

droth@droth:~/web/drupal$ php scripts/run-tests.sh --url http://localhost/drupal --class ImageThemeFunctionWebTestCase

Drupal test run
---------------

Tests to be run:
 - Image theme functions (ImageThemeFunctionWebTestCase)

Test run started:
 Monday, July 30, 2012 - 00:25

Test summary
------------

Image theme functions 2 passes, 1 fail, and 8 exceptions

Test run duration: 18 sec

droth@droth:~/web/drupal$ drush -y vset clean_url 0
clean_url was set to 0.                                                                                                                                               [success]
droth@droth:~/web/drupal$ php scripts/run-tests.sh --url http://localhost/drupal --class ImageThemeFunctionWebTestCase

Drupal test run
---------------

Tests to be run:
 - Image theme functions (ImageThemeFunctionWebTestCase)

Test run started:
 Monday, July 30, 2012 - 00:26

Test summary
------------

Image theme functions 3 passes, 0 fails, and 8 exceptions

Test run duration: 18 sec
jthorson’s picture

Just confirming that the bot does indeed run with clean URLs off.

Incidently, this is the first time that has caused an unexplained 'pass' instead of an unexplained 'failure'! ;)

tim.plunkett’s picture

Version: 8.x-dev » 7.x-dev
Status: Needs work » Needs review
FileSize
4.64 KB
3.19 KB

Well I know this needs to be fixed in D8, but there is also the need to backport #1696416: Regression: theme_image_style() is broken, and I will have to figure out a way to do this all without making too much work.

One option is to commit this as is, and then open a major follow-up to fix the D8 tests to use xpath, and then backport the theme_image_style tests and the xpath fixes at once.

Switching back to D7 for now just to run the bot.

tim.plunkett’s picture

Actually the "parse exact HTML to prove theme functions work" is systemic, I really do think an xpath follow up is best.

tim.plunkett’s picture

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community
tim.plunkett’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
765 bytes
3.69 KB

This is how other parts of core work around this issue.

tim.plunkett’s picture

cweagans’s picture

Status: Needs review » Reviewed & tested by the community

I don't have any objection to the patch in #47. Using it on a client project and it does what it's supposed to (we were getting some notices emitted because file_entity wasn't passing a title or alt attribute to the theme function and with the patch, that is no longer the case).

The code looks pretty straightforward, so I say RTBC.

David_Rothstein’s picture

Status: Reviewed & tested by the community » Needs review

I was about to commit this but then noticed there's a subtle behavior change here.

Before the patch, you could explicitly do:

theme('image_formatter', array('item' => array( .... 'alt' => NULL ... )))

in order to force the "alt" attribute not to be printed in the HTML at all. After the patch, though, the "alt" attribute will still be printed, but as an empty string.

That doesn't sound like too big of a deal to me, except that the documentation for theme_image() (which theme_image_formatter() ultimately calls) goes to great lengths to explain that this option is available. So perhaps we don't want to break it?

In any case, preserving the old behavior might be as simple as changing this code to use array_key_exists() instead:

+  if (isset($item['alt'])) {
+    $image['alt'] = $item['alt'];
+  }

Drupal 8 isn't affected by this, just Drupal 7.

tim.plunkett’s picture

FileSize
3.66 KB

Good point. Updated the patch.

Status: Needs review » Needs work

The last submitted patch, drupal-1038932-51.patch, failed testing.

tim.plunkett’s picture

Status: Needs work » Needs review
FileSize
3.66 KB

I clearly forgot how array_key_exists works.

tim.plunkett’s picture

#53: drupal-1038932-53.patch queued for re-testing.

tim.plunkett’s picture

Assigned: tim.plunkett » Unassigned

This addressed #50, and still passes. RTBC anyone?

attiks’s picture

Status: Needs review » Reviewed & tested by the community

Looks good, slipped my mind

webchick’s picture

webchick’s picture

Status: Reviewed & tested by the community » Fixed

Tim and I talked, and he pointed out that there are many places that could use the new assertThemeOutput(), so doing that more core-wide could be a follow-up, rather than a blocker here. I'm inclined to agree.

Committed and pushed to 7.x. Thanks!

Status: Fixed » Closed (fixed)
Issue tags: -Needs backport to D7

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

Anonymous’s picture

Issue summary: View changes

added issue summary