Text Overlay using Node Data

mikeytown2 - January 24, 2009 - 02:09
Project:Imagecache Actions
Version:6.x-1.1
Component:Text Actions Module
Category:support request
Priority:normal
Assigned:Unassigned
Status:closed
Description

Any hints on how to do this?

The Taxonomy logic can be lifted from here (in the comments)
http://drupal.org/node/64135
http://drupal.org/node/69076

So in short how can I make it Overlay some text based on a True; or nothing if False.

#1

mikeytown2 - January 24, 2009 - 04:37
Title:Custom Actions: Text Overlay If Taxonomy Term Selected» Text Overlay If Taxonomy Term Selected
Component:Custom Actions Module» Text Actions Module

K so the text module can eval php... anyway here is my test code.

<?php
 
//code snippet for custom text baised on term id.
 
$text = 'False';
 
$term_id = 70; // Term ID.

 
if ((arg(0) == 'node') && is_numeric(arg(1))) {
   
$terms = taxonomy_node_get_terms(arg(1));
    foreach(
$terms as $term) {
      if (
$term->tid == $term_id) {
        
$text = 'True';
      }
    }
  }
  return
$text;
?>

#2

mikeytown2 - January 24, 2009 - 07:38

How can I get the node that is associated with the pic? Arg() returns the path to the jpg not the node.

<?php
 
// This snippet returns TRUE if the node we are
  // currently viewing is tagged with a term which is
  // the 'desired_term' and we are not in edit mode (arg(2)).

  // put here the term ID you're interested in
 
$desired_term = 70;

 
$node = node_load(108); // cached
  //$node = node_load(arg(1)); // cached
  // If the term does not exist we're done
 
if (is_array($node->taxonomy)) {
    foreach (
$node->taxonomy as $term) {
      if (
$term->tid == $desired_term) {
        return
'TRUE';
      }
    }
    return
'1/2 True';
  }
  return
'FALSE';
?>

#3

mikeytown2 - February 2, 2009 - 11:33
Title:Text Overlay If Taxonomy Term Selected» Text Overlay using Node Data

Going to mess around with

$_SERVER[’HTTP_REFERER’];

But I think it only would work if your viewing that pic on that node. doing it in a View would mess up the image generation most likely.

Another idea would be to search the DB. Any hints on what tables to search in to get a 1 to 1 match (using filename)? $_SERVER['REQUEST_URI']; should give me the filename.

#4

dman - February 2, 2009 - 11:49

You are looking in the right places, but have a look at the existing source of imagecache_actions/textactions.inc ...
inside textactions_evaluate_text() the filepath is already available for use in your custom code - as $caption->path or $image->filepath.

$node should already be available if you were using image.module. This is a prime use of this facility, so that function was built-in. (although maybe not well documented)
And the same could be made available if someone was able to repair the imagefield version.

<?php

function textactions_node_from_filepath($filepath) {
 
$sql = "SELECT nid FROM {image} INNER JOIN {files} AS f WHERE f.filepath = '%s' LIMIT 1";
  if (
$nid = db_result(db_query($sql, $filepath))){
    return
node_load($nid);
  }
}
?>

#5

mikeytown2 - February 3, 2009 - 00:10

$node doesn't work 100% of the time.

Been looking at the Drupal DB and this is what I think will work...
Get file name via REQUEST_URI

<?php
//Get URI
//Split string into array, delimited by '/'
//Pop the last value off of array
//Split string into array, delimited by '?'
//Grab the first value of the array
//U now have the last value without GET variables
$pic_location = array_shift(explode('?',array_pop(explode('/',$_SERVER['REQUEST_URI']))));
?>

Search files table for that name

SELECT *
FROM `files`
WHERE `filename` = '$pic_location'

Grab fid from the query. $fid

Search CCK field for that fid (any way to search all cck imagefields?)

SELECT *
FROM `content_field_FIELD-NAME`
WHERE `field_image_main_fid` = $fid

grab nid from the query. $nid
We now have the node ID. Time to party.

#6

mikeytown2 - February 3, 2009 - 00:30

Here's the query for all the image fields

SELECT *
FROM `content_node_field`
WHERE `type` LIKE 'image'

grab field_name

#7

dman - February 3, 2009 - 00:33

Yes, $node is not yet supported FOR IMAGEFIELDS. I think I documented that a few times.
It's currently working with image.module only. See above.

That code you've got there is probably half of the bit we need to re-enable imagefield support - although we still need the magic that will detect the image field name. That's the bit that got tricky between D5 and D6 and was disabled.

