start work to allow custom accent colors

This commit is contained in:
RaidMax 2019-07-27 08:18:49 -05:00
parent ab4ce41015
commit 3b9b99a07e
20 changed files with 499 additions and 285 deletions

2
.gitignore vendored
View File

@ -236,3 +236,5 @@ launchSettings.json
/Plugins/ScriptPlugins/VpnDetectionPrivate.js
**/Master/env_master
/GameLogServer/log_env
/WebfrontCore/wwwroot/css/main.min.css
/WebfrontCore/wwwroot/css/main.css

View File

@ -45,6 +45,7 @@ namespace IW4MAdmin.Application
public CancellationToken CancellationToken => _tokenSource.Token;
public string ExternalIPAddress { get; private set; }
public bool IsRestartRequested { get; private set; }
public IMiddlewareActionHandler MiddlewareActionHandler { get; private set; } = new MiddlewareActionHandler();
static ApplicationManager Instance;
private readonly List<Command> Commands;
private readonly List<MessageToken> MessageTokens;

View File

@ -0,0 +1,50 @@
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.Misc
{
class MiddlewareActionHandler : IMiddlewareActionHandler
{
private static readonly IDictionary<string, IList<object>> _actions = new Dictionary<string, IList<object>>();
public async Task<T> Execute<T>(T value, string name = null)
{
string key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name;
if (_actions.ContainsKey(key))
{
foreach (var action in _actions[key])
{
try
{
value = await ((IMiddlewareAction<T>)action).Invoke(value);
}
// todo: probably log this somewhere
catch { }
}
return value;
}
return value;
}
public void Register<T>(T actionType, IMiddlewareAction<T> action, string name = null)
{
string key = string.IsNullOrEmpty(name) ? typeof(T).ToString() : name;
if (_actions.ContainsKey(key))
{
_actions[key].Add(action);
}
else
{
_actions.Add(key, new[] { action });
}
}
}
}

View File

@ -10,13 +10,17 @@ namespace SharedLibraryCore.Configuration
{
[LocalizedDisplayName("SETUP_ENABLE_WEBFRONT")]
[ConfigurationLinked("WebfrontBindUrl", "ManualWebfrontUrl")]
[ConfigurationLinked("WebfrontBindUrl", "ManualWebfrontUrl", "WebfrontPrimaryColor", "WebfrontSecondaryColor")]
public bool EnableWebFront { get; set; }
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_BIND_URL")]
public string WebfrontBindUrl { get; set; }
[ConfigurationOptional]
[LocalizedDisplayName("WEBFRONT_CONFIGURATION_MANUAL_URL")]
public string ManualWebfrontUrl { get; set; }
[ConfigurationOptional]
public string WebfrontPrimaryColor { get; set; }
[ConfigurationOptional]
public string WebfrontSecondaryColor { get; set; }
[LocalizedDisplayName("SETUP_ENABLE_MULTIOWN")]
public bool EnableMultipleOwners { get; set; }

View File

@ -46,6 +46,7 @@ namespace SharedLibraryCore.Interfaces
/// <returns></returns>
Task<IList<T>> ExecuteSharedDatabaseOperation<T>(string operationName);
void RegisterSharedDatabaseOperation(Task<IList> operation, string operationName);
IMiddlewareActionHandler MiddlewareActionHandler { get; }
IRConParser GenerateDynamicRConParser();
IEventParser GenerateDynamicEventParser();
string Version { get;}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces
{
public interface IMiddlewareAction<T>
{
Task<T> Invoke(T original);
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace SharedLibraryCore.Interfaces
{
public interface IMiddlewareActionHandler
{
void Register<T>(T actionType, IMiddlewareAction<T> action, string name = null);
Task<T> Execute<T>(T value, string name = null);
}
}

View File

@ -12,7 +12,7 @@ namespace WebfrontCore.Controllers
{
public IActionResult Edit()
{
if (Client.Level != SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
if (Client.Level < SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
{
return Unauthorized();
}
@ -23,7 +23,7 @@ namespace WebfrontCore.Controllers
[HttpPost]
public async Task<IActionResult> Edit(ApplicationConfiguration newConfiguration, bool addNewServer = false, bool shouldSave = false)
{
if (Client.Level != SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
if (Client.Level < SharedLibraryCore.Database.Models.EFClient.Permission.Owner)
{
return Unauthorized();
}

View File

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace WebfrontCore.Controllers
{
[Route("dynamic")]
public class DynamicFileController : BaseController
{
private static readonly IDictionary<string, string> _fileCache = new Dictionary<string, string>();
[Route("css/{fileName}")]
public async Task<IActionResult> Css(string fileName)
{
if (fileName.EndsWith(".css"))
{
if (!_fileCache.ContainsKey(fileName))
{
#if DEBUG
string path = $"X:\\IW4MAdmin\\WebfrontCore\\wwwroot\\css\\{fileName}";
#else
string path = $"wwwroot\\css\\{fileName}";
#endif
string cssData = await System.IO.File.ReadAllTextAsync(path);
cssData = await Manager.MiddlewareActionHandler.Execute(cssData, "custom_css_accent");
_fileCache.Add(fileName, cssData);
}
return Content(_fileCache[fileName], "text/css");
}
return StatusCode(400);
}
}
}

View File

@ -0,0 +1,90 @@
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace WebfrontCore.Middleware
{
public class CustomCssAccentMiddlewareAction : IMiddlewareAction<string>
{
private readonly Color _primaryColor;
private readonly Color _secondaryColor;
private readonly Color _originalPrimaryColor;
private readonly Color _originalSecondaryColor;
public CustomCssAccentMiddlewareAction(string originalPrimaryColor, string originalSecondaryColor, string primaryColor, string secondaryColor)
{
_originalPrimaryColor = Color.FromArgb(Convert.ToInt32(originalPrimaryColor.Substring(1).ToString(), 16));
_originalSecondaryColor = Color.FromArgb(Convert.ToInt32(originalSecondaryColor.Substring(1).ToString(), 16));
_primaryColor = string.IsNullOrEmpty(primaryColor) ? _originalPrimaryColor : Color.FromArgb(Convert.ToInt32(primaryColor.Substring(1).ToString(), 16));
_secondaryColor = string.IsNullOrEmpty(secondaryColor) ? _originalSecondaryColor : Color.FromArgb(Convert.ToInt32(secondaryColor.Substring(1).ToString(), 16));
}
public async Task<string> Invoke(string original)
{
string originalPrimaryHex = ColorToHex(_originalPrimaryColor);
string originalPrimaryDec = ColorToDec(_originalPrimaryColor);
string originalSecondaryHex = ColorToHex(_originalSecondaryColor);
string originalSecondaryDec = ColorToDec(_originalSecondaryColor);
string primaryHex = ColorToHex(_primaryColor);
string primaryDec = ColorToDec(_primaryColor);
string secondaryHex = ColorToHex(_secondaryColor);
string secondaryDec = ColorToDec(_secondaryColor);
string originalPrimaryDarkenHex = ColorToHex(LightenDarkenColor(_originalPrimaryColor, -10));
string originalPrimaryDarkenDec = ColorToDec(LightenDarkenColor(_originalPrimaryColor, -10));
string originalSecondaryDarkenHex = ColorToHex(LightenDarkenColor(_originalSecondaryColor, -10));
string originalSecondaryDarkenDec = ColorToDec(LightenDarkenColor(_originalSecondaryColor, -10));
string primaryDarkenHex = ColorToHex(LightenDarkenColor(_primaryColor, -10));
string primaryDarkenDec = ColorToDec(LightenDarkenColor(_primaryColor, -10));
string secondaryDarkenHex = ColorToHex(LightenDarkenColor(_secondaryColor, -10));
string secondaryDarkenDec = ColorToDec(LightenDarkenColor(_secondaryColor, -10));
return original
.Replace(originalPrimaryHex, primaryHex, StringComparison.OrdinalIgnoreCase)
.Replace(originalPrimaryDec, primaryDec, StringComparison.OrdinalIgnoreCase)
.Replace(originalSecondaryHex, secondaryHex, StringComparison.OrdinalIgnoreCase)
.Replace(originalSecondaryDec, secondaryDec, StringComparison.OrdinalIgnoreCase)
.Replace(originalPrimaryDarkenHex, primaryDarkenHex, StringComparison.OrdinalIgnoreCase)
.Replace(originalPrimaryDarkenDec, primaryDarkenDec, StringComparison.OrdinalIgnoreCase)
.Replace(originalSecondaryDarkenHex, secondaryDarkenHex, StringComparison.OrdinalIgnoreCase)
.Replace(originalSecondaryDarkenDec, secondaryDarkenDec, StringComparison.OrdinalIgnoreCase);
}
private string ColorToHex(Color color) => $"#{color.R.ToString("X2")}{color.G.ToString("X2")}{color.B.ToString("X2")}";
private string ColorToDec(Color color) => $"{(int)color.R}, {(int)color.G}, {(int)color.B}";
/// <summary>
/// Adapted from https://css-tricks.com/snippets/javascript/lighten-darken-color/
/// </summary>
/// <param name="col"></param>
/// <param name="amt"></param>
/// <returns></returns>
private Color LightenDarkenColor(Color col, float amt)
{
var num = col.ToArgb();
int r = (num >> 16) + (int)(amt * (num >> 16));
if (r > 255) r = 255;
else if (r < 0) r = 0;
int g = ((num >> 8) & 0x00FF) + (int)(amt * ((num >> 8) & 0x00FF));
if (g > 255) g = 255;
else if (g < 0) g = 0;
int b = (num & 0x0000FF) + (int)(amt * (num & 0x0000FF));
if (b > 255) b = 255;
else if (b < 0) b = 0;
return Color.FromArgb(r, g, b);
}
}
}

View File

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using SharedLibraryCore.Interfaces;
using WebfrontCore.Middleware;
namespace WebfrontCore
{
@ -19,6 +20,8 @@ namespace WebfrontCore
public static Task Init(IManager mgr, CancellationToken cancellationToken)
{
Manager = mgr;
var config = Manager.GetApplicationSettings().Configuration();
Manager.MiddlewareActionHandler.Register(null, new CustomCssAccentMiddlewareAction("#007ACC", "#fd7e14", config.WebfrontPrimaryColor, config.WebfrontSecondaryColor), "custom_css_accent");
return BuildWebHost().RunAsync(cancellationToken);
}

View File

@ -4,7 +4,7 @@
string formatTranslation(string translationKey, params object[] values)
{
var split = loc[translationKey].Split("::");
return $"<span class='font-weight-bold text-primary'>{SharedLibraryCore.Utilities.FormatExt(split[0], values)}</span><span>{split[1]}</span>";
return split.Count() == 2 ? $"<span class='font-weight-bold text-primary'>{SharedLibraryCore.Utilities.FormatExt(split[0], values)}</span><span>{split[1]}</span>" : translationKey;
}
}
<div class="row mb-4 border-bottom border-top pt-3 pb-3 bg-dark">

View File

@ -18,12 +18,11 @@
<link href='https://fonts.googleapis.com/css?family=Open+Sans' rel='stylesheet' type='text/css'>
<environment include="Development">
<link rel="stylesheet" href="~/css/bootstrap-custom.css" />
<link rel="stylesheet" href="~/dynamic/css/main.css" />
<link rel="stylesheet" href="~/lib/open-iconic/font/css/open-iconic-bootstrap.css" />
<link rel="stylesheet" href="~/css/profile.css" />
</environment>
<environment include="Production">
<link rel="stylesheet" href="~/css/global.min.css" />
<link rel="stylesheet" href="~/dynamic/css/global.min.css" />
</environment>
</head>
<body>

View File

@ -5,9 +5,8 @@
"outputFileName": "wwwroot/css/global.min.css",
// An array of relative input file paths. Globbing patterns supported
"inputFiles": [
"wwwroot/css/bootstrap-custom.css",
"wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap.css",
"wwwroot/css/profile.css"
"wwwroot/css/main.css",
"wwwroot/lib/open-iconic/font/css/open-iconic-bootstrap.css"
]
},
{

View File

@ -6,5 +6,9 @@
{
"outputFile": "wwwroot/js/server.es5.js",
"inputFile": "wwwroot/js/server.js"
},
{
"outputFile": "wwwroot/css/main.css",
"inputFile": "wwwroot/css/main.scss"
}
]

View File

@ -7,7 +7,7 @@ $primary: $blue !default;
$secondary: $orange !default;
$light: rgb(204, 204, 204) !default;
$dark: rgb(24, 24, 24) !default;
$body-bg: rgb(34,34,34) !default;
$body-bg: rgb(34, 34, 34) !default;
$big-dark: #191919;
$body-color: $white !default;
$link-color: $white !default;
@ -26,274 +26,4 @@ $navbar-toggler-font-size: $h5-font-size !default;
$navbar-dark-hover-color: $primary !default;
@import '../lib/bootstrap/scss/bootstrap.scss';
/*custom variables*/
/* bootstrap overrides */
.navbar-nav .nav-link:hover {
background-color: $body-bg;
}
nav.navbar-dark {
border-bottom: 1px solid $secondary;
}
a.nav-link {
padding: $spacer * 1.5;
}
.server-history-row {
height: 100px;
padding: 0 !important;
}
.canvasjs-chart-credit {
display: none;
}
.table thead th,
.table th,
.table td {
border: none;
}
.server-history,
.server-activity,
#mobile_seperator,
.border-bottom {
border-bottom: 1px solid $primary !important;
}
.server-history {
background-color: $big-dark;
}
.border-top {
border-top: 1px solid $primary !important;
}
#client_search {
background-color: #222222 !important;
border-radius: 0;
border: 1px solid $orange;
color: $white;
}
.server-join-button, .server-join-button:hover {
color: $white !important;
}
a.link-inverse {
color: $primary;
}
a.link-inverse:hover {
color: $white;
}
@media (min-width: 768px) {
.d-md-table-header-group {
display: table-header-group !important;
}
}
@media (min-width: 992px) {
.d-lg-table-header-group {
display: table-header-group !important;
}
}
#console_command_response {
min-height: 20rem;
}
#console_command_response hr {
border-color: #6c757d;
}
#console .form-control, #console button {
border-radius: 0;
border-color: $primary;
border-left: 0;
border-right: 0;
}
.close {
text-shadow: none !important;
}
.modal-footer {
border-top-color: $orange;
}
.modal-header {
border-bottom-color: $orange;
}
form *, select {
border-radius: 0 !important;
}
#penalty_filter_selection {
border-left: none !important;
border-right: none !important;
border-bottom: none !important;
border-top: 2px solid rgba(0, 122, 204, 0.5) !important;
}
.oi-fix-navbar {
line-height: 1.5 !important;
top: 0 !important;
font-size: 1rem !important;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(359deg);
}
to {
-webkit-transform: rotate(0deg);
}
}
.layout-loading-icon {
position: fixed !important;
left: 50%;
top: 50% !important;
margin-left: -37px;
margin-top: -37px;
color: $primary;
z-index: 100;
font-size: 4rem;
-webkit-animation: rotation 1s infinite linear;
background-color: $black;
background-color: rgba(0, 0,0, 0.5);
border-radius: 40px;
padding: 5px;
visibility: hidden;
}
.loader-load-more {
border-radius: 0;
}
.input-border-transition {
-webkit-transition: border 500ms ease-out;
-moz-transition: border 500ms ease-out;
-o-transition: border 500ms ease-out;
}
.input-text-danger {
border-color: $red !important;
}
.form-control:focus {
box-shadow: none;
-webkit-box-shadow: none;
}
.dropdown-item {
border-bottom: 1px solid $secondary;
margin-top: -3px;
}
.striped > div:nth-child(even) {
background-color: rgba(0, 0, 0, 0.125);
}
.striped > div:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.2);
}
.client-rating-graph {
min-height: 100px;
}
.client-rating-icon {
}
.client-rating-change-up, .client-rating-change-down {
font-size: 0.75rem;
}
.client-rating-change-amount {
font-size: 1rem;
}
.client-message, .automated-penalty-info-detailed, .oi {
cursor: pointer;
}
#footer_text {
font-size: 0.85rem;
}
.footer-mobile {
position: fixed;
bottom: 1em;
right: 1em;
}
.nav-tabs, .nav-tabs .nav-link.active {
color: $white;
color: $white !important;
border: none;
}
.nav-tabs .nav-link:hover {
color: $blue;
background-color: rgba(24, 24, 24, 0.75);
border: none;
}
.nav-tabs .nav-link {
border-radius: 0;
padding: 1.5rem;
color: #6c757d;
border: none;
min-width: 20rem;
background-color: #181818;
}
.nav-tabs .nav-link.active {
border: none;
background-color: $blue;
}
.nav-tabs .nav-link.active:hover {
background-color: rgba(0, 122, 204, 0.75);
}
.nav-tabs .nav-item {
margin-bottom: 0;
}
.mt-n1 {
margin-top: -0.25rem !important;
}
.mt-n2 {
margin-top: -0.5rem !important;
}
/* Configuration */
.configuration-form input[type='text'], .configuration-form input[type='number'], input.text-box {
border: 1px solid $orange;
}
.hide {
display: none;
}
.client-location-flag {
width: 3rem;
height: 1.5rem;
background-image: url('/images/radar/hud_weapons/hud_neutral.png');
background-size: contain;
background-repeat: no-repeat
}
@import '../lib/bootstrap/scss/bootstrap.scss';

