feat: adds ability for approved users to approve artwork

This commit is contained in:
Paul Couture 2024-01-13 10:45:40 -06:00
parent bc66edb3ce
commit eb931bbb6a
9 changed files with 198 additions and 87 deletions

View File

@ -8,11 +8,10 @@ services:
dockerfile: Dockerfile
container_name: ${CONTAINER_NAME:-pcag-laravel}
volumes:
- /var/www/html/vendor/
- ${PATH_TO_SITE}:/var/www/html:cached
- ${PATH_TO_STATIC}:/static:cached
- ./nginx/default.conf:/etc/nginx/sites-available/default:cached
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:cached
- ${PATH_TO_SITE}:/var/www/html
- ${PATH_TO_STATIC}:/static
- ./nginx/default.conf:/etc/nginx/sites-available/default
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
environment:
TZ: UTC
PUID: ${UID:-1000}
@ -27,19 +26,7 @@ services:
LARAVEL_SCHEDULE_ENABLED: true
PHP_OPEN_BASEDIR: "/var/www/html:/static"
ports:
- "8776:80"
restart: always
networks:
- proxy
- default
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Entry Point for https
- "traefik.http.routers.podcastartgen-secure.entrypoints=websecure"
- "traefik.http.routers.podcastartgen-secure.rule=Host(`www.noagendaartgenerator.com`) || HOST(`noagendaartgenerator.com`)"
- "traefik.http.routers.podcastartgen-secure.service=podcastartgen-service"
- "traefik.http.services.podcastartgen-service.loadbalancer.server.port=80"
- "80:80"
links:
- db
- redis
@ -47,25 +34,13 @@ services:
static:
image: nginx:alpine
volumes:
- ${PATH_TO_STATIC}:/usr/share/nginx/html:cached
- ./static:/usr/share/nginx/html
environment:
TZ: UTC
PUID: ${UID:-1000}
PGID: ${GID:-1000}
ports:
- "8777:80"
networks:
- proxy
- default
restart: always
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
# Entry Point for https
- "traefik.http.routers.podcastartstatic-secure.entrypoints=websecure"
- "traefik.http.routers.podcastartstatic-secure.rule=Host(`static.noagendaartgenerator.com`)"
- "traefik.http.routers.podcastartstatic-secure.service=podcastartstatic-service"
- "traefik.http.services.podcastartstatic-service.loadbalancer.server.port=80"
- "8181:80"
db:
image: mariadb:latest
env_file: .env
@ -76,46 +51,17 @@ services:
MARIADB_DATABASE: ${LIVE_DB_DATABASE}
MARIADB_PASSWORD: ${LIVE_DB_PASSWORD}
volumes:
- "./db/data:/var/lib/mysql:cached"
- "./db/dump:/docker-entrypoint-initdb.d:cached"
networks:
- proxy
- default
- "./db/data:/var/lib/mysql"
- "./db/dump:/docker-entrypoint-initdb.d"
ports:
- "51533:3306" # Adjust the external port if needed
restart: always
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.podcastartdb-secure.entrypoints=websecure"
- "traefik.http.routers.podcastartdb-secure.rule=Host(`podcastartdb.noagenda.dev`)"
- "traefik.http.routers.podcastartdb-secure.service=podcastartdb-service"
- "traefik.http.services.podcastartdb-service.loadbalancer.server.port=3306"
- "3306:3306"
redis:
image: redis:alpine
command: redis-server --appendonly yes --requirepass ${REDIS_SEC_PASSWORD}
image: redis:latest
ports:
- "56379:6379"
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:cached
- ./redis/redis-data:/var/lib/redis:cached
environment:
- REDIS_REPLICATION_MODE=master
restart: always
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.paredis-secure.entrypoints=websecure"
- "traefik.http.routers.paredis-secure.rule=Host(`paredis.noagenda.dev`)"
- "traefik.http.routers.paredis-secure.service=paredis-service"
- "traefik.http.services.paredis-service.loadbalancer.server.port=6379"
- "6379:6379"
npm:
image: node:latest
working_dir: /var/www/html
entrypoint: ["npm"]
volumes:
- ./site:/var/www/html
networks:
proxy:
external: true
- ./site:/var/www/html

View File

