mirror of
https://github.com/sp-tarkov/forge.git
synced 2025-02-12 12:10:41 -05:00
Mod detail page work
This commit is contained in:
parent
62185179cd
commit
c026870bed
@ -20,7 +20,6 @@ LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Only 'mysql' and 'pgsql' are supported due to a 'naturalsort' database function.
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
@ -29,7 +28,8 @@ DB_USERNAME=root
|
||||
DB_PASSWORD=
|
||||
|
||||
# This is only needed if you are running the app:import-woltlab-data command.
|
||||
# For normal development you should just seed the database with fake data.
|
||||
# For normal development you should just seed the database with fake data:
|
||||
# `php artisan migrate:fresh --seed`
|
||||
DB_WOLTLAB_CONNECTION=mysql
|
||||
DB_WOLTLAB_HOST=127.0.0.1
|
||||
DB_WOLTLAB_PORT=3306
|
||||
|
@ -14,6 +14,8 @@ use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use League\HTMLToMarkdown\HtmlConverter;
|
||||
use Stevebauman\Purify\Facades\Purify;
|
||||
|
||||
class ImportWoltlabData extends Command
|
||||
{
|
||||
@ -212,19 +214,19 @@ class ImportWoltlabData extends Command
|
||||
}
|
||||
|
||||
$insertData[] = [
|
||||
'hub_id' => $mod->fileID,
|
||||
'hub_id' => (int) $mod->fileID,
|
||||
'user_id' => User::whereHubId($mod->userID)->value('id'),
|
||||
'name' => $modContent ? $modContent->subject : '',
|
||||
'slug' => $modContent ? Str::slug($modContent->subject) : '',
|
||||
'teaser' => $modContent ? (strlen($modContent->teaser) > 100 ? Str::take($modContent->teaser, 97).'...' : $modContent->teaser) : '',
|
||||
'description' => $modContent ? $modContent->message : '',
|
||||
'description' => $this->convertModDescription($modContent?->message ?? ''),
|
||||
'thumbnail' => $this->fetchModThumbnail($command, $curl, $mod->fileID, $mod->iconHash, $mod->iconExtension),
|
||||
'license_id' => License::whereHubId($mod->licenseID)->value('id'),
|
||||
'source_code_link' => $this->fetchSourceLinkValue($modOptions),
|
||||
'featured' => $mod->isFeatured,
|
||||
'featured' => (bool) $mod->isFeatured,
|
||||
'contains_ai_content' => $this->fetchContainsAiContentValue($modOptions),
|
||||
'contains_ads' => $this->fetchContainsAdsValue($modOptions),
|
||||
'disabled' => $mod->isDisabled,
|
||||
'disabled' => (bool) $mod->isDisabled,
|
||||
'created_at' => Carbon::parse($mod->time, 'UTC'),
|
||||
'updated_at' => Carbon::parse($mod->lastChangeTime, 'UTC'),
|
||||
];
|
||||
@ -408,11 +410,11 @@ class ImportWoltlabData extends Command
|
||||
'hub_id' => $version->versionID,
|
||||
'mod_id' => $modId,
|
||||
'version' => $version->versionNumber,
|
||||
'description' => $versionContent['description'] ?? '',
|
||||
'description' => $this->convertModDescription($versionContent['description'] ?? ''),
|
||||
'link' => $version->downloadURL,
|
||||
'spt_version_id' => SptVersion::whereHubId($versionLabel)->value('id'),
|
||||
'virus_total_link' => $this->fetchVirusTotalLink($modOptions),
|
||||
'downloads' => (int) $version->downloads,
|
||||
'downloads' => max((int) $version->downloads, 0), // Ensure the value is at least 0
|
||||
'disabled' => (bool) $version->isDisabled,
|
||||
'created_at' => Carbon::parse($version->uploadTime, 'UTC'),
|
||||
'updated_at' => Carbon::parse($version->uploadTime, 'UTC'),
|
||||
@ -449,8 +451,10 @@ class ImportWoltlabData extends Command
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function updateDisabledPropertty(): void
|
||||
protected function convertModDescription(string $description): string
|
||||
{
|
||||
$this->output->newLine();
|
||||
// Alright, hear me out... Shut up.
|
||||
$converter = new HtmlConverter();
|
||||
return $converter->convert(Purify::clean($description));
|
||||
}
|
||||
}
|
||||
|
@ -76,10 +76,10 @@ class Mod extends Model
|
||||
->orderByDesc(
|
||||
SptVersion::select('version')
|
||||
->whereColumn('mod_versions.spt_version_id', 'spt_versions.id')
|
||||
->orderByDesc(DB::raw('naturalSort(version)'))
|
||||
->orderByDesc('version')
|
||||
->take(1),
|
||||
)
|
||||
->orderByDesc(DB::raw('naturalSort(version)'))
|
||||
->orderByDesc('version')
|
||||
->take(1),
|
||||
])
|
||||
->havingNotNull('latest_spt_version_id')
|
||||
|
@ -17,7 +17,9 @@
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/scout": "^10.9",
|
||||
"laravel/tinker": "^2.9",
|
||||
"livewire/livewire": "^3.0"
|
||||
"league/html-to-markdown": "^5.1",
|
||||
"livewire/livewire": "^3.0",
|
||||
"stevebauman/purify": "^6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.13",
|
||||
|
218
composer.lock
generated
218
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "bea00e78566494d8d997f51c6871ca02",
|
||||
"content-hash": "b42a2bd2686065b7db27628579fde13c",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
@ -878,6 +878,67 @@
|
||||
],
|
||||
"time": "2023-10-06T06:47:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "ezyang/htmlpurifier",
|
||||
"version": "v4.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/bbc513d79acf6691fa9cf10f192c90dd2957f18c",
|
||||
"reference": "bbc513d79acf6691fa9cf10f192c90dd2957f18c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~5.6.0 || ~7.0.0 || ~7.1.0 || ~7.2.0 || ~7.3.0 || ~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cerdic/css-tidy": "^1.7 || ^2.0",
|
||||
"simpletest/simpletest": "dev-master"
|
||||
},
|
||||
"suggest": {
|
||||
"cerdic/css-tidy": "If you want to use the filter 'Filter.ExtractStyleBlocks'.",
|
||||
"ext-bcmath": "Used for unit conversion and imagecrash protection",
|
||||
"ext-iconv": "Converts text to and from non-UTF-8 encodings",
|
||||
"ext-tidy": "Used for pretty-printing HTML"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"library/HTMLPurifier.composer.php"
|
||||
],
|
||||
"psr-0": {
|
||||
"HTMLPurifier": "library/"
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/library/HTMLPurifier/Language/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-2.1-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Edward Z. Yang",
|
||||
"email": "admin@htmlpurifier.org",
|
||||
"homepage": "http://ezyang.com"
|
||||
}
|
||||
],
|
||||
"description": "Standards compliant HTML filter written in PHP",
|
||||
"homepage": "http://htmlpurifier.org/",
|
||||
"keywords": [
|
||||
"html"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/ezyang/htmlpurifier/issues",
|
||||
"source": "https://github.com/ezyang/htmlpurifier/tree/v4.17.0"
|
||||
},
|
||||
"time": "2023-11-17T15:01:25+00:00"
|
||||
},
|
||||
{
|
||||
"name": "fruitcake/php-cors",
|
||||
"version": "v1.3.0",
|
||||
@ -2757,6 +2818,95 @@
|
||||
},
|
||||
"time": "2024-05-06T20:05:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/html-to-markdown",
|
||||
"version": "5.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/html-to-markdown.git",
|
||||
"reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/0b4066eede55c48f38bcee4fb8f0aa85654390fd",
|
||||
"reference": "0b4066eede55c48f38bcee4fb8f0aa85654390fd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-xml": "*",
|
||||
"php": "^7.2.5 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mikehaertl/php-shellcommand": "^1.1.0",
|
||||
"phpstan/phpstan": "^1.8.8",
|
||||
"phpunit/phpunit": "^8.5 || ^9.2",
|
||||
"scrutinizer/ocular": "^1.6",
|
||||
"unleashedtech/php-coding-standard": "^2.7 || ^3.0",
|
||||
"vimeo/psalm": "^4.22 || ^5.0"
|
||||
},
|
||||
"bin": [
|
||||
"bin/html-to-markdown"
|
||||
],
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "5.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\HTMLToMarkdown\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Colin O'Dell",
|
||||
"email": "colinodell@gmail.com",
|
||||
"homepage": "https://www.colinodell.com",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Nick Cernis",
|
||||
"email": "nick@cern.is",
|
||||
"homepage": "http://modernnerd.net",
|
||||
"role": "Original Author"
|
||||
}
|
||||
],
|
||||
"description": "An HTML-to-markdown conversion helper for PHP",
|
||||
"homepage": "https://github.com/thephpleague/html-to-markdown",
|
||||
"keywords": [
|
||||
"html",
|
||||
"markdown"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/html-to-markdown/issues",
|
||||
"source": "https://github.com/thephpleague/html-to-markdown/tree/5.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://www.colinodell.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://www.paypal.me/colinpodell/10.00",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/colinodell",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/league/html-to-markdown",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2023-07-12T21:21:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/mime-type-detection",
|
||||
"version": "1.15.0",
|
||||
@ -4576,6 +4726,72 @@
|
||||
],
|
||||
"time": "2024-03-06T16:03:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "stevebauman/purify",
|
||||
"version": "v6.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/stevebauman/purify.git",
|
||||
"reference": "303d23e5756a1fd0e9b34f14459ec6aa327a8412"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/stevebauman/purify/zipball/303d23e5756a1fd0e9b34f14459ec6aa327a8412",
|
||||
"reference": "303d23e5756a1fd0e9b34f14459ec6aa327a8412",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ezyang/htmlpurifier": "^4.17",
|
||||
"illuminate/contracts": "^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"illuminate/support": "^7.0|^8.0|^9.0|^10.0|^11.0",
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^5.0|^6.0|^7.0|^8.0|^9.0",
|
||||
"phpunit/phpunit": "^8.0|^9.0|^10.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Stevebauman\\Purify\\PurifyServiceProvider"
|
||||
],
|
||||
"aliases": {
|
||||
"Purify": "Stevebauman\\Purify\\Facades\\Purify"
|
||||
}
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Stevebauman\\Purify\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Steve Bauman",
|
||||
"email": "steven_bauman@outlook.com"
|
||||
}
|
||||
],
|
||||
"description": "An HTML Purifier / Sanitizer for Laravel",
|
||||
"keywords": [
|
||||
"Purifier",
|
||||
"clean",
|
||||
"cleaner",
|
||||
"html",
|
||||
"laravel",
|
||||
"purification",
|
||||
"purify"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/stevebauman/purify/issues",
|
||||
"source": "https://github.com/stevebauman/purify/tree/v6.2.0"
|
||||
},
|
||||
"time": "2024-03-12T15:22:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.1.0",
|
||||
|
112
config/purify.php
Normal file
112
config/purify.php
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
use Stevebauman\Purify\Definitions\Html5Definition;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default Config
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This option defines the default config that is provided to HTMLPurifier.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => 'default',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Config sets
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure various sets of configuration for differentiated use of HTMLPurifier.
|
||||
| A specific set of configuration can be applied by calling the "config($name)" method on
|
||||
| a Purify instance. Feel free to add/remove/customize these attributes as you wish.
|
||||
|
|
||||
| Documentation: http://htmlpurifier.org/live/configdoc/plain.html
|
||||
|
|
||||
| Core.Encoding The encoding to convert input to.
|
||||
| HTML.Doctype Doctype to use during filtering.
|
||||
| HTML.Allowed The allowed HTML Elements with their allowed attributes.
|
||||
| HTML.ForbiddenElements The forbidden HTML elements. Elements that are listed in this
|
||||
| string will be removed, however their content will remain.
|
||||
| CSS.AllowedProperties The Allowed CSS properties.
|
||||
| AutoFormat.AutoParagraph Newlines are converted in to paragraphs whenever possible.
|
||||
| AutoFormat.RemoveEmpty Remove empty elements that contribute no semantic information to the document.
|
||||
|
|
||||
*/
|
||||
|
||||
'configs' => [
|
||||
|
||||
'default' => [
|
||||
'Core.Encoding' => 'utf-8',
|
||||
'HTML.Doctype' => 'HTML 4.01 Strict',
|
||||
'HTML.Allowed' => 'h1,h2,h3,h4,h5,h6,b,strong,i,em,s,del,a[href|title],ul,ol,li,p,br,span,img[width|height|alt|src],blockquote',
|
||||
'HTML.ForbiddenElements' => '',
|
||||
'HTML.TargetBlank' => true,
|
||||
'CSS.AllowedProperties' => 'font-weight,font-style,text-decoration,color,background-color,text-align',
|
||||
'AutoFormat.RemoveEmpty.RemoveNbsp' => true,
|
||||
'AutoFormat.AutoParagraph' => false,
|
||||
'AutoFormat.RemoveEmpty' => true,
|
||||
'AutoFormat.RemoveSpansWithoutAttributes' => true,
|
||||
'URI.AllowedSchemes' => ['http' => true, 'https' => true, 'ftp' => true],
|
||||
],
|
||||
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTMLPurifier definitions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify a class that augments the HTML definitions used by
|
||||
| HTMLPurifier. Additional HTML5 definitions are provided out of the box.
|
||||
| When specifying a custom class, make sure it implements the interface:
|
||||
|
|
||||
| \Stevebauman\Purify\Definitions\Definition
|
||||
|
|
||||
| Note that these definitions are applied to every Purifier instance.
|
||||
|
|
||||
| Documentation: http://htmlpurifier.org/docs/enduser-customize.html
|
||||
|
|
||||
*/
|
||||
|
||||
'definitions' => Html5Definition::class,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| HTMLPurifier CSS definitions
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify a class that augments the CSS definitions used by
|
||||
| HTMLPurifier. When specifying a custom class, make sure it implements
|
||||
| the interface:
|
||||
|
|
||||
| \Stevebauman\Purify\Definitions\CssDefinition
|
||||
|
|
||||
| Note that these definitions are applied to every Purifier instance.
|
||||
|
|
||||
| CSS should be extending $definition->info['css-attribute'] = values
|
||||
| See HTMLPurifier_CSSDefinition for further explanation
|
||||
|
|
||||
*/
|
||||
|
||||
'css-definitions' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Serializer
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The storage implementation where HTMLPurifier can store its serializer files.
|
||||
| If the filesystem cache is in use, the path must be writable through the
|
||||
| storage disk by the web server, otherwise an exception will be thrown.
|
||||
|
|
||||
*/
|
||||
|
||||
'serializer' => [
|
||||
'driver' => env('CACHE_DRIVER', 'file'),
|
||||
'cache' => \Stevebauman\Purify\Cache\CacheDefinitionCache::class,
|
||||
],
|
||||
];
|
@ -1,79 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
if (config('database.default') === 'sqlite') {
|
||||
throw new \Exception('This project does not support SQLite. Update to MySQL or PostgreSQL.');
|
||||
}
|
||||
|
||||
if (config('database.default') === 'mysql') {
|
||||
// https://www.drupal.org/project/natsort
|
||||
DB::unprepared("
|
||||
DROP FUNCTION IF EXISTS naturalSort;
|
||||
CREATE FUNCTION naturalSort (s VARCHAR (255)) RETURNS VARCHAR (255) NO SQL DETERMINISTIC BEGIN
|
||||
DECLARE orig VARCHAR (255) DEFAULT s;
|
||||
DECLARE ret VARCHAR (255) DEFAULT '';
|
||||
IF s IS NULL THEN
|
||||
RETURN NULL;
|
||||
ELSEIF NOT s REGEXP '[0-9]' THEN
|
||||
SET ret = s;
|
||||
ELSE
|
||||
SET s = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(s, '0', '#'), '1', '#'), '2', '#'), '3', '#'), '4', '#');
|
||||
SET s = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(s, '5', '#'), '6', '#'), '7', '#'), '8', '#'), '9', '#');
|
||||
SET s = REPLACE(s, '.#', '##');
|
||||
SET s = REPLACE(s, '#,#', '###');
|
||||
BEGIN
|
||||
DECLARE numpos INT;
|
||||
DECLARE numlen INT;
|
||||
DECLARE numstr VARCHAR (255);
|
||||
lp1: LOOP
|
||||
SET numpos = locate('#', s);
|
||||
IF numpos = 0 THEN
|
||||
SET ret = concat(ret, s);
|
||||
LEAVE lp1;
|
||||
END IF;
|
||||
SET ret = concat(ret, substring(s, 1, numpos - 1));
|
||||
SET s = substring(s, numpos);
|
||||
SET orig = substring(orig, numpos);
|
||||
SET numlen = char_length(s) - char_length(trim(LEADING '#' FROM s));
|
||||
SET numstr = cast(REPLACE(substring(orig, 1, numlen), ',', '') AS DECIMAL (13, 3));
|
||||
SET numstr = lpad(numstr, 15, '0');
|
||||
SET ret = concat(ret, '[', numstr, ']');
|
||||
SET s = substring(s, numlen + 1);
|
||||
SET orig = substring(orig, numlen + 1);
|
||||
END LOOP;
|
||||
END;
|
||||
END IF;
|
||||
SET ret = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(ret, ' ', ''), ',', ''), ':', ''), '.', ''), ';', '' ), '(', ''), ')', '');
|
||||
RETURN ret;
|
||||
END;
|
||||
");
|
||||
}
|
||||
|
||||
if (config('database.default') === 'pgsql') {
|
||||
// http://www.rhodiumtoad.org.uk/junk/naturalsort.sql
|
||||
DB::unprepared('
|
||||
create or replace function naturalSort(text)
|
||||
returns bytea language sql immutable strict as
|
||||
$f$ select string_agg(convert_to(coalesce(r[2],length(length(r[1])::text) || length(r[1])::text || r[1]),\'SQL_ASCII\'),\'\x00\')
|
||||
from regexp_matches($1, \'0*([0-9]+)|([^0-9]+)\', \'g\') r; $f$;
|
||||
');
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
if (config('database.default') === 'sqlite') {
|
||||
throw new \Exception('This project does not support SQLite. Update to MySQL or PostgreSQL.');
|
||||
}
|
||||
|
||||
if (config('database.default') === 'mysql' || config('database.default') === 'pgsql') {
|
||||
DB::unprepared('DROP FUNCTION IF EXISTS naturalSort');
|
||||
}
|
||||
}
|
||||
};
|
@ -15,6 +15,10 @@ input[type="checkbox"] {
|
||||
@apply text-gray-800 dark:text-gray-300 focus:ring-gray-600 dark:focus:ring-gray-500 border-gray-300 dark:border-gray-700 rounded;
|
||||
}
|
||||
|
||||
main a:not(.mod-list-component):not(.tab) {
|
||||
@apply underline text-gray-800 hover:text-black dark:text-gray-200 dark:hover:text-white;
|
||||
}
|
||||
|
||||
.badge-version {
|
||||
@apply bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-100;
|
||||
|
||||
@ -38,3 +42,45 @@ input[type="checkbox"] {
|
||||
@apply bg-yellow-100 dark:bg-yellow-700 text-yellow-700 dark:text-yellow-100;
|
||||
}
|
||||
}
|
||||
|
||||
.user-markdown {
|
||||
b, strong {
|
||||
@apply font-bold;
|
||||
}
|
||||
i, em {
|
||||
@apply italic
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
@apply font-bold mt-4 mb-2 text-black dark:text-white;
|
||||
}
|
||||
h1 {
|
||||
@apply text-2xl;
|
||||
}
|
||||
h2 {
|
||||
@apply text-xl;
|
||||
}
|
||||
h3 {
|
||||
@apply text-lg;
|
||||
}
|
||||
h4 {
|
||||
@apply text-base;
|
||||
}
|
||||
h5 {
|
||||
@apply text-sm;
|
||||
}
|
||||
h6 {
|
||||
@apply text-xs;
|
||||
}
|
||||
p {
|
||||
@apply my-2 text-gray-800 dark:text-gray-300;
|
||||
}
|
||||
ul {
|
||||
@apply list-disc mb-2;
|
||||
}
|
||||
ol {
|
||||
@apply list-decimal mb-2;
|
||||
}
|
||||
li {
|
||||
@apply my-2 ml-7 text-gray-800 dark:text-gray-300;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
@foreach ($mods as $mod)
|
||||
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}">
|
||||
<a href="/mod/{{ $mod->id }}/{{ $mod->slug }}" class="mod-list-component">
|
||||
<div class="flex flex-col group h-full w-full max-w-md mx-auto bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl overflow-hidden md:max-w-2xl hover:shadow-lg hover:bg-gray-50 dark:hover:bg-black hover:shadow-gray-400 dark:hover:shadow-black transition-all duration-200">
|
||||
<div class="h-auto md:h-full md:flex">
|
||||
<div class="h-auto md:h-full md:shrink-0 overflow-hidden">
|
||||
|
@ -7,34 +7,68 @@
|
||||
</x-slot>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 max-w-7xl mx-auto pb-6 px-4 gap-6 sm:px-6 lg:px-8">
|
||||
<div class="lg:col-span-2 flex flex-col gap-6">
|
||||
<div class="p-4 sm:p-6 text-center sm:text-left bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl">
|
||||
<div class="flex flex-col sm:flex-row gap-4 sm:gap-6">
|
||||
<div class="grow-0 shrink-0 flex justify-center items-center">
|
||||
@if(empty($mod->thumbnail))
|
||||
<img src="https://placehold.co/144x144/EEE/31343C?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="block dark:hidden w-36 rounded-lg">
|
||||
<img src="https://placehold.co/144x144/31343C/EEE?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="hidden dark:block w-36 rounded-lg">
|
||||
@else
|
||||
<img src="{{ $mod->thumbnail }}" alt="{{ $mod->name }}" class="w-36 rounded-lg">
|
||||
@endif
|
||||
</div>
|
||||
<div class="grow flex flex-col justify-center items-center sm:items-start text-gray-800 dark:text-gray-200">
|
||||
<h2 class="pb-1 sm:p-0 text-3xl font-bold text-gray-900 dark:text-white">
|
||||
{{ $mod->name }}
|
||||
<span class="font-light text-nowrap text-gray-700 dark:text-gray-400">
|
||||
{{ $mod->latestSptVersion->version }}
|
||||
</span>
|
||||
</h2>
|
||||
<p>{{ __('Created by') }} {{ $mod->user->name }}</p>
|
||||
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
||||
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lg:col-span-2 p-4 sm:p-6 text-center sm:text-left bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl">
|
||||
<div class="flex flex-col sm:flex-row gap-4 sm:gap-6">
|
||||
<div class="grow-0 flex justify-center items-center">
|
||||
@if(empty($mod->thumbnail))
|
||||
<img src="https://placehold.co/144x144/EEE/31343C?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="block dark:hidden w-36 rounded-lg">
|
||||
<img src="https://placehold.co/144x144/31343C/EEE?font=source-sans-pro&text={{ $mod->name }}" alt="{{ $mod->name }}" class="hidden dark:block w-36 rounded-lg">
|
||||
@else
|
||||
<img src="{{ $mod->thumbnail }}" alt="{{ $mod->name }}" class="w-36 rounded-lg">
|
||||
@endif
|
||||
<div>
|
||||
<div class="sm:hidden">
|
||||
<label for="tabs" class="sr-only">Select a tab</label>
|
||||
<!-- Use an "onChange" listener to redirect the user to the selected tab URL. -->
|
||||
<select id="tabs" name="tabs" class="block w-full rounded-md border-gray-300 focus:border-grey-500 focus:ring-grey-500">
|
||||
<option selected>Description</option>
|
||||
<option>Versions</option>
|
||||
<option>Comments</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="grow flex flex-col justify-center items-center sm:items-start text-gray-800 dark:text-gray-200">
|
||||
<h2 class="pb-1 sm:p-0 text-3xl font-bold text-gray-900 dark:text-white">
|
||||
{{ $mod->name }}
|
||||
<span class="font-light text-nowrap text-gray-700 dark:text-gray-400">
|
||||
{{ $mod->latestSptVersion->version }}
|
||||
</span>
|
||||
</h2>
|
||||
<p>{{ __('Created by') }} {{ $mod->user->name }}</p>
|
||||
<p>{{ $mod->latestSptVersion->sptVersion->version }} {{ __('Compatible') }}</p>
|
||||
<p>{{ $mod->total_downloads }} {{ __('Downloads') }}</p>
|
||||
<div class="hidden sm:block">
|
||||
<nav class="isolate flex divide-x divide-gray-200 dark:divide-gray-800 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl" aria-label="Tabs">
|
||||
<a href="#description" class="tab rounded-l-xl group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10" aria-current="page">
|
||||
<span>Description</span>
|
||||
<span aria-hidden="true" class="bg-gray-500 absolute inset-x-0 bottom-0 h-0.5"></span>
|
||||
</a>
|
||||
<a href="#versions" class="tab group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||
<span>Versions</span>
|
||||
<span aria-hidden="true" class="bg-transparent absolute inset-x-0 bottom-0 h-0.5"></span>
|
||||
</a>
|
||||
<a href="#comments" class="tab rounded-r-xl group relative min-w-0 flex-1 overflow-hidden py-4 px-4 text-center text-sm font-medium text-gray-900 dark:text-white bg-white dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-black dark:hover:text-white focus:z-10">
|
||||
<span>Comments</span>
|
||||
<span aria-hidden="true" class="bg-transparent absolute inset-x-0 bottom-0 h-0.5"></span>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="description" class="user-markdown p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl">
|
||||
{{-- The description below is safe to write directly because it has been run though HTMLPurifier during the import process. --}}
|
||||
<p>{!! Str::markdown($mod->description) !!}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-span-1 flex flex-col gap-6">
|
||||
<a href="{{ $mod->latestSptVersion->link }}" class="block">
|
||||
<button type="button" class="w-full">{{ __('Download Latest Version') }}</button>
|
||||
<button type="button" class="w-full">{{ __('Download Latest Version') }} ({{ $mod->latestSptVersion->version }})</button>
|
||||
</a>
|
||||
|
||||
<div class="p-4 sm:p-6 bg-white dark:bg-gray-950 rounded-xl shadow-md dark:shadow-gray-950 drop-shadow-2xl">
|
||||
@ -60,20 +94,26 @@
|
||||
</p>
|
||||
</li>
|
||||
@endif
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Latest VirusTotal Result') }}</h3>
|
||||
<p class="truncate">
|
||||
<a href="{{ $mod->latestSptVersion->virus_total_link }}" title="{{ $mod->latestSptVersion->virus_total_link }}" target="_blank">
|
||||
{{ $mod->latestSptVersion->virus_total_link }}
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Includes advertising?') }}</h3>
|
||||
</li>
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Includes AI generated content?') }}</h3>
|
||||
</li>
|
||||
@if($mod->latestSptVersion->virus_total_link)
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Latest VirusTotal Result') }}</h3>
|
||||
<p class="truncate">
|
||||
<a href="{{ $mod->latestSptVersion->virus_total_link }}" title="{{ $mod->latestSptVersion->virus_total_link }}" target="_blank">
|
||||
{{ $mod->latestSptVersion->virus_total_link }}
|
||||
</a>
|
||||
</p>
|
||||
</li>
|
||||
@endif
|
||||
@if($mod->contains_ads)
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Includes Advertising') }}</h3>
|
||||
</li>
|
||||
@endif
|
||||
@if($mod->contains_ai_content)
|
||||
<li class="px-4 py-4 sm:px-0">
|
||||
<h3>{{ __('Includes AI Generated Content') }}</h3>
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user