You will see that the above textactions_node_from_filepath($filepath) function already has the original file filepath, so no REQUEST_URI hacking there needed.

#8

mikeytown2 - February 3, 2009 - 02:10

here's my guess at it... doesn't work but should be close. Maybe u can help me

<?php
//Get URI
//Split string into array, delimited by '/'
//Pop the last value off of array
//Split string into array, delimited by '?'
//Grab the first value of the array
//U now have the last value without GET variables (filename)
$filename = array_shift(explode('?',array_pop(explode('/',$_SERVER['REQUEST_URI']))));

//Search files table for that filename
$sql = "SELECT fid FROM {files} WHERE {filename} = '$s' LIMIT 1";
$field_id = db_result(db_query($sql, $filename));

//search all cck fields for image type
$sql = "SELECT field_name FROM {content_node_field} WHERE {type} = 'image'";
$cck_image_field_names_FROM = db_fetch_array(db_query($sql));
$cck_image_field_names_WHERE = $cck_image_field_names_FROM;
//add 'content_' to the begining of every item in the array
foreach ($cck_image_field_names_FROM as &$value) {
   
$value = 'content_' . $value;
}
unset(
$value); // break the reference with the last element
//Prep for SQL Query
$cck_image_field_names_FROM = implode(", ", $cck_image_field_names_FROM); // content_field_FIELD-NAME1, content_field_FIELD-NAME2, content_field_FIELD-NAME3
$cck_image_field_names_WHERE = implode(", ", $cck_image_field_names_WHERE); //field_FIELD-NAME1, field_FIELD-NAME2, field_FIELD-NAME3

//Search CCK field for that fid, get Node ID
$sql = "SELECT nid FROM '%s' WHERE '%s' = '%s' LIMIT 1";
$nid = db_result(db_query($sql, $cck_image_field_names_FROM, $cck_image_field_names_WHERE, $field_id));
?>

I think the problem is the last WHERE sql command.

EDIT: FIXED some dumb errors

#9

dman - February 3, 2009 - 02:21

It's doing the right thing (by sight) yep...

But I feel there must be some better API calls to - eg - cck_get_all_field_names()
I simply haven't had the dedication to delve into the latest CCK API code to find the bits we need. Yet direct DB queries like this are also to be avoided. Especially for something as deep as CCK.
... which is why I've been avoiding it altogether!

RE the above code, I don't know if it's even possible to select from multiple tables like that on the fly.

Lazy way, but me, I'd probably loop on the tables and make multiple queries. Not good code, but at least understandable.

The code above will certainly have problems - although I can't debug it right now.

#10

mikeytown2 - February 5, 2009 - 01:26
Status:active» needs review

Place inside "Text:" Text Area Field and set "Evaluate text as PHP code" to TRUE.

If someone wants to fix the SQL statements so they are safe, that would be nice

<?php
//Get URI
//Split string into array, delimited by '/'
//Pop the last value off of array
//Split string into array, delimited by '?'
//Grab the first value of the array
//U now have the last value without GET variables (filename)
$out = "";
$file = array_shift(explode('?',array_pop(explode('/',$_SERVER['REQUEST_URI']))));

 
//mutiple file ID's might have the same name, get all
 
$sqlA = "SELECT fid FROM {files} WHERE filepath LIKE '%" .$file. "%'";
 
$result = db_query($sqlA);
 
$fids = array();
  while (
$temp = db_fetch_array($result)) {
   
$fids[] = $temp['fid'];
  }


 
// Find out if any filefield contains this file, and if so, which field
  // and node it belongs to. Required for later access checking.
 
$cck_files = array();
 
 
//Itterate though all file ID's
 
