| @copyright 2007-2026 (c) AVE.cms | @link https://ave-cms.ru | @version 3.3 */ namespace App\Adminx\Rubrics; defined('BASEPATH') || die('Direct access to this location is not allowed.'); use App\Content\Fields\FieldContext; use App\Content\Fields\FieldRegistry; class FieldAdapter { public static function editPreview(array $field) { $type = FieldTypes::one((string) $field['rubric_field_type']); if (!$type) { return self::result('error', 'Тип поля не зарегистрирован', '', array()); } $plugin = FieldRegistry::get((string) $field['rubric_field_type']); if (!$plugin) { return self::result('error', 'Native-тип поля не зарегистрирован', '', array()); } try { $context = new FieldContext((string) $field['rubric_field_default'], $field, 'edit'); $html = $plugin->renderEdit($context); $html = is_string($html) ? trim($html) : ''; if ($html === '') { return self::result('warning', 'Native-тип не вернул HTML для режима edit', '', array()); } $normalized = self::normalizeHtml($html); $html = $normalized['html']; return self::result('ok', 'Native edit-render выполнен через FieldRegistry.', $html, array(), $normalized['notes']); } catch (\Throwable $e) { return self::result('error', $e->getMessage(), '', array()); } } protected static function normalizeHtml($html) { $html = (string) $html; $notes = array(); $scriptCount = preg_match_all('#]*>.*?#is', $html, $m); if ($scriptCount > 0) { $html = preg_replace('#]*>.*?#is', '', $html); $notes[] = 'Preview: legacy script-теги отключены (' . (int) $scriptCount . ').'; } $handlerCount = preg_match_all('#\s+on[a-z]+\s*=\s*(".*?"|\'.*?\'|[^\s>]+)#is', $html, $m); if ($handlerCount > 0) { $html = preg_replace('#\s+on[a-z]+\s*=\s*(".*?"|\'.*?\'|[^\s>]+)#is', '', $html); $notes[] = 'Preview: inline JS-обработчики удалены (' . (int) $handlerCount . ').'; } $rewrites = 0; $html = preg_replace_callback('/\b(href|src|action)\s*=\s*([\'"])(.*?)\2/i', function ($m) use (&$rewrites) { $url = trim((string) $m[3]); $normalized = self::normalizeUrl($url); if ($normalized !== $url) { $rewrites++; } return $m[1] . '=' . $m[2] . htmlspecialchars($normalized, ENT_QUOTES, 'UTF-8') . $m[2]; }, $html); $rootAssets = preg_match_all('#\b(?:href|src|action)\s*=\s*([\'"])/(admin|class|fields|images|lib|modules|templates|tmp|uploads)/#i', $html, $m); if ($rewrites > 0) { $notes[] = 'Preview: legacy asset-пути приведены к корню сайта (' . (int) $rewrites . ').'; } elseif ($rootAssets > 0) { $notes[] = 'Preview: legacy asset-пути используют корень сайта.'; } return array('html' => $html, 'notes' => $notes); } protected static function normalizeUrl($url) { $url = (string) $url; $trimmed = trim($url); if ($trimmed === '' || $trimmed[0] === '#' || $trimmed[0] === '/') { return $url; } if (preg_match('#^(https?:)?//#i', $trimmed) || preg_match('#^(mailto|tel|data):#i', $trimmed)) { return $url; } if (preg_match('#^javascript:#i', $trimmed)) { return '#'; } $path = preg_replace('#^\./#', '', $trimmed); if (preg_match('#^(admin|class|fields|images|lib|modules|templates|tmp|uploads)/#i', $path)) { return '/' . ltrim($path, '/'); } return $url; } protected static function result($status, $message, $html, array $assets, array $notes = array()) { return array( 'status' => $status, 'message' => $message, 'html' => $html, 'assets' => array_values($assets), 'notes' => array_values(array_unique($notes)), ); } }