View File

@ -0,0 +1,266 @@
@import 'bootstrap-custom.scss';
@import 'profile.scss';
.navbar-nav .nav-link:hover {
background-color: $body-bg;
}
nav.navbar-dark {
border-bottom: 1px solid $secondary;
}
a.nav-link {
padding: $spacer * 1.5;
}
.server-history-row {
height: 100px;
padding: 0 !important;
}
.canvasjs-chart-credit {
display: none;
}
.table thead th,
.table th,
.table td {
border: none;
}
.server-history,
.server-activity,
#mobile_seperator,
.border-bottom {
border-bottom: 1px solid $primary !important;
}
.server-history {
background-color: $big-dark;
}
.border-top {
border-top: 1px solid $primary !important;
}
#client_search {
background-color: #222222 !important;
border-radius: 0;
border: 1px solid $orange;
color: $white;
}
.server-join-button, .server-join-button:hover {
color: $white !important;
}
a.link-inverse {
color: $primary;
}
a.link-inverse:hover {
color: $white;
}
@media (min-width: 768px) {
.d-md-table-header-group {
display: table-header-group !important;
}
}
@media (min-width: 992px) {
.d-lg-table-header-group {
display: table-header-group !important;
}
}
#console_command_response {
min-height: 20rem;
}
#console_command_response hr {
border-color: #6c757d;
}
#console .form-control, #console button {
border-radius: 0;
border-color: $primary;
border-left: 0;
border-right: 0;
}
.close {
text-shadow: none !important;
}
.modal-footer {
border-top-color: $orange;
}
.modal-header {
border-bottom-color: $orange;
}
form *, select {
border-radius: 0 !important;
}
#penalty_filter_selection {
border-left: none !important;
border-right: none !important;
border-bottom: none !important;
border-top: 2px solid $blue !important;
}
.oi-fix-navbar {
line-height: 1.5 !important;
top: 0 !important;
font-size: 1rem !important;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(359deg);
}
to {
-webkit-transform: rotate(0deg);
}
}
.layout-loading-icon {
position: fixed !important;
left: 50%;
top: 50% !important;
margin-left: -37px;
margin-top: -37px;
color: $primary;
z-index: 100;
font-size: 4rem;
-webkit-animation: rotation 1s infinite linear;
background-color: $black;
background-color: rgba(0, 0,0, 0.5);
border-radius: 40px;
padding: 5px;
visibility: hidden;
}
.loader-load-more {
border-radius: 0;
}
.input-border-transition {
-webkit-transition: border 500ms ease-out;
-moz-transition: border 500ms ease-out;
-o-transition: border 500ms ease-out;
}
.input-text-danger {
border-color: $red !important;
}
.form-control:focus {
box-shadow: none;
-webkit-box-shadow: none;
}
.dropdown-item {
border-bottom: 1px solid $secondary;
margin-top: -3px;
}
.striped > div:nth-child(even) {
background-color: rgba(0, 0, 0, 0.125);
}
.striped > div:nth-child(odd) {
background-color: rgba(0, 0, 0, 0.2);
}
.client-rating-graph {
min-height: 100px;
}
.client-rating-icon {
}
.client-rating-change-up, .client-rating-change-down {
font-size: 0.75rem;
}
.client-rating-change-amount {
font-size: 1rem;
}
.client-message, .automated-penalty-info-detailed, .oi {
cursor: pointer;
}
#footer_text {
font-size: 0.85rem;
}
.footer-mobile {
position: fixed;
bottom: 1em;
right: 1em;
}
.nav-tabs, .nav-tabs .nav-link.active {
color: $white;
color: $white !important;
border: none;
}
.nav-tabs .nav-link:hover {
color: $blue;
background-color: rgba(24, 24, 24, 0.75);
border: none;
}
.nav-tabs .nav-link {
border-radius: 0;
padding: 1.5rem;
color: #6c757d;
border: none;
min-width: 20rem;
background-color: #181818;
}
.nav-tabs .nav-link.active {
border: none;
background-color: $blue;
}
.nav-tabs .nav-link.active:hover {
opacity: 0.75;
}
.nav-tabs .nav-item {
margin-bottom: 0;
}
.mt-n1 {
margin-top: -0.25rem !important;
}
.mt-n2 {
margin-top: -0.5rem !important;
}
/* Configuration */
.configuration-form input[type='text'], .configuration-form input[type='number'], input.text-box {
border: 1px solid $orange;
}
.hide {
display: none;
}
.client-location-flag {
width: 3rem;
height: 1.5rem;
background-image: url('/images/radar/hud_weapons/hud_neutral.png');
background-size: contain;
background-repeat: no-repeat
}

