This is an untested draft, I'll come back to this, but it may already be helpful!
Dependencies:
"ezyang/htmlpurifier": "^4.12",
This is an untested draft, I'll come back to this, but it may already be helpful!
Dependencies:
"ezyang/htmlpurifier": "^4.12",
| <?php | |
| namespace App\Repositories; | |
| use A17\Twill\Models\Block; | |
| use App\Services\Purifier; | |
| class BlockRepository | |
| { | |
| private $purifierService; | |
| public function __construct() | |
| { | |
| $this->purifierService = app(Purifier::class); | |
| } | |
| private function fixAllHtmlItems($content) | |
| { | |
| if (is_string($content)) { | |
| return $this->fixHtmlString($content); | |
| } | |
| if (is_array($content)) { | |
| foreach ($content as $key => $value) { | |
| $content[$key] = $this->fixAllHtmlItems($value); | |
| } | |
| } | |
| return $content; | |
| } | |
| /** | |
| * @param \A17\Twill\Models\Block $block | |
| */ | |
| public function fixBlockHtmls(Block $block): void | |
| { | |
| $block['content'] = $this->fixAllHtmlItems($block['content']); | |
| $block->save(); | |
| } | |
| public function fixHtml() | |
| { | |
| foreach (Block::all() as $block) { | |
| $this->fixBlockHtmls($block); | |
| } | |
| } | |
| private function fixHtmlString(string $content) | |
| { | |
| if (is_html($content)) { | |
| return $this->purifierService->purify( | |
| $this->removeNonPrintableChars($content), | |
| ); | |
| } | |
| return $content; | |
| } | |
| private function removeNonPrintableChars(string $string) | |
| { | |
| $string = str_replace( | |
| ['<br>', '<br />', '<br/>', "\n", "\r", '\n', '\r'], | |
| '', | |
| $string, | |
| ); | |
| return preg_replace('/[\x00-\x1F\x7F]/u', '', $string); | |
| } | |
| } |
| <?php | |
| namespace App\Repositories; | |
| use App\Models\Post; | |
| use A17\Twill\Repositories\Behaviors\HandleFiles; | |
| use A17\Twill\Repositories\Behaviors\HandleSlugs; | |
| use A17\Twill\Repositories\Behaviors\HandleMedias; | |
| use A17\Twill\Repositories\Behaviors\HandleBlocks; | |
| use A17\Twill\Repositories\Behaviors\HandleRevisions; | |
| use A17\Twill\Repositories\Behaviors\HandleTranslations; | |
| class PostRepository extends Repository | |
| { | |
| use HandleBlocks, | |
| HandleTranslations, | |
| HandleSlugs, | |
| HandleMedias, | |
| HandleFiles, | |
| HandleRevisions; | |
| public function __construct(Post $model) | |
| { | |
| $this->model = $model; | |
| } | |
| private function fixHtmls($id) | |
| { | |
| if (request()->get('fix_html_on_update')) { | |
| $this->model | |
| ->findOrFail($id) | |
| ->blocks->each( | |
| fn($block) => app(BlockRepository::class)->fixBlockHtmls( | |
| $block, | |
| ), | |
| ); | |
| } | |
| } | |
| /** | |
| * @param mixed $id | |
| * @param array $fields | |
| * @return void | |
| */ | |
| public function update($id, $fields) | |
| { | |
| parent::update($id, $fields); | |
| $this->fixHtmls($id); | |
| } | |
| } |
| <?php | |
| namespace App\Services; | |
| use HTMLPurifier; | |
| use HTMLPurifier_Config; | |
| class Purifier | |
| { | |
| private function listWrongTags($html) | |
| { | |
| preg_match_all('#(<\s?\\\/.{1,5}>)#', $html, $matches); | |
| $tags = []; | |
| foreach ($matches[0] as $tag) { | |
| $newTag = str_replace('<\/', '</', $tag); | |
| $newTag = str_replace('< \/', '</', $newTag); | |
| $tags[$tag] = $newTag; | |
| } | |
| return $tags; | |
| } | |
| private function removeBrokenHtmlTags($html) | |
| { | |
| foreach ($this->listWrongTags($html) as $tag => $newTag) { | |
| $html = str_replace($tag, $newTag, $html); | |
| } | |
| return str_replace(['\n\r', '\r\n'], ['', ''], $html); | |
| } | |
| public function purify($html) | |
| { | |
| if (blank($html)) { | |
| return false; | |
| } | |
| if (strip_tags_and_entities($html) == $html) { | |
| return false; | |
| } | |
| $pure = $this->removeBrokenHtmlTags($html); | |
| $config = HTMLPurifier_Config::createDefault(); | |
| $config->set('HTML.Doctype', 'XHTML 1.0 Transitional'); | |
| $config->set('HTML.SafeIframe', true); | |
| $config->set('HTML.Trusted', true); | |
| $config->set( | |
| 'URI.SafeIframeRegexp', | |
| '%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/|vernissage.fondationlouisvuitton.eventmaker.io/|player.freecaster.com/|api.soundcloud.com/tracks/|w.soundcloud.com/player/)%', | |
| ); | |
| $pure = (new HTMLPurifier($config))->purify($pure); | |
| return $this->removeHtmlentitiedTags($html, $pure); | |
| } | |
| private function removeHtmlentitiedTags($html, string $newHtml) | |
| { | |
| foreach ($this->listWrongTags($html) as $tag => $newTag) { | |
| $newHtml = str_replace( | |
| [htmlentities($tag), htmlentities($newTag)], | |
| ['', ''], | |
| $newHtml, | |
| ); | |
| } | |
| return $newHtml; | |
| } | |
| } |