Commit b16b8fec authored by Mikhail Khasaya's avatar Mikhail Khasaya Committed by Mikhail Khasaya
Browse files

Issue #3301520 by Munavijayalakshmi, Harsh panchal, dillix: Phpcs Drupal standard coding issue

parent 27b531d4
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
name: ODT Importer
type: module
description: This module converts .odt documents to HTML code and store them into configured text field.
name: 'ODT Importer'
description: 'This module converts .odt documents to HTML code and store them into configured text field.'
core_version_requirement: ^9 || ^10
package: Fields
dependencies:
+8 −5
Original line number Diff line number Diff line
<?php

/**
 * @file
 * Contains odt_importer.install.
 */

/**
 * Implements hook_requirements().
 * @param $phase
 * @return array
 */
function odt_importer_requirements($phase) {
  $requirements = [];
@@ -18,7 +21,7 @@ function odt_importer_requirements($phase) {
      ];
    }

    if (!in_array("zip", stream_get_wrappers())) {
    if (!in_array('zip', stream_get_wrappers())) {
      $requirements['zip'] = [
        'title' => 'Zip Extension',
        'value' => $t('Not installed'),
+8 −5
Original line number Diff line number Diff line
@@ -4,8 +4,8 @@
 * @file
 * Contains odt_importer.module.
 */

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\odt_importer\OdtConverter;
use Drupal\Core\Routing\RouteMatchInterface;

@@ -43,10 +43,10 @@ function odt_importer_entity_presave(EntityInterface $entity) {
    $fields = \Drupal::service('entity_field.manager')->getFieldDefinitions($entity->getEntityType()->id(), $entity->bundle());
    foreach ($fields as $field_name => $field_definition) {
      if (!empty($field_definition->getTargetBundle()) && $field_definition->getType() == 'odt_file') {
        // Load destination field from settings and check its existance
        // Load destination field from settings and check its existance.
        $destination_field = $field_definition->getSettings()['destination_field'];
        if ($entity->hasField($destination_field)) {
          // Save user defined format
          // Save user defined format.
          // @todo create separate format and use it by default or instead NULL?
          $format = (isset($entity->$destination_field->format) && !empty($entity->$destination_field->format)) ? $entity->$destination_field->format : NULL;

@@ -71,7 +71,10 @@ function odt_importer_entity_presave(EntityInterface $entity) {
            'images_folder' => 'images',
          ], $odt_file_path);

          $entity->set($destination_field, (['value' => $odt_converted->markup(), 'format' => $format]));
          $entity->set($destination_field, ([
            'value' => $odt_converted->markup(),
            'format' => $format,
          ]));
        }

        break;
+139 −129
Original line number Diff line number Diff line
@@ -2,23 +2,23 @@

namespace Drupal\odt_importer;

use \XMLReader;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
 * Defines an ODT to HTML conversion implementation.
 * Global ToDos.
 *
 * 1. Parameter and return typehinting for class methods:
 * https://www.drupal.org/docs/develop/standards/coding-standards#s-parameter-and-return-typehinting.
 *
 * 2. Create templates for elements for easy overriding markup in themes.
 */

/**
 * Global ToDos: 
 * - Parameter and return typehinting for class methods:
 * https://www.drupal.org/docs/develop/standards/coding-standards#s-parameter-and-return-typehinting
 * - Create templates for elements for easy overriding markup in themes
 * Defines an ODT to HTML conversion implementation.
 */

class OdtConverter {

  use MessengerTrait;
  use StringTranslationTrait;

@@ -34,7 +34,7 @@ class OdtConverter {
   *
   * @var string
   */
  protected $images_folder;
  protected $imagesFolder;

  /**
   * Converted html.
@@ -55,7 +55,7 @@ class OdtConverter {
   */
  public function __construct($config, $odt_file) {
    // Configuration settings.
    // ToDo: Add this settings to Drupal configuration GUI
    // @todo Add this settings to Drupal configuration GUI.
    $this->features['header'] = (bool) $config['features']['header'] ?? TRUE;
    $this->features['list'] = (bool) $config['features']['list'] ?? TRUE;
    $this->features['table'] = (bool) $config['features']['table'] ?? TRUE;
@@ -67,7 +67,7 @@ class OdtConverter {
    $this->features['toc'] = (bool) $config['features']['toc'] ?? TRUE;
    $this->features['bookmark'] = (bool) $config['features']['bookmark'] ?? TRUE;

    $this->images_folder = $config['images_folder'] ?? 'images';
    $this->imagesFolder = $config['images_folder'] ?? 'images';

    $this->html = '';
    $this->footnotes = '';
@@ -87,7 +87,7 @@ class OdtConverter {
  /**
   * {@inheritdoc}
   */
  protected function is_image($file) {
  protected function isImage($file) {

    $image_extensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];
    $ext = pathinfo($file, PATHINFO_EXTENSION);
@@ -95,15 +95,15 @@ class OdtConverter {
      return FALSE;
    }

    // ToDo: change mime_content_type to symphony MimeTypeGuesserInterface
    // @todo Change mime_content_type to symphony MimeTypeGuesserInterface?
    return (strpos(@mime_content_type($file), 'image') === 0);
  }

  /**
   * {@inheritdoc}
   */
  protected function copy_file($from, $to) {
    //ToDo: change to file.repository service
  protected function copyFile($from, $to) {
    // @todo Change to file.repository service.
    $file = file_get_contents($from);

    // Load the service statically.
@@ -114,7 +114,6 @@ class OdtConverter {

  /**
   * Function that preparses the XML and push it to convert()
   *
   */
  protected function prepare($odt_file, $xml_string = NULL) {
    if ($xml_string === NULL) {
@@ -126,12 +125,11 @@ class OdtConverter {
      }
    }

    // Remove page numbers from TOC
    // Remove page numbers from TOC.
    $xml_string = preg_replace('/[ \t]*<text:tab\/>\d{1,}/isu', '', $xml_string);

    // Replace invalid links to h1,h2,h3 & etc. anchors
    //href="#\d{1,}\.([\w\t\d ]){2,}\|outline
    
    // href="#\d{1,}\.([\w\t\d ]){2,}\|outline.
    preg_match_all("/<text:bookmark-start text:name=\"([A-Za-z0-9_-]+)\"\/>(.*?)<text:bookmark-end text:name=\"([A-Za-z0-9_-]+)\"\/>/isu", $xml_string, $raw_bookmarks);

    $bookmarks = [];
@@ -145,20 +143,19 @@ class OdtConverter {
      return $bookmarks[$match[1]];
    }, $xml_string);

    // Revise this:
    // Convert breaks to br's
    // @todo Revise converion breaks to br's:
    // $xml_string = str_replace('<text:line-break/>', '<br />', $xml_string);
    
    return $this->convert($odt_file, $xml_string);
  }

  /**
   * Function that parses the XML and converts it to HTML. If $xml is not
   * provided, extract content.xml from $odt_file
   * Function that parses the XML and converts it to HTML.
   *
   * If $xml is not provided, extract content.xml from $odt_file.
   */
  protected function convert($odt_file, $xml_string = NULL) {
    // ToDo: move to protected variable?
    $xml = new XMLReader();
    // @todo Move to protected variable?
    $xml = new \XMLReader();

    if ($xml_string === NULL) {
      if (@$xml->open('zip://' . $odt_file . '#content.xml') === FALSE) {
@@ -175,17 +172,16 @@ class OdtConverter {
      }
    }

    // Now, convert the xml from a string to an 
    // Now, convert the xml from a string to an HTML.
    $elements_tree = [];

    static $styles = ['Quotations' => ['tags' => ['blockquote']]];

    $translation_table = [];

    // ToDo: Check if we need to return this here? See below: case 'draw:frame'
    // @todo Check if we need to return this here? See below: case 'draw:frame'.
    // $translation_table['draw:frame'] = 'div class="odt-frame"';
  
    // ToDo: implement support for ol li lists
    // @todo Implement support for ol li lists.
    if ($this->features['list']) {
      $translation_table['text:list'] = 'ul';
      $translation_table['text:list-item'] = 'li';
@@ -211,44 +207,52 @@ class OdtConverter {
    }

    while ($xml->read()) {
      $opened_tags = []; //This array will contain the HTML tags opened in every iteration
      // This array will contain the HTML tags opened in every iteration.
      $opened_tags = [];

      if ($xml->nodeType === XMLReader::END_ELEMENT) {
        // Handle a closing tag
      if ($xml->nodeType === \XMLReader::END_ELEMENT) {
        // Handle a closing tag.
        if (empty($elements_tree)) {
          continue;
        }

        // Close every opened tag. This should also handle malformed XML files.
        do {
          $element = array_pop($elements_tree);
          if ($element && $element['tags']) {
            //Close opened tags
            // Close opened tags.
            $element['tags'] = array_reverse($element['tags']);
            foreach ($element['tags'] as $html_tag) {
              $html_tag = current(explode(' ', $html_tag));
              $this->html .= '</' . $html_tag . '>';
            }
          }
        } while ($xml->name !== $element['name'] && $element); //Close every opened tags. This should also handle malformed XML files
        } while ($xml->name !== $element['name'] && $element);
        continue;
      }
      elseif (in_array($xml->nodeType, [XMLReader::ELEMENT, XMLReader::TEXT, XMLReader::SIGNIFICANT_WHITESPACE])) {
        // Handle tags
      elseif (in_array($xml->nodeType, [
        \XMLReader::ELEMENT,
        \XMLReader::TEXT,
        \XMLReader::SIGNIFICANT_WHITESPACE,
      ])) {
        // Handle tags.
        switch ($xml->name) {
          // Text
          // Text.
          case '#text':
            $this->html .= htmlspecialchars($xml->value);
            break;

          // Title
          // Title.
          case 'text:h':
            if ($this->features['header']) {
              $n = $xml->getAttribute('text:outline-level');
              if ($n > 6) $n = 6;
              if ($n > 6) {
                $n = 6;
              }
              $opened_tags[] = 'h' . $n;
              $bookmark = '';

              // Bookmarks when table of contents exists in odt
              // Bookmarks when table of contents exists in odt.
              if ($this->features['bookmark']) {
                $child_xml_string = $xml->readInnerXML();
                if (!empty($child_xml_string)) {
@@ -266,26 +270,26 @@ class OdtConverter {
            }
            break;

          // Paragraph
          // Paragraph.
          case 'text:p':
            //Just convert odf <text:p> to html <p>
            // Just convert odf <text:p> to html <p>.
            if ($xml->isEmptyElement) {
              // Self closed empty <text:p/> tag
              // Self closed empty <text:p/> tag.
              $this->html .= "\n<p>&nbsp;</p>";
            }
            else {
              $tags = @$styles[$xml->getAttribute('text:style-name')]['tags'];
              if (!(is_array($tags) && in_array('blockquote', $tags))) {
                // Do not print a <p> immediatly after or before a <blockquote>
                // Do not print a <p> immediatly after or before a <blockquote>.
                $opened_tags[] = 'p';
                $this->html .= "\n<p>";
              }
            }
            break;

          // Link
          // ToDo: add options for rel="nofollow" and target="_blank"
          // for external links
          // Link.
          // @todo add options for rel="nofollow" and target="_blank"
          // for external links.
          case 'text:a':
            if ($this->features['link']) {
              $href = $xml->getAttribute('xlink:href');
@@ -302,7 +306,7 @@ class OdtConverter {
            $this->html .= '&nbsp;&nbsp;&nbsp;&nbsp;';
            break;

          // Frame
          // Frame.
          case 'draw:frame':
            if ($this->features['frame']) {
              $frame_type = $xml->getAttribute('text:anchor-type');
@@ -321,20 +325,20 @@ class OdtConverter {
            }
            break;

          // Image
          // Image.
          case 'draw:image':
            if ($this->features['image']) {
              $image_file = 'zip://' . $odt_file . '#' . $xml->getAttribute("xlink:href");
              if (isset($this->images_folder) && is_dir($this->images_folder)) {
                if ($this->is_image($image_file)) {
                  $image_to_save = $this->images_folder . '/' . basename($image_file);
                  if (!($src = $this->copy_file($image_file, $image_to_save))) {
              if (isset($this->imagesFolder) && is_dir($this->imagesFolder)) {
                if ($this->isImage($image_file)) {
                  $image_to_save = $this->imagesFolder . '/' . basename($image_file);
                  if (!($src = $this->copyFile($image_file, $image_to_save))) {
                    $this->messenger()->addError('Unable to move image file.');
                    break;
                  }
                  else {
                    // ToDo: make config option for size of inline images
                    // ToDo: check this with xml and webp images
                    // @todo Make config option for size of inline images.
                    // @todo Check this with xml and webp images
                    $image_size = getimagesize($image_to_save);
                    if ((int) $image_size[0] < 128) {
                      $class = 'img-inline';
@@ -362,35 +366,38 @@ class OdtConverter {
            }
            break;

          case "text:line-break"://br
            $this->html .= "\n";//<br />
          // Line break (<br />).
          case "text:line-break":
            $this->html .= "\n";
            break;

          case "style:style":
            $name = $xml->getAttribute('style:name');
            $parent = $xml->getAttribute('style:parent-style-name');
            if (array_key_exists($parent, $styles)) {
              $styles[$name] = $styles[$parent]; //Not optimal
              // Not optimal.
              $styles[$name] = $styles[$parent];
            }

            if ($xml->isEmptyElement) {
              break; //We can't handle that at the moment
              // We can't handle that at the moment.
              break;
            }

            // Read one tag
            while ($xml->read() && ($xml->name != 'style:style' || $xml->nodeType != XMLReader::END_ELEMENT)) {
            // Read one tag.
            while ($xml->read() && ($xml->name != 'style:style' || $xml->nodeType != \XMLReader::END_ELEMENT)) {
              if ($xml->name == 'style:text-properties') {
                //Creates the style and add <em> to its tags
                // Creates the style and add <em> to its tags.
                if ($xml->getAttribute('fo:font-style') == 'italic') {
                  $styles[$name]['tags'][] = 'em';
                }

                //Creates the style and add <strong> to its tags
                // Creates the style and add <strong> to its tags.
                if ($xml->getAttribute('fo:font-weight') == 'bold') {
                  $styles[$name]['tags'][] = 'strong';
                }

                //Creates the style and add <b> to its tags
                // Creates the style and add <b> to its tags.
                if ($xml->getAttribute('style:text-underline-style') == 'solid') {
                  $styles[$name]['tags'][] = 'u';
                }
@@ -398,18 +405,18 @@ class OdtConverter {
            }
            break;

          // Note
          // Note.
          case 'text:note':
            if ($this->features['note']) {
              $note_id = $xml->getAttribute('text:id');
              $note_name = 'Note';

              // Read one tag and stop on </style:style>              
              while ($xml->read() &&  ($xml->name != 'text:note' || $xml->nodeType != XMLReader::END_ELEMENT)) {
                if ($xml->name == 'text:note-citation' && $xml->nodeType == XMLReader::ELEMENT) {
              // Read one tag and stop on </style:style>.
              while ($xml->read() &&  ($xml->name != 'text:note' || $xml->nodeType != \XMLReader::END_ELEMENT)) {
                if ($xml->name == 'text:note-citation' && $xml->nodeType == \XMLReader::ELEMENT) {
                  $note_name = $xml->readString();
                }
                elseif ($xml->name == 'text:note-body' && $xml->nodeType == XMLReader::ELEMENT) {
                elseif ($xml->name == 'text:note-body' && $xml->nodeType == \XMLReader::ELEMENT) {
                  $note_content = $this->convert($odt_file, $xml->readOuterXML());
                }
              }
@@ -420,13 +427,14 @@ class OdtConverter {
              $this->footnotes .= '<a class="footnote-name" href="#anchor-odt-' . $note_id . '">' . $note_name . ' .</a> ';
              $this->footnotes .= $note_content;
              $this->footnotes .= '</div>' . "\n";
            } else {
            }
            else {
              $xml->next();
              break;
            }
            break;

          // Annotation
          // Annotation.
          case 'office:annotation':
            if ($this->features['annotation']) {
              $annotation_id = (isset($annotation_id)) ? $annotation_id + 1 : 1;
@@ -435,19 +443,19 @@ class OdtConverter {
              $annotation_date = '';
              do {
                $xml->read();
                if ($xml->name == 'dc:creator' && $xml->nodeType == XMLReader::ELEMENT) {
                if ($xml->name == 'dc:creator' && $xml->nodeType == \XMLReader::ELEMENT) {
                  $annotation_creator = $xml->readString();
                }
                elseif($xml->name == 'dc:date' && $xml->nodeType == XMLReader::ELEMENT) {
                  // ToDo: Add date format to module configuration
                elseif ($xml->name == 'dc:date' && $xml->nodeType == \XMLReader::ELEMENT) {
                  // @todo Add date format to module configuration.
                  $annotation_date = date("jS \of F Y, H\h i\m", strtotime($xml->readString()));
                }
                elseif ($xml->nodeType == XMLReader::ELEMENT) {
                elseif ($xml->nodeType == \XMLReader::ELEMENT) {
                  $annotation_content .= $xml->readString();
                  $xml->next();
                }
              } while (!($xml->name === 'office:annotation' && $xml->nodeType === XMLReader::END_ELEMENT));
              //End of the note
                // End of the note.
              } while (!($xml->name === 'office:annotation' && $xml->nodeType === \XMLReader::END_ELEMENT));

              $this->html .= '<sup><a href="#odt-annotation-' . $annotation_id . '" name="anchor-odt-annotation-' . $annotation_id . '" title="Annotation (' . $annotation_creator . ')">(' . $annotation_id . ')</a></sup>';
              $this->footnotes .= "\n" . '<div class="odt-annotation" id="odt-annotation-' . $annotation_id . '" >';
@@ -469,17 +477,18 @@ class OdtConverter {
                break;
              }
              $tag = explode(' ', $translation_table[$xml->name], 1);
              //$tag[0] is the tag name, other indexes are attributes
              // $tag[0] is the tag name, other indexes are attributes.
              $opened_tags[] = $tag[0];
              $this->html .= "\n<" . $translation_table[$xml->name] . '>';
            }
        }
      }

      if ($xml->nodeType === XMLReader::ELEMENT && !($xml->isEmptyElement)) { //Opening tag
      // Opening tag.
      if ($xml->nodeType === \XMLReader::ELEMENT && !($xml->isEmptyElement)) {
        $current_element_style = $xml->getAttribute('text:style-name');
        if ($current_element_style && isset($styles[$current_element_style])) {
          //Styling tags management
          // Styling tags management.
          foreach ($styles[$current_element_style]['tags'] as $html_tag) {
            $this->html .= '<' . $html_tag . '>';
            $opened_tags[] = $html_tag;
@@ -487,9 +496,10 @@ class OdtConverter {
        }
        $elements_tree[] = [
          'name' => $xml->name,
          'tags' => $opened_tags
          'tags' => $opened_tags,
        ];
      }
    }
  }

}
+10 −21
Original line number Diff line number Diff line
@@ -3,17 +3,12 @@
namespace Drupal\odt_importer\Plugin\Field\FieldType;

use Drupal\Component\Utility\Random;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StreamWrapper\StreamWrapperInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\file\Entity\File;
use Drupal\file\Plugin\Field\FieldType\FileItem;
use Symfony\Component\Mime\MimeTypeGuesserInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;

/**
@@ -37,8 +32,7 @@ class OdtFileItem extends FileItem {
   * {@inheritdoc}
   */
  public static function defaultStorageSettings() {
    return [
    ] + parent::defaultStorageSettings();
    return [] + parent::defaultStorageSettings();
  }

  /**
@@ -102,7 +96,6 @@ class OdtFileItem extends FileItem {

    // @todo check that this lib attached from parrent or uncomment line:
    // $element['#attached']['library'][] = 'file/drupal.file';

    $this->getFieldDefinition();
    $this->getFieldStorageDefinition();
    $this->getSettings();
@@ -131,13 +124,17 @@ class OdtFileItem extends FileItem {
    // Remove the description option.
    unset($element['description_field']);

    // Get all text fields for current entity type and bundle
    // Get all text fields for current entity type and bundle.
    $destination_field_options = [];

    $entity = $form_state->getFormObject()->getEntity();
    $fields = \Drupal::service('entity_field.manager')->getFieldDefinitions($entity->get('entity_type'), $entity->get('bundle'));
    foreach ($fields as $field_name => $field_definition) {
      if (!empty($field_definition->getTargetBundle()) && in_array($field_definition->getType(), ['text', 'text_long', 'text_with_summary'])) {
      if (!empty($field_definition->getTargetBundle()) && in_array($field_definition->getType(), [
        'text',
        'text_long',
        'text_with_summary',
      ])) {
        $destination_field_options[$field_name] = $field_definition->getLabel();
      }
    }
@@ -155,15 +152,6 @@ class OdtFileItem extends FileItem {
    return $element;
  }

  /**
   * {@inheritdoc}
   */
  public function preSave() {
    parent::preSave();

    // @todo Add validation for destination field?
  }

  /**
   * {@inheritdoc}
   */
@@ -199,4 +187,5 @@ class OdtFileItem extends FileItem {
    // OdtFile items do not have per-item visibility settings.
    return FALSE;
  }

}
+1 −1

File changed.

Contains only whitespace changes.

Loading