1

Here's my problem. I have a D6 site which has been in production for a number of years, and in consequence has used various methods (and modules) for displaying images within body text of book pages. The latest of these modules is IMCE. Up to now (unless I've missed something), none of these mechanisms had any management of the links to images incorporated in the body text. As a result, I have a lot of links pointing to various places in my files directory,

With the arrival of Drupal 8, I've discovered "managed files" and the possibility of using "Inline Images" with CKEditor, such that files in the body text are properly managed, and you get a display in the Files listing of where the images are used.

Since this suits my use case fine, I think I don't need any alternative modules any more. However, here comes the problem: having migrated the site from D6 to D8, the physical directory where the files are kept has changed and since all the files have been copied over, all the body text links are broken.

What to do? Well, I decided to write a drush command which would look through all the body texts that contain <img> tags, extract the image file references, find the real file, create a managed file entity, attach that entity to the node, change the <img src="my file ref"> and save it back to the body.

Thanks to Berdir's friendly advice, I abandoned the original idea of using SQL to go directly through the table, and decided to have a go at Entity Queries instead (first time ever!). So we have this:

$query = \Drupal::entityQuery('node');
$query->condition('status', 1);
$query->condition('type', 'book');
$entity_ids = $query->execute();
foreach($entity_ids as $nid) {
  $node = Node::load($nid);
  $body = $node->get('body')->getValue();
  $body_text = $body['value'];
  $summary = $body['summary'];
  $format = $body['format'];
  }

So far so good. I won't bore you with the details of how I extract the file name and directory, but I can do this. So now I want to create and save a file entity, and then attach it to the node. This code creates the file entity (but see Berdir's answer below, not everything is necessary here):

    $file_name = end(explode("/", $src));
    $file = entity_create('file');
    $file->setFilename($file_name);
    $file->setFileUri('public://images/'.$file_name);
    $file->setSize($size);
    $file->language('en');
    $file->isNew();
    $file->setTemporary();
    $file->save();

(I confess that I'm really struggling to get my head round the Drupal 8 API and object programming...).

This actually works, it does create a temporary file entity which shows up properly in the Content list (except the MIME type is missing, that needs adding).

However, this is probably not the way to go because there surely must be some mechanism for creating the file entity and attaching it to the node without going through all this - just as CKEditor does when you add an inline image.

I've tried getting a list of all the methods proposed by the Node class, and thought maybe $mynode->referencedEntities() would show me the way - but this doesn't include inline images (I tried with a new test node that does contain an inline image, and it doesn't show here). Should it perhaps appear via a link to the body text as a field entity? And if so, where to go to get this information, and manipulate it?

I have found this in the Editor core module, perhaps this is the way to go?

function _editor_record_file_usage(array $uuids, EntityInterface $entity) {
   foreach ($uuids as $uuid) {
    if ($file = \Drupal::entityManager()->loadEntityByUuid('file', $uuid)) {
    if ($file->status !== FILE_STATUS_PERMANENT) {
      $file->status = FILE_STATUS_PERMANENT;
      $file->save();
    }
  \Drupal::service('file.usage')->add($file, 'editor', $entity->getEntityTypeId(), $entity->id());
    }
  }
}
0

A few things..

You should avoid raw sql queries against entity tables, they are managed and it is not guaranteed that the structure will always break. Use entity queries and then load the entities using Node::loadMultiple() for example, or $storage->loadMultiple().

A few lines of your existing code are not necessary. the size is calculated, language() is a getter that has no arguments, it is automatically new.

To set the uid, you can use setOwner($user) or setOwnerId($uid), and there are corresponding getter methods on the node objects, so once you load them, you can just set it from the node with $file->setOwner($node->getOwner())

However, that seems to be a completely different question than what you have in the title. That just syncs the owner (which is not that important, not for public files at least). It doesn't do anything to connect them to the node. To get the usage tracking, you can rely on what Drupal does by default if you include the UUID as an data attribute. I don't remember the exact name right now, just manually add an image with CKEditor and you'll see it in the source code. If that's there then Drupal does it for you automatically.

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.