migrate_spip-1.0.0/src/Plugin/SpipRichText/Tables.php

src/Plugin/SpipRichText/Tables.php
<?php

declare(strict_types=1);

namespace Drupal\migrate_spip\Plugin\SpipRichText;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\migrate_spip\Attribute\SpipRichText;
use Drupal\migrate_spip\SpipRichTextBase;

/**
 * Manage SPIP tables.
 */
#[SpipRichText(
  id: 'tables',
  label: new TranslatableMarkup('Tables'),
  weight: -30,
)]
final class Tables extends SpipRichTextBase {

  /**
   * Define th span shortcut pattern.
   *
   * @var string
   */
  const TH_SPAN_PATTERN = '\s*(:?{{[^{}]+}}\s*)?|<';

  /**
   * {@inheritdoc}
   */
  public function apply(string $text): string {
    $patterns = $replacements = [];

    // Tables init.
    $patterns[] = "#^\n?\|#S";
    $replacements[] = "\n\n|";

    // Tables start.
    $patterns[] = "#\n\n+\|#S";
    $replacements[] = "\n\n\n\n|";

    // Tables end.
    $patterns[] = "#\|(\n\n+|\n?$)#S";
    $replacements[] = "|\n\n\n\n";

    $text = preg_replace(
      $patterns,
      $replacements,
      $text
    );

    return preg_replace_callback(
      "#((?!\n\|)*)(\n\|.*\|\n)([^|]+|$)#UmsS",
      [self::class, 'replaceTable'],
      $text
    );
  }

  /**
   * Replace SPIP table element to HTML table.
   *
   * @param array $match
   *   The matched table element.
   *
   * @return string
   *   The HTML table.
   *
   * @SuppressWarnings(PHPMD.UnusedPrivateMethod)
   */
  private static function replaceTable(array $match): string {
    return $match[1] .
      self::applyTableBlock($match[2]) .
      $match[3];
  }

