Add all files

This commit is contained in:
root
2025-11-30 11:57:22 +01:00
parent 2ee51cac4f
commit 044271db3d
1476 changed files with 142544 additions and 0 deletions

View 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;
}
}

View 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');
}
}

View 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');
}
}

View 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());
}
}

View 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);
}
}

View 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,
]);
}
}

View 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');
}
}