43 Commits

Author SHA1 Message Date
a989f3e92b fix: fixes artwork count on podcasts index page (#6)
Reviewed-on: #6
Co-authored-by: Paul Couture <paul@paulcouture.com>
Co-committed-by: Paul Couture <paul@paulcouture.com>
2024-01-13 11:46:38 -06:00
b8d1a164f1 Merge pull request 'fix: limit episode art to approved arworks' (#5) from fix/episode_filter_unapproved into master
Reviewed-on: #5
2024-01-13 11:32:21 -06:00
37d496bbca fix: limit episode art to approved arworks 2024-01-13 11:30:38 -06:00
eb931bbb6a feat: adds ability for approved users to approve artwork 2024-01-13 10:45:40 -06:00
bc66edb3ce feat: cleanup after move to new production server. 2024-01-07 14:24:04 +00:00
e10736d51e fix: updated yarn.lock 2024-01-06 17:42:08 +00:00
9101bab010 fix: updating default favicon 2024-01-06 17:15:44 +00:00
1d56e833a8 fix: migrating to bare metal. 2024-01-06 15:59:04 +00:00
ac28e86e68 fix: fixing unpublished artwork epsideo card 2023-12-22 23:17:53 -06:00
97fa5a6f9f fix: adding noscript to hide pre-loader. 2023-12-22 12:18:48 -06:00
bf30235e1d fix: allowing users to change artist display name. 2023-12-21 15:31:06 -06:00
c46664492d fix: removing broken links to unpublished episodes 2023-12-21 15:17:18 -06:00
06e5101199 fix: removing broken links to unpublished episodes 2023-12-21 15:00:54 -06:00
9630f331a2 fix: moving v4v on artist cards. 2023-12-21 16:49:18 +00:00
173986da5e fix: user can update bio info 2023-12-21 10:20:49 -06:00
ca34ab4b37 fix: login with username or email added. 2023-12-21 05:16:15 +00:00
a156a44130 fix: adding ability to update artist bg 2023-12-20 19:45:19 +00:00
87198b46a5 fix: optimized artists index 2023-12-20 07:07:33 +00:00
40024b69ae fix: setting up gcm 2023-12-20 07:01:38 +00:00
aa79de5688 fix: adding restart directives to docker compose 2023-12-20 06:53:00 +00:00
f7a27e7f62 fix: added og headers, lots of cleanup. 2023-12-20 01:02:15 +00:00
7772be7dc5 fix: adding indexes to tables 2023-12-18 09:44:09 -06:00
d5c7e9e4f5 fix: performance tweak for submitted page 2023-12-18 09:28:45 -06:00
89450f0b79 fix: performance tweaks. 2023-12-18 15:20:45 +00:00
23e551374a fix: caching the leaderboard 2023-12-17 23:22:54 -06:00
3e3e76773c fix: fixing avatar widths 2023-12-17 16:31:07 -06:00
a6dc77feff fix: changing num per page on submitted. 2023-12-17 16:01:35 -06:00
3b45563ce5 fix: adding cache missing from artwork controller 2023-12-17 15:55:54 -06:00
ae697fbaae fix: caching podcasts list 2023-12-17 15:51:24 -06:00
ebdc50a697 fix: fixing issues found at launch 2023-12-17 21:43:00 +00:00
3d3029ceb6 fix: adding user avatar editing 2023-12-17 12:32:35 -06:00
a2ecb62a05 fix: themed manage account forms 2023-12-16 11:40:39 -06:00
a4bd889d23 fix: modifying profile edit pages, added trusted proxies 2023-12-16 15:51:30 +00:00
4ba362b4fd fix: tweaking profile update template. 2023-12-15 10:00:01 -06:00
c55bec5b28 fix: fixing guest header 2023-12-15 01:03:28 -06:00
efba3dff84 fix: fixing login and forgot password 2023-12-15 00:53:04 -06:00
81525acb82 fix: guest blade issues 2023-12-14 23:17:34 -06:00
f39ce73978 fix: fixing footer navigation 2023-12-14 22:47:10 -06:00
ebd803282c fix: cleanup artwork and episode template 2023-12-14 22:22:57 -06:00
a37789ff8a fix: correcting issues on artist profile page. 2023-12-14 21:57:33 -06:00
2e3b848ff5 fix: initial changes to allow for intial deployment (#4)
Reviewed-on: #4
Co-authored-by: Paul Couture <paul@paulcouture.com>
Co-committed-by: Paul Couture <paul@paulcouture.com>
2023-12-14 20:59:11 -06:00
26c4b2c959 fix: fixing naming conventions for initial deployment. (#3)
Reviewed-on: #3
Co-authored-by: Paul Couture <paul@paulcouture.com>
Co-committed-by: Paul Couture <paul@paulcouture.com>
2023-12-14 15:21:54 -06:00
c4398c641e feat/factory_creation (#1)
Prepping for launch.

Reviewed-on: #1
Co-authored-by: Paul Couture <paul@paulcouture.com>
Co-committed-by: Paul Couture <paul@paulcouture.com>
2023-12-14 11:33:03 -06:00
180 changed files with 25325 additions and 10130 deletions

7
.gitignore vendored
View File

@@ -29,5 +29,10 @@ Homestead.yaml
Homestead.json Homestead.json
/.vagrant /.vagrant
.phpunit.result.cache .phpunit.result.cache
legacypublic
migrated_artworks_files.tar.gz
migrated_thumbnail_files.tar.gz
site/.yarn/releases/yarn-1.22.19.cjs site/.yarn/releases/yarn-1.22.19.cjs
site/public/assets
redis/*
staticassets

View File

@@ -4,7 +4,7 @@ Modernizing the No Agenda Art Generator.
Since 2010, the [No Agenda Art Generator](https://noagendaartgenerator.com) has been producing album art for the [No Agenda Podcast](https://noagendashow.net) live via community collaboration by the artists that make up the best podcast art creators in the universe. Since 2010, the [No Agenda Art Generator](https://noagendaartgenerator.com) has been producing album art for the [No Agenda Podcast](https://noagendashow.net) live via community collaboration by the artists that make up the best podcast art creators in the universe.
In October 2016, the 2.0 release of the Art Generator began based on Laravel 4.2. It has served the podcast and community well, but this project seeks to make it easier for collaborators to contribute, modify, and maintain the art generator while making the product available to more podcasts and artists. In October 2014, the 2.0 release of the Art Generator began based on Laravel 4.2. It has served the podcast and community well, but this project seeks to make it easier for collaborators to contribute, modify, and maintain the art generator while making the product available to more podcasts and artists.
### License ### License

View File

@@ -8,20 +8,23 @@ services:
dockerfile: Dockerfile dockerfile: Dockerfile
container_name: ${CONTAINER_NAME:-pcag-laravel} container_name: ${CONTAINER_NAME:-pcag-laravel}
volumes: volumes:
- ./site:/var/www/html - ${PATH_TO_SITE}:/var/www/html
- ./static:/static - ${PATH_TO_STATIC}:/static
- ./nginx/default.conf:/etc/nginx/sites-available/default
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
environment: environment:
TZ: UTC TZ: UTC
PUID: ${UID:-1000} PUID: ${UID:-1000}
PGID: ${GID:-1000} PGID: ${GID:-1000}
REDIS_HOST: redis REDIS_HOST: redis
DB_HOST: db DB_HOST: db
DB_DATABASE: ${DB_DATABASE} DB_DATABASE: ${LIVE_DB_DATABASE}
DB_USERNAME: ${DB_USERNAME} DB_USERNAME: ${LIVE_DB_USERNAME}
DB_PASSWORD: ${DB_PASSWORD} DB_PASSWORD: ${LIVE_DB_PASSWORD}
LARAVEL_QUEUE_ENABLED: true LARAVEL_QUEUE_ENABLED: true
LARAVEL_QUEUE_OPTIONS: --timeout=60 --tries=3 redis LARAVEL_QUEUE_OPTIONS: --timeout=60 --tries=3 redis
LARAVEL_SCHEDULE_ENABLED: true LARAVEL_SCHEDULE_ENABLED: true
PHP_OPEN_BASEDIR: "/var/www/html:/static"
ports: ports:
- "80:80" - "80:80"
links: links:
@@ -43,10 +46,10 @@ services:
env_file: .env env_file: .env
environment: environment:
TZ: UTC TZ: UTC
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MARIADB_ROOT_PASSWORD: ${LIVE_DB_ROOT_PASSWORD}
MARIADB_USER: ${DB_USERNAME} MARIADB_USER: ${LIVE_DB_USERNAME}
MARIADB_DATABASE: ${DB_DATABASE} MARIADB_DATABASE: ${LIVE_DB_DATABASE}
MARIADB_PASSWORD: ${DB_PASSWORD} MARIADB_PASSWORD: ${LIVE_DB_PASSWORD}
volumes: volumes:
- "./db/data:/var/lib/mysql" - "./db/data:/var/lib/mysql"
- "./db/dump:/docker-entrypoint-initdb.d" - "./db/dump:/docker-entrypoint-initdb.d"

42
nginx/default.conf Normal file
View File

@@ -0,0 +1,42 @@
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name _;
root /var/www/html/public;
index index.html index.htm index.php;
# SSL
ssl_certificate /etc/ssl/web/server.crt;
ssl_certificate_key /etc/ssl/web/server.key;
# additional config
include extra.d/*.conf;
# health check
location /ping {
access_log off;
include snippets/fastcgi-php.conf;
fastcgi_read_timeout 5s;
fastcgi_pass unix:/var/run/php/php-fpm.sock;
}
location ^~ /livewire {
try_files $uri $uri/ /index.php?$query_string;
}
# handle .php
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
include snippets/fastcgi-php.conf;
}
# index.php fallback
location / {
try_files $uri $uri/ /index.php?$query_string;
}
}

View File

@@ -0,0 +1,11 @@
#snippets/legacy_mappings.conf
map $uri $legacy_mapping {
~^/assets/(?<filename>.+)$ $filename;
default $uri;
}
map $filename $modified_filename {
~/(.*) $1---;
default $filename;
}

145
nginx/nginx.conf Normal file
View File

@@ -0,0 +1,145 @@
# Generated by nginxconfig.io
# https://www.serverion.com/nginx-config/#?0.domain=_&0.path=%2Fvar%2Fwww%2Fhtml&0.redirect=false&0.force_https=false&0.cert_type=custom&0.ssl_certificate=%2Fetc%2Fssl%2Fweb%2Fserver.crt&0.ssl_certificate_key=%2Fetc%2Fssl%2Fweb%2Fserver.key&0.wordpress&0.proxy_path=%2Fping&0.proxy_pass=unix:%2Fvar%2Frun%2Fphp%2Fphp-fpm.sock&0.index=index.html&content_security_policy=default-src%20'self'%20http:%20https:%20data:%20blob:%20'unsafe-inline';%20frame-ancestors%20'self';&php_server=%2Fvar%2Frun%2Fphp%2Fphp7.3-fpm.sock&expires_media=max&expires_svg=max&expires_fonts=max&user=www-data%20www-data&client_max_body_size=2048&symlink=false
pcre_jit on;
worker_processes auto;
worker_rlimit_nofile 100000;
user www-data www-data;
events {
worker_connections 65535;
use epoll;
multi_accept on;
}
http {
# define common MIME types
include mime.types;
# define the default MIME type
default_type application/octet-stream;
# disable emitting nginx version
server_tokens off;
# disable the directory listing output
autoindex off;
# disable automatic generation of the "ETag"
etag off;
# disable warnings about uninitialized variables are logged
uninitialized_variable_warn off;
# ======================================================================== #
# the bucket size for the maps hash table
map_hash_bucket_size 256;
map_hash_max_size 4096;
# the bucket size for the server names hash tables
server_names_hash_bucket_size 256;
server_names_hash_max_size 4096;
# the bucket size for variables hash tables
variables_hash_max_size 4096;
variables_hash_bucket_size 4096;
# ======================================================================== #
# cache informations about FDs, frequently accessed files
# can boost performance, but you need to test those values
open_file_cache max=200000 inactive=30s;
open_file_cache_valid 30s;
open_file_cache_min_uses 1;
open_file_cache_errors off;
open_log_file_cache max=10 inactive=30s min_uses=1 valid=5m;
# ======================================================================== #
# logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log error;
# to boost I/O on HDD we can disable access logs
log_not_found off;
log_subrequest off;
rewrite_log on;
# copies data between one FD and other from within the kernel
# faster than read() + write()
sendfile off;
sendfile_max_chunk 1m;
# send headers in one piece, it is better than sending them one by one
tcp_nopush on;
# don't buffer data sent, good for small data bursts in real time
tcp_nodelay on;
# large files can be read and sent using multi-threading
# without blocking a worker process
aio threads;
directio 1m;
# how to compare modification time
ssi on;
if_modified_since off;
# set default size of the slice
slice 1m;
# ======================================================================== #
# allow the server to close connection on non responding client,
# this will free up memory
reset_timedout_connection on;
# timeout for reading client request header -- default: 60
client_header_timeout 10s;
# request timed out -- default: 60
client_body_timeout 75s;
# if the request body size is more than the buffer size, then the entire (or partial)
# request body is written into a temporary file
client_body_buffer_size 128k;
# if client stop responding, free up memory -- default: 60
send_timeout 30s;
# server will close connection after this time -- default: 75
keepalive_timeout 30s;
# number of requests client can make over keep-alive
keepalive_requests 100000;
# maximum number and size of buffers
# for large headers to read from client request -- default: 4 8k;
large_client_header_buffers 4 16k;
# ======================================================================== #
# SSL
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites
ssl_dhparam /etc/ssl/dhparam.pem;
# Mozilla Intermediate configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
# OCSP Stapling
# ssl_stapling on;
# ssl_stapling_verify on;
resolver 1.1.1.1 1.0.0.1 8.8.8.8 8.8.4.4 208.67.222.222 208.67.220.220 valid=60s;
resolver_timeout 2s;
# ======================================================================== #
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}

1977
nginx/phpfpm8_2.ini Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Artwork;
use App\Models\Episode;
use Illuminate\Support\Facades\DB;
class AddMissingArtworkIdsToEpisodes extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naart:artwork-to-episodes';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$oldEpisodes = DB::connection('legacy')->select('SELECT * FROM episodes WHERE artwork_id IS NOT NULL AND published = 1');
foreach($oldEpisodes as $oldEpisode) {
$this->info('Checking old episode ' . $oldEpisode->show_date);
$episode = Episode::where('legacy_id', $oldEpisode->id)->first();
$artwork = Artwork::where('legacy_id', $oldEpisode->artwork_id)->first();
if ($episode && $artwork) {
$this->line('Have artwork ' . $artwork->title . ' for episode ' . $episode->title);
$episode->artwork_id = $artwork->id;
$episode->timestamps = false;
if ($episode->isDirty()) {
$this->info('I need to update this.');
//$episode->save();
} else {
$this->line('No Change Needed.');
}
} else {
$this->error('I am lost.');
}
}
}
}

View File

@@ -0,0 +1,346 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Artwork;
use App\Models\Episode;
use App\Models\Artist;
use App\Models\Podcast;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;
use Carbon\Carbon;
use ImageOptimizer;
class AddMissingV3ArtworksCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:add-missing-v3-artworks-command';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$missing_artworks = [
[
'url' => 'https://noagenda.dev/old/img/7e5e1ca05eb622e2.png',
'title' => 'Show Art 1613',
'artist' => 'dirty-jersey-whore',
'artist_id' => 1318,
'episode' => 1613,
],
[
'url' => 'https://noagenda.dev/old/img/6ac5a1220e954d10.jpg',
'title' => 'No Stinkin\' Title Provided',
'artist' => 'dirty-jersey-whore',
'artist_id' => 1318,
'episode' => 1613,
],
[
'url' => 'https://noagenda.dev/old/img/eb9c5a07a25db93b.jpg',
'title' => 'McMystery',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/16dce3f9249b42e8.png',
'title' => 'Blinking Red Lights!',
'artist' => 'mountainjay',
'artist_id' => 891,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/8a6153d61715434b.jpg',
'title' => 'Day One Dictator!',
'artist' => 'matthewdropco1972',
'artist_id' => 1173,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/bf6322f359b0727f.png',
'title' => 'Vivek\'s Notebook',
'artist' => 'sir-paul-couture',
'artist_id' => 4,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/81e71a8e2b24874b.jpg',
'title' => 'Plastic Shoes',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/51f16e95c0e86cc9.jpg',
'title' => 'My Transhausen Blewupen',
'artist' => 'francisco-scaramanga',
'artist_id' => 1377,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/b3b6c7988b5fd7f0.jpeg',
'title' => 'McPartner',
'artist' => 'dame-kenny-ben',
'artist_id' => 1121,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/c9c52179e5eedc6f.png',
'title' => 'Red Flashing Lights',
'artist' => 'jack-evans',
'artist_id' => 1224,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/2a29bb2974959b8a.jpg',
'title' => 'Threat Level Swift',
'artist' => 'darren-oneill',
'artist_id' => 756,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/0ed3c698ba336ea9.jpg',
'title' => 'Blinking Lights',
'artist' => 'matthewdropco1972',
'artist_id' => 1173,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/04658d1ccb786d26.png',
'title' => 'Deep State University, In The Morning',
'artist' => 'mountainjay',
'artist_id' => 891,
'episode' => 1614,
],
[
'url' => 'https://noagenda.dev/old/img/61273125525b7861.jpg',
'title' => 'Chad GPT',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/479090be93d46fad.jpg',
'title' => 'Chet GPT',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/a533697498ad7f75.png',
'title' => 'Word Salad, Roundy Variant',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/bcd81059b0440049.png',
'title' => 'word salad (that AI allegedly produces)',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/ddfbcc471e4f265f.png',
'title' => 'microphone hand (without show number)',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/c11e2b8a88774fca.png',
'title' => 'microphone hand',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/f0c08370c93980b8.jpg',
'title' => 'Bye Joe',
'artist' => 'darren-oneill',
'artist_id' => 756,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/9fc5c17fc0ec547e.jpg',
'title' => 'Coof Train',
'artist' => 'darren-oneill',
'artist_id' => 756,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/99e317f0dd6dd10c.jpg',
'title' => 'Comb Your Hair!',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/c579a7ef6ab53869.jpg',
'title' => 'Milgram',
'artist' => 'clip-custodian',
'artist_id' => 1431,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/ba791114371437bc.jpeg',
'title' => '67% are Killers',
'artist' => 'dame-kenny-ben',
'artist_id' => 1121,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/05b40baf17249ea5.jpg',
'title' => 'Garage Sale Find',
'artist' => 'francisco-scaramanga',
'artist_id' => 1377,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/fc626cd5542c71d8.jpg',
'title' => 'NA Show Art',
'artist' => 'monsieur-pierrey',
'artist_id' => 58,
'episode' => 1615,
],
[
'url' => 'https://noagenda.dev/old/img/2dc17c67400465ad.jpg',
'title' => 'Merry Christmas Ukraine',
'artist' => 'clip-custodian',
'artist_id' => 1431,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/18724b09fd552fc0.jpg',
'title' => 'THE END IS HERE',
'artist' => 'francisco-scaramanga',
'artist_id' => 1377,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/cefbe40e1afdf45c.jpg',
'title' => 'AI Winter is Coming',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/045db414cd60cf2a.jpg',
'title' => 'Space Race Force',
'artist' => 'Nessworks',
'artist_id' => 1159,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/f817a154c260076f.png',
'title' => 'No Agenda Verse',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/618ddba291dbe349.png',
'title' => 'NA Show Art',
'artist' => 'comic-strip-blogger',
'artist_id' => 680,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/322eda9c9e44933c.jpg',
'title' => 'Chinese Satellite',
'artist' => 'darren-oneill',
'artist_id' => 756,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/2c9ecc0646cc49dc.jpeg',
'title' => 'Naked No Agenda',
'artist' => 'KorrectDaRekard',
'artist_id' => 1021,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/96ba274872584092.jpg',
'title' => 'NA Show Art',
'artist' => 'matthewdropco1972',
'artist_id' => 1173,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/af8ab3953ba21524.jpg',
'title' => 'False Idols',
'artist' => 'matt-boisvert',
'artist_id' => 1368,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/ec5e76093063d663.jpg',
'title' => 'Collect Them All!',
'artist' => 'matt-boisvert',
'artist_id' => 1368,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/c28ec70cfd544c9b.jpg',
'title' => 'Santa\'s New List',
'artist' => 'matt-boisvert',
'artist_id' => 1368,
'episode' => 1616,
],
[
'url' => 'https://noagenda.dev/old/img/6d228246fb744cb7.jpg',
'title' => 'Christmas is Coming',
'artist' => 'matt-boisvert',
'artist_id' => 1368,
'episode' => 1616,
],
];
foreach ($missing_artworks as $art) {
$artist = Artist::find($art['artist_id']);
$episode = Episode::where('episode_number', $art['episode'])->first();
$this->line('Artist: ' . $artist->slug);
$this->line('Episode: ' . $episode->episode_date->format('Y-m-d'));
$artwork = new Artwork;
$artwork->title = $art['title'];
$artwork->artist_id = $artist->id;
$artwork->created_at = $episode->episode_date->format('Y-m-d ' . now()->format('H:i:s'));
$artwork->updated_at = $episode->episode_date->format('Y-m-d ' . now()->format('H:i:s'));
$artwork->episode_id = $episode->id;
$artwork->podcast_id = 1;
$artwork->approved_by = 4;
$basename = $episode->episode_date->format('Y/m/')
. Str::slug($artist->name)
. '_'
. Str::slug($art['title'])
. '_'
. Str::random(8)
. '.jpg';
$artwork->filename = $basename;
$artwork->save();
$filename = 'artworks/' . $basename;
$thumbnailName = 'thumbnails/' . $basename;
$this->line($basename);
$img = Image::make(file_get_contents($art['url']))->resize(3000, 3000)->encode('jpg', 100);
$thumbImg = Image::make(file_get_contents($art['url']))->resize(512, 512)->encode('jpg', 100);
$imgLocation = Storage::disk('static')->put($filename, $img);
$thumbLocation = Storage::disk('static')->put($thumbnailName, $thumbImg);
ImageOptimizer::optimize(Storage::disk('static')->path($filename));
ImageOptimizer::optimize(Storage::disk('static')->path($thumbnailName));
}
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;
use App\Models\User;
use App\Models\Artist;
use App\Models\Podcast;
use App\Models\Episode;
use App\Models\Artwork;
use ImageOptimizer;
class GetMissingArtworkMovedCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naart:wrapup';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$missingArtworks = DB::connection('legacy')->select('SELECT * FROM artworks WHERE id > 30835 AND approved_by IS NOT NULL');
foreach ($missingArtworks as $missingArtwork) {
$localPath = '/legacypublic' . $missingArtwork->path . '/' . $missingArtwork->filename;
$user = User::where('legacy_id', $missingArtwork->user_id)->with('artists')->first();
$artwork = Artwork::where('legacy_id', $missingArtwork->id)->first();
$episode = Episode::where('legacy_id', $missingArtwork->episode_id)->first();
$approver = User::where('legacy_id', $missingArtwork->approved_by)->with('artists')->first();
if ($artwork) {
$this->line('Artwork ID: ' . $artwork->id);
}
if ($episode) {
$this->line('Episode: ' . $episode->episode_number . ' "' . $episode->title . '"');
}
if (!$artwork) {
$newFilename = $this->uniqueName($episode, $user, $missingArtwork);
$state = [
'title' => $missingArtwork->title,
'description' => '',
'artist_id' => $user->artists->first()->id,
'episode_id' => $episode->id,
'podcast_id' => 1,
'overlay_id' => null,
'filename' => $newFilename . '.jpg',
'created_at' => Carbon::parse($missingArtwork->created_at),
'updated_at' => Carbon::parse($missingArtwork->updated_at),
'legacy_id' => $missingArtwork->id,
'approved_by' => $approver->artists->first()->id,
];
$artwork = Artwork::factory()->state($state)->create();
}
$this->line('Artist: ' . $user->artists->first()->name);
$this->line($localPath);
$filename = 'artworks/' . $artwork->filename;
$thumbnailName = 'thumbnails/' . $artwork->filename;
if (Storage::disk('static')->exists($filename)) {
$this->error($filename . ' already exists. ' . Storage::disk('static')->size($filename));
}
if (Storage::disk('static')->exists($thumbnailName)) {
$this->error($thumbnailName . ' already exists. ' . Storage::disk('static')->size($thumbnailName));
}
$img = Image::make($localPath)
->resize(3000, null, function ($constraint) {
$constraint->aspectRatio();
})
->encode('jpg', 100);
$thumbImg = Image::make($localPath)
->resize(512, null, function ($constraint) {
$constraint->aspectRatio();
})
->encode('jpg', 100);
$imgLocation = Storage::disk('static')->put($filename, $img);
$thumbLocation = Storage::disk('static')->put($thumbnailName, $thumbImg);
$size_before = Storage::disk('static')->size($filename);
$thumb_size_before = Storage::disk('static')->size($thumbnailName);
ImageOptimizer::optimize(Storage::disk('static')->path($filename));
ImageOptimizer::optimize(Storage::disk('static')->path($thumbnailName));
$size_after = Storage::disk('static')->size($filename);
$thumb_size_after = Storage::disk('static')->size($thumbnailName);
$diff = $size_before - $size_after;
$thumbDiff = $thumb_size_before - $thumb_size_after;
$this->line('Filesize before: ' . $size_before);
$this->line('Filesize after: ' . $size_after);
$this->line('Thumb Filesize before: ' . $thumb_size_before);
$this->line('Thumb Filesize after: ' . $thumb_size_after);
}
}
private function checkExistingFilename($name) {
$checkArtwork = Artwork::where('filename', $name . '.jpg')->count();
return $checkArtwork;
}
private function uniqueName($episode, $user, $missingArtwork) {
$i = 0;
$uniqueFilename = $episode->episode_date->format('Y/m/') . Str::slug($user->artists->first()->name . '-' . $missingArtwork->title) . '_' . $missingArtwork->id;
$exists = $this->checkExistingFilename($uniqueFilename);
if (!$exists) {
return $uniqueFilename;
}
while(!$exists) {
$i++;
$uniqueFilename = $uniqueFilename . '_v' . $i;
$exists = $this->checkExistingFilename($uniqueFilename);
}
return $uniqueFilename;
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Carbon\Carbon;
use Intervention\Image\Facades\Image;
use ImageOptimizer;
use App\Models\User;
use App\Models\Artist;
use App\Models\Artwork;
use App\Models\Podcast;
use App\Models\Episode;
class ImportMissingLegacyArtworkCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naart:import-legacy-artwork {legacy_artwork_id}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Used to import a single legacy artwork item that was missed in the migration.';
/**
* Execute the console command.
*/
public function handle()
{
$legacyArtwork = DB::connection('legacy')->table('artworks')->where('id', $this->argument('legacy_artwork_id'))->first();
$legacyEpisode = DB::connection('legacy')->table('episodes')->where('id', $legacyArtwork->episode_id)->first();
dump($legacyEpisode);
dump($legacyArtwork);
$user = User::where('legacy_id', $legacyArtwork->user_id)->with('artists')->first();
$artist = $user->artists->first();
dump($artist);
$podcast = Podcast::find(1);
$episode = Episode::where('episode_number', $legacyEpisode->episode_number)->first();
dump($episode);
$date = Carbon::parse($legacyArtwork->created_at);
$basename = $date->format('Y')
. '/'
. $date->format('m')
. '/'
. Str::slug($artist->name)
. '_'
. Str::slug($legacyArtwork->title)
. '_'
. Str::random(8)
. '.jpg';
$thumbnailName = 'thumbnails/' . $basename;
$artworkName = 'artworks/' . $basename;
$artworkExists = Artwork::where('artist_id', $artist->id)
->where('episode_id', $episode->id)
->where('title', $legacyArtwork->title)
->count();
$thumbnailExists = false;
$artExists = false;
dump($artworkExists);
dump([$thumbnailName, $artworkName]);
if (Storage::disk('static')->exists($thumbnailName)) {
$this->line('Thumbnail already exists.');
$thumbnailExists = true;
}
if (Storage::disk('static')->exists($artworkName)) {
$this->line('Artwork already exists.');
$artExists = true;
}
dump([$thumbnailExists, $artExists]);
if (!$artworkExists) {
$artwork = Artwork::factory()->state([
'title' => $legacyArtwork->title,
'artist_id' => $artist->id,
'description' => null,
'overlay_id' => null,
'podcast_id' => $podcast->id,
'episode_id' => $episode->id,
'approved_by' => 4,
'filename' => $basename,
'legacy_filename' => $legacyArtwork->path . '/' . $legacyArtwork->filename,
'legacy_id' => $legacyArtwork->id,
'created_at' => $legacyArtwork->created_at,
'updated_at' => $legacyArtwork->created_at,
])->create();
$img = Image::make('/var/www/html/naartgen/podcastartgenerator/static' . $artwork->legacy_filename)
->resize(3000, null, function($constraint) {
$constraint->aspectRatio();
})
->encode('jpg', 100)
->save(Storage::disk('static')->path('/artworks') . '/' . $artwork->filename);
$thumbImg = Image::make('/var/www/html/naartgen/podcastartgenerator/static' . $artwork->legacy_filename)
->resize(512, null, function($constraint) {
$constraint->aspectRatio();
})
->encode('jpg', 100)
->save(Storage::disk('static')->path('/thumbnails') . '/' . $artwork->filename);
ImageOptimizer::optimize(Storage::disk('static')->path('/artworks/' . $artwork->filename));
ImageOptimizer::optimize(Storage::disk('static')->path('/thumbnails/' . $artwork->filename));
dump($artwork);
}
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Artwork;
class LegacyNginxMappingCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naart:legacy-nginx-mapping';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*/
public function handle()
{
$oldLocations = [];
$newLocations = [];
$this->line('# legacy_mappings.conf');
$this->line('');
$this->line('map $uri $new_location {');
$artworks = Artwork::whereNotNull('legacy_filename')->get();
foreach ($artworks as $artwork) {
if (!in_array($artwork->legacy_filename, $oldLocations) && !in_array($artwork->legacy_filename, $newLocations)) {
$oldLocations[] = $artwork->legacy_filename;
$newLocations[] = $artwork->legacy_filename;
$this->line(' "' . $artwork->legacy_filename . '" "/legacy-asset/?legacy_filename=' . urlencode($artwork->legacy_filename) . '";');
}
}
$this->line(' default $uri;');
$this->line('}');
$this->line('');
}
}

View File

@@ -0,0 +1,38 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Artwork;
use App\Models\Episode;
use Illuminate\Support\Facades\DB;
class RefreshLeaderBoardCacheCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'naart:leaderboard-cache';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Clears all leaderboard caches.';
/**
* Execute the console command.
*/
public function handle()
{
$this->line('Clearing Leaderboard Caches...');
cache()->forget('leaderboardTwelveMonths');
cache()->forget('leaderboardTwelveMonthsLanding');
cache()->forget('leaderboardAllTime');
cache()->forget('leaderboardRollingSixMonth');
cache()->forget('leaderboardNinetyDays');
}
}

View File

@@ -0,0 +1,27 @@
<?php
if (!function_exists('numberSuffix')) {
function numberSuffix($number) {
if (!is_int($number) || $number < 1) {
return $number;
}
$lastDigit = $number % 10;
$secondLastDigit = ($number / 10) % 10;
if ($secondLastDigit == 1) {
return 'th';
}
switch ($lastDigit) {
case 1:
return 'st';
case 2:
return 'nd';
case 3:
return 'rd';
default:
return 'th';
}
}
}

View File

@@ -3,7 +3,13 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Artist; use App\Models\Artist;
use App\Models\Artwork;
use App\Models\Podcast;
use App\Models\Episode;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
class ArtistController extends Controller class ArtistController extends Controller
{ {
@@ -14,7 +20,18 @@ class ArtistController extends Controller
*/ */
public function index() public function index()
{ {
// $user = auth()->user();
$artists = Artist::whereHas('artworks')
->withCount('artworks')
->orderBy('artworks_count', 'desc')
->paginate(100);
$podcasts = Podcast::where('published', true)->get();
return view('profile.artists', [
'user' => $user,
'pageTitle' => 'Artists',
'podcasts' => $podcasts,
'artists' => $artists,
]);
} }
/** /**
@@ -44,9 +61,23 @@ class ArtistController extends Controller
* @param \App\Models\Artist $artist * @param \App\Models\Artist $artist
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function show(Artist $artist) public function show(Request $request, $slug)
{ {
// $user = auth()->user();
$artist = Artist::where('slug', $slug)
->firstOrFail();
$artworks = Artwork::where('artist_id', $artist->id)
->with('episode')
->with('podcast')
->orderBy('artworks.created_at', 'desc')
->paginate($perPage = 92, $columns = ['*'], $pageName = 'artworks');
$podcasts = Podcast::where('published', true)->with('episodes')->get();
return view('profile.artist', [
'user' => $user,
'artist' => $artist,
'artworks' => $artworks,
'podcasts' => $podcasts,
]);
} }
/** /**
@@ -69,7 +100,30 @@ class ArtistController extends Controller
*/ */
public function update(Request $request, Artist $artist) public function update(Request $request, Artist $artist)
{ {
// $user = auth()->user();
$artist = $user->artists->first();
$rules = [
'location' => ['string', 'max:255'],
'alby' => ['email'],
'website' => ['url'],
'nasocial' => ['starts_with:@'],
'name' => ['string', 'unique:artists', 'max:255'],
];
$validator = Validator::make($request->all(), $rules);
if ($validator->fails()) {
return back()
->withErrors($validator)
->withInput();
}
$artist->location = $request->location;
$artist->alby = $request->alby;
$artist->website = $request->website;
$artist->nasocial = $request->nasocial;
$artist->name = $request->name;
if ($artist->isDirty()) {
$artist->save();
}
return redirect('/profile');
} }
/** /**

View File

@@ -3,7 +3,20 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Artwork; use App\Models\Artwork;
use App\Models\Podcast;
use App\Models\Episode;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\File;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Cache;
use ImageOptimizer;
class ArtworkController extends Controller class ArtworkController extends Controller
{ {
@@ -14,7 +27,23 @@ class ArtworkController extends Controller
*/ */
public function index() public function index()
{ {
// $user = auth()->user();
$artworks = Artwork::whereNotNull('approved_by')
->join('episodes', 'artworks.episode_id', '=', 'episodes.id')
->select('artworks.*', DB::raw('episodes.episode_number episode_number'))
->with('artist')
->with('podcast')
->with('episode')
->orderBy('episode_number', 'desc')
->orderBy('artworks.created_at', 'desc')
->paginate($perPage = 52, $columns = ['*'], $pageName = 'artworks');
$podcasts = $this->publishedPodcasts();
return view('explore.artworks', [
'user' => $user,
'pageTitle' => 'Explore',
'artworks' => $artworks,
'podcasts' => $podcasts,
]);
} }
/** /**
@@ -24,7 +53,48 @@ class ArtworkController extends Controller
*/ */
public function create() public function create()
{ {
// $user = auth()->user();
$podcasts = $this->publishedPodcasts();
return view('artworks.submit', [
'user' => $user,
'pageTitle' => 'Submit New Artwork',
'podcasts' => $podcasts,
]);
}
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');
} }
/** /**
@@ -33,20 +103,88 @@ class ArtworkController extends Controller
* @param \Illuminate\Http\Request $request * @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function store(Request $request) public function store(Request $request): RedirectResponse
{ {
// $validator = Validator::make($request->all(), [
'title' => ['required', 'max:255',],
'podcast' => ['required', 'exists:podcasts,id',],
'description' => ['nullable',],
'file' => ['required', 'image', Rule::dimensions()->ratio(1)->minWidth(512),],
]);
if ($validator->fails()) {
return back()
->withErrors($validator)
->withInput();
}
Log::channel('artwork_import')->info('making new artwork model.');
$podcast = Podcast::where('id', $request->podcast)->with(['episodes' => function($query) {
$query->orderBy('episode_number', 'desc')->limit(1);
}])->first();
$episode = $podcast->episodes->first();
$artist = auth()->user()->artists()->first();
$rawFile = $request->file('file');
$filename = now()->format('Y')
. '/'
. now()->format('m')
. '/'
. Str::slug($artist->name)
. '-'
. Str::slug($request->title)
. '_'
. Str::random(8)
. '.jpg';
$artwork = Artwork::factory()->state([
'title' => $request->title,
'artist_id' => $artist->id,
'description' => $request->description,
'overlay_id' => null,
'podcast_id' => $podcast->id,
'episode_id' => $episode->id,
//'approved_by' => 4,
'filename' => $filename,
'created_at' => now(),
'updated_at' => now(),
])->create();
$img = Image::make($rawFile)->resize(3000, null, function($constraint){
$constraint->aspectRatio();
})
->encode('jpg', 100)
->save(Storage::disk('static')->path('/artworks') . '/' . $artwork->filename);
$thumbImg = Image::make($request->file('file'))->resize(512, null, function($constraint){
$constraint->aspectRatio();
})
->encode('jpg', 100)
->save(Storage::disk('static')->path('/thumbnails') . '/' . $artwork->filename);
ImageOptimizer::optimize(Storage::disk('static')->path('/artworks/' . $artwork->filename));
ImageOptimizer::optimize(Storage::disk('static')->path('/thumbnails/' . $artwork->filename));
return redirect('/artworks/' . $artwork->id);
} }
/** /**
* Display the specified resource. * Display the specified resource.
* *
* @param \App\Models\Artwork $artwork * @param \Illuminate\Http\Request $request
* @param the id of the \App\Models\Artwork $id
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function show(Artwork $artwork) public function show(Request $request, $id)
{ {
// $user = auth()->user();
$artwork = Artwork::where('id', $id)
->with('podcast')
->with('episode')
->with('artist')
->first();
if (is_null($artwork->approved_by) && $user && $user->id != $artwork->id) {
return redirect('artworks');
}
if (is_null($artwork->approved_by) && !$user) {
return redirect('artworks');
}
return view('artworks.artwork', [
'artwork' => $artwork,
'user' => $user,
]);
} }
/** /**
@@ -82,4 +220,40 @@ class ArtworkController extends Controller
{ {
// //
} }
public function legacyArtLink(Request $request, $any = null)
{
phpinfo();
dd($request->path());
//$artwork = Artwork::where('legacy_filename', '/assets/artwork/')
}
public function downloadArchiveList(Request $request, $type = 'sd')
{
$artworks = Artwork::whereNotNull('approved_by')
->orderBy('created_at', 'desc')
->pluck('filename');
$output = '';
if ($type == 'sd') {
foreach($artworks as $artwork) {
$output .= '"https://static.noagendaartgenerator.com/thumbnails/' . $artwork . '"' . "\r\n";
}
} else {
foreach($artworks as $artwork) {
$output .= '"https://static.noagendaartgenerator.com/artworks/' . $artwork . '"' . "\r\n";
}
}
return response($output, 200)
->header('Content-type', 'text/plain')
->header('Content-Length', strlen($output))
->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

@@ -4,10 +4,12 @@ namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\User; use App\Models\User;
use App\Models\Artist;
use App\Providers\RouteServiceProvider; use App\Providers\RouteServiceProvider;
use Illuminate\Auth\Events\Registered; use Illuminate\Auth\Events\Registered;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Hash;
use Illuminate\Validation\Rules; use Illuminate\Validation\Rules;
@@ -31,17 +33,24 @@ class RegisteredUserController extends Controller
public function store(Request $request): RedirectResponse public function store(Request $request): RedirectResponse
{ {
$request->validate([ $request->validate([
'name' => ['required', 'string', 'max:255'], 'name' => ['unique:artists,name', 'required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class], 'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class],
'password' => ['required', 'confirmed', Rules\Password::defaults()], 'password' => ['required', 'confirmed', Rules\Password::defaults()],
]); ]);
$user = User::create([ $user = User::create([
'name' => $request->name, 'name' => trim($request->name),
'email' => $request->email, 'email' => trim(strtolower($request->email)),
'password' => Hash::make($request->password), 'password' => Hash::make($request->password),
]); ]);
$artist = Artist::create([
'user_id' => $user->id,
'name' => trim($request->name),
'slug' => Str::slug(trim($request->name)),
'location' => 'No Agenda Nation',
]);
event(new Registered($user)); event(new Registered($user));
Auth::login($user); Auth::login($user);

View File

@@ -2,8 +2,12 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Models\Episode;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\Podcast;
use App\Models\Artworks;
use App\Models\Episode;
class EpisodeController extends Controller class EpisodeController extends Controller
{ {
@@ -44,9 +48,27 @@ class EpisodeController extends Controller
* @param \App\Models\Episode $episode * @param \App\Models\Episode $episode
* @return \Illuminate\Http\Response * @return \Illuminate\Http\Response
*/ */
public function show(Episode $episode) public function show(Request $request, $podcast_slug, $slug)
{ {
// $user = auth()->user();
$episode = Episode::where('slug', $slug)
->with('approvedArtworks')
->with('artwork')
->with('podcast')
->firstOrFail();
$podcasts = Podcast::where('published', true)->with('episodes', function ($query) {
$query->orderBy('episode_number', 'desc');
$query->where('published', true);
$query->take(10);
})->get();
return view('episodes.episode', [
'user' => $user,
'pageTitle' => '"' . $episode->title . '" ' . $episode->podcast->name . ' Episode ' . number_format($episode->episode_number + 0),
'podcast' => $episode->podcast,
'episode' => $episode,
'artworks' => $episode->approvedArtworks,
'podcasts' => $podcasts,
]);
} }
/** /**

View File

@@ -4,33 +4,89 @@ namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\Artwork; use App\Models\Artwork;
use App\Models\Artist; use App\Models\Artist;
use App\Models\Episode; use App\Models\Episode;
class PageController extends Controller class PageController extends Controller
{ {
public function landing() public function landing(Request $request)
{ {
$user = auth()->user(); $user = auth()->user();
$headerCounters = $this->getHeaderCounters(); $headerCounters = $this->getHeaderCounters();
$recentEpisodes = $this->mostRecentEpisodes(); $recentEpisodes = $this->mostRecentEpisodes();
$recentSubmissions = $this->mostRecentSubmissions();
$leaderboard = $this->leaderboardTwelveMonthsLanding();
return view('home.page', [ return view('home.page', [
'user' => $user, 'user' => $user,
'pageTitle' => 'Home',
'headerCounters' => $headerCounters, 'headerCounters' => $headerCounters,
'recentEpisodes' => $recentEpisodes, 'recentEpisodes' => $recentEpisodes,
'recentSubmissions' => $recentSubmissions,
'leaderboard' => $leaderboard,
'preferredTheme' => $request->session()->get('preferred_theme') ?? 'dark',
]); ]);
} }
public function leaderboards(Request $request)
{
$user = auth()->user();
$current_year = now()->format('Y');
$start_year = 2011;
$years = [];
while($start_year < $current_year) {
$years[] = $start_year;
$start_year++;
}
arsort($years);
return view('leaderboards.leaderboards', [
'user' => $user,
'leaderboardAllTime' => $this->leaderboardAllTime(),
'leaderboardPastTwelveMonths' => $this->leaderboardTwelveMonths(),
'leaderboardRollingSixMonths' => $this->leaderboardRollingSixMonths(),
'leaderboardRollingNinetyDays' => $this->leaderboardRollingNinetyDays(),
'leaderboardYears' => $years,
]);
}
public function support(Request $request)
{
return view('home.support.page', [
'user' => auth()->user(),
'pageTitle' => 'History and Support',
'headerCounters' => $this->getHeaderCounters(),
'recentEpisodes' => $this->mostRecentEpisodes(),
'recentSubmissions' => $this->mostRecentSubmissions(),
'leaderboard' => $this->leaderboardTwelveMonthsLanding(),
'preferredTheme' => $request->session()->get('preferred_theme') ?? 'dark',
]);
}
private function mostRecentSubmissions() {
$artworks = Cache::remember('latestSubmissions', 30, function() {
return Artwork::whereNotNull('approved_by')
->with('artist')
->with('episode')
->with('podcast')
->orderBy('created_at', 'desc')
->limit(10)
->get();
});
return $artworks;
}
private function mostRecentEpisodes() private function mostRecentEpisodes()
{ {
$episodes = Cache::remember('latestEpisodes', 30, function() { $episodes = Cache::remember('latestEpisodes', 30, function() {
return Episode::where('published', true) return Episode::where('published', true)
->whereHas('artwork')
->with('podcast') ->with('podcast')
->with('artwork') ->with('artwork')
->with('artwork.artist') ->with('artwork.artist')
->orderBy('episode_date', 'desc') ->orderBy('episode_number', 'desc')
->limit(10) ->limit(5)
->get(); ->get();
}); });
return $episodes; return $episodes;
@@ -39,13 +95,13 @@ class PageController extends Controller
private function getHeaderCounters() private function getHeaderCounters()
{ {
$headerCounters = []; $headerCounters = [];
$artworkCountNumber = Cache::remember('artworkCountNumber', 10, function() { $artworkCountNumber = Cache::remember('artworkCountNumber', 30, function() {
return Artwork::all()->count(); return Artwork::whereNotNull('approved_by')->count();
}); });
$artistCountNumber = Cache::remember('artistCountNumber', 10, function() { $artistCountNumber = Cache::remember('artistCountNumber', 30, function() {
return Artist::all()->count(); return Artist::all()->count();
}); });
$episodeCountNumber = Cache::remember('episodeCountNumber', 10, function() { $episodeCountNumber = Cache::remember('episodeCountNumber', 30, function() {
return Episode::all()->count(); return Episode::all()->count();
}); });
$headerCounters['Artworks'] = $this->shortNumberCount($artworkCountNumber); $headerCounters['Artworks'] = $this->shortNumberCount($artworkCountNumber);
@@ -76,4 +132,164 @@ class PageController extends Controller
return $response; return $response;
} }
private function leaderboardByYear($year) {
$leaderboard = cache()->remember('leaderboardForYear' . $year, 30, function() {
$startDate = Carbon::createFromFormat('Y-m-d', $year . '-01-01')->startOfDay()->format('Y-m-d');
$endDate = $startDate->copy()->endOfYear()->format('Y-m-d');
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->where('episodes.episode_date', '>=', $startDate)
->where('episodes.episode_date', '<=', $endDate)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(50)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function leaderboardTwelveMonths() {
$leaderboard = cache()->remember('leaderboardTwelveMonths', 30, function() {
$endDate = now()->endOfDay()->subYear()->format('Y-m-d');
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->where('episodes.episode_date', '>=', $endDate)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(50)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function leaderboardTwelveMonthsLanding() {
$leaderboard = cache()->remember('leaderboardTwelveMonthsLanding', 30, function() {
$endDate = now()->endOfDay()->subYear()->format('Y-m-d');
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->where('episodes.episode_date', '>=', $endDate)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(10)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function leaderboardAllTime() {
$leaderboard = cache()->remember('leaderboardAllTime', 30, function() {
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(100)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function leaderboardRollingSixMonths() {
$leaderboard = cache()->remember('leaderboardRollingSixMonth', 30, function() {
$endDate = now()->endOfDay()->subMonths(6)->format('Y-m-d');
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->where('episodes.episode_date', '>=', $endDate)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(50)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function leaderboardRollingNinetyDays() {
$leaderboard = cache()->remember('leaderboardNinetyDays', 30, function() {
$endDate = now()->endOfDay()->subDays(90)->format('Y-m-d');
$leaderboard = DB::table('episodes')
->join('artworks', 'artworks.id', '=', 'episodes.artwork_id')
->join('artists', 'artists.id', '=', 'artworks.artist_id')
->select([
DB::raw('artists.id as artistId'),
DB::raw('artists.name as artistName'),
DB::raw('count(artworks.id) as artworkCount')
])
->where('episodes.published', 1)
->where('episodes.episode_date', '>=', $endDate)
->groupBy('artistId')
->orderByDesc('artworkCount')
->limit(50)
->get();
$leaderboard = $this->addArtistModelToLeaderboard($leaderboard);
return $leaderboard;
});
return $leaderboard;
}
private function addArtistModelToLeaderboard($leaderboard) {
$artistIds = [];
foreach ($leaderboard as $lb) {
$artistIds[] = $lb->artistId;
}
$artists = Artist::whereIn('id', $artistIds)->get();
foreach ($leaderboard as $lb) {
$lb->artist = $artists->where('id', $lb->artistId)->first();
}
$p = 0;
foreach ($leaderboard as $lb) {
$p++;
$lb->position = $p;
}
return $leaderboard;
}
public function setSessionTheme(Request $request) {
}
} }

View File

@@ -3,8 +3,34 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use App\Models\Podcast;
use App\Models\Artworks;
use App\Models\Episode;
class PodcastController extends Controller class PodcastController extends Controller
{ {
// public function show(Request $request, $slug)
{
$user = auth()->user();
$podcast = Podcast::where('slug', $slug)
->where('published', true)
->firstOrFail();
$episodes = Episode::where('published', true)
->whereNotNull('artwork_id')
->with('artwork')
->with('approvedArtworks')
->where('podcast_id', $podcast->id)
->orderBy('episode_number', 'desc')->paginate(100);
$podcasts = Podcast::where('published', true)->with('episodes')->get();
return view('podcasts.podcast', [
'user' => $user,
'pageTitle' => $podcast->name,
'podcast' => $podcast,
'episodes' => $episodes,
'podcasts' => $podcasts,
]);
}
} }

View File

@@ -18,6 +18,8 @@ class ProfileController extends Controller
{ {
return view('profile.edit', [ return view('profile.edit', [
'user' => $request->user(), 'user' => $request->user(),
'artist' => $request->user()->artists->first(),
'avatar' => $request->user()->artists->first()->avatar,
]); ]);
} }

View File

@@ -39,7 +39,7 @@ class Kernel extends HttpKernel
], ],
'api' => [ 'api' => [
// \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
\Illuminate\Routing\Middleware\ThrottleRequests::class.':api', \Illuminate\Routing\Middleware\ThrottleRequests::class.':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Routing\Middleware\SubstituteBindings::class,
], ],

View File

@@ -12,7 +12,7 @@ class TrustProxies extends Middleware
* *
* @var array<int, string>|string|null * @var array<int, string>|string|null
*/ */
protected $proxies; protected $proxies = '*';
/** /**
* The headers that should be used to detect proxies. * The headers that should be used to detect proxies.

View File

@@ -2,12 +2,14 @@
namespace App\Http\Requests\Auth; namespace App\Http\Requests\Auth;
use App\Models\User;
use Illuminate\Auth\Events\Lockout; use Illuminate\Auth\Events\Lockout;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\RateLimiter; use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException; use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Hash;
class LoginRequest extends FormRequest class LoginRequest extends FormRequest
{ {
@@ -27,7 +29,7 @@ class LoginRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'email' => ['required', 'string', 'email'], 'login' => ['required', 'string'],
'password' => ['required', 'string'], 'password' => ['required', 'string'],
]; ];
} }
@@ -41,6 +43,8 @@ class LoginRequest extends FormRequest
{ {
$this->ensureIsNotRateLimited(); $this->ensureIsNotRateLimited();
/*
if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
RateLimiter::hit($this->throttleKey()); RateLimiter::hit($this->throttleKey());
@@ -48,7 +52,20 @@ class LoginRequest extends FormRequest
'email' => trans('auth.failed'), 'email' => trans('auth.failed'),
]); ]);
} }
*/
$user = User::where('email', $this->login)
->orWhere('name', $this->login)
->first();
if (!$user || !Hash::check($this->password, $user->password)) {
RateLimiter::hit($this->throttleKey());
throw ValidationException::withMessages([
'login' => __('auth.failed'),
]);
}
Auth::login($user, $this->boolean('remember'));
RateLimiter::clear($this->throttleKey()); RateLimiter::clear($this->throttleKey());
} }

View File

@@ -56,12 +56,12 @@ class StashAndOptimizeLegacyArtworkJob implements ShouldQueue
$this->createArtwork($basename); $this->createArtwork($basename);
return; return;
} }
$img = Image::make('https://noagendaartgenerator.com' . $this->artwork->path . '/' . $this->artwork->filename) $img = Image::make('/legacypublic' . $this->artwork->path . '/' . $this->artwork->filename)
->resize(3000, null, function ($constraint) { ->resize(3000, null, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
}) })
->encode('jpg', 100); ->encode('jpg', 100);
$thumbImg = Image::make('https://noagendaartgenerator.com' . $this->artwork->path . '/' . $this->artwork->filename) $thumbImg = Image::make('/legacypublic' . $this->artwork->path . '/' . $this->artwork->filename)
->resize(512, null, function ($constraint) { ->resize(512, null, function ($constraint) {
$constraint->aspectRatio(); $constraint->aspectRatio();
}) })

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Livewire\Artist;
use Livewire\Component;
use Livewire\WithFileUploads;
use Spatie\Image\Image;
use Intervention\Image\Facades\Image as InterventionImage;
use ImageOptimizer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
use Illuminate\Http\RedirectResponse;
use Livewire\Attributes\Validate;
class Avatar extends Component
{
use WithFileUploads;
public $avatar;
public $x;
public $y;
public $width;
public $height;
public function render()
{
return view('livewire.artist.avatar');
}
public function save()
{
$disk = Storage::disk('static');
$avatar = $this->avatar->store('avatars', 'static');
Image::load($disk->path($avatar))
->manualCrop($this->width, $this->height, $this->x, $this->y)
->save();
Image::load($disk->path($avatar))
->width(350)
->height(350)
->save();
ImageOptimizer::optimize($disk->path($avatar));
auth()->user()->artists()->first()->update(compact('avatar'));
$this->avatar = null;
return redirect(request()->header('Referer'));
}
}

