Skip to content
Merged

3.6.5 #585

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
6c5eeae
Update Crowdin configuration file
Luffyyy Oct 24, 2025
91b965f
Update Crowdin configuration file
Luffyyy Oct 24, 2025
956e3bc
New Crowdin updates (#563)
Luffyyy Oct 24, 2025
8eadeaf
Add Crowdin badge to README
Luffyyy Oct 24, 2025
14eab8c
(frontend) Clarify dependencies (#565)
KashEight Oct 26, 2025
5b59151
New Crowdin updates (#564)
Luffyyy Oct 27, 2025
fa61277
New Crowdin updates (#569)
Luffyyy Nov 4, 2025
8d8640e
New Crowdin updates (#572)
Luffyyy Nov 14, 2025
e8e1f64
Bump laravel/framework from 12.32.5 to 12.35.1 in /backend (#566)
dependabot[bot] Nov 14, 2025
8f893ea
Bump symfony/http-foundation from 7.3.4 to 7.3.7 in /backend (#574)
dependabot[bot] Nov 14, 2025
54840ef
Bump @nuxt/devtools from 2.6.2 to 2.6.4 in /frontend (#573)
dependabot[bot] Nov 14, 2025
7077e1d
Bump js-yaml from 4.1.0 to 4.1.1 in /frontend (#575)
dependabot[bot] Nov 14, 2025
a5263a4
Bump koa from 2.16.2 to 2.16.3 in /frontend (#560)
dependabot[bot] Nov 14, 2025
c072567
Bump vite from 7.1.5 to 7.1.11 in /frontend (#559)
dependabot[bot] Nov 14, 2025
1712dd5
Fix french causing error
Luffyyy Nov 14, 2025
77c9c22
Fix image uploading not working when uploading 20 files
Luffyyy Nov 14, 2025
44ab00b
Fix announcement dismiss button not properly stopping propagation
Luffyyy Nov 14, 2025
3d5e95f
Delete audit logs when user deletes them instead of nullifying
Luffyyy Nov 14, 2025
fb18482
Click on notifications should close notifications modal + ctrl to not
Luffyyy Nov 14, 2025
82cc49d
Fix email password reset being case sensitive
Luffyyy Nov 15, 2025
67a1923
Prevent positive integer unique names
Luffyyy Nov 15, 2025
9e8de7d
New Crowdin updates (#577)
Luffyyy Nov 29, 2025
6565efc
Allow hiding tags in games
Luffyyy Dec 1, 2025
03f0123
Fix game threads embed
Luffyyy Dec 1, 2025
76811b5
Fix clicking on "x" in selects not preventing click on the select
Luffyyy Dec 1, 2025
eb75414
Fix unable to save changes on modified tags if discarded once
Luffyyy Dec 1, 2025
717cb8e
Added use system date format for formatting full dates
Luffyyy Dec 1, 2025
45d8e79
Small adjustment for thread page
Luffyyy Dec 1, 2025
444caef
Add plunk email service
Luffyyy Dec 1, 2025
b8d8170
Version bump
Luffyyy Dec 1, 2025
03ea2af
Bump node-forge from 1.3.1 to 1.3.2 in /frontend (#582)
dependabot[bot] Dec 1, 2025
d69e6be
Bump fast-equals from 5.2.2 to 5.3.2 in /frontend (#547)
dependabot[bot] Dec 1, 2025
787c028
Bump league/flysystem-aws-s3-v3 from 3.29.0 to 3.30.1 in /backend (#570)
dependabot[bot] Dec 1, 2025
a841222
New Crowdin updates (#583)
Luffyyy Dec 1, 2025
d473529
Bump vite from 7.1.5 to 7.2.6 in /frontend (#584)
dependabot[bot] Dec 1, 2025
affd90e
Upgrade Nuxt
Luffyyy Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<p align="center"><a href="https://modworkshop.net" target="_blank"><img src="https://modworkshop.net/assets/mws_logo_white.svg" width="200"></a></p>

# ModWorkshop Monorepo

[![Crowdin](https://badges.crowdin.net/modworkshop/localized.svg)](https://crowdin.com/project/modworkshop)

This repository holds all of the code for modworkshop + docker compose + env files.

## Installing
Expand All @@ -22,4 +25,4 @@ On Windows you'll need WSL2.

## Security
If you discover a security vulnerability, please submit a [private vulnerability report](https://github.com/ModWorkshop/site/security/advisories/new).
Alternatively, you can email me directly at luffy@modworkshop.net.
Alternatively, you can email me directly at luffy@modworkshop.net.
26 changes: 24 additions & 2 deletions backend/app/Http/Controllers/GameController.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Models\Game;
use App\Models\AuditLog;
use App\Models\ModManager;
use App\Models\Tag;
use App\Models\User;
use App\Services\APIService;
use App\Services\ModService;
Expand Down Expand Up @@ -49,6 +50,8 @@ public function update(Request $request, Game $game=null)
'webhook_url' => 'string|nullable|max:1000',
'mod_manager_ids' => 'array|nullable',
'mod_manager_ids.*' => 'integer|min:1|exists:mod_managers,id|nullable',
'hidden_tag_ids' => 'array|nullable',
'hidden_tag_ids.*' => 'integer|min:1|exists:tags,id|nullable',
'default_mod_manager_id' => 'exists:mod_managers,id|nullable'
];

Expand All @@ -58,11 +61,13 @@ public function update(Request $request, Game $game=null)

$val = $request->validate($validateArr);

APIService::nullToEmptyArr($val, 'mod_manager_ids', 'hidden_tag_ids');
APIService::nullToEmptyStr($val, 'webhook_url', 'buttons');

$thumbnailFile = Arr::pull($val, 'thumbnail_file');
$bannerFile = Arr::pull($val, 'banner_file');
$modManagerIds = Arr::pull($val, 'mod_manager_ids');
$hiddenTagIds = Arr::pull($val, 'hidden_tag_ids');

$wasCreated = false;
if (!isset($game)) {
Expand All @@ -83,14 +88,27 @@ public function update(Request $request, Game $game=null)
$modManagerIds = [];
foreach ($modManagers as $manager) {
if (!empty($manager->game_id)) {
abort(406, 'You cannot add mod managers that a game owns.');
abort(406, 'You cannot add mod managers that any game owns.');
}

$modManagerIds[] = $manager->id;
}
$game->modManagers()->sync($modManagerIds);
}

if (isset($hiddenTagIds)) {
$tags = Tag::whereIn('id', $hiddenTagIds)->get();
$hiddenTagIds = [];
foreach ($tags as $tag) {
if (!empty($tag->game_id)) {
abort(406, 'You cannot hide tags that any game owns.');
}

$hiddenTagIds[] = $tag->id;
}
$game->hiddenTags()->sync($hiddenTagIds);
}

APIService::storeImage($thumbnailFile, 'games/images', $game->thumbnail, [
'thumbnailSize' => 200,
'onSuccess' => fn($path) => $game->thumbnail = $path,
Expand All @@ -109,7 +127,10 @@ public function update(Request $request, Game $game=null)
$game->update($val);
}

return $game;
$game->loadMissing('modManagers');
$game->loadMissing('hiddenTags');

return new GameResource($game);
}

/**
Expand Down Expand Up @@ -179,6 +200,7 @@ public function show(Game $game)

$game->loadCount('viewableMods');
$game->loadMissing('modManagers');
$game->loadMissing('hiddenTags');

if (Auth::hasUser()) {
$game->loadMissing('followed');
Expand Down
7 changes: 4 additions & 3 deletions backend/app/Http/Controllers/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function register(Request $request)
{
$val = $request->validate([
'name' => ['required'],
'unique_name' => 'alpha_dash:ascii|nullable|min:3|max:50',
'unique_name' => ['alpha_dash:ascii', 'not_regex:/^\d+$/', 'nullable', 'min:3', 'max:50'],
'email' => ['required', 'email', new \nickurt\StopForumSpam\Rules\IsSpamEmail(2)],
'password' => ['required', APIService::getPasswordRule(), 'max:128'],
'avatar_file' => 'nullable|max:512000|mimes:png,webp,avif,gif,jpg',
Expand Down Expand Up @@ -290,8 +290,9 @@ public function validateProvider(string $provider)
*/
public function forgotPassword(Request $request)
{
$request->validate(['email' => 'required|email|max:255']);
Password::sendResetLink($request->only('email'));
$val = $request->validate(['email' => 'required|email|max:255']);
$email = [Str::lower($val['email'])];
Password::sendResetLink($email);
# Is there a need to handle errors?
}

Expand Down
11 changes: 10 additions & 1 deletion backend/app/Http/Controllers/ModController.php
Original file line number Diff line number Diff line change
Expand Up @@ -288,8 +288,17 @@ public function update(ModUpsertRequest $request, Game $game=null, Mod $mod=null
$mod = Mod::create($val);
}

$tagsHiddenByGame = $mod->game->hiddenTags;
$filteredTags = [];
foreach ($tags as $tag) {
\Log::info('exists?', ['tag' => $tag, 'check' => $tagsHiddenByGame->where('id', $tag)->first()]);
if (!$tagsHiddenByGame->where('id', $tag)->first()) {
$filteredTags[] = $tag;
}
}

if(isset($tags)) {
$mod->tags()->sync($tags);
$mod->tags()->sync($filteredTags);
}

$currentTags = $mod->tags;
Expand Down
6 changes: 6 additions & 0 deletions backend/app/Http/Controllers/TagController.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public function index(FilteredRequest $request, Game $game=null)
$val = $request->val([
'game_id' => 'integer|min:1|nullable|exists:games,id',
'type' => 'string|in:mod,forum',
'show_hidden' => 'boolean|nullable',
'global' => 'boolean|nullable'
]);

Expand All @@ -39,6 +40,11 @@ public function index(FilteredRequest $request, Game $game=null)
}
if (isset($val['global']) && $val['global']) {
$q->orWhereNull('game_id');
if (isset($gameId) && !($val['show_hidden'] ?? false)) {
$q->whereDoesntHaveIn('gamesHiding', function($q) use($gameId) { // Filters out tags that are hidden by the game
$q->where('game_hidden_tags.game_id', $gameId);
});
}
}
if (isset($val['type'])) {
$q->where(fn($q) => $q->where('type', $val['type'])->orWhere('type', '')->orWhereNull('type'));
Expand Down
4 changes: 2 additions & 2 deletions backend/app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public function update(Request $request, User $user)

$valRules =[
'name' => 'string|nullable|min:3|max:30',
'unique_name' => 'alpha_dash:ascii|nullable|min:3|max:50',
'unique_name' => ['alpha_dash:ascii', 'not_regex:/^\d+$/', 'nullable', 'min:3', 'max:50'],
'avatar_file' => ['nullable', 'is_image'],
'custom_color' => 'string|max:7|nullable',
'bio' => 'string|spam_check|nullable|max:3000',
Expand Down Expand Up @@ -344,7 +344,7 @@ public function setUserRoles(Request $request, User $user) {
public function destroy(Request $request, User $user)
{
$val = $request->validate([
'unique_name' => 'alpha_dash:ascii|required|min:3|max:50',
'unique_name' => ['alpha_dash:ascii', 'not_regex:/^\d+$/', 'nullable', 'min:3', 'max:50'],
'are_you_sure' => 'required|boolean',
]);

Expand Down
1 change: 1 addition & 0 deletions backend/app/Http/Resources/GameResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public function toArray($request)
'announcements' => $this->when($isCurrent, fn() => $this->announcements),
'mods_count' => $this->whenCounted('viewableMods'),
'mod_manager_ids' => $this->whenLoaded('modManagers', fn () => Arr::pluck($this->modManagers, 'id')),
'hidden_tag_ids' => $this->whenLoaded('hiddenTags', fn () => Arr::pluck($this->hiddenTags, 'id')),
];
}
}
4 changes: 4 additions & 0 deletions backend/app/Models/Game.php
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@ public function modManagers() {
return $this->belongsToMany(ModManager::class)->without('game');
}

public function hiddenTags() {
return $this->belongsToMany(Tag::class, 'game_hidden_tags')->without('game');
}

/**
* Returns whether the game is followed by the authenticated user
*/
Expand Down
22 changes: 22 additions & 0 deletions backend/app/Models/GameHiddenTag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class GameHiddenTag extends Model
{
public function game(): BelongsTo
{
return $this->belongsTo(Game::class);
}

public function games() {
return $this->belongsToMany(Game::class);
}

public function getMorphClass(): string {
return 'game_hidden_tag';
}
}
4 changes: 4 additions & 0 deletions backend/app/Models/Tag.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ public function mods() {
return $this->morphedByMany(Mod::class, 'taggable');
}

public function gamesHiding() {
return $this->belongsToMany(Tag::class, 'game_hidden_tags')->without('game');
}

public function threads() {
return $this->morphedByMany(Thread::class, 'taggable');
}
Expand Down
2 changes: 1 addition & 1 deletion backend/app/Policies/ImagePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public function view(?User $user, Image $image)
*/
public function create(User $user, Mod $mod)
{
if ($mod->files()->count() >= 25) {
if ($mod->images()->count() >= 20) {
return false;
}

Expand Down
1 change: 1 addition & 0 deletions backend/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"lcobucci/jwt": "4.3.0",
"league/flysystem-aws-s3-v3": "^3.10",
"mailersend/laravel-driver": "^2.7",
"marceloeatworld/plunk-laravel": "^0.1.1",
"nickurt/laravel-stopforumspam": "^2.0",
"sentry/sentry-laravel": "^4.1",
"socialiteproviders/discord": "^4.1",
Expand Down
Loading
Loading