View File

@ -135,7 +135,7 @@
}
#profile_aliases_btn {
color: rgb(0, 122, 204);
color: $blue;
cursor: pointer;
}

View File

@ -55,7 +55,8 @@ $('.server-history-row').each(function (index, element) {
let clientHistory = $(this).data('clienthistory');
let serverId = $(this).data('serverid');
let maxClients = parseInt($('#server_header_' + serverId + ' .server-maxclients').text());
let color = $(this).data('online') === 'True' ? 'rgba(0, 122, 204, 0.432)' : '#ff6060';
let primaryColor = document.documentElement.style.getPropertyValue('--primary');
let color = $(this).data('online') === 'True' ? primaryColor.endsWith('80') ? primaryColor : primaryColor + '80' : '#ff6060';
let width = $('.server-header').first().width();
let historyChart = getPlayerHistoryChart(clientHistory, serverId, width, color, maxClients);
historyChart.render();

View File

@ -20,6 +20,8 @@
if (interval < 1)
interval = 1;
let primaryColor = document.documentElement.style.getPropertyValue('--primary');
return new CanvasJS.Chart(id, {
backgroundColor: 'transparent',
height: height,
@ -55,7 +57,7 @@
},
data: [{
type: 'splineArea',
color: 'rgba(0, 122, 204, 0.25)',
color: primaryColor.endsWith('80') ? primaryColor : primaryColor + '40',
markerSize: 3.5,
dataPoints: fixedData
}]