  /**
   * Converting string block to tables.
   *
   * @param string $block
   *   The table block string.
   *
   * @return string
   *   The processed table block.
   *
   * @see https://github.com/spip/textwheel/blob/2.0/inc/texte.php
   * @see traiter_tableau()
   *
   * @SuppressWarnings(PHPMD.CyclomaticComplexity)
   * @SuppressWarnings(PHPMD.ElseExpression)
   * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
   * @SuppressWarnings(PHPMD.IfStatementAssignment)
   * @SuppressWarnings(PHPMD.NPathComplexity)
   * @SuppressWarnings(PHPMD.ShortVariable)
   */
  private static function applyTableBlock(string $block): string {
    // Define "unique" id for array ids.
    $tabid = substr(md5($block), 0, 4);

    // Break the table into rows.
    preg_match_all(',([|].*)[|]\n,UmsS', $block, $regs, PREG_PATTERN_ORDER);
    $lignes = [];
    $tableStart = $summary = '';
    $l = 0;

    // Process each row.
    $reg_line1 = ',^(\|(' . self::TH_SPAN_PATTERN . '))+$,sS';
    $reg_line_all = ',^(' . self::TH_SPAN_PATTERN . ')$,sS';
    $hc = $hl = [];
    $theadOk = FALSE;
    foreach ($regs[1] as $ligne) {
      $l++;

      // Manage first line.
      if (!$theadOk and $l == 1) {
        // - <caption> et summary dans la premiere ligne (seulement si on n'a pas dépassé le premier thead) :
        //   || caption | summary || (|summary est optionnel)
        if (preg_match(',^\|\|([^|]*)(\|(.*))?$,sS', rtrim($ligne, '|'), $cap)) {
          $cap = array_pad($cap, 4, '');
          $l = 0;
          $summary = '';
          $describedBy = trim($cap[3]);
          $describedById = 'dby' . $tabid;
          if ($describedBy) {
            $summary = ' aria-describedby="' . $describedById . '"';
          }
          if ($caption = trim($cap[1])) {
            if ($describedBy) {
              $caption .= '<br /> <small id="' . $describedById . '" class="summary offscreen">' . $describedBy . '</small>';
            }
            $tableStart .= '<caption>' . $caption . "</caption>\n";
          }
          elseif ($describedBy) {
            $tableStart .= '<caption id="' . $describedById . '" class="summary offscreen"><small>' . $describedBy . "</small></caption>\n";
          }
        }
        // - <thead> sous la forme |{{titre}}|{{titre}}|
        //   Attention thead oblige a avoir tbody
        else {
          if (preg_match($reg_line1, $ligne)) {
            preg_match_all('/\|([^|]*)/S', $ligne, $cols);
            $ligne = '';
            $cols = $cols[1];
            $colspan = 1;
            for ($c = count($cols) - 1; $c >= 0; $c--) {
              $attr = '';
              if ($cols[$c] == '<') {
                $colspan++;
              }
              else {
                if ($colspan > 1) {
                  $attr = ' colspan="' . $colspan . '"';
                  $colspan = 1;
                }
                // Inutile de garder le strong qui n'a servi que de marqueur.
                $cols[$c] = str_replace(['{', '}'], '', $cols[$c]);
                $ligne = "<th id=\"id{$tabid}_c$c\"$attr>" . trim($cols[$c]) . "</th>$ligne";
                // Pour mettre dans les headers des td.
                $hc[$c] = "id{$tabid}_c$c";
              }
            }

            $tableStart .= '<thead><tr class="row_first">' .
              $ligne . "</tr></thead>\n";
            $l = 0;
            $theadOk = TRUE;
          }
        }
      }

      // Normal line otherwise.
      if ($l) {
        // Gerer les listes a puce dans les cellules
        // on declenche simplement sur \n- car il y a les
        // -* -# -? -! (qui produisent des -&nbsp;!)
        if (strpos($ligne, "\n-") !== FALSE) {
          // Apply list formatting.
          $ligne = \Drupal::service('plugin.manager.spip_rich_text')
            ->createInstance('lists')
            ->apply($ligne);
        }

        // Tout mettre dans un tableau 2d.
        preg_match_all('/\|([^|]*)/S', $ligne, $cols);

        // Pas de paragraphes dans les cellules.
        foreach ($cols[1] as &$col) {
          if (strlen($col = trim($col))) {
            $col = preg_replace("/\n{2,}/S", '<br /> <br />', $col);
            $col = str_replace("\n", self::AUTOBR . "\n", $col);
          }
        }

        // Assembler le tableau.
        $lignes[] = $cols[1];
      }
    }

    // Maintenant qu'on a toutes les cellules,
    // on prépare une liste de rowspan par défaut, à partir
    // du nombre de colonnes dans la premiere ligne.
    // Repérer également les colonnes numériques pour les cadrer a droite.
    $rowspans = $numeric = [];
    $n = $lignes ? count($lignes[0]) : 0;
    $k = count($lignes);
    // Distinguer les colonnes numériques à point ou a virgule,
    // pour les alignements éventuels sur "," ou ".".
    $numeric_class = [
      '.' => 'point',
      ',' => 'virgule',
      TRUE => '',
    ];
    for ($i = 0; $i < $n; $i++) {
      $align = TRUE;
      for ($j = 0; $j < $k; $j++) {
        $rowspans[$j][$i] = 1;
        if ($align and preg_match('/^[{+-]*(?:\s|\d)*([.,]?)\d*[}]*$/', trim($lignes[$j][$i] ?? ''), $r)) {
          if ($r[1]) {
            $align = $r[1];
          }
        }
        else {
          $align = '';
        }
      }
      $numeric[$i] = $align ? (' class="numeric ' . $numeric_class[$align] . '"') : '';
    }
    for ($j = 0; $j < $k; $j++) {
      if (preg_match($reg_line_all, $lignes[$j][0])) {
        // Pour mettre dans les headers des td.
        $hl[$j] = "id{$tabid}_l$j";
      }
      else {
        unset($hl[0]);
      }
    }
    if (!isset($hl[0])) {
      $hl = [];
    } // toute la colonne ou rien

    // Et on parcourt le tableau a l'envers pour ramasser les
    // colspan et rowspan en passant.
    $html = '';

    for ($l = count($lignes) - 1; $l >= 0; $l--) {
      $cols = $lignes[$l];
      $colspan = 1;
      $ligne = '';

      for ($c = count($cols) - 1; $c >= 0; $c--) {
        $attr = $numeric[$c] ?? '';
        $cell = trim($cols[$c]);
        if ($cell == '<') {
          $colspan++;
        }
        elseif ($cell == '^') {
          $rowspans[$l - 1][$c] += $rowspans[$l][$c];
        }
        else {
          if ($colspan > 1) {
            $attr .= ' colspan="' . $colspan . '"';
            $colspan = 1;
          }
          if (($x = $rowspans[$l][$c] ?? NULL) > 1) {
            $attr .= ' rowspan="' . $x . '"';
          }
          $b = ($c == 0 and isset($hl[$l])) ? 'th' : 'td';
          $h = ($hc[$c] ?? '') . ' ' . (($b == 'td' and isset($hl[$l])) ? $hl[$l] : '');
          if ($h = trim($h)) {
            $attr .= ' headers="' . $h . '"';
          }
          // Inutile de garder le strong qui n'a servi que de marqueur.
          if ($b == 'th') {
            $attr .= ' id="' . $hl[$l] . '"';
            $cols[$c] = str_replace(['{', '}'], '', $cols[$c]);
          }
          $ligne = "\n<$b" . $attr . '>' . trim($cols[$c]) . "</$b>" . $ligne;
        }
      }

      // Ligne complete.
      $class = $l % 2 ? 'odd' : 'even';
      $html = "<tr class=\"row_$class $class\">$ligne</tr>\n$html";
    }

    return '<table' . $summary . ">\n"
      . $tableStart
      . "<tbody>\n"
      . $html
      . "</tbody>\n</table>";
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc