HEX
Server: LiteSpeed
System: Linux server315.web-hosting.com 4.18.0-553.54.1.lve.el8.x86_64 #1 SMP Wed Jun 4 13:01:13 UTC 2025 x86_64
User: globfdxw (6114)
PHP: 8.1.34
Disabled: NONE
Upload Files
File: //home/globfdxw/www/wp-content/plugins/wpforms-pdf/src/Notifications/Fields/Richtext.php
<?php

namespace WPFormsPDF\Notifications\Fields;

use DOMXPath;

/**
 * Class Richtext field.
 *
 * @since 1.2.0
 */
class Richtext {

	/**
	 * Class constructor.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	public function __construct() {

		$this->hooks();
	}

	/**
	 * Register hooks.
	 *
	 * @since 1.2.0
	 *
	 * @return void
	 */
	private function hooks(): void {

		add_filter(
			'wpforms_pdf_notifications_fields_field_message_html_richtext',
			[ $this, 'field_row_html_value' ],
			100,
			7
		);
	}

	/**
	 * Transform richtext field message HTML to multiple rows by paragraphs.
	 *
	 * @since 1.2.0
	 *
	 * @param string|mixed $field_markup Field HTML row markup.
	 * @param array        $field        Field data.
	 * @param array        $form_data    Form data.
	 * @param array        $template     Template data.
	 *
	 * @return string
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function field_row_html_value( $field_markup, array $field, array $form_data, array $template ): string { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed

		$field_markup = (string) $field_markup;

		if ( $field_markup === '' ) {
			return $field_markup;
		}

		list( $dom, $xpath ) = Helpers::load_dom( $field_markup );
		$rows                = $this->query_richtext_rows( $xpath );

		if ( ! $rows || $rows->length === 0 ) {
			return $field_markup;
		}

		for ( $i = $rows->length - 1; $i >= 0; $i-- ) {
			$tr       = $rows->item( $i );
			$value_td = Helpers::get_value_cell( $xpath, $tr );

			if ( ! $value_td ) {
				continue;
			}

			$has_field_name     = Helpers::has_name_cell( $xpath, $tr );
			$value_html         = Helpers::get_inner_html( $dom, $value_td );
			$parts              = $this->split_value_into_parts( $value_html );
			$field_name_td_html = $has_field_name ? Helpers::get_name_cell_html( $dom, $xpath, $tr ) : '';

			Helpers::replace_row_with_parts( $dom, $tr, 'field-richtext', $has_field_name, $field_name_td_html, $parts );
		}

		return Helpers::save_html( $dom );
	}

	/**
	 * Query richtext rows.
	 *
	 * @since 1.2.0
	 *
	 * @param DOMXPath $xpath XPath instance.
	 *
	 * @return DOMNodeList|false
	 */
	private function query_richtext_rows( DOMXPath $xpath ) {

		return $xpath->query( '//tr[contains(@class,"field-richtext")]' );
	}

	/**
	 * Normalize and split into paragraph parts.
	 *
	 * @since 1.2.0
	 *
	 * @param string $value_html HTML of the value cell.
	 *
	 * @return array
	 */
	private function split_value_into_parts( string $value_html ): array {

		// Normalize BR tags to be XML-friendly for DOMDocumentFragment::appendXML.
		$value_html = preg_replace( '#<br\s*/?>#i', '<br />', $value_html );

		// Strip outer <div> wrappers if present.
		if ( preg_match( '#^<div[^>]*>(.*)</div>$#is', $value_html, $m ) ) {
			$value_html = $m[1];
		}

		$parts = preg_split( '#</p>\s*<p>#i', $value_html );

		// Ensure each part is wrapped in a single <p>...</p>.
		foreach ( $parts as &$part ) {
			$part = $this->ensure_paragraph_wrapped( $part );
			// Double-sanitize BRs inside each part, in case wrapping introduced/moved text.
			$part = preg_replace( '#<br\s*/?>#i', '<br />', $part );
		}

		unset( $part );

		return $parts;
	}

	/**
	 * Ensures the provided string is wrapped within paragraph tags.
	 *
	 * @since 1.2.0
	 *
	 * @param string $part The string to be checked and wrapped with paragraph tags, if necessary.
	 *
	 * @return string
	 */
	private function ensure_paragraph_wrapped( string $part ): string {

		$part = trim( $part );

		if ( strpos( $part, '<p' ) !== 0 ) {
			$part = '<p>' . $part;
		}

		if ( substr( $part, -4 ) !== '</p>' ) {
			$part .= '</p>';
		}

		return $part;
	}
}