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; | |
} | |
} |