mirror of
https://github.com/claradex/nativegallery.git
synced 2025-06-05 05:47:00 +03:00
update models
This commit is contained in:
parent
39193c88c0
commit
9a88345057
2 changed files with 167 additions and 31 deletions
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use \App\Services\{DB, Date, Auth};
|
||||
use \App\Services\{DB, Date, Auth, Emoji, Word};
|
||||
use \App\Models\{User, Photo, Vote};
|
||||
|
||||
class Comment
|
||||
|
@ -24,6 +24,123 @@ class Comment
|
|||
$content = json_decode($this->c['content'], true);
|
||||
return $content[$table];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private function processContent($rawText)
|
||||
{
|
||||
// 1. Обработка упоминаний и смайлов
|
||||
$withTags = Emoji::parseSmileys(Word::processMentions($rawText));
|
||||
|
||||
// 2. Селективное экранирование
|
||||
$safeContent = $this->selectiveHtmlEscape($withTags);
|
||||
|
||||
// 3. Обрезка контента
|
||||
return $this->truncateContent($safeContent, 200);
|
||||
}
|
||||
|
||||
private function selectiveHtmlEscape(string $html): string
|
||||
{
|
||||
// 0. Если текст не UTF‑8, конвертируем из CP1251
|
||||
if (!mb_check_encoding($html, 'UTF-8')) {
|
||||
$html = mb_convert_encoding($html, 'UTF-8', 'CP1251');
|
||||
}
|
||||
|
||||
// 1. Разбиваем на «теги» и «текст», сохраняя теги
|
||||
$parts = preg_split('/(<[^>]+>)/u', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
|
||||
foreach ($parts as &$part) {
|
||||
// 2. Тег — пропускаем
|
||||
if (preg_match('/^<[^>]+>$/u', $part)) {
|
||||
continue;
|
||||
}
|
||||
// 3. Текст — сначала декодируем все сущности, потом экранируем спецсимволы
|
||||
// ENT_QUOTES|ENT_HTML5 и false у double_encode гарантируют корректную работу с etc.
|
||||
$decoded = html_entity_decode($part, ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$part = htmlspecialchars($decoded, ENT_QUOTES | ENT_HTML5, 'UTF-8', false);
|
||||
}
|
||||
unset($part);
|
||||
|
||||
// 4. Собираем обратно
|
||||
return implode('', $parts);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private function truncateContent(string $html, int $maxLength): string
|
||||
{
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
libxml_use_internal_errors(true);
|
||||
|
||||
$wrapped = '<?xml encoding="UTF-8"><div>' . $html . '</div>';
|
||||
|
||||
$dom->loadHTML($wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
|
||||
libxml_clear_errors();
|
||||
|
||||
$xpath = new \DOMXPath($dom);
|
||||
$node = $xpath->query('//div')->item(0);
|
||||
$this->truncateNode($node, $maxLength);
|
||||
|
||||
|
||||
return $dom->saveHTML($node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function truncateNode(\DOMNode $node, &$remaining)
|
||||
{
|
||||
if ($remaining <= 0) return;
|
||||
|
||||
foreach ($node->childNodes as $child) {
|
||||
if ($child instanceof \DOMText) {
|
||||
$text = $child->nodeValue;
|
||||
$visible = mb_substr($text, 0, $remaining);
|
||||
$hidden = mb_substr($text, $remaining);
|
||||
|
||||
if ($remaining < mb_strlen($text)) {
|
||||
$child->nodeValue = $visible;
|
||||
$remaining = 0;
|
||||
|
||||
// Создаём элемент для скрытой части
|
||||
$hiddenNode = $child->ownerDocument->createElement('span');
|
||||
$hiddenNode->setAttribute('class', 'hidden-text');
|
||||
$hiddenTextNode = $child->ownerDocument->createTextNode($hidden);
|
||||
$hiddenNode->appendChild($hiddenTextNode);
|
||||
|
||||
// Вставляем hiddenNode после текущего текстового узла
|
||||
$parent = $child->parentNode;
|
||||
if ($parent) {
|
||||
if ($child->nextSibling) {
|
||||
$parent->insertBefore($hiddenNode, $child->nextSibling);
|
||||
} else {
|
||||
$parent->appendChild($hiddenNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Создаём кнопку "показать больше"
|
||||
$button = $child->ownerDocument->createElement('a');
|
||||
$buttonText = $child->ownerDocument->createTextNode('показать больше');
|
||||
$button->appendChild($buttonText);
|
||||
$button->setAttribute('class', 'toggle-message');
|
||||
if ($parent) {
|
||||
$parent->appendChild($button);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$remaining -= mb_strlen($text);
|
||||
} else {
|
||||
$this->truncateNode($child, $remaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
public function i()
|
||||
{
|
||||
$user = new User($this->c['user_id']);
|
||||
|
@ -39,8 +156,6 @@ class Comment
|
|||
echo '
|
||||
<div style="float:right; text-align:right" class="sm">
|
||||
<span class="message_date">' . Date::zmdate($this->c['posted_at']) . '</span><br>
|
||||
<a href="#" class="quoteLink dot">Цитировать</a>
|
||||
·
|
||||
<a href="#' . $this->c['id'] . '" class="cmLink dot">Ссылка</a>
|
||||
';
|
||||
|
||||
|
@ -73,19 +188,31 @@ class Comment
|
|||
$commclass = '';
|
||||
}
|
||||
echo '</span></div>
|
||||
<div class="rank">Фото: ' . Photo::fetchAll($this->c['user_id']) . ' ' . $admintype . '</div>
|
||||
<div class="message-text">' . preg_replace("~(?:[\p{M}]{1})([\p{M}])+?~uis", "", htmlspecialchars($this->c['body'])) . '</div>
|
||||
';
|
||||
if ($content['filetype'] === 'img') {
|
||||
echo '<div class="message-text"><img src="'.$content['src'].'" width="250"></div>';
|
||||
}
|
||||
if ($content['filetype'] === 'video') {
|
||||
echo '<div class="message-text"><video controls src="'.$content['src'].'" width="250"></div>';
|
||||
}
|
||||
echo '
|
||||
<div class="rank">Фото: ' . Photo::fetchAll($this->c['user_id']) . ' ' . $admintype . '</div>'; ?>
|
||||
<div class="message-text">
|
||||
<?php
|
||||
// Правильный порядок:
|
||||
$processedText = $this->processContent($this->c['body']);
|
||||
|
||||
// Шаг 4: Вывод без дополнительного экранирования
|
||||
echo '<div class="message-text">' . $processedText . '</div>';
|
||||
|
||||
// ========== Вспомогательные методы ==========
|
||||
|
||||
|
||||
?>
|
||||
</div> <?php
|
||||
|
||||
if ($content['filetype'] === 'img') {
|
||||
echo '<div class="message-text"><img src="' . $content['src'] . '" width="250"></div>';
|
||||
}
|
||||
if ($content['filetype'] === 'video') {
|
||||
echo '<div class="message-text"><video controls src="' . $content['src'] . '" width="250"></div>';
|
||||
}
|
||||
echo '
|
||||
<div class="comment-votes-block">
|
||||
';
|
||||
echo '<style>
|
||||
echo '<style>
|
||||
.dropdown {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
|
@ -105,28 +232,28 @@ class Comment
|
|||
display: block;
|
||||
}
|
||||
</style>';
|
||||
if ($this->c['user_id'] === Auth::userid() || $photo->i('user_id') === Auth::userid()) {
|
||||
echo '
|
||||
if ($this->c['user_id'] === Auth::userid() || $photo->i('user_id') === Auth::userid()) {
|
||||
echo '
|
||||
<div class="dropdown">
|
||||
<a style="color: #000" class="compl" href="#">...</a>
|
||||
<div class="dropdown-content">';
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<a href="#" onclick="pinComment(<?= $this->c['id'] ?>); return false;"><?=$pinc?></a><br>
|
||||
<?php
|
||||
if ($this->c['user_id'] === Auth::userid()) { ?>
|
||||
<a style="margin-bottom: 10px;" href="#" onclick="createModal(<?= $this->c['id'] ?>, 'EDIT_COMMENT', '<?= htmlspecialchars($this->c['body']) ?>', 'modaledit<?= $this->c['id'] ?>'); return false;">Редактировать</a><br>
|
||||
<a href="#" onclick="createModal(<?= $this->c['id'] ?>, 'DELETE_COMMENT', '', 'modaldel<?= $this->c['id'] ?>'); return false;">Удалить</a>
|
||||
<?php }
|
||||
|
||||
echo '
|
||||
|
||||
?>
|
||||
|
||||
<a href="#" onclick="pinComment(<?= $this->c['id'] ?>); return false;"><?= $pinc ?></a><br>
|
||||
<?php
|
||||
if ($this->c['user_id'] === Auth::userid()) { ?>
|
||||
<a style="margin-bottom: 10px;" href="#" onclick="createModal(<?= $this->c['id'] ?>, 'EDIT_COMMENT', '<?= htmlspecialchars($this->c['body']) ?>', 'modaledit<?= $this->c['id'] ?>'); return false;">Редактировать</a><br>
|
||||
<a href="#" onclick="createModal(<?= $this->c['id'] ?>, 'DELETE_COMMENT', '', 'modaldel<?= $this->c['id'] ?>'); return false;">Удалить</a>
|
||||
<?php }
|
||||
|
||||
echo '
|
||||
</div>
|
||||
</div>
|
||||
';
|
||||
}
|
||||
echo '
|
||||
}
|
||||
echo '
|
||||
<div class="wvote" wid="' . $this->c['id'] . '">
|
||||
<a href="#" vote="1" class="w-btn s2"><span>+</span></a>
|
||||
|
||||
|
@ -139,5 +266,5 @@ class Comment
|
|||
</div>
|
||||
</div>
|
||||
</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,5 +15,14 @@ class User {
|
|||
$content = json_decode(self::i('content'), true);
|
||||
return $content[$table];
|
||||
}
|
||||
public function getPhotoUrl(): string
|
||||
{
|
||||
return $this->i('photourl');
|
||||
}
|
||||
|
||||
public function getId(): int
|
||||
{
|
||||
return (int)$this->i('user_id');
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue