diff --git a/db/000_tables.sql b/db/000_tables.sql index 027ea89d..2115c794 100644 --- a/db/000_tables.sql +++ b/db/000_tables.sql @@ -261,8 +261,9 @@ CREATE TABLE IF NOT EXISTS `modReleases` ( `lastModified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`releaseId`), -- This has to include identifier, as one mod can contain releases for multiple identifier's. - -- This also has to include the modId, as tool/other mods dont need to have a identifier. - UNIQUE INDEX `identifier` (`modId`, `identifier`, `version`), + -- This also has to include the modId, as tool/other mods don't need to have a identifier. + UNIQUE INDEX `hybrid_identifier` (`modId`, `identifier`, `version`), + INDEX `identifier` (`identifier`), -- For search by identifier UNIQUE INDEX `assetid` (`assetId`), INDEX `modid` (`modId`), CONSTRAINT `FK_modReleases_assetId` FOREIGN KEY (`assetId`) REFERENCES `assets`(`assetId`) ON UPDATE CASCADE ON DELETE CASCADE, diff --git a/db/134_migrate.sql b/db/134_migrate.sql new file mode 100644 index 00000000..65d54274 --- /dev/null +++ b/db/134_migrate.sql @@ -0,0 +1,23 @@ +USE `moddb`; + +DELIMITER $$ + +CREATE OR REPLACE PROCEDURE upgrade_database__moderation() +BEGIN + +IF NOT EXISTS( (SELECT * FROM information_schema.STATISTICS WHERE TABLE_SCHEMA='moddb' AND + TABLE_NAME='fmodReleases' AND COLUMN_NAME='identifier' and INDEX_NAME='hybrid_identifier') ) THEN + + ALTER TABLE `modReleases` RENAME INDEX `identifier` TO `hybrid_identifier`; + ALTER TABLE `modReleases` ADD INDEX `identifier` (`identifier`); -- For search by identifier + +END IF; + + +END $$ + +CALL upgrade_database__moderation() $$ + +DELIMITER ; + + diff --git a/lib/core.php b/lib/core.php index e0a556b0..40c992be 100644 --- a/lib/core.php +++ b/lib/core.php @@ -294,7 +294,7 @@ function createADOConnection($config, $persistent = true) */ function forceRedirectAfterPOST() { - header('Location: '.$_SERVER['REQUEST_URI'], true, 303); + header('Location: '.$_SERVER['REQUEST_URI'], true, HTTP_FOUND); } /** This function forces a location header to the provided location, as well as status 303. @@ -305,7 +305,7 @@ function forceRedirectAfterPOST() function forceRedirect($url) { if(gettype($url) === 'array') $url = buildLocalUri($url); - header('Location: '.$url, true, 303); + header('Location: '.$url, true, HTTP_FOUND); } /** Constructs a uri from a parse_url result shaped array. @@ -836,7 +836,8 @@ function maybeFormatDownloadTrackingUrlDependingOnFileExt($file) } const HTTP_CREATED = 201; -const HTTP_FOUND = 302; +const HTTP_FOUND = 302; // temp redirect +const HTTP_SEE_OTHER = 303; // permanent redirect const HTTP_BAD_REQUEST = 400; const HTTP_UNAUTHORIZED = 401; const HTTP_FORBIDDEN = 403; diff --git a/list-mod.php b/list-mod.php index c50dc60f..1da22b71 100644 --- a/list-mod.php +++ b/list-mod.php @@ -2,6 +2,25 @@ include($config["basepath"] . "lib/search-mods.php"); +// Mod ID search: identifier uniquely identifies a mod, so redirect directly to its page. +if(!empty($_GET['i'])) { + $modid = trim($_GET['text']); + $retractionFilter = canModerate(null, $user) ? '' : 'AND mrr.releaseId IS NULL'; + $mod = $con->getRow(" + SELECT m.urlAlias, m.assetId + FROM modReleases mr + JOIN mods m ON m.modId = mr.modId + LEFT JOIN modReleaseRetractions mrr ON mrr.releaseId = mr.releaseId + WHERE mr.identifier = ? + $retractionFilter + LIMIT 1 + ", [$modid]); + + if($mod) exit(forceRedirect(formatModPath($mod))); + else addMessage(MSG_CLASS_WARN, "No mod found with identifier '$modid'.", true); + // if we failed to find the mod directly we drop into the normal search from here +} + if(isset($_GET['paging'])) { if($paramError = validateModSearchInputs($searchParams, true)) { http_response_code(HTTP_BAD_REQUEST); @@ -40,6 +59,7 @@ 'gameversions' => !empty($filters['gameversions']) ? array_flip($filters['gameversions']) : [], 'tags' => [], 'stati' => !empty($filters['stati']) ? array_flip($filters['stati']) : [ STATUS_RELEASED => true, STATUS_LOCKED => true ], + 'searchId' => !empty($_GET['i']), ]; if(!empty($filters['tags'])) { diff --git a/templates/list-mod.tpl b/templates/list-mod.tpl index 3624491f..88b36ff1 100644 --- a/templates/list-mod.tpl +++ b/templates/list-mod.tpl @@ -8,12 +8,20 @@ - + {if $selectedParams['searchId']} + + {else} + + {/if} + + + + - @@ -22,7 +30,7 @@ - {foreach from=$selectedParams['tags'] item=tag} {/foreach} @@ -30,14 +38,14 @@ - {if !empty($selectedParams['contributor'])}{/if} - {foreach from=$majorGameVersions item=version} @@ -46,7 +54,7 @@ - {foreach from=$gameVersions item=version} {/foreach} @@ -55,7 +63,7 @@ {if $selectedParams['category'] !== 's'} - @@ -67,7 +75,7 @@ {/if} - @@ -77,7 +85,7 @@ {if canModerate(null, $user)} - @@ -156,6 +164,20 @@ attachRemoteSearchHandler(document.getElementById('tags-box')); }); + + const searchBoxEl = document.getElementById('search-box'); + const otherFilterEls = document.querySelectorAll('select[name="side"], #tags-box select, #contributor-box select, select[name="c"], select[name="t"], select[name="mv"], select[name="gv[]"], select[name="stati[]"]'); + const modidToggleEl = document.querySelector('input[name="i"]'); + modidToggleEl.addEventListener('change', function(e) \{ + const active = e.target.checked; + searchBoxEl.dataset.label = active ? 'Exact ModID' : 'Text'; + searchBoxEl.title = active ? 'Searches for a specific ModID.' : 'Searches mod names, summaries and descriptions.' + for(const el of otherFilterEls) \{ + el.disabled = active; + $(el).trigger('chosen:updated'); + } + }); + let fetchCursor = '{$fetchCursorJS}'; const scrollTrigger = document.getElementById('scroll-trigger'); if(scrollTrigger) {