1

I am trying to replace individual images in old node body with a default image using media + ckeditor programatically so as to save disk space as there are lot of images in server.

I was successful in replacing image for an image field programmatically. However, I am not able to determine how do I replace those images embedded within a body along with body text while loading a node using node_load.

I intend to replace these images using hook_cron and run replacement operations on older nodes images.

  • Parse the body into an XML document, search for img tags, manipulate, then write back to HTML if memory serves Drupal has a few methods in the filter includes/module that might be able to help but failing that there are many ways to accomplish it yourself using standard php – Clive Mar 15 '17 at 7:47
  • @Clive, Well thats is ok will it also update the image count/stats in file_usage table information if I do so like this. I want to remove that image file from file system too not just replace the link. – sarathkm Mar 15 '17 at 8:36
  • Each img tag has data-fid which represents the file id. Should I run file_delete on these fids and replace with custom fid that I created using file_save_data? – sarathkm Mar 15 '17 at 8:45
  • If that data attribute is what the media module uses to determine the referenced file then yes, it sounds like what you suggested would be exactly the way to do it. Probably worth running in a test environment first of course :) – Clive Mar 15 '17 at 9:07
0

The purpose of this question was to replace old node images with a default image so as to reduce disk space usage, as old nodes are not frequently visited by users.

To achieve this first you load the node using node_load function, and grab the body field:

$node = node_load($nodeID);
$body = isset($node->body[$node->language][0]) ?$node->body[$node->language][0] : array();

$text = isset($body['value']) ?  $body['value'] : $body['safe_value'];

After this you create DOMDocument object:

$doc = new DOMDocument(null, 'UTF-8');

Here, UTF-8 is for languages that are not Latin/Plain English.

$doc->loadHTML(mb_convert_encoding($text, 'HTML-ENTITIES', 'UTF-8'));

Convert your text to HTML entities following UTF-8 format.

$tags = $doc->getElementsByTagName('img');

You grab all your image tag from body.

Next, Loop through those tags:

foreach ($tags as $tag) {
// Get default Image.
$file_temp = file_get_contents($urlToDefaultImage);
// Get Image information.
$info = pathinfo($urlToDefaultImage);
// Replace if it exists.
$file = file_save_data($file_temp, 'public://' . $info['basename'], FILE_EXISTS_REPLACE);
// Get old image fid.
$oldImage = $tag->getAttribute('data-fid');
// Remove the file info from db and server if not used by any.
$status = file_delete(file_load($oldImage)),
$tag->setAttribute('src', file_create_url($file->uri));
$tag->setAttribute('data-fid', $file->fid);
}

After looping through each img tags save the newly generated HTML.

$replacedBody = $doc->saveHTML();

Remove extra html, body and Doctype from the new body using str_replace:

$replacedBody = str_replace('<html><body>', '', $replacedBody);
$replacedBody = str_replace('<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">', '', $replacedBody);
$replacedBody = str_replace('</body></html>', '', $replacedBody);

Next update the newly formated body to node object:

$node->body[$node->language][0]['value'] = html_entity_decode($replacedBody);
$node->body[$node->language][0]['safe_value'] = html_entity_decode($replacedBody);

Save the node

node_save($node);

Bonus: Updating images in node image field.

Grab the field data.

$field = !empty($node->field_{machine_name}[$node->language]) ?  $node->field_{machine_name}[$node->language] : array();

Here, {machine_name} represents the name for image field you created using Content UI.

// Loop through old images.
foreach($field as $key => $row) {

    // Check if the image 
    if($row['type'] == 'image') {

        // Get default Image.
        $file_temp = file_get_contents($urlToDefaultImage);

        // Get Image information.
        $info = pathinfo($urlToDefaultImage);

        // Replace if it exists.
        $file = file_save_data($file_temp, 'public://' . $info['basename'], FILE_EXISTS_REPLACE);

        // Change the current image with default.
        $node->field_{machine_name}[$node->language][$key] = array(
        'fid' => $file->fid,
        'filename' => $file->filename,
        'filemime' => $file->filemime,
        'uid' => $file->uid,
        'uri' => $file->uri,
        'status' => $file->status,
        'display' => 1,
        );
    }
}

Performing these operation will remove default image from your db and server and will save disk space. While we need to use file_delete function when removing files from body, it is not needed when when perform this operation while we replace on an image/media field. file_delete function will safely delete the files from server if that file is not used by any other nodes in website, if second parameter is not set to TRUE. While file_manage_delete does the same thing, though it will not lookup for database and check for its existing usage, it will simply release your disk space from that file, result in 404 not found image on other reused nodes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.