diff --git a/mod-picker/app/assets/javascripts/BackendAPI/modService.js b/mod-picker/app/assets/javascripts/BackendAPI/modService.js index 5129a6cb3..3b1846c5e 100644 --- a/mod-picker/app/assets/javascripts/BackendAPI/modService.js +++ b/mod-picker/app/assets/javascripts/BackendAPI/modService.js @@ -302,7 +302,7 @@ app.service('modService', function(backend, $q, pageUtils, objectUtils, contribu this.sanitizeModOption = function(option) { var sanitizedPlugins = service.sanitizePlugins(option.plugins); - return { + var sanitizedOption = { id: option.id, _destroy: option._destroy, name: option.name, @@ -314,6 +314,10 @@ app.service('modService', function(backend, $q, pageUtils, objectUtils, contribu plugin_dumps: sanitizedPlugins, asset_paths: option.assets }; + if (option.download_link) { + sanitizedOption.download_link = option.download_link; + } + return sanitizedOption; }; this.buildDestroyedOldPlugins = function(newOption, mod) { diff --git a/mod-picker/app/assets/javascripts/BackendAPI/userService.js b/mod-picker/app/assets/javascripts/BackendAPI/userService.js index 1bc1c0e41..865e8149f 100644 --- a/mod-picker/app/assets/javascripts/BackendAPI/userService.js +++ b/mod-picker/app/assets/javascripts/BackendAPI/userService.js @@ -57,6 +57,7 @@ app.service('userService', function(backend, $q, userSettingsService, userTitleS permissions.isModerator = user.role === 'moderator'; permissions.isNewsWriter = user.role === 'writer'; permissions.isHelper = user.role === 'helper'; + permissions.isBetaTester = user.role === 'beta_tester'; permissions.isRestricted = user.role === 'restricted'; permissions.isBanned = user.role === 'banned'; permissions.isSignedIn = true; @@ -65,7 +66,8 @@ app.service('userService', function(backend, $q, userSettingsService, userTitleS permissions.canSubmitMods = !permissions.isRestricted; permissions.canContribute = !permissions.isRestricted; permissions.canManageMods = permissions.canModerate || permissions.isHelper; - permissions.canUseCustomSources = permissions.canModerate; + permissions.canDownloadModList = permissions.canModerate || permissions.isHelper || permissions.isBetaTester; + permissions.canUseCustomSources = true; permissions.canSetModMetadata = permissions.canModerate; permissions.canChangeAvatar = (rep >= 10) || permissions.canModerate; permissions.canChangeTitle = (rep >= 1280) || permissions.canModerate; diff --git a/mod-picker/app/assets/javascripts/Directives/editMod/modAnalysisManager.js b/mod-picker/app/assets/javascripts/Directives/editMod/modAnalysisManager.js index 4a59de8cd..04a2d7fc3 100644 --- a/mod-picker/app/assets/javascripts/Directives/editMod/modAnalysisManager.js +++ b/mod-picker/app/assets/javascripts/Directives/editMod/modAnalysisManager.js @@ -7,15 +7,25 @@ app.directive('modAnalysisManager', function() { } }); -app.controller('modAnalysisManagerController', function($scope, $rootScope, pluginService, objectUtils, assetUtils) { +app.controller('modAnalysisManagerController', function($scope, $rootScope, $timeout, pluginService, assetUtils, objectUtils, modOptionUtils) { // inherited variables $scope.currentGame = $rootScope.currentGame; + // set up old nested mod options + $scope.$watch('mod.mod_options', function() { + if (!$scope.mod.mod_options) return; + $scope.oldNestedOptions = modOptionUtils.getNestedModOptions($scope.mod.mod_options); + }); + $scope.changeAnalysisFile = function(event) { var input = event.target; if (input.files && input.files[0]) { - $scope.loadAnalysisFile(input.files[0]); - input.value = ""; + $scope.loadingAnalysis = true; + $scope.$digest(); + $timeout(function() { + $scope.loadAnalysisFile(input.files[0]); + input.value = ""; + }, 10); } }; @@ -26,6 +36,7 @@ app.controller('modAnalysisManagerController', function($scope, $rootScope, plug $scope.clearAnalysis = function() { if ($scope.mod.analysis) { delete $scope.mod.analysis; + delete $scope.nestedOptions; } else { $scope.mod.mod_options.forEach(function(modOption) { modOption._destroy = true; @@ -141,7 +152,9 @@ app.controller('modAnalysisManagerController', function($scope, $rootScope, plug $scope.prepareModOption = function(option) { $scope.getBaseName(option); option.display_name = angular.copy(option.base_name); + option.plugins_count = option.plugins.length; if (option.assets && option.assets.length) { + option.asset_files_count = option.assets.length; option.nestedAssets = assetUtils.getNestedAssets(option.assets); } $scope.loadExistingOption(option); @@ -161,6 +174,8 @@ app.controller('modAnalysisManagerController', function($scope, $rootScope, plug analysis.mod_options.forEach($scope.prepareModOption); $scope.$applyAsync(function() { $scope.mod.analysis = analysis; + $scope.nestedOptions = modOptionUtils.getNestedModOptions(analysis.mod_options); + $scope.loadingAnalysis = false; $scope.getRequirementsFromAnalysis(); }); }; diff --git a/mod-picker/app/assets/javascripts/Directives/editMod/modOption.js b/mod-picker/app/assets/javascripts/Directives/editMod/modOption.js index e4ceb5d84..20894e73f 100644 --- a/mod-picker/app/assets/javascripts/Directives/editMod/modOption.js +++ b/mod-picker/app/assets/javascripts/Directives/editMod/modOption.js @@ -4,7 +4,8 @@ app.directive('modOption', function() { templateUrl: '/resources/directives/editMod/modOption.html', scope: { option: '=', - oldOptions: '=' + oldOptions: '=', + canSetDownloadLinks: '=?' }, controller: 'modOptionController' } @@ -13,6 +14,18 @@ app.directive('modOption', function() { app.controller('modOptionController', function($scope, formUtils) { $scope.focusText = formUtils.focusText; + $scope.toggleExpansion = function() { + $scope.option.expanded = !$scope.option.expanded; + }; + + $scope.togglePluginsExpansion = function() { + $scope.option.pluginsExpanded = !$scope.option.pluginsExpanded; + }; + + $scope.toggleAssetsExpansion = function() { + $scope.option.assetsExpanded = !$scope.option.assetsExpanded; + }; + $scope.removeModOption = function() { if ($scope.option.id) { $scope.option._destroy = true; diff --git a/mod-picker/app/assets/javascripts/Directives/editMod/modSources.js b/mod-picker/app/assets/javascripts/Directives/editMod/modSources.js index 213acf72e..5e83b7628 100644 --- a/mod-picker/app/assets/javascripts/Directives/editMod/modSources.js +++ b/mod-picker/app/assets/javascripts/Directives/editMod/modSources.js @@ -27,7 +27,8 @@ app.controller('modSourcesController', function($scope, $rootScope, sitesFactory var sourceUsed = $scope.mod.sources.find(function(item, index) { return index != sourceIndex && item.label === source.label }); - var urlFormat = sitesFactory.getModUrlFormat(site, $rootScope.currentGame); + var currentGameName = $rootScope.currentGame.nexus_name; + var urlFormat = sitesFactory.getModUrlFormat(site, currentGameName); var match = source.url.match(urlFormat); source.valid = !sourceUsed && match != null; }; @@ -47,7 +48,8 @@ app.controller('modSourcesController', function($scope, $rootScope, sitesFactory $scope.scrapeSource = function(source) { // exit if the source is invalid var site = sitesFactory.getSite(source.label); - var urlFormat = sitesFactory.getModUrlFormat(site, $rootScope.currentGame); + var currentGameName = $rootScope.currentGame.nexus_name; + var urlFormat = sitesFactory.getModUrlFormat(site, currentGameName); var match = source.url.match(urlFormat); if (!match) { return; @@ -108,7 +110,17 @@ app.controller('modSourcesController', function($scope, $rootScope, sitesFactory } }; + $scope.setCustomSourceLabel = function(source) { + var currentGameName = $rootScope.currentGame.nexus_name; + var matchingSite = sitesFactory.getSiteFromUrl(source.url, currentGameName); + if (matchingSite) { + source.label = matchingSite.label; + } + }; + $scope.validateCustomSource = function(source) { - source.valid = (source.label.length > 3) && (source.url.length > 12); + source.labelToShort = source.label.length < 4; + source.isDirectLink = source.url.match(/\.(7z|rar|zip)$/i); + source.valid = !source.labelToShort && !source.isDirectLink; }; }); \ No newline at end of file diff --git a/mod-picker/app/assets/javascripts/Directives/shared/modOptionTree.js b/mod-picker/app/assets/javascripts/Directives/showMod/modOptionTree.js similarity index 90% rename from mod-picker/app/assets/javascripts/Directives/shared/modOptionTree.js rename to mod-picker/app/assets/javascripts/Directives/showMod/modOptionTree.js index cdf2b426b..2d657b91f 100644 --- a/mod-picker/app/assets/javascripts/Directives/shared/modOptionTree.js +++ b/mod-picker/app/assets/javascripts/Directives/showMod/modOptionTree.js @@ -1,7 +1,7 @@ app.directive('modOptionTree', function() { return { restrict: 'E', - templateUrl: '/resources/directives/shared/modOptionTree.html', + templateUrl: '/resources/directives/showMod/modOptionTree.html', scope: { modOptions: '=' }, diff --git a/mod-picker/app/assets/javascripts/Factories/columnsFactory.js b/mod-picker/app/assets/javascripts/Factories/columnsFactory.js index 0403937d9..247c685a7 100644 --- a/mod-picker/app/assets/javascripts/Factories/columnsFactory.js +++ b/mod-picker/app/assets/javascripts/Factories/columnsFactory.js @@ -793,6 +793,7 @@ app.service('columnsFactory', function() { data: function(item) { return item.tools_count + item.custom_tools_count; }, + sortData: "tools_count", filter: "number" }, { @@ -802,6 +803,7 @@ app.service('columnsFactory', function() { data: function(item) { return item.mods_count + item.custom_mods_count; }, + sortData: "mods_count", filter: "number" }, { @@ -811,6 +813,7 @@ app.service('columnsFactory', function() { data: function(item) { return item.plugins_count + item.custom_plugins_count; }, + sortData: "plugins_count", filter: "number" }, { diff --git a/mod-picker/app/assets/javascripts/Factories/sitesFactory.js b/mod-picker/app/assets/javascripts/Factories/sitesFactory.js index 51d3974d0..50974e4be 100644 --- a/mod-picker/app/assets/javascripts/Factories/sitesFactory.js +++ b/mod-picker/app/assets/javascripts/Factories/sitesFactory.js @@ -4,58 +4,109 @@ // userIndex is the regex group to capture to get the user's username/id app.service('sitesFactory', function() { - this.sites = function() { - return [ - { - label: "Nexus Mods", - shortLabel: "Nexus", - dataLabel: "nexus", - includeGame: true, - modUrlFormat: "www\.nexusmods\.com\/\[game\]\/mods\/([0-9]+)(\/\?)?", - baseUserUrlFormat: "https://forums.nexusmods.com/index.php?showuser=", - userUrlFormat: /forums\.nexusmods\.com\/index\.php\?showuser=([0-9]+)(\/)?/i, - badUserUrlFormat: /forums\.nexusmods\.com\/index\.php\?\/user\/([A-Za-z0-9\-]+)(\/)?/i, - modUrlBase: "https://www.nexusmods.com/{game}/mods/{id}", - userIndex: 2, - loginUrl: "https://forums.nexusmods.com/", - logoPath: "/images/nexus_logo.png" - }, - { - label: "Steam Workshop", - shortLabel: "Steam", - dataLabel: "workshop", - modUrlFormat: /steamcommunity\.com\/sharedfiles\/filedetails\/\?id=([0-9]+)(\&)?.*/i, - userUrlFormat: /steamcommunity\.com\/(id|profiles)\/([A-Za-z0-9\_]+)(\/)?/i, - modUrlBase: "https://steamcommunity.com/sharedfiles/filedetails/?id={id}", - userIndex: 3, - loginUrl: "https://steamcommunity.com/login/", - logoPath: "/images/workshop_logo.png" - }, - { - label: "Lover's Lab", - shortLabel: "Lab", - dataLabel: "lab", - modUrlFormat: /www\.loverslab\.com\/files\/file\/([0-9]+)\-([0-9a-z\-]+)(\/)?/i, - userUrlFormat: /www\.loverslab\.com\/user\/([a-zA-Z0-9\-]+)(\/)?/i, - modUrlBase: "https://www.loverslab.com/files/file/{id}", - userIndex: 2, - loginUrl: "https://www.loverslab.com/", - logoPath: "/images/lab_logo.png" - }, - { - hidden: true, - label: "Steam Store", - shortLabel: "Steam", - logoPath: "/images/workshop_logo.png" - }, - { - hidden: true, - label: "GitHub", - shortLabel: "GitHub", - logoPath: "/images/github_logo.png" - } - ]; - }; + var factory = this; + + this.sites = [ + { + label: "Nexus Mods", + shortLabel: "Nexus", + dataLabel: "nexus", + includeGame: true, + modUrlFormat: "www\.nexusmods\.com\/\[game\]\/mods\/([0-9]+)(\/\?)?", + baseUserUrlFormat: "https://forums.nexusmods.com/index.php?showuser=", + userUrlFormat: /forums\.nexusmods\.com\/index\.php\?showuser=([0-9]+)(\/)?/i, + badUserUrlFormat: /forums\.nexusmods\.com\/index\.php\?\/user\/([A-Za-z0-9\-]+)(\/)?/i, + modUrlBase: "https://www.nexusmods.com/{game}/mods/{id}", + userIndex: 1, + loginUrl: "https://forums.nexusmods.com/", + logoPath: "/images/nexus_logo.png" + }, + { + label: "Steam Workshop", + shortLabel: "Steam", + dataLabel: "workshop", + modUrlFormat: /steamcommunity\.com\/sharedfiles\/filedetails\/\?id=([0-9]+)(\&)?.*/i, + userUrlFormat: /steamcommunity\.com\/(id|profiles)\/([A-Za-z0-9\_]+)(\/)?/i, + modUrlBase: "https://steamcommunity.com/sharedfiles/filedetails/?id={id}", + userIndex: 2, + loginUrl: "https://steamcommunity.com/login/", + logoPath: "/images/workshop_logo.png" + }, + { + label: "Lover's Lab", + shortLabel: "Lab", + dataLabel: "lab", + modUrlFormat: /www\.loverslab\.com\/files\/file\/([0-9]+)\-([0-9a-z\-]+)(\/)?/i, + userUrlFormat: /www\.loverslab\.com\/user\/([a-zA-Z0-9\-]+)(\/)?/i, + modUrlBase: "https://www.loverslab.com/files/file/{id}", + userIndex: 1, + loginUrl: "https://www.loverslab.com/", + logoPath: "/images/lab_logo.png" + }, + { + hidden: true, + label: "Steam Store", + shortLabel: "Steam", + modUrlFormat: /store\.steampowered\.com\/app\/([0-9]+)/i, + logoPath: "/images/workshop_logo.png" + }, + { + hidden: true, + label: "GitHub", + shortLabel: "GitHub", + modUrlFormat: /github.com\/([a-zA-Z0-9]+)\/([a-zA-Z0-9]+)/i, + logoPath: "/images/github_logo.png" + }, + { + hidden: true, + label: "Tumblr", + shortLabel: "Tumblr", + modUrlFormat: /([a-zA-Z0-9\-\_]+)\.tumblr\.com\/post\/([0-9]+)/i, + logoPath: "/images/tumblr_logo.png" + }, + { + hidden: true, + label: "Blogspot", + shortLabel: "Blogspot", + modUrlFormat: /([a-zA-Z0-9\-\_]+)\.blogspot\.(com|ca)\/([0-9]+)\/([0-9]+)\/([a-zA-Z\-]+)/i, + logoPath: "/images/blogger_logo.png" + }, + { + hidden: true, + label: "Imgur", + shortLabel: "Imgur", + modUrlFormat: /imgur\.com\/([0-9a-zA-Z]+)/i, + logoPath: "/images/imgur_logo.png" + }, + { + hidden: true, + label: "Google Site", + shortLabel: "Google Site", + modUrlFormat: /sites\.google\.com\/site\/([0-9a-zA-Z\-]+)/i, + logoPath: "/images/google_logo.png" + }, + { + hidden: true, + label: "Skyrim Modtype", + shortLabel: "Skyrim Modtype", + modUrlFormat: /modtype\.doorblog\.jp\/archives\/([0-9]+)\.html/i, + logoPath: "/images/skyrim_modtype_logo.png" + }, + { + hidden: true, + label: "ModDB", + shortLabel: "ModDB", + modUrlFormat: /moddb\.com\/mods\/([a-zA-Z\-]+)/i, + logoPath: "/images/moddb_logo.png" + }, + { + hidden: true, + label: "Curse", + shortLabel: "Curse", + modUrlFormat: /mods\.curse\.com\/mods\/([a-zA-Z0-9\-]+)\/([a-zA-Z0-9\-]+)/i, + logoPath: "/images/curse_logo.png" + } + ]; this.customSite = function() { return { @@ -66,20 +117,26 @@ app.service('sitesFactory', function() { }; this.getSite = function(label) { - var sites = this.sites(); - return sites.find(function(site) { + return factory.sites.find(function(site) { return site.label === label; }) || this.customSite(); }; + this.getSiteFromUrl = function(url, gameName) { + return factory.sites.find(function(site) { + var urlFormat = factory.getModUrlFormat(site, gameName); + return url.match(urlFormat); + }); + }; + this.getModUrl = function(label, id, gameName) { - var site = this.getSite(label); + var site = factory.getSite(label); return site.modUrlBase.replace("{id}", id).replace("{game}", gameName); }; - this.getModUrlFormat = function(site, game) { + this.getModUrlFormat = function(site, gameName) { if (site.dataLabel === "nexus") { - return new RegExp(site.modUrlFormat.replace("[game]", game.nexus_name), "i"); + return new RegExp(site.modUrlFormat.replace("[game]", gameName), "i"); } else { return site.modUrlFormat; } diff --git a/mod-picker/app/assets/javascripts/Filters/bytesFilter.js b/mod-picker/app/assets/javascripts/Filters/bytesFilter.js index 6c9c2568d..93a50ad95 100644 --- a/mod-picker/app/assets/javascripts/Filters/bytesFilter.js +++ b/mod-picker/app/assets/javascripts/Filters/bytesFilter.js @@ -1,7 +1,8 @@ app.filter('bytes', function() { return function(number, precision) { if (typeof number === "string") number = parseInt(number); - if (isNaN(parseFloat(number)) || !isFinite(number) || number == 0) return '-'; + if (isNaN(parseFloat(number)) || !isFinite(number)) return '-'; + if (number == 0) return '0 bytes'; return number.toBytes(precision); } }); \ No newline at end of file diff --git a/mod-picker/app/assets/javascripts/Services/ObjectUtils.js b/mod-picker/app/assets/javascripts/Services/ObjectUtils.js index 44a7d784f..43ea1baf2 100644 --- a/mod-picker/app/assets/javascripts/Services/ObjectUtils.js +++ b/mod-picker/app/assets/javascripts/Services/ObjectUtils.js @@ -76,6 +76,16 @@ app.service('objectUtils', function() { return newValue; } } + // handle dates + else if (oldValue.constructor === Date || newValue.constructor === Date) { + if (oldValue.constructor === newValue.constructor) { + if (oldValue.getTime() !== newValue.getTime()) { + return newValue; + } + } else { + return newValue; + } + } // else handle objects else { return service.getDifferentObjectValues(oldValue, newValue); @@ -134,7 +144,7 @@ app.service('objectUtils', function() { diff = service.getDifferentValues(oldObj[property], newObj[property]); // if there are differences, we save them if (typeof diff !== 'undefined' && (typeof diff !== 'object' || - diff == null || !service.isEmptyObject(diff))) { + diff == null || diff.constructor === Date || !service.isEmptyObject(diff))) { foundDifferences = true; result[property] = diff; } diff --git a/mod-picker/app/assets/javascripts/Services/modValidationService.js b/mod-picker/app/assets/javascripts/Services/modValidationService.js index 42e3dfcef..3c899d77c 100644 --- a/mod-picker/app/assets/javascripts/Services/modValidationService.js +++ b/mod-picker/app/assets/javascripts/Services/modValidationService.js @@ -15,22 +15,20 @@ app.service('modValidationService', function() { customSources.forEach(function(source) { sourcesValid = sourcesValid && source.valid; }); - // if we are only submitting custom sources, we need to verify - // we have all general info - if (!mod.sources.length) { - sourcesValid = sourcesValid && mod.name && mod.authors && - mod.released; - } } else { // if we don't have any custom sources we should verify we have // the scraped data for at least one official source - sourcesValid = sourcesValid && (mod.nexus || mod.workshop || mod.lab || oldSources); + sourcesValid = sourcesValid && (!!mod.nexus || !!mod.workshop || !!mod.lab || oldSources); } return sourcesValid; }; + this.metadataValid = function(mod) { + return !!mod.name && !!mod.authors && !!mod.released; + }; + this.licenseValid = function(mod_license) { if (mod_license.custom) { return mod_license.text_body && mod_license.text_body.length > 4; @@ -66,7 +64,7 @@ app.service('modValidationService', function() { var setIds = []; set.forEach(function(item) { var itemId = item[key]; - setValid = setValid && itemId; + setValid = setValid && !!itemId; if (!itemId || item._destroy) return; var idPresent = setIds.indexOf(itemId) > -1; if (idPresent) { @@ -92,13 +90,13 @@ app.service('modValidationService', function() { this.configsValid = function(config_files) { var configsValid = true; config_files.forEach(function(configFile) { - configsValid = configsValid && configFile.filename.length && - configFile.install_path.length && configFile.text_body.length; + configsValid = configsValid && !!configFile.filename.length && + !!configFile.install_path.length && !!configFile.text_body.length; }); return configsValid; }; this.categoriesValid = function(mod) { - return mod.categories && mod.categories.length <= 2 && (mod.is_official || mod.categories.length); + return !!mod.categories && mod.categories.length <= 2 && (mod.is_official || !!mod.categories.length); }; }); \ No newline at end of file diff --git a/mod-picker/app/assets/javascripts/Services/sortUtils.js b/mod-picker/app/assets/javascripts/Services/sortUtils.js index 1c8eee491..c85b2f520 100644 --- a/mod-picker/app/assets/javascripts/Services/sortUtils.js +++ b/mod-picker/app/assets/javascripts/Services/sortUtils.js @@ -268,7 +268,7 @@ app.service('sortUtils', function($q, categoryService, colorsFactory, modListSer model[key].sort(function(a, b) { return a.index - b.index; }); - } + }; // load sort into view this.loadSort = function(columns, sortedColumn, sort) { diff --git a/mod-picker/app/assets/javascripts/Views/mod/editMod.js b/mod-picker/app/assets/javascripts/Views/mod/editMod.js index 39a140d7e..dc8e3fc1a 100644 --- a/mod-picker/app/assets/javascripts/Views/mod/editMod.js +++ b/mod-picker/app/assets/javascripts/Views/mod/editMod.js @@ -87,7 +87,7 @@ app.controller('editModController', function($scope, $rootScope, $state, modObje $scope.mod = angular.copy(modObject); modLoaderService.loadMod($scope.mod); $scope.originalMod = angular.copy($scope.mod); - $scope.sites = sitesFactory.sites(); + $scope.sites = sitesFactory.sites; $scope.errors = {}; $scope.image = { sizes: [ @@ -119,6 +119,7 @@ app.controller('editModController', function($scope, $rootScope, $state, modObje var isAuthor = author && author.role == 'author'; $scope.canManageOptions = $scope.permissions.canModerate || isAuthor; $scope.canChangeStatus = (isAuthor && $scope.mod.status == "good") || $scope.permissions.isAdmin; + $scope.canSetDownloadLinks = $scope.permissions.canModerate || isAuthor; // set up tabs $scope.tabs = tabsFactory.buildEditModTabs($scope.canManageOptions); @@ -130,6 +131,7 @@ app.controller('editModController', function($scope, $rootScope, $state, modObje // validate the mod $scope.checkIfValid = function() { $scope.sourcesValid = modValidationService.sourcesValid($scope.mod); + $scope.metadataValid = modValidationService.metadataValid($scope.mod); $scope.licensesValid = modValidationService.licensesValid($scope.mod); $scope.authorsValid = modValidationService.authorsValid($scope.mod.mod_authors); $scope.requirementsValid = modValidationService.requirementsValid($scope.mod.requirements); @@ -137,7 +139,7 @@ app.controller('editModController', function($scope, $rootScope, $state, modObje $scope.categoriesValid = modValidationService.categoriesValid($scope.mod); // return result of all validations - $scope.valid = $scope.sourcesValid && $scope.authorsValid && $scope.requirementsValid && $scope.configsValid && $scope.categoriesValid; + $scope.valid = $scope.sourcesValid && $scope.metadataValid && $scope.authorsValid && $scope.requirementsValid && $scope.configsValid && $scope.categoriesValid; }; $scope.buildSource = function(scrapeLabel, infoLabel) { @@ -221,5 +223,5 @@ app.controller('editModController', function($scope, $rootScope, $state, modObje } }; - $scope.$watch('mod', $scope.checkIfValid, true); + $scope.$watchCollection('mod', $scope.checkIfValid, true); }); \ No newline at end of file diff --git a/mod-picker/app/assets/javascripts/Views/mod/editModAnalysis.js b/mod-picker/app/assets/javascripts/Views/mod/editModAnalysis.js index a91002f71..fcc200447 100644 --- a/mod-picker/app/assets/javascripts/Views/mod/editModAnalysis.js +++ b/mod-picker/app/assets/javascripts/Views/mod/editModAnalysis.js @@ -4,7 +4,7 @@ app.controller('editModAnalysisController', function($scope, modService, modLoad modService.editAnalysis($scope.mod.id).then(function(modOptions) { modLoaderService.loadAssets(modOptions); $scope.mod.mod_options = modOptions; - $scope.oldMod.mod_options = angular.copy(modOptions); + $scope.originalMod.mod_options = angular.copy(modOptions); }, function(response) { $scope.errors.analysis = response; }); diff --git a/mod-picker/app/assets/javascripts/Views/mod/submitMod.js b/mod-picker/app/assets/javascripts/Views/mod/submitMod.js index 6a0aa776c..33ddd3dcb 100644 --- a/mod-picker/app/assets/javascripts/Views/mod/submitMod.js +++ b/mod-picker/app/assets/javascripts/Views/mod/submitMod.js @@ -61,7 +61,7 @@ app.controller('submitModController', function($scope, $rootScope, $state, modSe $scope.permissions = angular.copy($rootScope.permissions); // initialize variables - $scope.sites = sitesFactory.sites(); + $scope.sites = sitesFactory.sites; $scope.tabs = tabsFactory.buildEditModTabs(false); $scope.mod = { game_id: window._current_game_id, @@ -105,10 +105,11 @@ app.controller('submitModController', function($scope, $rootScope, $state, modSe // a mod analysis, and at least one category $scope.checkIfValid = function() { $scope.sourcesValid = modValidationService.sourcesValid($scope.mod); + $scope.metadataValid = modValidationService.metadataValid($scope.mod); $scope.categoriesValid = modValidationService.categoriesValid($scope.mod); $scope.requirementsValid = modValidationService.requirementsValid($scope.mod.requirements); $scope.analysisValid = !!$scope.mod.analysis; - $scope.valid = $scope.sourcesValid && $scope.categoriesValid && $scope.analysisValid; + $scope.valid = $scope.sourcesValid && $scope.metadataValid && $scope.categoriesValid && $scope.analysisValid; }; $scope.submitImage = function(modId) { @@ -148,5 +149,7 @@ app.controller('submitModController', function($scope, $rootScope, $state, modSe }); }; - $scope.$watch('mod', $scope.checkIfValid, true); + $scope.$watchCollection('mod', $scope.checkIfValid); + $scope.$watch('mod.sources', $scope.checkIfValid, true); + $scope.$watch('mod.custom_sources', $scope.checkIfValid, true); }); diff --git a/mod-picker/app/assets/javascripts/Views/modList/modListDetails.js b/mod-picker/app/assets/javascripts/Views/modList/modListDetails.js index 70a1c3ea6..7b33df4fe 100644 --- a/mod-picker/app/assets/javascripts/Views/modList/modListDetails.js +++ b/mod-picker/app/assets/javascripts/Views/modList/modListDetails.js @@ -1,4 +1,4 @@ -app.controller('modListDetailsController', function($scope, $q, $timeout, tagService) { +app.controller('modListDetailsController', function($scope, $rootScope, $q, $timeout, tagService) { $scope.toggleExportDropdown = function($event) { $scope.showExportDropdown = !$scope.showExportDropdown; if (!$scope.showExportDropdown) { @@ -17,6 +17,12 @@ app.controller('modListDetailsController', function($scope, $q, $timeout, tagSer $scope.showImportModal = visible; }; + $scope.downloadModList = function() { + var gameName = $rootScope.currentGame.nexus_name; + var modListId = $scope.mod_list.id; + window.location = 'modpicker://' + gameName + '/mod-list/' + modListId; + }; + $scope.saveTags = function(updatedTags) { var action = $q.defer(); tagService.updateModListTags($scope.mod_list, updatedTags).then(function(data) { diff --git a/mod-picker/app/assets/javascripts/Views/user/user.js b/mod-picker/app/assets/javascripts/Views/user/user.js index 0c2ce0542..cad7e13fd 100644 --- a/mod-picker/app/assets/javascripts/Views/user/user.js +++ b/mod-picker/app/assets/javascripts/Views/user/user.js @@ -63,7 +63,7 @@ app.controller('userController', function($scope, $rootScope, $stateParams, user $scope.user = userObject.user; $scope.user.endorsed = userObject.endorsed; $scope.isCurrentUser = $scope.currentUser && $scope.currentUser.id == $scope.user.id; - $scope.roleClass = "user-role-" + $scope.user.role; + $scope.roleClass = "user-role-" + $scope.user.role.replace("_", "-"); $scope.pages = { profile_comments: {} }; @@ -72,6 +72,7 @@ app.controller('userController', function($scope, $rootScope, $stateParams, user moderator: "Moderator", writer: "News Writer", author: "Mod Author", + "beta_tester": "Beta Tester", restricted: "Restricted", banned: "Banned", "": "" diff --git a/mod-picker/app/assets/stylesheets/components/license.scss b/mod-picker/app/assets/stylesheets/components/license.scss index 54c59eaaf..9c5d3caa3 100644 --- a/mod-picker/app/assets/stylesheets/components/license.scss +++ b/mod-picker/app/assets/stylesheets/components/license.scss @@ -107,6 +107,10 @@ mod-license, license { color: #ec5; opacity: 0.7; } + + .fa { + cursor: default; + } } } } \ No newline at end of file diff --git a/mod-picker/app/assets/stylesheets/components/modOptionTree.scss b/mod-picker/app/assets/stylesheets/components/modOptionTree.scss index 0f3aabe8e..f1d45a040 100644 --- a/mod-picker/app/assets/stylesheets/components/modOptionTree.scss +++ b/mod-picker/app/assets/stylesheets/components/modOptionTree.scss @@ -8,6 +8,20 @@ mod-option-tree { } } + mod-option-tree { + ul { + padding-right: 5px; + + li { + display: inline-block; + min-width: 350px; + max-width: 350px; + } + } + } +} + +mod-option-tree, .mod-option-tree { .mod-option { display: flex; @@ -27,10 +41,36 @@ mod-option-tree { margin: 6px 2px 0 0; min-width: 16px; } + + input.fuzzy-input { + margin: 0; + } } - .mod-option-stats { + .mod-option-stats, .mod-option-link { + padding-left: 18px; + } + + .mod-option-link { + display: block; + margin-top: 6px; + margin-bottom: 2px; + + input[type="text"] { + height: 24px; + font-size: 13px; + vertical-align: middle; + padding: 0 2px; + } + + span.help-tooltip { + vertical-align: middle; + } + } + + .old-option { padding-left: 18px; + display: block; } } } @@ -47,17 +87,7 @@ mod-option-tree { } } - mod-option-tree { - ul { - padding-right: 5px; - - li { - display: inline-block; - min-width: 350px; - max-width: 350px; - } - } - + mod-option-tree, .mod-option-tree { .mod-option .mod-option-details { .mod-option-title { font-size: 18px; @@ -68,4 +98,42 @@ mod-option-tree { } } } +} + +.mod-option-tree { + padding-left: 10px; + margin: 16px 0; + + mod-option { + margin: 8px 0; + display: block; + } + + .mod-option-tree { + mod-option { + display: inline-block; + margin-right: 18px; + min-width: 700px; + max-width: 700px; + } + } + + .plugin-analysis-item { + padding: 6px 28px; + } + + asset-tree { + display: block; + margin: 6px 0 6px -8px; + + asset-tree { + margin: 0; + } + } + + .remove-item { + color: $text_color; + margin-left: 6px; + cursor: pointer; + } } \ No newline at end of file diff --git a/mod-picker/app/assets/stylesheets/general/buttons.scss b/mod-picker/app/assets/stylesheets/general/buttons.scss index fd7b90d3b..b9a62d327 100644 --- a/mod-picker/app/assets/stylesheets/general/buttons.scss +++ b/mod-picker/app/assets/stylesheets/general/buttons.scss @@ -180,6 +180,15 @@ div.action-box { } } +.special-box { + i.fa { + font-size: 16px; + display: inline-block; + margin: -2px 1px -1px -1px; + vertical-align: middle; + } +} + .yellow-box { background-color: $warning_secondary; diff --git a/mod-picker/app/assets/stylesheets/general/forms.scss b/mod-picker/app/assets/stylesheets/general/forms.scss index 422ea557b..0e9e852d9 100644 --- a/mod-picker/app/assets/stylesheets/general/forms.scss +++ b/mod-picker/app/assets/stylesheets/general/forms.scss @@ -229,6 +229,14 @@ form { margin-right: 4px; } + input.invalid { + box-shadow: $error_secondary 0 0 1px 1px; + + &:focus { + outline: none; + } + } + input[type="text"], input[type="password"], input[type="email"] { vertical-align: top; min-width: 350px; diff --git a/mod-picker/app/assets/stylesheets/pages/submitMod.scss b/mod-picker/app/assets/stylesheets/pages/submitMod.scss index dd87cc33a..6eba3bd6a 100644 --- a/mod-picker/app/assets/stylesheets/pages/submitMod.scss +++ b/mod-picker/app/assets/stylesheets/pages/submitMod.scss @@ -55,65 +55,6 @@ form { display: none; } - mod-option { - .subtitle { - margin-bottom: 15px; - text-transform: none; - - .remove-item { - color: $text_color; - margin-left: 16px; - cursor: pointer; - } - } - - label { - display: block; - margin-left: 10px; - text-transform: none; - margin-bottom: 10px; - - span { - color: $text_color; - font-weight: bold; - margin-right: 16px; - } - } - - strong { - margin-left: 10px; - } - - .analysis-container { - max-height: 400px; - max-width: 750px; - overflow-y: auto; - - .plugin-analysis-item { - margin-bottom: 8px; - - label { - margin-left: 30px; - font-size: 14px; - - span { - display: inline-block; - width: 120px; - margin-right: 40px; - color: $h4_color; - font-weight: 500; - } - } - - .stat-item { - padding-left: 30px; - margin-bottom: 4px; - max-width: 730px; - } - } - } - } - .subsection { margin-top: 10px; margin-bottom: 20px; diff --git a/mod-picker/app/controllers/api/v1/tags_controller.rb b/mod-picker/app/controllers/api/v1/tags_controller.rb index 91f6c0617..278654605 100644 --- a/mod-picker/app/controllers/api/v1/tags_controller.rb +++ b/mod-picker/app/controllers/api/v1/tags_controller.rb @@ -1,7 +1,7 @@ class Api::V1::TagsController < Api::ApiController # GET /all_tags def all - @tags = Tag.game(params[:game]) + @tags = Tag.game(params[:game]).where(hidden: false) render json: @tags end diff --git a/mod-picker/app/controllers/mod_lists_controller.rb b/mod-picker/app/controllers/mod_lists_controller.rb index fed487652..594f07360 100644 --- a/mod-picker/app/controllers/mod_lists_controller.rb +++ b/mod-picker/app/controllers/mod_lists_controller.rb @@ -2,6 +2,7 @@ class ModListsController < ApplicationController before_action :check_sign_in, only: [:create, :set_active, :update, :import, :update_tags, :create_star, :destroy_star] before_action :set_mod_list, only: [:hide, :clone, :add, :update, :import, :update_tags, :tools, :mods, :plugins, :export_modlist, :export_plugins, :export_links, :setup, :config_files, :analysis, :comments] before_action :soft_set_mod_list, only: [:set_active] + before_action :require_mpu_user_agent, only: [:setup] # GET /mod_lists def index @@ -145,6 +146,7 @@ def export_links # POST /mod_lists/:id/setup def setup authorize! :read, @mod_list + authorize! :setup, @mod_list decorator = ModListSetupDecorator.new(@mod_list) render text: SecureData.full(current_user, decorator.to_json) end @@ -344,6 +346,12 @@ def force_download(filename) response.headers["Content-Disposition"] = "attachment; filename=#{filename}" end + def require_mpu_user_agent + unless /ModPickerUtility\/([0-9\.]+)/.match(request.user_agent) + raise CanCan::AccessDenied.new("Not authorized!", :setup, ModList) + end + end + def new_mod_list_response(mod_list) mod_list.reload if params.has_key?(:active) && params[:active] diff --git a/mod-picker/app/controllers/mods_controller.rb b/mod-picker/app/controllers/mods_controller.rb index 1ee40a93f..8c8b954f2 100644 --- a/mod-picker/app/controllers/mods_controller.rb +++ b/mod-picker/app/controllers/mods_controller.rb @@ -93,6 +93,7 @@ def update authorize! :update_options, @mod, :message => "You are not allowed to update this mod's advanced options." if options_params.any? authorize! :assign_custom_sources, @mod, :message => "You are not allowed to assign custom sources." if params[:mod].has_key?(:custom_sources_attributes) authorize! :update_details, @mod, :message => "You are not allowed to update this mod's details." if details_params.any? + authorize! :update_download_links, @mod, :message => "You are not allowed to update this mod's download links." if download_links.any? builder = ModBuilder.new(current_user, mod_update_params) if builder.update @@ -425,7 +426,7 @@ def mod_update_params custom_sources_attributes: [:id, :label, :url, :_destroy], config_files_attributes: [:id, :filename, :install_path, :text_body, :_destroy], tag_names: [], - mod_options_attributes: [:id, :name, :display_name, :size, :md5_hash, :default, :is_installer_option, :_destroy, + mod_options_attributes: [:id, :name, :display_name, :download_link, :size, :md5_hash, :default, :is_installer_option, :_destroy, asset_paths: [], plugin_dumps: [:id, :filename, :is_esm, :used_dummy_plugins, :author, :description, :crc_hash, :record_count, :override_count, :file_size, :_destroy, master_plugins: [:filename, :crc_hash], @@ -443,9 +444,17 @@ def options_params params[:mod].slice(:is_utility, :is_mod_manager, :is_extender, :has_adult_content, :disallow_contributors, :disable_reviews, :lock_tags) end - def details_params - params[:mod].slice(:show_details_tab, :description, :notice, :notice_type, :support_link, :issues_link, :mod_licenses_attributes) - end + def details_params + params[:mod].slice(:show_details_tab, :description, :notice, :notice_type, :support_link, :issues_link, :mod_licenses_attributes) + end + + def download_links + if params[:mod].has_key?(:mod_options_attributes) + params[:mod][:mod_options_attributes].map {|m| m[:download_link]}.compact + else + [] + end + end def image_params { diff --git a/mod-picker/app/controllers/tags_controller.rb b/mod-picker/app/controllers/tags_controller.rb index d47e10852..ef31c2738 100644 --- a/mod-picker/app/controllers/tags_controller.rb +++ b/mod-picker/app/controllers/tags_controller.rb @@ -3,7 +3,7 @@ class TagsController < ApplicationController # GET /all_tags def all - @tags = Tag.game(params[:game]) + @tags = Tag.game(params[:game]).where(hidden: false) render json: @tags end diff --git a/mod-picker/app/models/ability.rb b/mod-picker/app/models/ability.rb index b329bfb52..8905f5702 100644 --- a/mod-picker/app/models/ability.rb +++ b/mod-picker/app/models/ability.rb @@ -92,6 +92,9 @@ def rep_320_abilities(user, can_contribute) end def rep_640_abilities(user, can_contribute) + end + + def rep_1280_abilities(user, can_contribute) can :set_custom_title, User, id: user.id end @@ -126,7 +129,7 @@ def can_monitor_site_activity def can_manage_mods # can update or hide any mod - can [:update, :hide, :approve, :assign_custom_sources, :update_authors, :update_options, :update_details], Mod + can [:update, :hide, :approve, :update_authors, :update_options, :update_details], Mod can :destroy, ModRequirement # can approve curator requests can :update, CuratorRequest @@ -163,6 +166,10 @@ def can_manage_reports can :resolve, BaseReport end + def can_setup_mod_lists + can :setup, ModList + end + def moderation_abilities(user) can_manage_users(user) can_monitor_site_activity @@ -171,6 +178,7 @@ def moderation_abilities(user) can_manage_mods can_manage_contributions can_manage_reports + can_setup_mod_lists end def can_manage_articles @@ -180,6 +188,11 @@ def can_manage_articles def helper_abilities can_manage_help_pages can_manage_mods + can_setup_mod_lists + end + + def beta_tester_abilities + can :setup, ModList end def can_create_new_contributions @@ -200,6 +213,7 @@ def can_submit_reports def can_submit_mods can :create, Mod + can :assign_custom_sources, Mod end def can_update_their_contributions(user) @@ -236,7 +250,7 @@ def mod_author_abilities(user) can :destroy, ModTag, { mod: { mod_authors: { user_id: user.id } } } cannot :create, Review, { mod: { mod_authors: { user_id: user.id, role: [0, 1] } } } # authors - can [:update_authors, :update_options, :update_details], Mod, { mod_authors: { user_id: user.id, role: 0 } } + can [:update_authors, :update_options, :update_details, :update_download_links], Mod, { mod_authors: { user_id: user.id, role: 0 } } can :change_status, Mod, { status: 0, mod_authors: { user_id: user.id, role: 0 } } can [:read, :update], CuratorRequest, { mod: { mod_authors: { user_id: user.id, role: 0 } } } # contributors @@ -316,6 +330,7 @@ def initialize(user) user_restrictions(user) unless user.can_moderate? can_manage_articles if user.news_writer? || user.admin? helper_abilities if user.helper? + beta_tester_abilities if user.beta_tester? # general permissions contributor_abilities(user) if can_contribute diff --git a/mod-picker/app/models/concerns/trackable.rb b/mod-picker/app/models/concerns/trackable.rb index 67c49bbd9..3bc9e8a80 100644 --- a/mod-picker/app/models/concerns/trackable.rb +++ b/mod-picker/app/models/concerns/trackable.rb @@ -77,15 +77,17 @@ def track_attribute_change(event, column) end def get_milestone(milestones, value) - for index in 0 ... milestones.size + for index in 0 .. milestones.size - 1 return index if value < milestones[index] end + milestones.size end def track_milestone_change(column, milestones) return unless attribute_changed?(column) old_milestone = get_milestone(milestones, attribute_was(column)) new_milestone = get_milestone(milestones, public_send(column)) + return if old_milestone == new_milestone ((old_milestone + 1)..new_milestone).each { |n| create_event(:"milestone#{n}") } end diff --git a/mod-picker/app/models/tag_group.rb b/mod-picker/app/models/tag_group.rb index fe89f674d..3afbeb99d 100644 --- a/mod-picker/app/models/tag_group.rb +++ b/mod-picker/app/models/tag_group.rb @@ -10,7 +10,7 @@ class TagGroup < ActiveRecord::Base # ASSOCIATIONS belongs_to :game belongs_to :category - has_many :tag_group_tags, :dependent => :destroy + has_many :tag_group_tags, -> { order(:index) }, :dependent => :destroy # COUNTER CACHE counter_cache :tag_group_tags, column: 'tags_count' @@ -22,9 +22,20 @@ class TagGroup < ActiveRecord::Base # CALLBACKS after_create :link_tags + def alphabetize + tag_group_tags.joins(:tag).reorder("tags.text").each_with_index do |t,i| + t.update(index: i) + end + end + + def set_indexes + tag_group_tags.each_with_index do |t,i| + t.update(index: i) + end + end + def next_index - tags = tag_group_tags.select { |tag| tag.id.present? } - tags.empty? ? 0 : tags.max_by(&:index) + tag_group_tags.empty? ? 0 : tag_group_tags.max_by(&:index) end def category_ids diff --git a/mod-picker/app/models/user.rb b/mod-picker/app/models/user.rb index 5ca3d33aa..f0d026883 100644 --- a/mod-picker/app/models/user.rb +++ b/mod-picker/app/models/user.rb @@ -113,6 +113,10 @@ def active_mod_list(game) a && a.mod_list end + def has_premium? + false + end + def admin? role.to_sym == :admin end @@ -133,6 +137,10 @@ def helper? role.to_sym == :helper end + def beta_tester? + role.to_sym == :beta_tester + end + def restricted? role.to_sym == :restricted end diff --git a/mod-picker/app/views/mod_list_mods/setup.json b/mod-picker/app/views/mod_list_mods/setup.json index bb3e66aeb..7935a9968 100644 --- a/mod-picker/app/views/mod_list_mods/setup.json +++ b/mod-picker/app/views/mod_list_mods/setup.json @@ -6,7 +6,7 @@ "methods": ["source_links"], "include": { "mod_options": { - "only": ["id", "name", "display_name", "size", "md5_hash", "is_installer_option"] + "only": ["id", "name", "display_name", "size", "md5_hash", "is_installer_option", "download_link"] } } } diff --git a/mod-picker/app/views/mod_list_mods/setup_tools.json b/mod-picker/app/views/mod_list_mods/setup_tools.json index 257a07cb9..e4943f69e 100644 --- a/mod-picker/app/views/mod_list_mods/setup_tools.json +++ b/mod-picker/app/views/mod_list_mods/setup_tools.json @@ -6,7 +6,7 @@ "methods": ["source_links"], "include": { "mod_options": { - "only": ["id", "name", "display_name", "size", "md5_hash", "is_installer_option"] + "only": ["id", "name", "display_name", "size", "md5_hash", "is_installer_option", "download_link"] } } } diff --git a/mod-picker/app/views/users/current.json b/mod-picker/app/views/users/current.json index d3526bd11..a259bb9bc 100644 --- a/mod-picker/app/views/users/current.json +++ b/mod-picker/app/views/users/current.json @@ -11,5 +11,5 @@ "only": ["game_id", "mod_list_id"] } }, - "methods": ["image_type", "recent_notifications"] + "methods": ["image_type", "recent_notifications", "has_premium?"] } \ No newline at end of file diff --git a/mod-picker/db/migrate/20170422200231_add_download_link_to_mod_options.rb b/mod-picker/db/migrate/20170422200231_add_download_link_to_mod_options.rb new file mode 100644 index 000000000..39e7ae1d7 --- /dev/null +++ b/mod-picker/db/migrate/20170422200231_add_download_link_to_mod_options.rb @@ -0,0 +1,5 @@ +class AddDownloadLinkToModOptions < ActiveRecord::Migration + def change + add_column :mod_options, :download_link, :string, after: :display_name + end +end diff --git a/mod-picker/db/schema.rb b/mod-picker/db/schema.rb index b78e029a3..ff9faf1a7 100644 --- a/mod-picker/db/schema.rb +++ b/mod-picker/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170411014320) do +ActiveRecord::Schema.define(version: 20170422200231) do create_table "active_mod_lists", force: :cascade do |t| t.integer "game_id", limit: 4, null: false @@ -664,6 +664,7 @@ t.integer "mod_id", limit: 4, null: false t.string "name", limit: 255, null: false t.string "display_name", limit: 255, null: false + t.string "download_link", limit: 255 t.integer "size", limit: 8, default: 0, null: false t.string "md5_hash", limit: 32 t.boolean "default", default: false, null: false diff --git a/mod-picker/db/static_seeds.rb b/mod-picker/db/static_seeds.rb index a7b5b1e4b..73f85578d 100644 --- a/mod-picker/db/static_seeds.rb +++ b/mod-picker/db/static_seeds.rb @@ -655,9 +655,9 @@ def seed_categories # Audiovisual sub-categories Category.create( - name: "Audiovisual - Animations", + name: "Audiovisual - Animations & Physics", parent_id: catAudiovisual.id, - description: "Any mod which adds or alters animations.", + description: "Any mod which adds or alters animations or physics.", priority: 193 ) Category.create( diff --git a/mod-picker/db/tag_groups/skyrim.json b/mod-picker/db/tag_groups/skyrim.json index dbec93921..fb614520a 100644 --- a/mod-picker/db/tag_groups/skyrim.json +++ b/mod-picker/db/tag_groups/skyrim.json @@ -1,9 +1,9 @@ [{ - "category_name" : "Audiovisual - Animations", + "category_name" : "Audiovisual - Animations & Physics", "name" : "Animation Type", "tag_names" : ["Movement", "Combat", "Magic", "Idle", "Action"] }, { - "category_name" : "Audiovisual - Animations", + "category_name" : "Audiovisual - Animations & Physics", "name" : "Animation Target", "tag_names" : ["Characters", "Creatures", "Items", "Objects", "Player Character"] }, { @@ -26,7 +26,7 @@ }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Content", - "tag_names" : ["Meshes", "Textures", "Shaders"] + "tag_names" : ["Meshes", "Textures", "Shaders", "Parallax", "LODs"] }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Texture Resolution", @@ -34,12 +34,16 @@ }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Target", - "tag_names" : ["Architecture", "Armor", "Clothing", "Clutter", "Creatures", "Food", "Landscape", "Potions", "Poisons", "Magic", "Sky", "User Interface", "Weapons"] + "tag_names" : ["Accessories", "Animals", "Architecture", "Armor", "Clothing", "Clutter", "Creatures", "Food", "Ingredients", "Items", "Jewelry", "Landscape", "Plants", "Potions", "Poisons", "Magic", "Sky", "User Interface", "Weapons"] }, { "category_name" : "Audiovisual - Post-processing", "name" : "Content", "exclusion_label" : "Other", "tag_names" : ["ENB Preset", "SweetFX Preset", "ReShade Preset", "FXAA Preset"] + }, { + "category_name" : "Audiovisual - Post-processing", + "name" : "Performance Impact", + "tag_names" : ["Low Performance Impact", "Medium Performance Impact", "High Performance Impact"] }, { "category_name" : "Audiovisual - Sounds & Music", "name" : "Sound Content", @@ -52,6 +56,10 @@ "category_name" : "Character Appearance", "name" : "Character Gender", "tag_names" : ["Male", "Female"] + }, { + "category_name" : "Character Appearance", + "name" : "Texture Resolution", + "tag_names" : ["256", "512", "1K", "2K", "4K", "8K"] }, { "category_name" : "Character Appearance - Body Mods", "name" : "Body Mod Content", @@ -71,7 +79,7 @@ }, { "category_name" : "Character Appearance", "name" : "Character Race", - "tag_names" : ["Humans", "Elves", "Beastmen", "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orsimer", "Redguard"] + "tag_names" : ["Humans", "Elves", "Beastmen", "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orsimer", "Redguard", "Vampires", "Vampire Lords", "Werewolves"] }, { "category_name" : "Fixes", "name" : "Fix Type", @@ -252,7 +260,7 @@ }, { "category_name" : "Locations - Overhauls", "name" : "Overhaul Target", - "tag_names" : ["Bridges", "Cities", "Castles", "Docks", "Forts", "Lighthouses", "Outposts", "Ruins", "Shrines", "Stores", "Temples", "Towns", "Dawnstar", "Falkreath", "Markarth", "Morthal", "Riften", "Raven Rock", "Solitude", "Whiterun", "Windhelm", "Winterhold"] + "tag_names" : ["Bridges", "Cities", "Castles", "Docks", "Forts", "Landscape", "Lighthouses", "Outposts", "Ruins", "Shrines", "Stores", "Temples", "Towns", "Dawnstar", "Falkreath", "Markarth", "Morthal", "Riften", "Raven Rock", "Solitude", "Whiterun", "Windhelm", "Winterhold"] }, { "category_name" : "New Characters", "name" : "Character Distribution", diff --git a/mod-picker/db/tag_groups/skyrim_se.json b/mod-picker/db/tag_groups/skyrim_se.json index dbec93921..fb614520a 100644 --- a/mod-picker/db/tag_groups/skyrim_se.json +++ b/mod-picker/db/tag_groups/skyrim_se.json @@ -1,9 +1,9 @@ [{ - "category_name" : "Audiovisual - Animations", + "category_name" : "Audiovisual - Animations & Physics", "name" : "Animation Type", "tag_names" : ["Movement", "Combat", "Magic", "Idle", "Action"] }, { - "category_name" : "Audiovisual - Animations", + "category_name" : "Audiovisual - Animations & Physics", "name" : "Animation Target", "tag_names" : ["Characters", "Creatures", "Items", "Objects", "Player Character"] }, { @@ -26,7 +26,7 @@ }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Content", - "tag_names" : ["Meshes", "Textures", "Shaders"] + "tag_names" : ["Meshes", "Textures", "Shaders", "Parallax", "LODs"] }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Texture Resolution", @@ -34,12 +34,16 @@ }, { "category_name" : "Audiovisual - Models & Textures", "name" : "Target", - "tag_names" : ["Architecture", "Armor", "Clothing", "Clutter", "Creatures", "Food", "Landscape", "Potions", "Poisons", "Magic", "Sky", "User Interface", "Weapons"] + "tag_names" : ["Accessories", "Animals", "Architecture", "Armor", "Clothing", "Clutter", "Creatures", "Food", "Ingredients", "Items", "Jewelry", "Landscape", "Plants", "Potions", "Poisons", "Magic", "Sky", "User Interface", "Weapons"] }, { "category_name" : "Audiovisual - Post-processing", "name" : "Content", "exclusion_label" : "Other", "tag_names" : ["ENB Preset", "SweetFX Preset", "ReShade Preset", "FXAA Preset"] + }, { + "category_name" : "Audiovisual - Post-processing", + "name" : "Performance Impact", + "tag_names" : ["Low Performance Impact", "Medium Performance Impact", "High Performance Impact"] }, { "category_name" : "Audiovisual - Sounds & Music", "name" : "Sound Content", @@ -52,6 +56,10 @@ "category_name" : "Character Appearance", "name" : "Character Gender", "tag_names" : ["Male", "Female"] + }, { + "category_name" : "Character Appearance", + "name" : "Texture Resolution", + "tag_names" : ["256", "512", "1K", "2K", "4K", "8K"] }, { "category_name" : "Character Appearance - Body Mods", "name" : "Body Mod Content", @@ -71,7 +79,7 @@ }, { "category_name" : "Character Appearance", "name" : "Character Race", - "tag_names" : ["Humans", "Elves", "Beastmen", "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orsimer", "Redguard"] + "tag_names" : ["Humans", "Elves", "Beastmen", "Altmer", "Argonian", "Bosmer", "Breton", "Dunmer", "Imperial", "Khajiit", "Nord", "Orsimer", "Redguard", "Vampires", "Vampire Lords", "Werewolves"] }, { "category_name" : "Fixes", "name" : "Fix Type", @@ -252,7 +260,7 @@ }, { "category_name" : "Locations - Overhauls", "name" : "Overhaul Target", - "tag_names" : ["Bridges", "Cities", "Castles", "Docks", "Forts", "Lighthouses", "Outposts", "Ruins", "Shrines", "Stores", "Temples", "Towns", "Dawnstar", "Falkreath", "Markarth", "Morthal", "Riften", "Raven Rock", "Solitude", "Whiterun", "Windhelm", "Winterhold"] + "tag_names" : ["Bridges", "Cities", "Castles", "Docks", "Forts", "Landscape", "Lighthouses", "Outposts", "Ruins", "Shrines", "Stores", "Temples", "Towns", "Dawnstar", "Falkreath", "Markarth", "Morthal", "Riften", "Raven Rock", "Solitude", "Whiterun", "Windhelm", "Winterhold"] }, { "category_name" : "New Characters", "name" : "Character Distribution", diff --git a/mod-picker/public/images/blogger_logo.png b/mod-picker/public/images/blogger_logo.png new file mode 100644 index 000000000..a02b75d16 Binary files /dev/null and b/mod-picker/public/images/blogger_logo.png differ diff --git a/mod-picker/public/images/curse_logo.png b/mod-picker/public/images/curse_logo.png new file mode 100644 index 000000000..4e56db05a Binary files /dev/null and b/mod-picker/public/images/curse_logo.png differ diff --git a/mod-picker/public/images/google_logo.png b/mod-picker/public/images/google_logo.png new file mode 100644 index 000000000..64d1bbab7 Binary files /dev/null and b/mod-picker/public/images/google_logo.png differ diff --git a/mod-picker/public/images/imgur_logo.png b/mod-picker/public/images/imgur_logo.png new file mode 100644 index 000000000..40de72ee2 Binary files /dev/null and b/mod-picker/public/images/imgur_logo.png differ diff --git a/mod-picker/public/images/moddb_logo.png b/mod-picker/public/images/moddb_logo.png new file mode 100644 index 000000000..742bd8f18 Binary files /dev/null and b/mod-picker/public/images/moddb_logo.png differ diff --git a/mod-picker/public/images/skyrim_modtype_logo.png b/mod-picker/public/images/skyrim_modtype_logo.png new file mode 100644 index 000000000..2f46aa6a9 Binary files /dev/null and b/mod-picker/public/images/skyrim_modtype_logo.png differ diff --git a/mod-picker/public/images/tumblr_logo.png b/mod-picker/public/images/tumblr_logo.png new file mode 100644 index 000000000..374c052eb Binary files /dev/null and b/mod-picker/public/images/tumblr_logo.png differ diff --git a/mod-picker/public/resources/directives/editMod/modAnalysisManager.html b/mod-picker/public/resources/directives/editMod/modAnalysisManager.html index aeb386490..805187464 100644 --- a/mod-picker/public/resources/directives/editMod/modAnalysisManager.html +++ b/mod-picker/public/resources/directives/editMod/modAnalysisManager.html @@ -10,17 +10,18 @@