for ($x = 0; $x <= count($fids); $x++) {
   
//Get A List Of All CCK Fields
   
foreach (content_fields() as $field) {
     
//If Field is an Image then
     
if ($field['type'] == 'image') {
       
       
//Grab All DB Column Names for that CCK Field
       
$db_info = content_database_info($field);
       
//Get Content Type DB name - FROM statement
       
$table = $db_info['table'];
       
//Get File ID DB Column Name - WHERE statement
       
$fid_column = $db_info['columns']['fid']['column'];

       
//Build Select Statement
       
$columns = array('vid', 'nid');
        foreach (
$db_info['columns'] as $property_name => $column_info) {
         
$columns[] = $column_info['column'] .' AS '. $property_name;
        }
       
$sqlB = "SELECT " . implode(', ', $columns) ."
                            FROM {"
. $table ."}
                            WHERE "
. $fid_column ." = " . $fids[$x];
       
$result = db_query($sqlB);
       
       
//Get NID
       
while ($temp = db_fetch_array($result)) {
          if (
$temp['nid'] != "" ) {
           
$cck_files[] = $temp['nid'];
          }
        }
      }
    }
  }
 
$nid = $cck_files[0];

 
$desired_term = 70;
 
$node = node_load($nid); // cached
  // If the term does not exist we're done
 
if (is_array($node->taxonomy)) {
    foreach (
$node->taxonomy as $term) {
      if (
$term->tid == $desired_term) {
        return
'True';
      }
    }
    return
'Taxonomy Term Does not match';
  }
  return
'Pic not connected to a node';
?>

@dman How can I access $filepath? variable is empty in the scope of the text eval.

#11

mikeytown2 - February 5, 2009 - 09:21

got the base name of the image & fixed some of the SQL so it more secure

<?php
//Get basename of image
$file = $image->source;

//multiple file ID's might have the same name, get all
$sqlA = "SELECT fid FROM {files} WHERE filepath = '%s'";
$result = db_query($sqlA, array($file));
$fids = array();
while (
$temp = db_fetch_array($result)) {
 
$fids[] = $temp['fid'];
}


// Find out if any filefield contains this file, and if so, which field
// and node it belongs to. Required for later access checking.
$cck_files = array();

//Iterate though all file ID's
for ($x = 0; $x <= count($fids); $x++) {
 
//Get A List Of All CCK Fields
 
foreach (content_fields() as $field) {
   
//If Field is an Image then
   
if ($field['type'] == 'image') {
     
     
//Grab All DB Column Names for that CCK Field
     
$db_info = content_database_info($field);
     
//Get Content Type DB name - FROM statement
     
$table = $db_info['table'];
     
//Get File ID DB Column Name - WHERE statement
     
$fid_column = $db_info['columns']['fid']['column'];

     
//Build Select Statement
     
$columns = array('vid', 'nid');
      foreach (
$db_info['columns'] as $property_name => $column_info) {
       
$columns[] = $column_info['column'] .' AS '. $property_name;
      }
     
$colums = implode(', ', $columns);
     
$sqlB = "SELECT ".$colums." FROM {".$table."} WHERE ".$fid_column." = ".$fids[$x];
     
$result = db_query($sqlB);
     
     
//Get NID
     
while ($temp = db_fetch_array($result)) {
        if (
$temp['nid'] != "" ) {
         
$cck_files[] = $temp['nid'];
        }
      }
    }
  }
}
$nid = $cck_files[0];

$desired_term = 70;
$node = node_load($nid); // cached
// If the term does not exist we're done
if (is_array($node->taxonomy)) {
  foreach (
$node->taxonomy as $term) {
    if (
$term->tid == $desired_term) {
      return
'True';
    }
  }
  return
'Taxonomy Term Does not match';
}
return
'Pic not connected to a node';
?>

I think this is the best I can do, without some sort of real access to the referring node.

#12

mikeytown2 - February 5, 2009 - 12:16

Got This
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1 query: SELECT vid, nid, field_image_additional_fid AS fid, field_image_additional_list AS list, field_image_additional_data AS data FROM content_field_image_additional WHERE field_image_additional_fid = in .../includes/common.inc(1648) : eval()'d code on line 38.

It has to do with my for loop. <= should be <
Line 18

#13

dman - February 7, 2009 - 16:26

OK.
Your efforts have prompted me to visit this and work it (or something based on it) into our text evaluator...
You showed it could be done, and provided enough of the groundwork. I then refactored the code a few times:
- placed the content-type loop outside the fid loop
- munged the SQL back down into just what we needed
- munged the lookup further to scan for multiple fid matches (unlikely maybe impossible , but whatever) in one query
- saw that filefield supports image 'description' next to the field so added support for that TOO.
- added support for upload.module and filefield.module while I was there.

Yeah, all good.
I'll roll it into dev.

Enter PHP code that will return your dynamic text. Do not use %php tags.

EG return format_date(time());

return $file_data->description ? $file_data->description : $node->title;

Note that executing incorrect PHP-code can break your Drupal site.

If it\'s an image.module image then a $node object with its values
may be available.

return $node->title;

return format_date($node->created);

If it\'s an image that has been attached to a node using CCK-filefield-imagefield
(or just filefield)
then as well as the parent $node object,
a $file_data object that may contain a file description from that file field.

return $file_data->description;
So far that seems to be the only available \'data\' provided by filefield,
but you can investigate the node strucure using devel.module or print_r()
to see what else this array actually contains.

If it\'s a file that\'s just been attached using upload.module,
a $file_data object may also have a description.

return $file_data->description;

If the image path is detected as belonging to more than one node, just the
data for the first one found is returned.

An "$image" object is also available, but that usually contains only technical data, including

return $image->source;

return basename($image->source);

return $image->info["filesize"];

#14

mikeytown2 - February 7, 2009 - 18:22

Sweet, I'm glad I inspired you.

- munged the lookup further to scan for multiple fid matches (unlikely maybe impossible, but whatever) in one query.

I've created and destroyed the same node with the same pics a couple of times, and the data was still in the DB from it. Thus I got multiple matches, with only one "live" one; so it is necessary. I spent some time figuring that one out.

#15

dman - February 7, 2009 - 22:01

Hm.
That (in the first case) sounds like we really SHOULD be paying attention to the node VERSION id, not just the nid.
Really should. I saw the column, but I just didn't want to think about it right then.

Me, I was hoping that the file-attachment utils will get with the program and start to support re-use of existing images where possible. That makes THIS job harder, but (IMO) website management easier/better.

#16

mikeytown2 - February 8, 2009 - 00:43

I should say the same node content, but it ended up having different node id's due to multiple deletions/creations. Versions/Revisions is something I never considered.

#17

dman - February 12, 2009 - 23:23
Status:needs review» fixed

I'll tenatively call this 'fixed'
it's in 6.x-1.2

#18

asak - February 14, 2009 - 14:40

So.. should this method now work with an image overlay as well? or just a text overlay?

(continuing my thoughts from http://drupal.org/node/323455#comment-1218756)

Nice work !

#19

mikeytown2 - February 14, 2009 - 17:35

some code that is related to this:
http://drupal.org/node/374202

Set the action via rules... edit a page auto regenerate images.

#20

System Message - February 28, 2009 - 17:40
Status:fixed» closed

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

#21

dman - March 18, 2009 - 01:26

Just using this old issue to publish a new image for the docs.
- I added a howto recipe of text-from-image possibilities Into the docs just now.

AttachmentSize
text.jpg 18.66 KB

#22

ccshannon - April 10, 2009 - 03:32

Does this mean if I have imagefield/filefield images displaying on a home or section page (so there are many imagecache images from various nodes on one page) and the text overlay action calls 'return $node->title;' it _should_ get the title of the node the image is from?

What I'm getting is all the images are getting the same node title from one node ... the newest node on the site. It's not even a node displayed on the same page as the images.

So, all the nodes get the same information. If I'm misunderstanding what the update covers (using 6.x-1.5) then maybe I should delve into mikeytown2's code to retrieve nid for fid?

Thanks for all the work on this, btw.

#23

dman - April 10, 2009 - 03:44

It certainly SHOULD be using the info relative to the image itself.
Not sure how many images could get one title, unless it was like node #0 and they were retrieving blanks or something (cannot happen)

Yes, the problem must be in the lookup that traces image-path to nid. It's a little heuristic at the moment,
have a look.

<?php
/**
* Given only a file filename, track back to see if we can detect the parent
* node and provide some context info.
*
* This will be different in different cases.
* Supported :
* image.module image nodes
* imagefield cck fields (may be multiple)
* TODO:
* upload.module attachments
* image_attach attachments
*/
function textactions_node_from_filepath($filepath, &$file_data = NULL) {

 
// lookup upload.module attachments
 
if (module_exists('upload')) {
   
$sql = "SELECT nid, f.fid FROM {upload} INNER JOIN {files} AS f WHERE f.filepath = '%s' LIMIT 1";
   
$results = db_query($sql, $filepath);
    if (
$row = db_fetch_array($results)) {
     
// Return immediately
     
$node = node_load($row['nid']);
     
// also include the file description
     
$file_data = $node->files[$row['fid']];
      return
$node;
    }
  }

 
// Lookup image.module nodes
 
if (module_exists('image')) {
   
$sql = "SELECT nid FROM {image} INNER JOIN {files} AS f WHERE f.filepath = '%s' LIMIT 1";
    if (
$nid = db_result(db_query($sql, $filepath))){
     
// Return immediately
     
return node_load($nid);
    }
  }


 
// Lookup filefield imagefield CCK attachments.
  //
  // Drupal 6 version here based largely on work done by mikeytown2
  // drupal.org/node/363434
 
  // Multiple file ID's might have the same name, get all
  // (but return just the first successful match)
 
$result = db_query("SELECT fid FROM {files} WHERE filepath = '%s'", $filepath);
 
$fids = array();
  while (
$row = db_fetch_array($result)) {
   
$fids[] = $row['fid'];
  }

  if (! empty(
$fids)) {
   
// Find out if any filefield contains this file, and if so, which field
    // and node it belongs to. Required for later access checking.
    // CCK filed analysis is in the outer loop, fids are scanned in the inner loop for a little speed.

    // Get A List Of All CCK Fields, it's the only we we can reverse the lookups
   
foreach (content_fields() as $field) {
     
// If Field is an Image (imagefield.module) or filefield then
     
if ($field['type'] == 'image' || $field['type'] == 'filefield') {
       
// Need to do lots of lookups to find out what the storage tables look like.
        // Grab All DB Column Names for that CCK Field
       
$db_info = content_database_info($field);
       
// Get Content Type DB name - FROM statement
       
$tablename = $db_info['table'];
       
//Get File ID DB Column Name - WHERE statement
       
$fid_column = $db_info['columns']['fid']['column'];

       
// Construct a Query that looks for all known fids in one go.
        // eg:
        // SELECT nid FROM content_type_story
        //   WHERE field_illustration_fid = 77
        //   OR field_illustration_fid = 99;
       
       
$wheres = array();
       
$query_args = array();
        foreach (
$fids as $fid) {
         
$wheres[] = " %s = %d ";
         
$query_args[] = $fid_column;
         
$query_args[] = $fid;
        }
       
$result = db_query("SELECT nid FROM {$tablename} WHERE ". join(' OR ', $wheres), $query_args);
        while (
$row = db_fetch_array($result)) {
         
// This while is a dummy loop - Just break out and return the first matching node.
          // If more than one node owns this image then ???
         
$node = node_load($row['nid']);

         
// Add even more info - the description data that MAY have been added to this file on this node.
          // return the data associated with this file also;
          // Slightly mushed together - I want it to mostly resemble the traditional file attachment object.
          // We have the node but lost track of the file
          // Need to scan again to make sure we got the right one :-{
         
if( $file_fields = $node->{$field['field_name']} ) {
            foreach(
$file_fields as $file_field) {
              if (
$file_field['fid'] == $fid) {
               
$file_data = (object) array_merge($file_field['data'], $file_field);
              }
            }
          }
          return
$node;
        }
      }
    }
  }
}
?>

#24

ccshannon - April 16, 2009 - 05:13

Upon further view, it's not the imagefield/filefield.

I have a homepage template that loads nodes via a number of nodereference fields stored in the homepage 'node'.

The template runs theme('imagecache' ....) on each image path from the loaded ref nodes. Some images receive a larger scale preset than others, depending on their placement in the page.

I added a "dpm($file_data)" (have dev module installed) to the PHP block for one of the presets and I expected to get a message for each image (in this case 10 images) but only got one and it was an object class, no data.

Tried again with dpm($image) and got one msg again: the image object for the sample image back in the Preset settings.

I wonder if I could pass the data I need to use (a short string stored in the referenced node) into the theme function for the img tag.

For instance, how would I extract the image's alt text for overlay?

Anyway, it appears to be the way I am calling imagecache from the template.

#25

dman - April 16, 2009 - 05:59

um ... ah?
Well that pipeline sounds like the sort of madness I'd build for myself, so it makes sense.
I'm thinking you are calling it too late however.

In short -
imagecache gets a request for a image to process. All it knows is the name of the requested file and the preset definitions.
Imagecache_actions gets that info and does its best to look at the database and figure out who 'owns' the file and return the associated node data.

Only *most* files have database entries to use. I may be wrong, but it sounds like the process is running on already-cooked files?
Already-processed imagecache files don't really exist in the DB. You'll have to pass in the actual uploaded img filename, not the already-processed derivative.

template runs theme('imagecache' ....) on each image path from the loaded ref nodes
That path may already be cooked. Which means I can't find the owner.

That said, the way I look at the database to guess the owner is highly dependant on CCKs database schema. There just was no good way to get from 'image' to 'owner'. That heuristic may need work. I could only model it on what appeared to work from introspection of the tables themselves.

A heuristic to guess the source of already-cooked file paths may be possible ... but it starting to get really out there.

 
 

Drupal is a registered trademark of Dries Buytaert.