Add all files
This commit is contained in:
171
extensions/Others/SocialBase/Models/Conversation.php
Normal file
171
extensions/Others/SocialBase/Models/Conversation.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\User;
|
||||
|
||||
class Conversation extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
protected $table = 'ext_social_conversations';
|
||||
|
||||
protected $fillable = [
|
||||
'type',
|
||||
'subject',
|
||||
'created_by',
|
||||
'last_message_at',
|
||||
'metadata',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'last_message_at' => 'datetime',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the creator of the conversation
|
||||
*/
|
||||
public function creator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'created_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all participants
|
||||
*/
|
||||
public function participants(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'ext_social_conversation_participants', 'conversation_id', 'user_id')
|
||||
->withPivot('last_read_at', 'is_muted', 'is_archived', 'is_pinned')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all messages
|
||||
*/
|
||||
public function messages(): HasMany
|
||||
{
|
||||
return $this->hasMany(Message::class)->orderBy('created_at', 'asc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last message
|
||||
*/
|
||||
public function lastMessage()
|
||||
{
|
||||
return $this->hasOne(Message::class)->latestOfMany();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unread messages count for a user
|
||||
*/
|
||||
public function getUnreadCountForUser($userId): int
|
||||
{
|
||||
$participant = $this->participants()->where('user_id', $userId)->first();
|
||||
|
||||
if (!$participant) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return $this->messages()
|
||||
->where('sender_id', '!=', $userId)
|
||||
->where('created_at', '>', $participant->pivot->last_read_at ?? $this->created_at)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark conversation as read for user
|
||||
*/
|
||||
public function markAsReadForUser($userId): void
|
||||
{
|
||||
$this->participants()->updateExistingPivot($userId, [
|
||||
'last_read_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user is participant
|
||||
*/
|
||||
public function hasParticipant($userId): bool
|
||||
{
|
||||
return $this->participants()->where('user_id', $userId)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get other participant in a direct conversation
|
||||
*/
|
||||
public function getOtherParticipant($userId)
|
||||
{
|
||||
if ($this->type !== 'direct') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->participants()->where('user_id', '!=', $userId)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find or create a direct conversation between two users
|
||||
*/
|
||||
public static function findOrCreateDirectConversation($userId1, $userId2): self
|
||||
{
|
||||
// Try to find existing conversation
|
||||
$conversation = self::where('type', 'direct')
|
||||
->whereHas('participants', function ($query) use ($userId1) {
|
||||
$query->where('user_id', $userId1);
|
||||
})
|
||||
->whereHas('participants', function ($query) use ($userId2) {
|
||||
$query->where('user_id', $userId2);
|
||||
})
|
||||
->first();
|
||||
|
||||
if ($conversation) {
|
||||
return $conversation;
|
||||
}
|
||||
|
||||
// Create new conversation
|
||||
$conversation = self::create([
|
||||
'type' => 'direct',
|
||||
'created_by' => $userId1,
|
||||
]);
|
||||
|
||||
$conversation->participants()->attach([$userId1, $userId2]);
|
||||
|
||||
return $conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if conversation is pinned for a user
|
||||
*/
|
||||
public function isPinnedForUser($userId): bool
|
||||
{
|
||||
$participant = $this->participants()->where('user_id', $userId)->first();
|
||||
return $participant ? (bool) $participant->pivot->is_pinned : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle pinned status for a user
|
||||
*/
|
||||
public function togglePinForUser($userId): bool
|
||||
{
|
||||
$participant = $this->participants()->where('user_id', $userId)->first();
|
||||
|
||||
if (!$participant) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$newPinnedStatus = !$participant->pivot->is_pinned;
|
||||
|
||||
$this->participants()->updateExistingPivot($userId, [
|
||||
'is_pinned' => $newPinnedStatus
|
||||
]);
|
||||
|
||||
return $newPinnedStatus;
|
||||
}
|
||||
}
|
||||
|
||||
133
extensions/Others/SocialBase/Models/Message.php
Normal file
133
extensions/Others/SocialBase/Models/Message.php
Normal file
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\User;
|
||||
use Paymenter\Extensions\Others\SocialBase\Traits\HasReactions;
|
||||
|
||||
class Message extends Model
|
||||
{
|
||||
use SoftDeletes, HasReactions;
|
||||
|
||||
protected $table = 'ext_social_messages';
|
||||
|
||||
protected $fillable = [
|
||||
'conversation_id',
|
||||
'sender_id',
|
||||
'content',
|
||||
'attachments',
|
||||
'type',
|
||||
'is_system',
|
||||
'system_type',
|
||||
'metadata',
|
||||
'is_moderated',
|
||||
'moderation_status',
|
||||
'moderated_by',
|
||||
'moderated_at',
|
||||
'moderation_reason',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_system' => 'boolean',
|
||||
'is_moderated' => 'boolean',
|
||||
'moderated_at' => 'datetime',
|
||||
'metadata' => 'array',
|
||||
'attachments' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the conversation
|
||||
*/
|
||||
public function conversation(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Conversation::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sender
|
||||
*/
|
||||
public function sender(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'sender_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the moderator
|
||||
*/
|
||||
public function moderator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'moderated_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users who read this message
|
||||
*/
|
||||
public function readBy(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(User::class, 'ext_social_message_reads', 'message_id', 'user_id')
|
||||
->withPivot('read_at')
|
||||
->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reports for this message
|
||||
*/
|
||||
public function reports()
|
||||
{
|
||||
return $this->hasMany(\Paymenter\Extensions\Others\SocialBase\Models\MessageReport::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark message as read by user
|
||||
*/
|
||||
public function markAsReadBy($userId): void
|
||||
{
|
||||
if (!$this->readBy()->where('user_id', $userId)->exists()) {
|
||||
$this->readBy()->attach($userId, ['read_at' => now()]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if message is read by user
|
||||
*/
|
||||
public function isReadBy($userId): bool
|
||||
{
|
||||
return $this->readBy()->where('user_id', $userId)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for non-system messages
|
||||
*/
|
||||
public function scopeUserMessages($query)
|
||||
{
|
||||
return $query->where('is_system', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for system messages
|
||||
*/
|
||||
public function scopeSystemMessages($query)
|
||||
{
|
||||
return $query->where('is_system', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for flagged messages
|
||||
*/
|
||||
public function scopeFlagged($query)
|
||||
{
|
||||
return $query->where('moderation_status', 'flagged');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for approved messages
|
||||
*/
|
||||
public function scopeApproved($query)
|
||||
{
|
||||
return $query->where('moderation_status', 'approved');
|
||||
}
|
||||
}
|
||||
67
extensions/Others/SocialBase/Models/MessageReport.php
Normal file
67
extensions/Others/SocialBase/Models/MessageReport.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\User;
|
||||
|
||||
class MessageReport extends Model
|
||||
{
|
||||
protected $table = 'ext_social_message_reports';
|
||||
|
||||
protected $fillable = [
|
||||
'message_id',
|
||||
'reported_by',
|
||||
'reason',
|
||||
'description',
|
||||
'status',
|
||||
'reviewed_by',
|
||||
'reviewed_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'reviewed_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the message being reported
|
||||
*/
|
||||
public function message(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Message::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who reported
|
||||
*/
|
||||
public function reporter(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reported_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who reviewed
|
||||
*/
|
||||
public function reviewer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reviewed_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for pending reports
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('status', 'pending');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for reviewed reports
|
||||
*/
|
||||
public function scopeReviewed($query)
|
||||
{
|
||||
return $query->where('status', 'reviewed');
|
||||
}
|
||||
}
|
||||
|
||||
190
extensions/Others/SocialBase/Models/ProfileComment.php
Normal file
190
extensions/Others/SocialBase/Models/ProfileComment.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\User;
|
||||
use Paymenter\Extensions\Others\SocialBase\Traits\HasReactions;
|
||||
|
||||
class ProfileComment extends Model
|
||||
{
|
||||
use SoftDeletes, HasReactions;
|
||||
|
||||
protected $table = 'ext_social_profile_comments';
|
||||
|
||||
protected $fillable = [
|
||||
'user_profile_id',
|
||||
'user_id',
|
||||
'parent_id',
|
||||
'content',
|
||||
'approved',
|
||||
'metadata',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'approved' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the profile this comment belongs to
|
||||
*/
|
||||
public function profile(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(UserProfile::class, 'user_profile_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who wrote the comment
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get parent comment (for replies)
|
||||
*/
|
||||
public function parent(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ProfileComment::class, 'parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get replies to this comment
|
||||
*/
|
||||
public function replies()
|
||||
{
|
||||
return $this->hasMany(ProfileComment::class, 'parent_id')->orderBy('created_at', 'asc');
|
||||
}
|
||||
|
||||
// Reaction functionality is now provided by the HasReactions trait
|
||||
|
||||
/**
|
||||
* Scope to get approved comments only
|
||||
*/
|
||||
public function scopeApproved($query)
|
||||
{
|
||||
return $query->where('approved', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get pending comments
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('approved', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get top-level comments (not replies)
|
||||
*/
|
||||
public function scopeTopLevel($query)
|
||||
{
|
||||
return $query->whereNull('parent_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comment can be edited by user
|
||||
*/
|
||||
public function canBeEditedBy(?User $user = null): bool
|
||||
{
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Owner can edit within time limit (e.g., 15 minutes)
|
||||
if ($user->id === $this->user_id) {
|
||||
return $this->created_at->diffInMinutes(now()) < 15;
|
||||
}
|
||||
|
||||
// Admins with permission can always edit
|
||||
return $user->hasPermission('socialbase.comments.update');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can delete this comment
|
||||
*/
|
||||
public function canBeDeletedBy(?User $user = null): bool
|
||||
{
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// User can delete their own comment
|
||||
if ($user->id === $this->user_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Profile owner can delete comments on their profile
|
||||
if ($user->id === $this->profile->user_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Admins with permission can delete
|
||||
return $user->hasPermission('socialbase.comments.delete') ?? false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if user can edit this comment
|
||||
*/
|
||||
public function canEdit(?User $user = null): bool
|
||||
{
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// User can edit their own comment within 15 minutes
|
||||
if ($user->id === $this->user_id) {
|
||||
return $this->created_at->diffInMinutes(now()) <= 15;
|
||||
}
|
||||
|
||||
// Admins can always edit
|
||||
return $user->hasPermission('socialbase.comments.edit') ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comment is editable (within time limit)
|
||||
*/
|
||||
public function isEditable(): bool
|
||||
{
|
||||
return $this->created_at->diffInMinutes(now()) <= 15;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user owns this comment
|
||||
*/
|
||||
public function isOwnedBy(?User $user = null): bool
|
||||
{
|
||||
return $user && $user->id === $this->user_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted creation date
|
||||
*/
|
||||
public function getFormattedDateAttribute(): string
|
||||
{
|
||||
return $this->created_at->diffForHumans();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status as string
|
||||
*/
|
||||
public function getStatusAttribute(): string
|
||||
{
|
||||
return $this->approved ? 'approved' : 'pending';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comment has been edited
|
||||
*/
|
||||
public function getIsEditedAttribute(): bool
|
||||
{
|
||||
return $this->updated_at->gt($this->created_at->addMinute());
|
||||
}
|
||||
}
|
||||
110
extensions/Others/SocialBase/Models/Reaction.php
Normal file
110
extensions/Others/SocialBase/Models/Reaction.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use App\Models\User;
|
||||
|
||||
class Reaction extends Model
|
||||
{
|
||||
protected $table = 'ext_social_reactions';
|
||||
|
||||
protected $fillable = [
|
||||
'reactable_type',
|
||||
'reactable_id',
|
||||
'user_id',
|
||||
'type',
|
||||
];
|
||||
|
||||
/**
|
||||
* Available reaction types with their emojis
|
||||
*/
|
||||
public const TYPES = [
|
||||
'like' => '👍',
|
||||
'love' => '❤️',
|
||||
'laugh' => '😂',
|
||||
'wow' => '😮',
|
||||
'sad' => '😢',
|
||||
'angry' => '😠',
|
||||
'celebrate' => '🎉',
|
||||
'thinking' => '🤔',
|
||||
'clap' => '👏',
|
||||
'fire' => '🔥',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the parent reactable model (comment, post, thread, etc.)
|
||||
*/
|
||||
public function reactable(): MorphTo
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who made the reaction
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the emoji for this reaction type
|
||||
*/
|
||||
public function getEmojiAttribute(): string
|
||||
{
|
||||
return self::TYPES[$this->type] ?? '👍';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get formatted type name
|
||||
*/
|
||||
public function getFormattedTypeAttribute(): string
|
||||
{
|
||||
return ucfirst($this->type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available reaction types for a specific context
|
||||
* Extensions can customize which reactions are available
|
||||
*/
|
||||
public static function getAvailableTypes(string $context = 'default'): array
|
||||
{
|
||||
$contextTypes = match($context) {
|
||||
'forum' => ['like', 'love', 'thinking', 'clap', 'fire'], // Forum-specific reactions
|
||||
'social' => ['like', 'love', 'celebrate', 'fire', 'thinking'], // Social/comment reactions
|
||||
'celebration' => ['like', 'love', 'celebrate', 'clap', 'fire'], // Achievement/celebration contexts
|
||||
default => array_keys(self::TYPES), // All reaction type names
|
||||
};
|
||||
|
||||
// Return type => emoji mapping for the context
|
||||
return array_intersect_key(self::TYPES, array_flip($contextTypes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get reactions for a specific reactable
|
||||
*/
|
||||
public function scopeForReactable($query, $reactableType, $reactableId)
|
||||
{
|
||||
return $query->where('reactable_type', $reactableType)
|
||||
->where('reactable_id', $reactableId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get reactions by type
|
||||
*/
|
||||
public function scopeOfType($query, string $type)
|
||||
{
|
||||
return $query->where('type', $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get reactions by user
|
||||
*/
|
||||
public function scopeByUser($query, int $userId)
|
||||
{
|
||||
return $query->where('user_id', $userId);
|
||||
}
|
||||
}
|
||||
303
extensions/Others/SocialBase/Models/UserProfile.php
Normal file
303
extensions/Others/SocialBase/Models/UserProfile.php
Normal file
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Models\User;
|
||||
use Paymenter\Extensions\Others\SocialBase\Traits\HasReactions;
|
||||
use Paymenter\Extensions\Others\SocialBase\Models\Message;
|
||||
use Paymenter\Extensions\Others\SocialBase\Models\ProfileComment;
|
||||
|
||||
class UserProfile extends Model
|
||||
{
|
||||
use SoftDeletes, HasReactions;
|
||||
|
||||
protected $table = 'ext_social_user_profiles';
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'display_name',
|
||||
'bio',
|
||||
'location',
|
||||
'website',
|
||||
'avatar_path',
|
||||
'banner_path',
|
||||
'is_public',
|
||||
'show_email',
|
||||
'show_joined_date',
|
||||
'allow_comments',
|
||||
'is_banned',
|
||||
'upload_restricted',
|
||||
'comment_restricted',
|
||||
'profile_edit_restricted',
|
||||
'restriction_reason',
|
||||
'restriction_level',
|
||||
'theme',
|
||||
'metadata',
|
||||
'moderation_status',
|
||||
'moderation_notes',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'is_public' => 'boolean',
|
||||
'show_email' => 'boolean',
|
||||
'show_joined_date' => 'boolean',
|
||||
'allow_comments' => 'boolean',
|
||||
'is_banned' => 'boolean',
|
||||
'upload_restricted' => 'boolean',
|
||||
'comment_restricted' => 'boolean',
|
||||
'profile_edit_restricted' => 'boolean',
|
||||
'metadata' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* Boot the model
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
// Handle cascading delete for associated data
|
||||
static::deleting(function ($profile) {
|
||||
// Delete comments on this profile
|
||||
$profile->comments()->delete();
|
||||
|
||||
// Delete profile comments made by this user
|
||||
ProfileComment::where('user_id', $profile->user_id)->delete();
|
||||
|
||||
// Delete messages sent by this user
|
||||
Message::where('sender_id', $profile->user_id)->delete();
|
||||
|
||||
// Delete reactions made by this user
|
||||
$profile->reactions()->delete();
|
||||
|
||||
// Delete avatar and banner files if they exist
|
||||
if ($profile->avatar_path) {
|
||||
Storage::disk('local')->delete($profile->avatar_path);
|
||||
}
|
||||
if ($profile->banner_path) {
|
||||
Storage::disk('local')->delete($profile->banner_path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the profile
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all comments for this profile
|
||||
*/
|
||||
public function comments(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProfileComment::class)->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get approved comments for this profile
|
||||
*/
|
||||
public function approvedComments(): HasMany
|
||||
{
|
||||
return $this->hasMany(ProfileComment::class)
|
||||
->where('approved', true)
|
||||
->orderBy('created_at', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the avatar URL
|
||||
*/
|
||||
public function getAvatarUrlAttribute(): ?string
|
||||
{
|
||||
if ($this->avatar_path) {
|
||||
return Storage::url($this->avatar_path);
|
||||
}
|
||||
|
||||
// Return default avatar or gravatar
|
||||
if ($this->user && $this->user->email) {
|
||||
$hash = md5(strtolower(trim($this->user->email)));
|
||||
return "https://www.gravatar.com/avatar/{$hash}?d=identicon&s=150";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the banner URL
|
||||
*/
|
||||
public function getBannerUrlAttribute(): ?string
|
||||
{
|
||||
if ($this->banner_path) {
|
||||
return Storage::url($this->banner_path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display name or fallback to user name
|
||||
*/
|
||||
public function getDisplayNameAttribute($value): string
|
||||
{
|
||||
return $value ?: ($this->user->name ?? 'Anonymous');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if profile is viewable by current user
|
||||
*/
|
||||
public function isViewableBy(?User $user = null): bool
|
||||
{
|
||||
// If profile is private, only allow specific access
|
||||
if (!$this->is_public) {
|
||||
// Guest users cannot view private profiles
|
||||
if (!$user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Owner can always view their own profile
|
||||
if ($user->id === $this->user_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Admins can view all profiles (users with all permissions or specific admin permissions)
|
||||
if ($user->hasPermission('*') || $user->hasPermission('socialbase.profiles.view')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Private profile, not owner, not admin - deny access
|
||||
return false;
|
||||
}
|
||||
|
||||
// Profile is public - allow access to everyone (including guests)
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if comments are allowed on this profile
|
||||
*/
|
||||
public function allowsComments(): bool
|
||||
{
|
||||
return $this->allow_comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get public profiles only
|
||||
*/
|
||||
public function scopePublic($query)
|
||||
{
|
||||
return $query->where('is_public', true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get profile statistics
|
||||
*/
|
||||
public function getStatsAttribute(): array
|
||||
{
|
||||
return [
|
||||
'comments_count' => $this->comments()->count(),
|
||||
'approved_comments_count' => $this->approvedComments()->count(),
|
||||
'joined_date' => $this->user->created_at ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can upload files
|
||||
*/
|
||||
public function canUpload(): bool
|
||||
{
|
||||
return !$this->upload_restricted && !$this->is_banned && $this->restriction_level !== 'banned';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can comment
|
||||
*/
|
||||
public function canComment(): bool
|
||||
{
|
||||
return !$this->comment_restricted && !$this->is_banned && $this->restriction_level !== 'banned';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user can edit profile
|
||||
*/
|
||||
public function canEditProfile(): bool
|
||||
{
|
||||
return !$this->profile_edit_restricted && !$this->is_banned && $this->restriction_level !== 'banned';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user has any restrictions
|
||||
*/
|
||||
public function hasRestrictions(): bool
|
||||
{
|
||||
return $this->is_banned ||
|
||||
$this->upload_restricted ||
|
||||
$this->comment_restricted ||
|
||||
$this->profile_edit_restricted ||
|
||||
$this->restriction_level !== 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get restriction status text
|
||||
*/
|
||||
public function getRestrictionStatusAttribute(): string
|
||||
{
|
||||
if ($this->is_banned) {
|
||||
return 'Banned';
|
||||
}
|
||||
|
||||
switch ($this->restriction_level) {
|
||||
case 'warning':
|
||||
return 'Warning';
|
||||
case 'limited':
|
||||
return 'Limited';
|
||||
case 'suspended':
|
||||
return 'Suspended';
|
||||
case 'banned':
|
||||
return 'Banned';
|
||||
default:
|
||||
return $this->hasRestrictions() ? 'Restricted' : 'Active';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get existing profile
|
||||
*/
|
||||
public static function getForUser(int $userId): ?UserProfile
|
||||
{
|
||||
// First try to find existing profile (including soft deleted)
|
||||
$profile = static::withTrashed()->where('user_id', $userId)->first();
|
||||
|
||||
if ($profile) {
|
||||
// If it's soft deleted, restore it
|
||||
if ($profile->trashed()) {
|
||||
$profile->restore();
|
||||
}
|
||||
return $profile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create profile with default values
|
||||
* Only use this when user explicitly wants to create a profile
|
||||
*/
|
||||
public static function createForUser(int $userId): UserProfile
|
||||
{
|
||||
return static::create([
|
||||
'user_id' => $userId,
|
||||
'is_public' => false, // Private by default until setup
|
||||
'restriction_level' => 'none',
|
||||
'show_email' => false,
|
||||
'show_joined_date' => true,
|
||||
'allow_comments' => true,
|
||||
]);
|
||||
}
|
||||
}
|
||||
68
extensions/Others/SocialBase/Models/UserReport.php
Normal file
68
extensions/Others/SocialBase/Models/UserReport.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Paymenter\Extensions\Others\SocialBase\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use App\Models\User;
|
||||
|
||||
class UserReport extends Model
|
||||
{
|
||||
protected $table = 'ext_social_user_reports';
|
||||
|
||||
protected $fillable = [
|
||||
'reported_user_id',
|
||||
'reported_by',
|
||||
'reason',
|
||||
'description',
|
||||
'status',
|
||||
'admin_notes',
|
||||
'reviewed_by',
|
||||
'reviewed_at',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'reviewed_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the user being reported
|
||||
*/
|
||||
public function reportedUser(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reported_user_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who made the report
|
||||
*/
|
||||
public function reporter(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reported_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who reviewed the report
|
||||
*/
|
||||
public function reviewer(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class, 'reviewed_by');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for pending reports
|
||||
*/
|
||||
public function scopePending($query)
|
||||
{
|
||||
return $query->where('status', 'pending');
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for reviewed reports
|
||||
*/
|
||||
public function scopeReviewed($query)
|
||||
{
|
||||
return $query->where('status', 'reviewed');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user