View File

@@ -0,0 +1,48 @@
<?php
namespace App\Livewire\Artist;
use Livewire\Component;
use Livewire\WithFileUploads;
use Spatie\Image\Image;
use Intervention\Image\Facades\Image as InterventionImage;
use ImageOptimizer;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\File;
use Illuminate\Http\RedirectResponse;
use Livewire\Attributes\Validate;
class Header extends Component
{
use WithFileUploads;
public $header;
public $x;
public $y;
public $width;
public $height;
public function render()
{
return view('livewire.artist.header');
}
public function save()
{
$disk = Storage::disk('static');
$header = $this->header->store('artist_headers', 'static');
Image::load($disk->path($header))
->manualCrop($this->width, $this->height, $this->x, $this->y)
->save();
Image::load($disk->path($header))
->width(270)
->height(185)
->save();
ImageOptimizer::optimize($disk->path($header));
auth()->user()->artists()->first()->update(compact('header'));
$this->header = null;
return redirect(request()->header('Referer'));
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Livewire;
use Livewire\Component;
class Counter extends Component
{
public $count = 1;
public function increment()
{
$this->count++;
}
public function decrement()
{
$this->count--;
}
public function render()
{
return view('livewire.counter');
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace App\Livewire;
use Livewire\Component;
class Themeswitch extends Component
{
public function light()
{
session()->put('preferred_theme', 'light');
}
public function dark()
{
session()->put('preferred_theme', 'dark');
}
public function render()
{
return view('livewire.themeswitch');
}
}

View File

@@ -16,9 +16,31 @@ class Artist extends Model
protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
protected $fillable = [
'user_id',
'name',
'slug',
'avatar',
'header',
'location',
'alby',
'naasocial',
'website',
'bio',
'created_at',
'updated_at',
'deleted_at',
];
public function user() public function user()
{ {
return $this->belongs_to(User::class); return $this->belongsTo(User::class);
} }
public function artworks() public function artworks()
@@ -41,4 +63,20 @@ class Artist extends Model
return $this->hasMany(Wallet::class); return $this->hasMany(Wallet::class);
} }
public function avatar()
{
if (!$this->avatar) {
return config('app.static_asset_url') . '/avatars/default_avatar_male.svg';
}
return config('app.static_asset_url') . '/' . $this->avatar;
}
public function header()
{
if (!$this->header) {
return config('app.static_asset_url') . '/artist_headers/default_artist_banner.png';
}
return config('app.static_asset_url') . '/' . $this->header;
}
} }

