CommentingService.php 7.39 KB
<?php

namespace FootyRoom\App\Comment;

use FootyRoom\Core\Comment\Author;
use FootyRoom\Core\Comment\Comment;
use FootyRoom\Core\Comment\CommentPolicy;
use FootyRoom\Core\Comment\ICommentable;
use FootyRoom\Core\CoreException;
use FootyRoom\Queries\Comment\CommentQueryHandler;
use FootyRoom\Queries\Comment\CommentsInPeriodQuery;
use FootyRoom\Queries\Comment\DuplicateCommentQuery;
use FootyRoom\Repositories\CommentRepository;
use FootyRoom\Repositories\Poll\PollRepository;
use FootyRoom\Support\Akismet;
use FootyRoom\Support\Kses;
use FootyRoom\Support\Markdown\MarkdownService;
use Illuminate\Contracts\Events\Dispatcher;

class CommentingService
{
    /**
     * @var \FootyRoom\Queries\Comment\CommentQueryHandler
     */
    protected $commentQueryHandler;

    /**
     * @var \Illuminate\Contracts\Events\Dispatcher
     */
    protected $events;

    /**
     * @var \FootyRoom\Core\Comment\CommentPolicy
     */
    protected $commentPolicy;

    /**
     * @var \FootyRoom\Repositories\CommentRepository
     */
    protected $commentRepo;

    /**
     * @var \FootyRoom\Repositories\Poll\PollRepository
     */
    protected $pollRepo;

    /**
     * Constructor.
     *
     * @param \FootyRoom\Queries\Comment\CommentQueryHandler $commentQueryHandler
     * @param \FootyRoom\Repositories\CommentRepository $commentRepo
     * @param \FootyRoom\Core\Comment\CommentPolicy $commentPolicy
     * @param \Illuminate\Contracts\Events\Dispatcher $events
     */
    public function __construct(
        CommentQueryHandler $commentQueryHandler,
        CommentRepository $commentRepo,
        CommentPolicy $commentPolicy,
        PollRepository $pollRepo,
        Dispatcher $events
    ) {
        $this->commentQueryHandler = $commentQueryHandler;
        $this->commentRepo = $commentRepo;
        $this->commentPolicy = $commentPolicy;
        $this->pollRepo = $pollRepo;
        $this->events = $events;
    }

    /**
     * Checks whether user is flooding the system with comments.
     *
     * @param int $userId
     * @param string $userIp
     */
    public function checkCommentFlooding($userId, $userIp = '')
    {
        $count = $this->commentQueryHandler->commentsInPeriod(
            new CommentsInPeriodQuery(900, $userId, $userIp)
        );

        if ($count > 12) {
            throw new CoreException('You seem to be posting way too fast. Slow down.');
        }
    }

    /**
     * Determines if this comment is SPAM.
     *
     * @param \FootyRoom\Core\Comment\Comment $comment
     *
     * @return bool
     */
    public function isSpam(Comment $comment)
    {
        $akismet = new Akismet('http://footyroom.com', 'f7596b73044d');

        $akismet->setCommentContent($comment->getContent());
        $akismet->setCommentAuthor($comment->getAuthor()->getName());

        return $akismet->isCommentSpam();
    }

    /**
     * Checks any strings for existence of blacklisted words.
     *
     * @param array $strings Array of strings to check.
     *
     * @throws \FootyRoom\Core\CoreException
     */
    public function checkBlacklistedWords(array $strings)
    {
        $words = ['nigger', 'niger', 'nazi', 'hitler', 'jew', 'jews', 'gaza'];

        foreach ($strings as $item) {
            if (preg_match("/\b(".implode('|', $words).")\b/i", $item)) {
                throw new CoreException('Your post contains blacklisted words.');
            }
        }
    }

    /**
     * Determines whether this comment is a duplicate.
     *
     * @param \FootyRoom\Core\Comment\Comment $comment
     *
     * @return bool
     */
    public function checkRecentDuplicate(Comment $comment)
    {
        $hasRecentDuplicate = $this->commentQueryHandler->hasRecentDuplicate(
            new DuplicateCommentQuery(
                $comment->getDiscussionId(),
                $comment->getContent(),
                $comment->getAuthor()->getUserId()
            )
        );

        if ($hasRecentDuplicate) {
            throw new CoreException('Seems like you have already posted a comment like that recently. Is this a duplicate? You might want to refresh the page to see your comment.');
        }
    }

    /**
     * Formats the comment content.
     *
     * @param string $content
     * @param array $images
     * @param bool $isMember
     *
     * @return string
     */
    public function formatComment($content, $images = [])
    {
        // Trim starting and trailing spaces.
        $content = trim($content);

        // Turn new lines into <br> tags.
        $content = nl2br($content, false);

        // Turn 3 or more consecutive <br> tags into only two tags.
        $content = preg_replace("/(<br ?\/?>\s*){3,}/", "<br>\n<br>\n", $content);

        if (is_array($images)) {
            // Add attached images to content.
            $insertedImages = 0;

            foreach ($images as $imageUrl) {
                $content .= '<img src="'.$imageUrl.'" />';

                // Limit to only two images.
                if (++$insertedImages >= 2) {
                    break;
                }
            }
        }

        $allowedTags = [
            'br' => [],
            'img' => [
                'src' => [],
                'height' => [],
                'width' => [],
            ],
        ];

        // Leave only allowed HTML tags, clean out the rest.
        return Kses::clean($content, $allowedTags);
    }

    /**
     * Makes a comment in specified post.
     *
     * @param string $content
     * @param string $contentHtml
     * @param \FootyRoom\Core\Comment\ICommentable $commentable
     * @param int $parentId
     * @param int $userId
     * @param string $username
     * @param string $userIp
     *
     * @throws \FootyRoom\Core\CoreException
     *
     * @return int Id of new comment.
     */
    public function comment($content, $contentHtml, ICommentable $commentable, $parentId, $userId, $username, $userIp = '')
    {
        $author = new Author(
            $userId,
            $username,
            $userIp
        );

        $comment = new Comment(
            $commentable->getCommentableType().':'.$commentable->getId(),
            $author,
            $content,
            $contentHtml,
            $parentId
        );

        $this->checkCommentFlooding($userId, $userIp);

        $this->checkBlacklistedWords([$content, $username]);

        $this->checkRecentDuplicate($comment);

        $commentable->comment($comment);

        $this->commentRepo->create($comment);

        foreach ($commentable->releaseEvents() as $event) {
            $this->events->dispatch($event);
        }

        foreach ($comment->releaseEvents() as $event) {
            $this->events->dispatch($event);
        }

        return $comment->getId();
    }

    public function commentSimple($content, $images, ICommentable $commentable, $parentId, $userId, $username)
    {
        $contentHtml = $this->formatComment($content, $images);

        return $this->comment($contentHtml, $contentHtml, $commentable, $parentId, $userId, $username);
    }

    public function commentMarkdown($markdown, ICommentable $commentable, $parentId, $userId, $username)
    {
        $parsedown = MarkdownService::getMarkdownCoverter();

        $contentHtml = $parsedown->text($markdown);

        $commentId = $this->comment($markdown, $contentHtml, $commentable, $parentId, $userId, $username);

        foreach ($parsedown->polls as $pollId) {
            $this->pollRepo->markPublished($pollId);
        }

        return $commentId;
    }
}