| @@ -1,16 +1,44 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | |
| 3 | 3 | namespace FluentCommunity\App\Models; |
| 4 | 4 | |
| 5 | +use FluentCommunity\App\Functions\Utility; | |
| 5 | 6 | use FluentCommunity\App\Models\XProfile; |
| 7 | +use FluentCommunity\App\Models\Activity; | |
| 8 | +use FluentCommunity\App\Services\FeedsHelper; | |
| 9 | +use FluentCommunity\App\Services\Helper; | |
| 6 | 10 | |
| 11 | +/** | |
| 12 | + * @property int $id | |
| 13 | + * @property int|null $user_id | |
| 14 | + * @property int|null $post_id | |
| 15 | + * @property int|null $parent_id | |
| 16 | + * @property string|null $message | |
| 17 | + * @property string|null $message_rendered | |
| 18 | + * @property mixed $meta | |
| 19 | + * @property string|null $type | |
| 20 | + * @property string|null $content_type | |
| 21 | + * @property string|null $status | |
| 22 | + * @property int|null $is_sticky | |
| 23 | + * @property int $reactions_count | |
| 24 | + * @property string|null $created_at | |
| 25 | + * @property string|null $updated_at | |
| 26 | + * @property-read User|null $user | |
| 27 | + * @property-read XProfile|null $xprofile | |
| 28 | + * @property-read Feed|null $post | |
| 29 | + * @property-read mixed $media | |
| 30 | + */ | |
| 7 | 31 | class Comment extends Model |
| 8 | 32 | { |
| 9 | 33 | protected $table = 'fcom_post_comments'; |
| 10 | 34 | |
| 11 | - protected $guarded = ['id']; | |
| 35 | + protected $guarded = [ 'id' ]; | |
| 12 | 36 | |
| 37 | + protected $casts = [ | |
| 38 | + 'is_sticky' => 'int', | |
| 39 | + ]; | |
| 40 | + | |
| 13 | 41 | protected $fillable = [ |
| 14 | 42 | 'user_id', |
| 15 | 43 | 'post_id', |
| 16 | 44 | 'parent_id', |
| @@ -22,13 +50,13 @@ | ||
| 22 | 50 | 'status', |
| 23 | 51 | 'is_sticky', |
| 24 | 52 | 'reactions_count', |
| 25 | 53 | 'created_at', |
| 26 | - 'updated_at' | |
| 54 | + 'updated_at', | |
| 27 | 55 | ]; |
| 28 | 56 | |
| 29 | 57 | protected $searchable = [ |
| 30 | - 'message' | |
| 58 | + 'message', | |
| 31 | 59 | ]; |
| 32 | 60 | |
| 33 | 61 | public static function boot() |
| 34 | 62 | { |
| @@ -38,8 +66,13 @@ | ||
| 38 | 66 | if (empty($model->user_id)) { |
| 39 | 67 | $model->user_id = get_current_user_id(); |
| 40 | 68 | } |
| 41 | 69 | $model->type = 'comment'; |
| 70 | + | |
| 71 | + if(empty($model->status)) { | |
| 72 | + $model->status = 'published'; | |
| 73 | + } | |
| 74 | + | |
| 42 | 75 | }); |
| 43 | 76 | |
| 44 | 77 | static::deleting(function ($comment) { |
| 45 | 78 | Media::where('sub_object_id', $comment->id) |
| @@ -44,11 +77,18 @@ | ||
| 44 | 77 | static::deleting(function ($comment) { |
| 45 | 78 | Media::where('sub_object_id', $comment->id) |
| 46 | 79 | ->where('object_source', 'comment') |
| 47 | 80 | ->update([ |
| 48 | - 'is_active' => 0 | |
| 81 | + 'is_active' => 0, | |
| 49 | 82 | ]); |
| 50 | 83 | |
| 84 | + $comment->reactions()->delete(); | |
| 85 | + | |
| 86 | + Activity::where('feed_id', $comment->post_id) | |
| 87 | + ->where('action_name', 'comment_added') | |
| 88 | + ->where('related_id', $comment->id) | |
| 89 | + ->delete(); | |
| 90 | + | |
| 51 | 91 | $notifications = Notification::where('object_id', $comment->id) |
| 52 | 92 | ->where('src_object_type', 'comment') |
| 53 | 93 | ->get(); |
| 54 | 94 | |
| @@ -104,8 +144,20 @@ | ||
| 104 | 144 | { |
| 105 | 145 | return $this->belongsTo(Feed::class, 'post_id', 'id')->withoutGlobalScopes(); |
| 106 | 146 | } |
| 107 | 147 | |
| 148 | + public function space() | |
| 149 | + { | |
| 150 | + return $this->hasOneThrough( | |
| 151 | + BaseSpace::class, | |
| 152 | + Feed::class, | |
| 153 | + 'id', // Foreign key on the feeds table | |
| 154 | + 'id', // Foreign key on the spaces table | |
| 155 | + 'post_id', // Local key on the comments table | |
| 156 | + 'space_id' // Local key on the feeds table | |
| 157 | + )->withoutGlobalScopes(); | |
| 158 | + } | |
| 159 | + | |
| 108 | 160 | public function reactions() |
| 109 | 161 | { |
| 110 | 162 | return $this->hasMany(Reaction::class, 'object_id', 'id') |
| 111 | 163 | ->where('object_type', 'comment'); |
| @@ -110,8 +162,32 @@ | ||
| 110 | 162 | return $this->hasMany(Reaction::class, 'object_id', 'id') |
| 111 | 163 | ->where('object_type', 'comment'); |
| 112 | 164 | } |
| 113 | 165 | |
| 166 | + public function scopeByContentModerationAccessStatus($query, $user, $space = null) | |
| 167 | + { | |
| 168 | + if (!$user || !Helper::isFeatureEnabled('content_moderation')) { | |
| 169 | + return $query->where('status', 'published'); | |
| 170 | + } | |
| 171 | + | |
| 172 | + if ( | |
| 173 | + $user->hasCommunityModeratorAccess() || | |
| 174 | + ($space && $user->hasSpacePermission('edit_any_comment', $space)) | |
| 175 | + ) { | |
| 176 | + return $query->whereIn('status', [ 'published', 'pending' ]); | |
| 177 | + } | |
| 178 | + | |
| 179 | + // This is a normal User. | |
| 180 | + return $query->where(function ($q) use ($user) { | |
| 181 | + $q->where('status', 'published') | |
| 182 | + ->orWhere(function ($q) use ($user) { | |
| 183 | + $q->where('status', 'pending') | |
| 184 | + ->where('user_id', $user->ID); | |
| 185 | + }); | |
| 186 | + }); | |
| 187 | + | |
| 188 | + } | |
| 189 | + | |
| 114 | 190 | /* |
| 115 | 191 | * Find all the user ids of a child comment who commented on the parent comment including the parent comment author |
| 116 | 192 | */ |
| 117 | 193 | public function getCommentParentUserIds($lastUserId = 0) |
| @@ -119,11 +195,11 @@ | ||
| 119 | 195 | if (!$this->parent_id) { |
| 120 | 196 | return []; |
| 121 | 197 | } |
| 122 | 198 | |
| 123 | - $parentComment = Comment::select(['user_id'])->find($this->parent_id); | |
| 124 | - $allUserIds = Comment::where('parent_id', $this->parent_id) | |
| 125 | - ->select(['user_id']) | |
| 199 | + $parentComment = self::select([ 'user_id' ])->find($this->parent_id); | |
| 200 | + $allUserIds = self::where('parent_id', $this->parent_id) | |
| 201 | + ->select([ 'user_id' ]) | |
| 126 | 202 | ->distinct('user_id') |
| 127 | 203 | ->when($lastUserId, function ($query) use ($lastUserId) { |
| 128 | 204 | $query->where('user_id', '>', $lastUserId); |
| 129 | 205 | }) |
| @@ -146,17 +222,24 @@ | ||
| 146 | 222 | } |
| 147 | 223 | |
| 148 | 224 | public function getMetaAttribute($value) |
| 149 | 225 | { |
| 150 | - $meta = maybe_unserialize($value); | |
| 226 | + $meta = Utility::safeUnserialize($value); | |
| 151 | 227 | |
| 152 | 228 | if (!$meta) { |
| 153 | 229 | $meta = []; |
| 154 | 230 | } |
| 155 | 231 | |
| 156 | - return $meta; | |
| 232 | + return Helper::sanitizeStoredMediaPreview($meta); | |
| 157 | 233 | } |
| 158 | 234 | |
| 235 | + public function getHumanExcerpt($length = 30) | |
| 236 | + { | |
| 237 | + $content = $this->message; | |
| 238 | + | |
| 239 | + return Helper::getHumanExcerpt($content, $length); | |
| 240 | + } | |
| 241 | + | |
| 159 | 242 | public function getEmailSubject($feed = null) |
| 160 | 243 | { |
| 161 | 244 | if (!$feed) { |
| 162 | 245 | $feed = $this->post; |
| @@ -169,17 +252,55 @@ | ||
| 169 | 252 | } else { |
| 170 | 253 | /* translators: %1$s is the post title and %2$s is the comment author name */ |
| 171 | 254 | $emailSubject = \sprintf(__('New reply of a comment in a post on %1$s - %2$s', 'fluent-community'), $this->post->getHumanExcerpt(40), $this->xprofile->display_name); |
| 172 | 255 | } |
| 173 | - } else { | |
| 174 | - if ($feed->title) { | |
| 256 | + } elseif ($feed->title) { | |
| 175 | 257 | /* translators: %1$s is the feed title and %2$s is the comment author name */ |
| 176 | 258 | $emailSubject = \sprintf(__('New comment on %1$s - %2$s', 'fluent-community'), $feed->title, $this->xprofile->display_name); |
| 177 | - } else { | |
| 178 | - /* translators: %1$s is the post title and %2$s is the comment author name */ | |
| 179 | - $emailSubject = \sprintf(__('New comment on a post on %1$s - %2$s', 'fluent-community'), $this->post->getHumanExcerpt(40), $this->xprofile->display_name); | |
| 180 | - } | |
| 259 | + } else { | |
| 260 | + /* translators: %1$s is the post title and %2$s is the comment author name */ | |
| 261 | + $emailSubject = \sprintf(__('New comment on a post on %1$s - %2$s', 'fluent-community'), $this->post->getHumanExcerpt(40), $this->xprofile->display_name); | |
| 181 | 262 | } |
| 182 | 263 | |
| 183 | 264 | return $emailSubject; |
| 265 | + } | |
| 266 | + | |
| 267 | + public function getCommentHtml($withPlaceholder = false, $buttonText = null) | |
| 268 | + { | |
| 269 | + $emailComposer = new \FluentCommunity\App\Services\Libs\EmailComposer(); | |
| 270 | + if ($withPlaceholder) { | |
| 271 | + $postPermalink = '##feed_permalink##'; | |
| 272 | + } else { | |
| 273 | + $postPermalink = $this->post->getPermalink() . '?comment_id=' . $this->id; | |
| 274 | + } | |
| 275 | + | |
| 276 | + if ($this->post->title) { | |
| 277 | + $postTitle = $this->post->title; | |
| 278 | + } else { | |
| 279 | + $postTitle = $this->post->getHumanExcerpt(120); | |
| 280 | + } | |
| 281 | + | |
| 282 | + $renderedMessage = $this->message_rendered; | |
| 283 | + | |
| 284 | + // Remove all the URLs with the text but make it underlined | |
| 285 | + $renderedMessage = preg_replace('/<a href="([^"]+)">([^<]+)<\/a>/', '<span style="text-decoration: underline !important;">$2</span>', $renderedMessage); | |
| 286 | + | |
| 287 | + $renderedMessage .= FeedsHelper::getMediaHtml($this->meta, $postPermalink); | |
| 288 | + | |
| 289 | + $buttonText = $buttonText ? $buttonText : __('View the comment', 'fluent-community'); | |
| 290 | + | |
| 291 | + $emailComposer->addBlock('boxed_content', $renderedMessage, [ | |
| 292 | + 'user' => $this->user, | |
| 293 | + 'permalink' => $postPermalink, | |
| 294 | + 'post_content' => $postTitle, | |
| 295 | + ]); | |
| 296 | + | |
| 297 | + $emailComposer->addBlock('button', $buttonText, [ | |
| 298 | + 'link' => $postPermalink, | |
| 299 | + ]); | |
| 300 | + | |
| 301 | + $emailComposer->setDefaultLogo(); | |
| 302 | + $emailComposer->setDefaultFooter($withPlaceholder); | |
| 303 | + | |
| 304 | + return $emailComposer->getHtml(); | |
| 184 | 305 | } |
| 185 | 306 | } |