View File

@@ -16,6 +16,12 @@ class Artwork extends Model
protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
public function podcast() public function podcast()
{ {
return $this->belongsTo(Podcast::class); return $this->belongsTo(Podcast::class);

View File

@@ -14,6 +14,13 @@ class Episode extends Model
protected $dates = ['episode_date', 'created_at', 'updated_at', 'deleted_at']; protected $dates = ['episode_date', 'created_at', 'updated_at', 'deleted_at'];
protected $casts = [
'episode_date' => 'date',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
public function podcast() public function podcast()
{ {
return $this->belongsTo(Podcast::class); return $this->belongsTo(Podcast::class);
@@ -24,6 +31,16 @@ class Episode extends Model
return $this->hasOne(Artwork::class, 'id', 'artwork_id'); return $this->hasOne(Artwork::class, 'id', 'artwork_id');
} }
public function approvedArtworks()
{
return $this->hasMany(Artwork::class)->whereNotNull('artworks.approved_by');
}
public function artworks()
{
return $this->hasMany(Artwork::class);
}
public function artist() public function artist()
{ {
return $this->hasOneThrough(Artist::class, Artwork::class); return $this->hasOneThrough(Artist::class, Artwork::class);

View File

@@ -16,6 +16,12 @@ class Overlay extends Model
protected $dates = ['created_at', 'updated_at', 'deleted_at']; protected $dates = ['created_at', 'updated_at', 'deleted_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
];
public function artist() public function artist()
{ {
return $this->belongsTo(Artist::class); return $this->belongsTo(Artist::class);

View File

@@ -16,6 +16,13 @@ class Podcast extends Model
protected $dates = ['created_at', 'updated_at', 'deleted_at', 'added_at']; protected $dates = ['created_at', 'updated_at', 'deleted_at', 'added_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'added_at' => 'datetime',
];
public function episodes() public function episodes()
{ {
return $this->hasMany(Episode::class); return $this->hasMany(Episode::class);

View File

@@ -14,8 +14,6 @@ class User extends Authenticatable
protected $table = 'users'; protected $table = 'users';
protected $dates = ['created_at', 'updated_at'];
/** /**
* The attributes that are mass assignable. * The attributes that are mass assignable.
* *
@@ -44,6 +42,17 @@ class User extends Authenticatable
*/ */
protected $casts = [ protected $casts = [
'email_verified_at' => 'datetime', 'email_verified_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
/**
* The attributes that should be appended.
*
* @var array<string, string>
*/
protected $appends = [
]; ];
public function artists() public function artists()
@@ -56,9 +65,9 @@ class User extends Authenticatable
return $this->hasManyThrough(Artwork::class, Artist::class); 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() public function wallets()

View File

@@ -13,6 +13,11 @@ class Wallet extends Model
protected $dates = ['created_at', 'updated_at']; protected $dates = ['created_at', 'updated_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function walletType() public function walletType()
{ {
return $this->hasOne(WalletType::class); return $this->hasOne(WalletType::class);

View File

@@ -13,6 +13,11 @@ class WalletType extends Model
protected $dates = ['created_at', 'updated_at']; protected $dates = ['created_at', 'updated_at'];
protected $casts = [
'created_at' => 'datetime',
'updated_at' => 'datetime',
];
public function wallets() public function wallets()
{ {
return $this->hasMany(Wallet::class); return $this->hasMany(Wallet::class);

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

@@ -3,6 +3,9 @@
namespace App\Providers; namespace App\Providers;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\View;
use App\Models\Podcast;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -19,6 +22,8 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function boot(): void public function boot(): void
{ {
// Paginator::useBootstrapFive();
$publishedPodcasts = Podcast::where('published', true)->select(['name', 'slug'])->get();
View::share(['navPodcasts' => $publishedPodcasts]);
} }
} }

View File

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

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Providers\Filament;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\DisableBladeIconComponents;
use Filament\Http\Middleware\DispatchServingFilamentEvent;
use Filament\Pages;
use Filament\Panel;
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use Filament\Widgets;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken;
use Illuminate\Routing\Middleware\SubstituteBindings;
use Illuminate\Session\Middleware\AuthenticateSession;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\View\Middleware\ShareErrorsFromSession;
class AdminPanelProvider extends PanelProvider
{
public function panel(Panel $panel): Panel
{
return $panel
->default()
->id('admin')
->path('admin')
->login()
->colors([
'primary' => Color::Amber,
])
->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([
Pages\Dashboard::class,
])
->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([
Widgets\AccountWidget::class,
Widgets\FilamentInfoWidget::class,
])
->middleware([
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
AuthenticateSession::class,
ShareErrorsFromSession::class,
VerifyCsrfToken::class,
SubstituteBindings::class,
DisableBladeIconComponents::class,
DispatchServingFilamentEvent::class,
])
->authMiddleware([
Authenticate::class,
]);
}
}

View File

@@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
* *
* @var string * @var string
*/ */
public const HOME = '/dashboard'; public const HOME = '/';
/** /**
* Define your route model bindings, pattern filters, and other route configuration. * Define your route model bindings, pattern filters, and other route configuration.

0
site/artisan Executable file → Normal file
View File

3
site/build.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
yarn build
mv ./public/build/.vite/manifest.json ./public/build/manifest.json

View File

@@ -2,16 +2,25 @@
"name": "laravel/laravel", "name": "laravel/laravel",
"type": "project", "type": "project",
"description": "The skeleton application for the Laravel framework.", "description": "The skeleton application for the Laravel framework.",
"keywords": ["laravel", "framework"], "keywords": [
"laravel",
"framework"
],
"license": "MIT", "license": "MIT",
"require": { "require": {
"php": "^8.1", "php": "^8.1",
"andreiio/blade-remix-icon": "^2.6",
"blade-ui-kit/blade-icons": "^1.5",
"filament/filament": "^3.0-stable",
"guzzlehttp/guzzle": "^7.2", "guzzlehttp/guzzle": "^7.2",
"intervention/image": "^2.7", "intervention/image": "^2.7",
"laravel/framework": "^10.10", "laravel/framework": "^10.10",
"laravel/sanctum": "^3.2", "laravel/sanctum": "^3.3",
"laravel/tinker": "^2.8", "laravel/tinker": "^2.8",
"livewire/livewire": "^2.12", "livewire/livewire": "^3.2",
"mckenziearts/blade-untitledui-icons": "^1.2",
"predis/predis": "^2.2",
"spatie/image": "^2.2",
"spatie/laravel-image-optimizer": "^1.7" "spatie/laravel-image-optimizer": "^1.7"
}, },
"require-dev": { "require-dev": {
@@ -29,7 +38,10 @@
"App\\": "app/", "App\\": "app/",
"Database\\Factories\\": "database/factories/", "Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/" "Database\\Seeders\\": "database/seeders/"
} },
"files": [
"app/Helpers/pcagHelpers.php"
]
}, },
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
@@ -39,7 +51,8 @@
"scripts": { "scripts": {
"post-autoload-dump": [ "post-autoload-dump": [
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
"@php artisan package:discover --ansi" "@php artisan package:discover --ansi",
"@php artisan filament:upgrade"
], ],
"post-update-cmd": [ "post-update-cmd": [
"@php artisan vendor:publish --tag=laravel-assets --ansi --force" "@php artisan vendor:publish --tag=laravel-assets --ansi --force"

3724
site/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -169,6 +169,7 @@ return [
App\Providers\AuthServiceProvider::class, App\Providers\AuthServiceProvider::class,
// App\Providers\BroadcastServiceProvider::class, // App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class, App\Providers\EventServiceProvider::class,
App\Providers\Filament\AdminPanelProvider::class,
App\Providers\RouteServiceProvider::class, App\Providers\RouteServiceProvider::class,
])->toArray(), ])->toArray(),

183
site/config/blade-icons.php Normal file
View File

@@ -0,0 +1,183 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Icons Sets
|--------------------------------------------------------------------------
|
| With this config option you can define a couple of
| default icon sets. Provide a key name for your icon
| set and a combination from the options below.
|
*/
'sets' => [
// 'default' => [
//
// /*
// |-----------------------------------------------------------------
// | Icons Path
// |-----------------------------------------------------------------
// |
// | Provide the relative path from your app root to your SVG icons
// | directory. Icons are loaded recursively so there's no need to
// | list every sub-directory.
// |
// | Relative to the disk root when the disk option is set.
// |
// */
//
// 'path' => 'resources/svg',
//
// /*
// |-----------------------------------------------------------------
// | Filesystem Disk
// |-----------------------------------------------------------------
// |
// | Optionally, provide a specific filesystem disk to read
// | icons from. When defining a disk, the "path" option
// | starts relatively from the disk root.
// |
// */
//
// 'disk' => '',
//
// /*
// |-----------------------------------------------------------------
// | Default Prefix
// |-----------------------------------------------------------------
// |
// | This config option allows you to define a default prefix for
// | your icons. The dash separator will be applied automatically
// | to every icon name. It's required and needs to be unique.
// |
// */
//
// 'prefix' => 'icon',
//
// /*
// |-----------------------------------------------------------------
// | Fallback Icon
// |-----------------------------------------------------------------
// |
// | This config option allows you to define a fallback
// | icon when an icon in this set cannot be found.
// |
// */
//
// 'fallback' => '',
//
// /*
// |-----------------------------------------------------------------
// | Default Set Classes
// |-----------------------------------------------------------------
// |
// | This config option allows you to define some classes which
// | will be applied by default to all icons within this set.
// |
// */
//
// 'class' => '',
//
// /*
// |-----------------------------------------------------------------
// | Default Set Attributes
// |-----------------------------------------------------------------
// |
// | This config option allows you to define some attributes which
// | will be applied by default to all icons within this set.
// |
// */
//
// 'attributes' => [
// // 'width' => 50,
// // 'height' => 50,
// ],
//
// ],
],
/*
|--------------------------------------------------------------------------
| Global Default Classes
|--------------------------------------------------------------------------
|
| This config option allows you to define some classes which
| will be applied by default to all icons.
|
*/
'class' => '',
/*
|--------------------------------------------------------------------------
| Global Default Attributes
|--------------------------------------------------------------------------
|
| This config option allows you to define some attributes which
| will be applied by default to all icons.
|
*/
'attributes' => [
// 'width' => 50,
// 'height' => 50,
],
/*
|--------------------------------------------------------------------------
| Global Fallback Icon
|--------------------------------------------------------------------------
|
| This config option allows you to define a global fallback
| icon when an icon in any set cannot be found. It can
| reference any icon from any configured set.
|
*/
'fallback' => '',
/*
|--------------------------------------------------------------------------
| Components
|--------------------------------------------------------------------------
|
| These config options allow you to define some
| settings related to Blade Components.
|
*/
'components' => [
/*
|----------------------------------------------------------------------
| Disable Components
|----------------------------------------------------------------------
|
| This config option allows you to disable Blade components
| completely. It's useful to avoid performance problems
| when working with large icon libraries.
|
*/
'disabled' => false,
/*
|----------------------------------------------------------------------
| Default Icon Component Name
|----------------------------------------------------------------------
|
| This config option allows you to define the name
| for the default Icon class component.
|
*/
'default' => 'icon',
],
];

View File

@@ -56,6 +56,26 @@ return [
'collation' => 'utf8mb4_unicode_ci', 'collation' => 'utf8mb4_unicode_ci',
'prefix' => '', 'prefix' => '',
'prefix_indexes' => true, 'prefix_indexes' => true,
'strict' => false,
'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([
PDO::MYSQL_ATTR_SSL_CA => env('MYSQL_ATTR_SSL_CA'),
]) : [],
],
'legacy' => [
'driver' => 'mysql',
'url' => env('LEGACY_DATABASE_URL'),
'host' => env('LEGACY_DB_HOST', '127.0.0.1'),
'port' => env('LEGACY_DB_PORT', '3306'),
'database' => env('LEGACY_DB_DATABASE', 'forge'),
'username' => env('LEGACY_DB_USERNAME', 'forge'),
'password' => env('LEGACY_DB_PASSWORD', ''),
'unix_socket' => env('DB_SOCKET', ''),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'prefix_indexes' => true,
'strict' => true, 'strict' => true,
'engine' => null, 'engine' => null,
'options' => extension_loaded('pdo_mysql') ? array_filter([ 'options' => extension_loaded('pdo_mysql') ? array_filter([

159
site/config/livewire.php Normal file
View File

@@ -0,0 +1,159 @@
<?php
return [
/*
|---------------------------------------------------------------------------
| Class Namespace
|---------------------------------------------------------------------------
|
| This value sets the root class namespace for Livewire component classes in
| your application. This value will change where component auto-discovery
| finds components. It's also referenced by the file creation commands.
|
*/
'class_namespace' => 'App\\Livewire',
/*
|---------------------------------------------------------------------------
| View Path
|---------------------------------------------------------------------------
|
| This value is used to specify where Livewire component Blade templates are
| stored when running file creation commands like `artisan make:livewire`.
| It is also used if you choose to omit a component's render() method.
|
*/
'view_path' => resource_path('views/livewire'),
/*
|---------------------------------------------------------------------------
| Layout
|---------------------------------------------------------------------------
| The view that will be used as the layout when rendering a single component
| as an entire page via `Route::get('/post/create', CreatePost::class);`.
| In this case, the view returned by CreatePost will render into $slot.
|
*/
'layout' => 'components.layouts.app',
/*
|---------------------------------------------------------------------------
| Lazy Loading Placeholder
|---------------------------------------------------------------------------
| Livewire allows you to lazy load components that would otherwise slow down
| the initial page load. Every component can have a custom placeholder or
| you can define the default placeholder view for all components below.
|
*/
'lazy_placeholder' => null,
/*
|---------------------------------------------------------------------------
| Temporary File Uploads
|---------------------------------------------------------------------------
|
| Livewire handles file uploads by storing uploads in a temporary directory
| before the file is stored permanently. All file uploads are directed to
| a global endpoint for temporary storage. You may configure this below:
|
*/
'temporary_file_upload' => [
'disk' => 'local', // Example: 'local', 's3' | Default: 'default'
'rules' => null, // Example: ['file', 'mimes:png,jpg'] | Default: ['required', 'file', 'max:12288'] (12MB)
'directory' => 'tmp', // Example: 'tmp' | Default: 'livewire-tmp'
'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1'
'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs...
'png', 'gif', 'bmp', 'svg', 'wav', 'mp4',
'mov', 'avi', 'wmv', 'mp3', 'm4a',
'jpg', 'jpeg', 'mpga', 'webp', 'wma',
],
'max_upload_time' => 5, // Max duration (in minutes) before an upload is invalidated...
],
/*
|---------------------------------------------------------------------------
| Render On Redirect
|---------------------------------------------------------------------------
|
| This value determines if Livewire will run a component's `render()` method
| after a redirect has been triggered using something like `redirect(...)`
| Setting this to true will render the view once more before redirecting
|
*/
'render_on_redirect' => false,
/*
|---------------------------------------------------------------------------
| Eloquent Model Binding
|---------------------------------------------------------------------------
|
| Previous versions of Livewire supported binding directly to eloquent model
| properties using wire:model by default. However, this behavior has been
| deemed too "magical" and has therefore been put under a feature flag.
|
*/
'legacy_model_binding' => false,
/*
|---------------------------------------------------------------------------
| Auto-inject Frontend Assets
|---------------------------------------------------------------------------
|
| By default, Livewire automatically injects its JavaScript and CSS into the
| <head> and <body> of pages containing Livewire components. By disabling
| this behavior, you need to use @livewireStyles and @livewireScripts.
|
*/
'inject_assets' => true,
/*
|---------------------------------------------------------------------------
| Navigate (SPA mode)
|---------------------------------------------------------------------------
|
| By adding `wire:navigate` to links in your Livewire application, Livewire
| will prevent the default link handling and instead request those pages
| via AJAX, creating an SPA-like effect. Configure this behavior here.
|
*/
'navigate' => [
'show_progress_bar' => true,
'progress_bar_color' => '#2299dd',
],
/*
|---------------------------------------------------------------------------
| HTML Morph Markers
|---------------------------------------------------------------------------
|
| Livewire intelligently "morphs" existing HTML into the newly rendered HTML
| after each update. To make this process more reliable, Livewire injects
| "markers" into the rendered Blade surrounding @if, @class & @foreach.
|
*/
'inject_morph_markers' => true,
/*
|---------------------------------------------------------------------------
| Pagination Theme
|---------------------------------------------------------------------------
|
| When enabling Livewire's pagination feature by using the `WithPagination`
| trait, Livewire will use Tailwind templates to render pagination views
| on the page. If you want Bootstrap CSS, you can specify: "bootstrap"
|
*/
'pagination_theme' => 'bootstrap',
];

View File

@@ -5,6 +5,7 @@ namespace Database\Factories;
use App\Models\Artist; use App\Models\Artist;
use App\Models\User; use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Str;
class ArtistFactory extends Factory class ArtistFactory extends Factory
{ {
@@ -22,12 +23,17 @@ class ArtistFactory extends Factory
*/ */
public function definition() public function definition()
{ {
$name = fake()->name();
$slug = Str::slug(strtolower(trim($name)));
return [ return [
'user_id' => User::factory(), 'user_id' => User::factory(),
'name' => fake()->name(), 'name' => $name,
'slug' => $slug,
'avatar' => fake()->imageUrl(512, 512), 'avatar' => fake()->imageUrl(512, 512),
'header' => fake()->imageUrl(270, 185), 'header' => fake()->imageUrl(270, 185),
'location' => fake()->city() . ', ' . fake()->state(), 'location' => fake()->city() . ', ' . fake()->state(),
'alby' => null,
'naasocial' => null,
'website' => rand(0, 1) ? fake()->url : null, 'website' => rand(0, 1) ? fake()->url : null,
'bio' => fake()->paragraphs(rand(1, 3), true), 'bio' => fake()->paragraphs(rand(1, 3), true),
]; ];

View File

@@ -39,6 +39,7 @@ class ArtworkFactory extends Factory
'created_at' => $created, 'created_at' => $created,
'updated_at' => $created, 'updated_at' => $created,
'legacy_id' => null, 'legacy_id' => null,
'approved_by' => null,
]; ];
} }
} }

View File

@@ -35,6 +35,7 @@ class UserFactory extends Factory
'email_verified_at' => now(), 'email_verified_at' => now(),
'password' => $password, 'password' => $password,
'remember_token' => Str::random(10), 'remember_token' => Str::random(10),
'legacy_id' => null,
]; ];
} }

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->bigInteger('approved_by')->nullable()->after('filename');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->dropColumn('approved_by');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->integer('legacy_id')->nullable()->unsigned();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('legacy_id');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->string('legacy_filename')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->dropColumn('legacy_filename');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('theme')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('theme');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->string('slug')->after('name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->dropColumn('slug');
});
}
};

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->index('podcast_id');
$table->index('artist_id');
$table->index('episode_id');
$table->index('legacy_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artworks', function (Blueprint $table) {
$table->dropIndex('podcast_id');
$table->dropIndex('artist_id');
$table->dropIndex('episode_id');
$table->dropIndex('legacy_id');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->index('user_id');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->dropIndex('user_id');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('podcasts', function (Blueprint $table) {
$table->index('published');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('podcasts', function (Blueprint $table) {
$table->dropIndex('published');
});
}
};

View File

@@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('episodes', function (Blueprint $table) {
$table->index('podcast_id');
$table->index('artwork_id');
$table->index('episode_date');
$table->index('published');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('episodes', function (Blueprint $table) {
$table->dropIndex('podcast_id');
$table->dropIndex('artwork_id');
$table->dropIndex('episode_date');
$table->dropIndex('published');
});
}
};

View File

@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->unique('name');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropUnique('name');
});
}
};

View File

@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->string('alby')->nullable()->after('location');
$table->string('nasocial')->nullable()->after('location');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('artists', function (Blueprint $table) {
$table->dropColumn('alby');
$table->dropColumn('nasocial');
});
}
};

View File

@@ -25,7 +25,7 @@ class SeedFromOldApiSeeder extends Seeder
{ {
//$this->populateLegacyArtworks(); //$this->populateLegacyArtworks();
//die(); //die();
$current_page = 283; $current_page = 0;
$totalArtworks = 0; $totalArtworks = 0;
$missingArtworks = 0; $missingArtworks = 0;
$missingModel = 0; $missingModel = 0;

View File

@@ -14,12 +14,14 @@
"postcss": "^8.4.6", "postcss": "^8.4.6",
"sass": "^1.63.6", "sass": "^1.63.6",
"tailwindcss": "^3.1.0", "tailwindcss": "^3.1.0",
"vite": "^4.0.0", "vite": "^5.0.9",
"wolfy87-eventemitter": "4.2.0" "wolfy87-eventemitter": "4.2.0"
}, },
"version": "0.0.0", "version": "0.0.0",
"dependencies": { "dependencies": {
"aos": "^3.0.0-beta.6", "aos": "^3.0.0-beta.6",
"cropperjs": "^1.6.1",
"guillotine": "^1.3.1",
"isotope-layout": "^3.0.6", "isotope-layout": "^3.0.6",
"jquery": "^3.7.0", "jquery": "^3.7.0",
"jquery-nice-select": "^1.1.0", "jquery-nice-select": "^1.1.0",

990
site/public/adm/adminer.css Normal file
View File

@@ -0,0 +1,990 @@
/** theme "easy on the eyes" for Adminer by p.galkaev@miraidenshi-tech.jp */
@import url(//fonts.googleapis.com/css?family=Source+Sans+Pro:400,900);
/* reset
----------------------------------------------------------------------- */
*,
*:after,
*:before {
margin: 0;
padding: 0;
outline: none;
cursor: default;
-webkit-appearance: none;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-print-color-adjust: exact;
}
/* for font awesome */
*:not(.fa) {
font-family: 'Source Sans Pro', sans-serif;
}
#logins a, #tables a, #tables span {
background: none;
}
p,
form
{
margin: 0;
margin-bottom: 20px;
font-size: 14px;
}
p:last-child,
form:last-child
{
margin-bottom: 0;
}
.type,
.options select
{
width: 100%;
}
sup{
display: none;
}
/* js tooltip
----------------------------------------------------------------------- */
.js .column {
position: absolute;
padding: 0;
margin-top: 0;
top: 50px;
z-index: 9;
left: 0px;
width: 100%;
}
.js .column:not(.hidden){
display: flex;
}
.js .column a{
text-align: center;
color: black;
font-weight: bold;
flex-grow: 1;
background: #fb4;
height: 40px;
line-height: 40px;
font-size: 15px;
font-weight: normal;
}
.js .column a:hover{
background-color: gold;
color: black;
}
#help {
position: absolute;
border: none;
background: #fb4;
font-family: monospace;
z-index: 1;
font-size: 14px;
line-height: 30px;
padding: 0;
}
#help a{
color: black;
height: 100%;
display: block;
padding: 0 10px;
}
#help a:hover{
background-color: gold;
}
#help, .js .column{
display: none;
}
/* error and message
----------------------------------------------------------------------- */
.error, .message {
padding: 5px 15px 7px;
margin: 10px 0;
font-size: 14px;
display: table;
border-radius: 3px;
color: white;
}
.error{
background-color: crimson;
}
.message{
background-color: seagreen;
}
/* scroll bar
----------------------------------------------------------------------- */
::selection {
background-color: #2a65ae;
}
/*
::-moz-selection {
background-color: #333;
}*/
/* scroll bar
----------------------------------------------------------------------- */
::-webkit-scrollbar {
background-color: black;
cursor: pointer;
}
::-webkit-scrollbar-thumb {
background-color: #555;
cursor: pointer;
}
::-webkit-scrollbar:vertical{
width: 6px;
}
::-webkit-scrollbar-thumb:vertical{
border-left: 0px solid black;
width: 6px;
}
::-webkit-scrollbar:horizontal{
height: 6px;
}
::-webkit-scrollbar-thumb:horizontal{
border-top: 0px solid black;
height: 6px;
}
::-webkit-scrollbar-corner{
color: black;
background-color: black;
border-color: black;
}
::-webkit-resizer{
background-color: #555;
border-radius: 100%;
}
/* html and body
----------------------------------------------------------------------- */
html,
body {
width: 100%;
height: 100%;
max-height: 100%;
overflow: hidden;
}
body{
min-height: 100%;
font-size: 14px;
position: relative;
color: #ccc;
background-color: black;
overflow: hidden;
display: flex;
flex-wrap: nowrap;
font: inherit;
}
/* headings
----------------------------------------------------------------------- */
h1{
font-size: 24px;
margin: 0;
padding: 0 18px;
border-bottom: 1px solid #444;
font-weight: bold;
height: 70px;
line-height: 70px;
color: #555;
background: none;
}
h2{
font-size: 24px;
margin: 0;
padding: 0;
padding-left: 50px;
border-bottom: 1px solid #333;
color: #2CC990;
font-weight: bold;
background: none;
height: 70px;
line-height: 70px;
text-transform: uppercase;
}
h3{
font-weight: bold;
font-size: 24px;
margin: 40px 0 10px;
color: #2CC990;
padding-bottom: 5px;
}
/* links
----------------------------------------------------------------------- */
a{
color: inherit;
cursor: pointer;
}
a:hover, a:visited{
color: inherit;
}
a:link:hover, a:visited:hover {
color: inherit;
text-decoration: none;
}
/* table
----------------------------------------------------------------------- */
table{
margin: 0;
margin-bottom: 20px;
border: 0;
border-collapse: collapse;
font-size: 13px;
width: 100%;
/*table-layout: fixed;*/
}
tr:hover th,
.checked th
{
background: #333 !important;
color: #ddd;
border-color: none;
}
tr:hover td,
.checked td
{
background: #222 !important;
color: #ddd;
border-color: none;
}
.links + table tr:hover th{
color: #ddd;
background: #336f5a !important;
}
.links + table tr:hover td{
background: #2CC990 !important;
color: #333;
}
p + table{
margin-top: 20px;
}
tr{
padding-bottom: 1px;
}
td, th {
border: 0;
border-right: 1px solid #333;
padding: 0 12px;
line-height: 30px;
position: relative;
}
td:last-child,
th:last-child{
border-right: none;
}
th{
position: relative;
background: #222;
font-weight: normal;
width: 17%;
border-left: 5px solid #336f5a;
border-bottom: 1px solid rgba(255, 255, 255, .13);
color: #999;
}
.checkable td:first-child{
background: #222;
border-right-style: solid;
}
table.checkable th{
border-left: none;
}
td{
background: #000;
border-bottom: 1px solid rgba(255, 255, 255, .1);
}
.odd th{
background: #222;
}
.odd td{
background: #000;
}
thead td,
thead th
{
background: transparent !important;
color: #ccc;
border-right-style: dashed;
font-weight: bold;
}
table#edit-fields td,
table#edit-fields th
{
padding: 0;
padding-left: 5px;
}
table#edit-fields thead th,
table#edit-fields thead td
{
padding-left: 10px;
}
thead tr:hover th,
thead tr:hover td,
.links + table thead tr:hover th,
.links + table thead tr:hover td,
table#edit-fields thead tr:hover th,
table#edit-fields thead tr:hover td
{
background-color: transparent !important;
color: inherit !important;
border-bottom: 1px solid rgba(255, 255, 255, .1) !important;
}
thead tr:hover th{
border-bottom: 1px solid rgba(255, 255, 255, .13) !important;
}
thead th {
border-left-color: transparent;
text-align: left;
padding: 10px;
}
/* form
----------------------------------------------------------------------- */
input,
select,
textarea
{
color: #333;
font-size: 15px;
height: 30px;
background-color: #ddd;
border: none;
border-radius: 3px;
line-height: 28px;
cursor: pointer;
padding: 0;
padding-left: 10px;
-webkit-appearance: none;
outline: none;
}
input:hover,
select:hover,
input:focus,
select:focus
{
background-color: #bbb;
}
th input,
td input,
th select,
td select,
td textarea
{
background-color: transparent;
color: pink;
width: 100%;
display: inline;
border-left: 1px dashed #555;
border-radius: 0;
}
th input:hover,
th select:hover,
td input:hover,
td select:hover,
th input:focus,
th select:focus,
td input:focus,
td select:focus
{
background-color: rgba(255, 255, 255, .15);
}
th input[type='checkbox'],
th input[type='radio'],
td input[type='checkbox'],
td input[type='radio']{
border-left: none;
background-color: transparent !important;
}
td input + a,
td input + a:visited
{
text-transform: uppercase;
margin-left: 5px;
color: dodgerblue;
font-size: 12px;
font-weight: normal;
}
td input + a:hover{
color: lightskyblue !important;
}
input.icon{
padding-left: 0;
}
input.icon::after{
content: '';
}
th select,
td select
{
color: lightcoral;
}
input[type='number'] {
min-width: 55px;
}
/* radio */
input[type='radio']{
-webkit-appearance: radio;
width: 18px;
height: 18px;
vertical-align: middle;
margin-left: 8px;
margin-right: 0;
}
/* checkbox */
input[type='checkbox']{
width: 30px;
height: 30px;
margin-right: 6px;
position: relative;
border-radius: 2px;
margin-left: 20px;
}
input[type=checkbox]:hover{
border-color: white;
}
input[type=checkbox]::after {
cursor: pointer;
position: absolute;
content: '×';
left: 17%;
top: 4.5%;
color: #ccc;
font-size: 35px;
font-family: sans-serif;
font-weight: bold;
}
input[type=checkbox]:hover::after {
color: #aaa;
}
input[type=checkbox]:checked::after {
color: #333;
}
td input[type='checkbox'],
th input[type='checkbox']
{
margin-left: 10px;
margin-right: 26px;
}
td input[type='checkbox']::after{
left: 10%;
top: -2px;
color: #333;
}
td input[type='checkbox']:hover::after{
color: #555;
}
td input[type='checkbox']:checked::after{
color: #ddd;
}
p input:first-child{
margin-left: 8px;
}
label{
line-height: 27px;
font-size: 14px;
}
th label{
line-height: 35px;
}
label input {
vertical-align: top;
}
/* submit */
input[type='submit']{
color: white;
background-color: royalblue;
padding: 0 25px;
margin-right: 20px;
border-radius: 2px;
}
input[type='submit']:hover{
background-color: #214ac5;
}
/* select */
select{
padding-left: 6px;
}
/* textarea */
textarea{
min-height: 150px;
width: 100%;
}
/* fieldset */
fieldset {
display: inline;
vertical-align: top;
padding: 4px 7px 7px;
margin: 0 5px 10px;
border: 1px dashed #555;
border-radius: 2px;
min-height: 60px;
}
fieldset > div{
display: flex;
}
fieldset > div * + p{
margin-left: 10px;
}
fieldset > div > div{
margin-left: 10px;
}
fieldset > div > div:first-child{
margin-left: 0;
}
fieldset > div input,
fieldset > div select
{
margin-right: 5px;
}
fieldset > div input[type='checkbox']{
margin-left: 5px;
}
fieldset input{
flex-grow: 1;
}
fieldset input[type='submit']{
margin-right: 10px;
}
fieldset input[type='submit']:last-of-type{
margin-right: 0;
}
legend{
font-size: 14px;
background-color: #000;
padding: 0 3px;
color: #999;
}
/* menu
----------------------------------------------------------------------- */
#menu{
height: 100%;
width: 300px;
background-color: #333;
position: relative;
order: 1;
flex-grow: 0;
flex-shrink: 0;
margin: 0;
padding: 0;
top: 0;
overflow-y: overlay;
}
#menu p {
padding: 18px;
margin: 0;
border-bottom: 1px solid #444;
}
/* logo */
#h1{
color: #555;
text-decoration: none;
font-style: inherit;
}
.version {
color: #555;
font-size: inherit;
}
/* db select */
#dbs select{
width: 228px;
margin-left: 8px;
}
/* links */
#menu .links{
padding-top: 0;
padding-bottom: 10px;
}
#menu .links a:nth-child(even){
margin-left: 6px;
}
#menu .links a{
display: inline-block;
vertical-align: top;
width: 127px;
height: 31px;
margin: 0;
margin-bottom: 10px;
border: 1px solid #555;
line-height: 27px;
text-align: center;
text-transform: uppercase;
font-size: 12px;
border-radius: 3px;
color: #999;
}
#menu .links a.active,
#menu .links a:hover
{
border: 1px solid #ccc;
font-weight: normal;
color: inherit;
}
/* tables */
#logins, #tables{
border-bottom: none;
line-height: 20px;
padding: 18px 0;
overflow-y: auto !important;
}
#tables br{
display: none;
}
#tables a {
float: right;
padding: 5px 18px 9px;
line-height: 17px;
color: #2CC990;
font-size: 13px;
}
#tables .structure, #tables .view {
float: none;
display: block;
color: inherit;
font-size: 14px;
}
#logins a {
display: block;
padding: 5px 18px 9px;
color: inherit;
font-size: 14px;
}
#tables a.select.active,
#tables a.select:hover
{
color: #fba;
}
#logins a:hover,
#tables a[title]:hover,
#tables a.active,
#tables a.select:hover + a,
#tables a.select.active + a
{
background-color: #555;
font-weight: normal;
}
/* content
----------------------------------------------------------------------- */
#content{
height: 100%;
width: 100%;
margin: 0;
padding: 0;
padding-left: 50px;
padding-right: 50px;
padding-bottom: 30px;
overflow-y: auto !important;
order: 2;
flex-grow: 1;
}
#breadcrumb{
position: relative;
display: none;
}
#content h2{
margin-left: -50px;
}
/* links */
#content .links a,
code.jush-sql ~ a,
#fieldset-history > a:first-child
{
display: inline-block;
height: 32px;
line-height: 30px;
padding: 0 10px;
border: 1px solid #666;
border-radius: 3px;
font-size: 12px;
text-transform: uppercase;
}
#content .links a:hover,
code.jush-sql ~ a:hover,
#fieldset-history > a:first-child:hover
{
color: #eee;
border-color: #eee;
}
#ajaxstatus + *{
margin-top: 18px;
}
#ajaxstatus + *.links {
margin-top: 0 !important;
height: 65px;
line-height: 55px;
margin-bottom: 0;
}
#ajaxstatus + .links a{
white-space: nowrap;
margin-right: 20px;
padding: 0;
padding-bottom: 5px;
border: 0;
border-radius: 0;
font-size: 15px;
font-weight: bold;
}
#ajaxstatus + .links a.active,
#ajaxstatus + .links a:hover
{
border-bottom: 1px solid;
border-color: inherit;
color: inherit;
}
/* fieldset search */
#fieldset-search > div > *{
margin-right: 5px;
margin-bottom: 5px;
}
/* fieldset search */
#fieldset-partition p{
margin-bottom: 0;
}
/* feldset history */
#fieldset-history{
flex-wrap: wrap;
}
#fieldset-history i{
display: none;
}
#fieldset-history input[type='submit']{
flex-grow: 0;
order: 1;
margin-top: 1px;
margin-left: 17px;
}
#fieldset-history > div a:last-child{
order: 2;
}
#fieldset-history > a{
flex-grow: 0;
flex-basis: 5%;
min-width: 45px;
text-align: center;
margin-bottom: 10px;
margin-left: 5px;
}
#fieldset-history > .time{
flex-grow: 0;
flex-basis: 5%;
text-align: center;
line-height: 29px;
}
#fieldset-history > code{
flex-grow: 1;
flex-basis: 89%;
line-height: 29px;
}
#fieldset-history > .time{
flex-grow: 0;
flex-basis: 5%;
text-align: center;
}
/* sql
----------------------------------------------------------------------- */
.sqlarea{
border: 1px solid #444 !important;
width: 100% !important;
padding: 12px 15px !important;
font-size: 15px;
margin-bottom: 20px;
}
.jush-sql_code{
color: #fafafa !important;
font-family: 'Source Sans Pro', sans-serif !important;
}
.jush a, .jush a:visited{
color: #fba;
font-weight: normal;
}
.jush a:hover{
color: #fba;
cursor: pointer;
}
.jush-php_quo, .jush-quo, .jush-quo_one, .jush-php_eot, .jush-apo, .jush-sql_apo, .jush-sqlite_apo, .jush-sql_quo, .jush-sql_eot{
color: aquamarine;
}
.jush-bac, .jush-php_bac, .jush-bra, .jush-mssql_bra, .jush-sqlite_quo{
color: plum;
}
.jush-num, .jush-clr{
color: #85E2FF;
}
code {
background: #000;
font-size: 14px;
}
code.jush-sql ~ a{
position: relative;
margin-left: 5px;
/*margin-top: 20px;
margin-bottom: 20px; */
}
code.jush-sql ~ a:first-of-type{
margin-left: 30px;
}
code.jush-sql ~ a:first-of-type::before{
content: '◀';
color: #555;
position: absolute;
left: -22px;
font-size: 22px;
top: -1px;
}
/* logout form
----------------------------------------------------------------------- */
body > form{
position: absolute;
}