@ -37,9 +37,7 @@ public function index()
->orderBy('episode_number', 'desc')
->orderBy('artworks.created_at', 'desc')
->paginate($perPage = 52, $columns = ['*'], $pageName = 'artworks');
$podcasts = Cache::remember('publishedPodcasts', 30, function() {
return Podcast::where('published', true)->get();
});
$podcasts = $this->publishedPodcasts();
return view('explore.artworks', [
'user' => $user,
'pageTitle' => 'Explore',
@ -56,9 +54,7 @@ public function index()
public function create()
{
$user = auth()->user();
$podcasts = Cache::remember('publishedPodcasts', 30, function() {
return Podcast::where('published', true)->get();
});
$podcasts = $this->publishedPodcasts();
return view('artworks.submit', [
'user' => $user,
'pageTitle' => 'Submit New Artwork',
@ -66,6 +62,41 @@ public function create()
]);
}
public function pendingApproval(Request $request)
{
$user = auth()->user();
if ($request->user()->cannot('approve', Artwork::class)) {
abort(403);
}
$artworks = Artwork::whereNull('approved_by')
->orderBy('created_at', 'desc')
->paginate(50);
$podcasts = $this->publishedPodcasts();
return view('artworks.approvals', [
'user' => $user,
'pageTitle' => 'Approve Artworks',
'podcasts' => $podcasts,
'artworks' => $artworks,
]);
}
public function approve(Request $request)
{
$user = $request->user();
if ($request->user()->cannot('approve', Artwork::class)) {
abort(403);
}
$validated = $request->validate([
'artwork_id' => 'required|exists:artworks,id'
]);
$artwork = Artwork::find($request->artwork_id);
if (is_null($artwork->approved_by)) {
$artwork->approved_by = $user->artists->first()->id;
$artwork->save();
}
return redirect('/approve-artworks');
}
/**
* Store a newly created resource in storage.
*
@ -108,7 +139,7 @@ public function store(Request $request): RedirectResponse
'description' => $request->description,
'overlay_id' => null,
'podcast_id' => $podcast->id,
'episode_id' => $episode->id,
'episode_id' => $episode->id,
//'approved_by' => 4,
'filename' => $filename,
'created_at' => now(),
@ -190,16 +221,6 @@ public function destroy(Artwork $artwork)
//
}
public function approve(Artwork $artwork)
{
$user = auth()->user();
$awatingApproval = Artwork::whereNull('approved_by')
->with('podcast')
->with('podcast.episode')
->orderBy('created_at', 'asc')
->get();
}
public function legacyArtLink(Request $request, $any = null)
{
phpinfo();
@ -228,4 +249,11 @@ public function downloadArchiveList(Request $request, $type = 'sd')
->header('Content-Disposition', 'attachment; filename="naartgen-archivelist-' . $type . '.txt"');
}
private function publishedPodcasts() {
$podcasts = Cache::remember('publishedPodcasts', 30, function() {
return Podcast::where('published', true)->get();
});
return $podcasts;
}
}

View File

@ -55,6 +55,7 @@ public function show(Request $request, $podcast_slug, $slug)
->with('artworks')
->with('artwork')
->with('podcast')
->whereNotNull('artworks.approved_by')
->firstOrFail();
$podcasts = Podcast::where('published', true)->with('episodes', function ($query) {
$query->orderBy('episode_number', 'desc');

View File

@ -65,9 +65,9 @@ public function artworks()
return $this->hasManyThrough(Artwork::class, Artist::class);
}
public function episodes()
public function selectedForEpisodes()
{
return $this->hasManyThrough(Episode::class, Artwork::class);
return $this->artists()->first()->episodes;
}
public function wallets()

View File

@ -0,0 +1,34 @@
<?php
namespace App\Policies;
use App\Models\User;
use App\Models\Artwork;
class ArtworkPolicy
{
/**
* Create a new policy instance.
*/
public function __construct()
{
//
}
public function viewAny(?User $user): bool
{
return true;
}
public function approve(User $user): bool
{
if ($user->id == 4) {
return true;
}
$selectedCount = $user->selectedForEpisodes()->count();
if ($selectedCount > 5) {
return true;
}
return false;
}
}

View File

@ -4,6 +4,8 @@
// use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Policies\ArtworkPolicy;
use App\Models\Artwork;
class AuthServiceProvider extends ServiceProvider
{
@ -13,7 +15,7 @@ class AuthServiceProvider extends ServiceProvider
* @var array<class-string, class-string>
*/
protected $policies = [
//
Artwork::class => ArtworkPolicy::class,
];
/**

View File

@ -0,0 +1,93 @@
@extends('layouts.master')
@section('page-title', 'Approve Artwork')
@section('meta_description', 'Approve Artwork to have it published for non-priviledged eyeballs to view.')
@section('page-top')
<section class="inner-page-banner bg-2 bg-image">
<div class="container">
<div class="inner text-center">
<h1 class="title">Artwork Pending Approval</h1>
<nav class="mt-4">
<ol class="breadcrumb justify-content-center">
<li class="breadcrumb-item"><a href="/">Home</a></li>
<li class="breadcrumb-item active"><a href="/artworks">Explore Artwork</a></li>
</ol>
</nav>
</div>
</div>
</section>
@endsection
@section('page-content')
<section class="pt-120 pb-90 masonary-wrapper-activation">
<div class="container">
<div class="row">
{{ $artworks->links() }}
</div>
</div>
<div class="container">
<div class="d-flex-between flex-wrap">
<div
class="button-group default-tab-list isotop-filter flex-wrap filters-button-group d-flex justify-content-start justify-content-lg-start mb-6 ">
<button data-filter="*" class="is-checked"><span class="filter-text">View All</span></button>
@foreach ($podcasts as $podcast)
<button data-filter=".podcast--{{ $podcast->slug }}"><span class="filter-text">{{ $podcast->name }}</span></button>
@endforeach
</div>
</div>
<div class="grid-filter-wrapper masonry-list">
<div class="resizer"></div>
@foreach ($artworks as $artwork)
@php
$thisArtwork = $artwork;
$thisPodcast = $artwork->podcast;
$thisEpisode = $artwork->episode;
@endphp
<div class="grid-item podcast--{{ $artwork->podcast->slug }}">
<div class="explore-style-one">
<div class="thumb">
<img src="{{ config('app.static_asset_url') . '/thumbnails/' . $thisArtwork->filename ?? '#'}}"
alt="{{ $thisArtwork->title }} by {{ $thisArtwork->artist->name }}">
</div>
<div class="content">
<div class="header d-flex-between pt-4 pb-1">
<h3 class="title">&ldquo;{{ $thisArtwork->title }}&rdquo;</h3>
</div>
{{-- End .header --}}
<div class="product-owner py-1 d-flex-between">
<span class="bid-owner">Artwork By <br><strong><a
href="/artist/{{ $thisArtwork->artist->slug }}">{{ $thisArtwork->artist->name ?? 'Unknown' }}</a></strong></span>
<span class="profile-share d-flex-center"><a href="/artist/{{ $thisArtwork->artist->slug }}"
class="avatar" data-bs-toggle="tooltip"
data-bs-placement="top"
title="{{ $thisArtwork->artist->name }}">
<img class="avatar_img" src="{{ $thisArtwork->artist->avatar() }}"
alt="{{ $thisArtwork->artist->name }}"></a>
</span>
</div>
{{-- End .product-owner --}}
<div class="action-wrapper py-5 d-flex align-items-center justify-content-center">
<form action="/approve-artworks" method="POST" class="approve-artwork-form" name="approve-artwork-{{ $thisArtwork->id}}" enctype="multipart/form-data">
@csrf
<input name="artwork_id" type="hidden" value="{{ $thisArtwork->id }}">
<button type="submit" class="btn btn-gradient btn-medium justify-content-center"><span>Approve and Publish</span></button>
</form>
</div>
</div>
</div>
</div>
@endforeach
</div>
</div>
<div class="container">
<div class="row">
{{ $artworks->links() }}
</div>
</div>
</section>
@endsection

View File

@ -11,6 +11,11 @@
<li>
<a {!! request()->is(['create-artwork', 'create-artwork/*']) ? 'class="active"' : '' !!} href="/create-artwork">Create</a>
</li>
@can('approve', App\Models\Artwork::class)
<li>
<a {!! request()->is(['approve-artworks']) ? 'class="active"' : '' !!} href="/approve-artworks">Approve</a>
</li>
@endcan
@endif
<li>
<a rel="me" {!! request()->is(['support*']) ? 'class="active"' : '' !!} href="/support-development">History & Support</a>

View File

@ -43,6 +43,8 @@
Route::post('/create-artwork', [ArtworkController::class, 'store']);
Route::get('/update-avatar', [ArtistController::class, 'avatar'])->name('avatar.edit');
Route::post('/update-profile', [ArtistController::class, 'update'])->name('profile-details.edit');
Route::get('/approve-artworks', [ArtworkController::class, 'pendingApproval'])->name('show-approve-artwork');
Route::post('/approve-artworks', [ArtworkController::class, 'approve'])->name('approve-artwork');
});
Route::get('/counter', Counter::class);