Initial baseline: LaraBlog core with plugin commerce surface.
Captures the current working tree after theme slots, ArticleAccess, and the payment / paid-content plugins so subsequent work has a reviewable git history.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Domain\Blog;
|
||||
|
||||
use Mews\Purifier\Facades\Purifier;
|
||||
|
||||
class HtmlTeaser
|
||||
{
|
||||
/**
|
||||
* Build a teaser from already-rendered HTML.
|
||||
*
|
||||
* The result is always a plain-text excerpt and always withholds part of the
|
||||
* body: when the configured limit covers the whole article we still cut it
|
||||
* in half, so a gated article can never be served in full through a teaser.
|
||||
*/
|
||||
public function truncate(string $html, int $maxChars): string
|
||||
{
|
||||
if ($maxChars <= 0 || trim($html) === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$text = html_entity_decode(strip_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = trim(preg_replace('/\s+/u', ' ', $text) ?? '');
|
||||
|
||||
if ($text === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$length = mb_strlen($text);
|
||||
$limit = min($maxChars, max(1, (int) floor($length / 2)));
|
||||
|
||||
$snippet = mb_substr($text, 0, $limit);
|
||||
if ($limit < $length) {
|
||||
$snippet .= '…';
|
||||
}
|
||||
|
||||
return Purifier::clean('<p>'.e($snippet).'</p>', 'article');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user