mirror of
https://github.com/HDInnovations/UNIT3D-Community-Edition.git
synced 2026-04-24 03:59:08 -05:00
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author HDVinnie <hdinnovations@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Staff;
|
||||
|
||||
use App\Models\Watchlist;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
/**
|
||||
* @see \Tests\Todo\Feature\Http\Controllers\WatchlistControllerTest
|
||||
*/
|
||||
class WatchlistController extends Controller
|
||||
{
|
||||
/**
|
||||
* Watchlist.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
final public function index(): \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
{
|
||||
return \view('Staff.watchlist.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Store A New Watched User.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
final public function store(Request $request, int $id): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$watchedUser = new Watchlist();
|
||||
$watchedUser->user_id = $id;
|
||||
$watchedUser->staff_id = $request->user()->id;
|
||||
$watchedUser->message = $request->input('message');
|
||||
|
||||
$v = \validator($watchedUser->toArray(), [
|
||||
'user_id' => 'required|exists:users,id',
|
||||
'staff_id' => 'required|exists:users,id',
|
||||
'message' => 'required|min:3',
|
||||
]);
|
||||
|
||||
if ($v->fails()) {
|
||||
return \redirect()->route('staff.watchlist.index')
|
||||
->withErrors($v->errors());
|
||||
}
|
||||
$watchedUser->save();
|
||||
|
||||
return \redirect()->route('staff.watchlist.index')
|
||||
->withSuccess('User Successfully Being Watched');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete A Watched User.
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @throws \Exception
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
final public function destroy(int $id): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$watchedUser = Watchlist::findOrFail($id);
|
||||
$watchedUser->delete();
|
||||
|
||||
return \redirect()->route('staff.watchlist.index')
|
||||
->withSuccess('Successfully Stopped Watching User');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* NOTICE OF LICENSE.
|
||||
*
|
||||
* UNIT3D Community Edition is open-sourced software licensed under the GNU Affero General Public License v3.0
|
||||
* The details is bundled with this project in the file LICENSE.txt.
|
||||
*
|
||||
* @project UNIT3D Community Edition
|
||||
*
|
||||
* @author HDVinnie <hdinnovations@protonmail.com>
|
||||
* @license https://www.gnu.org/licenses/agpl-3.0.en.html/ GNU Affero General Public License v3.0
|
||||
*/
|
||||
|
||||
namespace App\Http\Livewire;
|
||||
|
||||
use Livewire\Component;
|
||||
use App\Models\Watchlist;
|
||||
use Livewire\WithPagination;
|
||||
|
||||
class WatchlistSearch extends Component
|
||||
{
|
||||
use WithPagination;
|
||||
|
||||
public $user;
|
||||
public $perPage = 25;
|
||||
public $searchTerm = '';
|
||||
public $sortField = 'created_at';
|
||||
public $sortDirection = 'desc';
|
||||
|
||||
final public function paginationView()
|
||||
{
|
||||
return 'vendor.pagination.livewire-pagination';
|
||||
}
|
||||
|
||||
final public function updatingSearchTerm()
|
||||
{
|
||||
$this->resetPage();
|
||||
}
|
||||
|
||||
final public function mount()
|
||||
{
|
||||
$this->user = \auth()->user();
|
||||
}
|
||||
|
||||
final public function getUsersProperty()
|
||||
{
|
||||
return Watchlist::query()
|
||||
->with(['user', 'author'])
|
||||
->when($this->searchTerm, function ($query) {
|
||||
return $query->where('message', 'LIKE', '%'.$this->searchTerm.'%');
|
||||
})
|
||||
->orderBy($this->sortField, $this->sortDirection)
|
||||
->paginate($this->perPage);
|
||||
}
|
||||
|
||||
final public function sortBy($field)
|
||||
{
|
||||
if ($this->sortField === $field) {
|
||||
$this->sortDirection = $this->sortDirection === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
$this->sortDirection = 'asc';
|
||||
}
|
||||
$this->sortField = $field;
|
||||
}
|
||||
|
||||
final public function render(): \Illuminate\Contracts\View\Factory | \Illuminate\Contracts\View\View | \Illuminate\Contracts\Foundation\Application
|
||||
{
|
||||
return \view('livewire.watchlist-search', [
|
||||
'watchedUsers' => $this->users
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Watchlist extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* Belongs To A User.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id', 'id')->withDefault([
|
||||
'username' => 'System',
|
||||
'id' => '1',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Belongs To A Uploader.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
public function author()
|
||||
{
|
||||
// Not needed yet but may use this soon.
|
||||
|
||||
return $this->belongsTo(User::class, 'staff_id', 'id')->withDefault([
|
||||
'username' => 'System',
|
||||
'id' => '1',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateWatchlistsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('watchlists', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->integer('user_id')->unique()->index();
|
||||
$table->integer('staff_id')->index();
|
||||
$table->text('message');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('watchlists');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
@extends('layout.default')
|
||||
|
||||
@section('title')
|
||||
<title>Watchlist @lang('common.search') - @lang('staff.staff-dashboard') - {{ config('other.title') }}</title>
|
||||
@endsection
|
||||
|
||||
@section('meta')
|
||||
<meta name="description" content="Watchlist Search - @lang('staff.staff-dashboard')">
|
||||
@endsection
|
||||
|
||||
@section('breadcrumb')
|
||||
<li>
|
||||
<a href="{{ route('staff.dashboard.index') }}" itemprop="url" class="l-breadcrumb-item-link">
|
||||
<span itemprop="title" class="l-breadcrumb-item-link-title">@lang('staff.staff-dashboard')</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('staff.watchlist.index') }}" itemprop="url" class="l-breadcrumb-item-link">
|
||||
<span itemprop="title" class="l-breadcrumb-item-link-title">Watchlist @lang('common.search')</span>
|
||||
</a>
|
||||
</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<style>
|
||||
td {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
</style>
|
||||
<div class="container">
|
||||
<div class="block">
|
||||
<div class="header gradient silver">
|
||||
<div class="inner_content">
|
||||
<div class="page-title">
|
||||
<h1 style="margin: 0;">Watched Users</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box container">
|
||||
@livewire('watchlist-search')
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,88 @@
|
||||
<div>
|
||||
<div class="mb-10 form-inline pull-right">
|
||||
<div class="form-group">
|
||||
@lang('common.quantity')
|
||||
<select wire:model="perPage" class="form-control">
|
||||
<option>25</option>
|
||||
<option>50</option>
|
||||
<option>100</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<input type="text" wire:model="searchTerm" class="form-control" style="width: 275px;" placeholder="Search by message"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-body no-padding">
|
||||
<table class="table vertical-align table-hover">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th style="width: 15%;">
|
||||
<div sortable wire:click="sortBy('user_id')" :direction="$sortField === 'user_id' ? $sortDirection : null" role="button">
|
||||
Watching
|
||||
@include('livewire.includes._sort-icon', ['field' => 'user_id'])
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 15%;">
|
||||
<div sortable wire:click="sortBy('staff_id')" :direction="$sortField === 'staff_id' ? $sortDirection : null" role="button">
|
||||
Watched By
|
||||
@include('livewire.includes._sort-icon', ['field' => 'staff_id'])
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 40%;" class="hidden-sm hidden-xs">
|
||||
<div sortable wire:click="sortBy('message')" :direction="$sortField === 'message' ? $sortDirection : null"
|
||||
role="button">
|
||||
Message
|
||||
@include('livewire.includes._sort-icon', ['field' => 'message'])
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 15%;">
|
||||
<div sortable wire:click="sortBy('created_at')" :direction="$sortField === 'created_at' ? $sortDirection : null" role="button">
|
||||
Created At
|
||||
@include('livewire.includes._sort-icon', ['field' => 'created_at'])
|
||||
</div>
|
||||
</th>
|
||||
<th style="width: 15%;">@lang('common.action')</th>
|
||||
</tr>
|
||||
@foreach ($watchedUsers as $watching)
|
||||
<tr>
|
||||
<td>
|
||||
<a href="{{ route('users.show', ['username' => $watching->user->username]) }}">
|
||||
<span class="badge-user text-bold">
|
||||
{{ $watching->user->username }}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('users.show', ['username' => $watching->author->username]) }}">
|
||||
<span class="badge-user text-bold">
|
||||
{{ $watching->author->username }}
|
||||
</span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs">{{ $watching->message }}</td>
|
||||
<td class="hidden-sm hidden-xs">{{ $watching->created_at }}</td>
|
||||
<td>
|
||||
<form action="{{ route('staff.watchlist.destroy', ['id' => $watching->id]) }}" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-xs btn-info" type="submit">
|
||||
<i class="{{ config('other.font-awesome') }} fa-eye-slash"></i> Unwatch
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
@if (! $watchedUsers->count())
|
||||
<div class="margin-10">
|
||||
@lang('common.no-result')
|
||||
</div>
|
||||
@endif
|
||||
<br>
|
||||
<div class="text-center">
|
||||
{{ $watchedUsers->links() }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -136,6 +136,11 @@
|
||||
<i class="{{ config('other.font-awesome') }} fa-users"></i> @lang('staff.user-search')
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('staff.watchlist.index') }}">
|
||||
<i class="{{ config('other.font-awesome') }} fa-eye"></i> Watchlist
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('staff.gifts.index') }}">
|
||||
<i class="{{ config('other.font-awesome') }} fa-gift"></i> @lang('staff.user-gifting')
|
||||
|
||||
@@ -338,7 +338,7 @@
|
||||
|
||||
@if ($torrent->anon !== 1 && $uploader->private_profile !== 1)
|
||||
@if (auth()->user()->isFollowing($uploader->id))
|
||||
<form class="form-inline" role="form"action="{{ route('follow.destroy', ['username' => $uploader->username]) }}"
|
||||
<form class="form-inline" role="form" action="{{ route('follow.destroy', ['username' => $uploader->username]) }}"
|
||||
style="display: inline-block;" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
|
||||
@@ -79,6 +79,12 @@
|
||||
</i>
|
||||
</a>
|
||||
@endif
|
||||
@php $watched = App\Models\Watchlist::whereUserId($user->id)->first(); @endphp
|
||||
@if ($watched && auth()->user()->group->is_modo)
|
||||
<i class="{{ config('other.font-awesome') }} fa-eye fa-beat text-danger" aria-hidden="true" data-toggle="tooltip"
|
||||
title="" data-original-title="User is being watched!">
|
||||
</i>
|
||||
@endif
|
||||
</h2>
|
||||
<h4>@lang('common.group'): <span class="badge-user text-bold"
|
||||
style="color:{{ $user->group->color }}; background-image:{{ $user->group->effect }};"><i
|
||||
@@ -92,6 +98,18 @@
|
||||
<button class="btn btn-xs btn-warning" data-toggle="modal"
|
||||
data-target="#modal_user_note"><span
|
||||
class="{{ config('other.font-awesome') }} fa-sticky-note"></span> @lang('user.note') </button>
|
||||
@if(! $watched)
|
||||
<button class="btn btn-xs btn-danger" data-toggle="modal" data-target="#modal_user_watch">
|
||||
<span class="{{ config('other.font-awesome') }} fa-eye"></span> Watch </button>
|
||||
@else
|
||||
<form style="display: inline;" action="{{ route('staff.watchlist.destroy', ['id' => $watched->id]) }}" method="POST">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-xs btn-warning" type="submit">
|
||||
<i class="{{ config('other.font-awesome') }} fa-eye-slash"></i> Unwatch
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
@if ($user->group->id == 5)
|
||||
<button class="btn btn-xs btn-warning" data-toggle="modal"
|
||||
data-target="#modal_user_unban"><span
|
||||
|
||||
@@ -225,3 +225,39 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal fade" id="modal_user_watch" tabindex="-1" role="dialog" aria-labelledby="modal_user_gift"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-dark modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header mx-auto">
|
||||
<div class="text-center">
|
||||
<p style="font-size: 27px;">Watch User: {{ $user->username }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="py-3">
|
||||
<form role="form" method="POST"
|
||||
action="{{ route('staff.watchlist.store', ['id' => $user->id]) }}">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<label for="report_reason">Reason</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="message"></label>
|
||||
<textarea class="form-control" rows="5" name="message" cols="50" id="message"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input class="btn btn-danger" type="submit" value="Submit">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="close ml-auto" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -907,5 +907,14 @@ Route::group(['middleware' => 'language'], function () {
|
||||
Route::get('/', 'WarningController@index')->name('index');
|
||||
});
|
||||
});
|
||||
|
||||
// Watchlist
|
||||
Route::group(['prefix' => 'watchlist'], function () {
|
||||
Route::name('staff.watchlist.')->group(function () {
|
||||
Route::get('/', 'WatchlistController@index')->name('index');
|
||||
Route::post('/{id}/store', 'WatchlistController@store')->name('store');
|
||||
Route::delete('/{id}/destroy', 'WatchlistController@destroy')->name('destroy');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user