1795
site/public/adm/index.php Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.fi-pagination-items,.fi-pagination-overview,.fi-pagination-records-per-page-select:not(.fi-compact){display:none}@supports (container-type:inline-size){.fi-pagination{container-type:inline-size}@container (min-width: 28rem){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@container (min-width: 56rem){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}@supports not (container-type:inline-size){@media (min-width:640px){.fi-pagination-records-per-page-select.fi-compact{display:none}.fi-pagination-records-per-page-select:not(.fi-compact){display:inline}}@media (min-width:768px){.fi-pagination:not(.fi-simple)>.fi-pagination-previous-btn{display:none}.fi-pagination-overview{display:inline}.fi-pagination:not(.fi-simple)>.fi-pagination-next-btn{display:none}.fi-pagination-items{display:flex}}}.tippy-box[data-animation=fade][data-state=hidden]{opacity:0}[data-tippy-root]{max-width:calc(100vw - 10px)}.tippy-box{background-color:#333;border-radius:4px;color:#fff;font-size:14px;line-height:1.4;outline:0;position:relative;transition-property:transform,visibility,opacity;white-space:normal}.tippy-box[data-placement^=top]>.tippy-arrow{bottom:0}.tippy-box[data-placement^=top]>.tippy-arrow:before{border-top-color:initial;border-width:8px 8px 0;bottom:-7px;left:0;transform-origin:center top}.tippy-box[data-placement^=bottom]>.tippy-arrow{top:0}.tippy-box[data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:initial;border-width:0 8px 8px;left:0;top:-7px;transform-origin:center bottom}.tippy-box[data-placement^=left]>.tippy-arrow{right:0}.tippy-box[data-placement^=left]>.tippy-arrow:before{border-left-color:initial;border-width:8px 0 8px 8px;right:-7px;transform-origin:center left}.tippy-box[data-placement^=right]>.tippy-arrow{left:0}.tippy-box[data-placement^=right]>.tippy-arrow:before{border-right-color:initial;border-width:8px 8px 8px 0;left:-7px;transform-origin:center right}.tippy-box[data-inertia][data-state=visible]{transition-timing-function:cubic-bezier(.54,1.5,.38,1.11)}.tippy-arrow{color:#333;height:16px;width:16px}.tippy-arrow:before{border-color:transparent;border-style:solid;content:"";position:absolute}.tippy-content{padding:5px 9px;position:relative;z-index:1}.tippy-box[data-theme~=light]{background-color:#fff;box-shadow:0 0 20px 4px #9aa1b126,0 4px 80px -8px #24282f40,0 4px 4px -2px #5b5e6926;color:#26323d}.tippy-box[data-theme~=light][data-placement^=top]>.tippy-arrow:before{border-top-color:#fff}.tippy-box[data-theme~=light][data-placement^=bottom]>.tippy-arrow:before{border-bottom-color:#fff}.tippy-box[data-theme~=light][data-placement^=left]>.tippy-arrow:before{border-left-color:#fff}.tippy-box[data-theme~=light][data-placement^=right]>.tippy-arrow:before{border-right-color:#fff}.tippy-box[data-theme~=light]>.tippy-backdrop{background-color:#fff}.tippy-box[data-theme~=light]>.tippy-svg-arrow{fill:#fff}.fi-sortable-ghost{opacity:.3}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
function r({state:i}){return{state:i,rows:[],shouldUpdateRows:!0,init:function(){this.updateRows(),this.rows.length<=0?this.rows.push({key:"",value:""}):this.updateState(),this.$watch("state",(t,e)=>{let s=o=>o===null?0:Array.isArray(o)?o.length:typeof o!="object"?0:Object.keys(o).length;s(t)===0&&s(e)===0||this.updateRows()})},addRow:function(){this.rows.push({key:"",value:""}),this.updateState()},deleteRow:function(t){this.rows.splice(t,1),this.rows.length<=0&&this.addRow(),this.updateState()},reorderRows:function(t){let e=Alpine.raw(this.rows),s=e.splice(t.oldIndex,1)[0];e.splice(t.newIndex,0,s),this.rows=e,this.updateState()},updateRows:function(){if(!this.shouldUpdateRows){this.shouldUpdateRows=!0;return}let t=[];for(let[e,s]of Object.entries(this.state??{}))t.push({key:e,value:s});this.rows=t},updateState:function(){let t={};this.rows.forEach(e=>{e.key===""||e.key===null||(t[e.key]=e.value)}),this.shouldUpdateRows=!1,this.state=t}}}export{r as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
function i({state:a,splitKeys:n}){return{newTag:"",state:a,createTag:function(){if(this.newTag=this.newTag.trim(),this.newTag!==""){if(this.state.includes(this.newTag)){this.newTag="";return}this.state.push(this.newTag),this.newTag=""}},deleteTag:function(t){this.state=this.state.filter(e=>e!==t)},reorderTags:function(t){let e=this.state.splice(t.oldIndex,1)[0];this.state.splice(t.newIndex,0,e),this.state=[...this.state]},input:{["x-on:blur"]:"createTag()",["x-model"]:"newTag",["x-on:keydown"](t){["Enter",...n].includes(t.key)&&(t.preventDefault(),t.stopPropagation(),this.createTag())},["x-on:paste"](){this.$nextTick(()=>{if(n.length===0){this.createTag();return}let t=n.map(e=>e.replace(/[/\-\\^$*+?.()|[\]{}]/g,"\\$&")).join("|");this.newTag.split(new RegExp(t,"g")).forEach(e=>{this.newTag=e,this.createTag()})})}}}}export{i as default};

View File

@@ -0,0 +1 @@
function t({initialHeight:e}){return{init:function(){this.render()},render:function(){this.$el.scrollHeight>0&&(this.$el.style.height=e+"rem",this.$el.style.height=this.$el.scrollHeight+"px")}}}export{t as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
function c(){return{collapsedGroups:[],isLoading:!1,selectedRecords:[],shouldCheckUniqueSelection:!0,init:function(){this.$wire.$on("deselectAllTableRecords",()=>this.deselectAllRecords()),this.$watch("selectedRecords",()=>{if(!this.shouldCheckUniqueSelection){this.shouldCheckUniqueSelection=!0;return}this.selectedRecords=[...new Set(this.selectedRecords)],this.shouldCheckUniqueSelection=!1})},mountBulkAction:function(e){this.$wire.set("selectedTableRecords",this.selectedRecords,!1),this.$wire.mountTableBulkAction(e)},toggleSelectRecordsOnPage:function(){let e=this.getRecordsOnPage();if(this.areRecordsSelected(e)){this.deselectRecords(e);return}this.selectRecords(e)},toggleSelectRecordsInGroup:async function(e){if(this.isLoading=!0,this.areRecordsSelected(this.getRecordsInGroupOnPage(e))){this.deselectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e));return}this.selectRecords(await this.$wire.getGroupedSelectableTableRecordKeys(e)),this.isLoading=!1},getRecordsInGroupOnPage:function(e){let s=[];for(let t of this.$root.getElementsByClassName("fi-ta-record-checkbox"))t.dataset.group===e&&s.push(t.value);return s},getRecordsOnPage:function(){let e=[];for(let s of this.$root.getElementsByClassName("fi-ta-record-checkbox"))e.push(s.value);return e},selectRecords:function(e){for(let s of e)this.isRecordSelected(s)||this.selectedRecords.push(s)},deselectRecords:function(e){for(let s of e){let t=this.selectedRecords.indexOf(s);t!==-1&&this.selectedRecords.splice(t,1)}},selectAllRecords:async function(){this.isLoading=!0,this.selectedRecords=await this.$wire.getAllSelectableTableRecordKeys(),this.isLoading=!1},deselectAllRecords:function(){this.selectedRecords=[]},isRecordSelected:function(e){return this.selectedRecords.includes(e)},areRecordsSelected:function(e){return e.every(s=>this.isRecordSelected(s))},toggleCollapseGroup:function(e){if(this.isGroupCollapsed(e)){this.collapsedGroups.splice(this.collapsedGroups.indexOf(e),1);return}this.collapsedGroups.push(e)},isGroupCollapsed:function(e){return this.collapsedGroups.includes(e)},resetCollapsedGroups:function(){this.collapsedGroups=[]}}}export{c as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

3
site/public/pinfo.php Normal file
View File

@@ -0,0 +1,3 @@
<?php
die('nope');
phpinfo();

View File

@@ -1,2 +1,2 @@
User-agent: * User-agent: *
Disallow: / Disallow: /profile

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 2.2 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Some files were not shown because too many files have changed in this diff Show More