1370 lines
40 KiB
Ucode
1370 lines
40 KiB
Ucode
//==============================================================================
|
|
// Web Application to handle remote administration of server
|
|
//
|
|
// Revised by Micheal Comeau
|
|
// Revised by Ron Prestenback
|
|
// © 1998-2004, Epic Games, Inc. All Rights Reserved
|
|
//==============================================================================
|
|
|
|
class UTServerAdmin extends WebApplication config;
|
|
|
|
// TODO:
|
|
// Do something with colspan.inc
|
|
var config string ActiveSkin;
|
|
|
|
// Each query handler represents a section in webadmin
|
|
var() config array<string> QueryHandlerClasses;
|
|
var array<xWebQueryHandler> QueryHandlers;
|
|
|
|
// Global Objects
|
|
var() array<class<WebSkin> > WebSkins; // array of webadmin skins
|
|
var() class<UTServerAdminSpectator> SpectatorType;
|
|
var() class<WebSkin> DefaultWebSkinClass;
|
|
var WebSkin CurrentSkin;// Currently loaded webadmin skin
|
|
var UTServerAdminSpectator Spectator; // Used to get console messages
|
|
var xAdminUser CurAdmin; // Currently logged admin (not thread safe)
|
|
var PlayInfo GamePI; // Contains all confiurable variables
|
|
var WebResponse Resp; // Non-thread safe reference to a WebResponse object
|
|
|
|
// Lists
|
|
var array<CacheManager.GameRecord> AllGames;
|
|
var array<CacheManager.MapRecord> AllMaps;
|
|
var array<CacheManager.MutatorRecord> AllMutators; // All mutators as determined by .int entries
|
|
var StringArray AGameType; // All available Game Types
|
|
var StringArray AExcMutators; // All available Mutators (Excluded)
|
|
var StringArray AIncMutators; // All Mutators currently in play
|
|
var StringArray Skins; // All Webadmin custom skins
|
|
|
|
// Characters which aren't rendered correctly in HTML
|
|
struct HtmlChar
|
|
{
|
|
var string Plain;
|
|
var string Coded;
|
|
};
|
|
var array<HtmlChar> SpecialChars;
|
|
var config string DefaultBG; // Non-highlighted items
|
|
var config string HighlightedBG; // Active links
|
|
|
|
// Pages
|
|
var config string RootFrame; // This is the master frame divided in 2: Top = Header, bottom = frame page
|
|
var config string HeaderPage; // This is the header menu
|
|
var config string MessagePage; // Name of the file containing the message template
|
|
var config string FramedMessagePage; // Name of file containing message template for sub-frames
|
|
var config string RestartPage; // This is the page that users will be transferred to when restarting the server
|
|
var string htm;
|
|
|
|
|
|
var config string AdminRealm; // Used by browsers to cache login information
|
|
|
|
// HTML variables used for skins
|
|
var string SkinPath; // Path to use for .htm and .inc content
|
|
var string SiteCSSFile; // CSS file to use
|
|
var string SiteBG; // Background color for this skin
|
|
var string StatusOKColor; // Color of status ok messages
|
|
var string StatusErrorColor; // Color of status error messages
|
|
|
|
// Table cells
|
|
var config string CellLeft; // Table cell, left justified
|
|
var config string CellCenter; // Table cell, center justified
|
|
var config string CellRight; // Table cell, right justified
|
|
var config string CellColSpan; // Spanned table cell
|
|
|
|
var config string NowrapLeft; // Nowrap table cell, left justified
|
|
var config string NowrapCenter; // Nowrap table cell, center justified
|
|
var config string NowrapRight; // Nowrap table cell, right justified
|
|
|
|
var config string RowLeft; // Table row, left justified
|
|
var config string RowCenter; // Table row, center justified
|
|
|
|
// Form objects
|
|
var config string CheckboxInclude;
|
|
var config string TextboxInclude;
|
|
var config string SubmitButtonInclude;
|
|
var config string RadioButtonInclude;
|
|
var config string SelectInclude;
|
|
var config string ResetButtonInclude;
|
|
var config string HiddenInclude;
|
|
var config string SkinSelectInclude;
|
|
|
|
// Global localization
|
|
var localized string Accept;
|
|
var localized string Deny;
|
|
var localized string Update;
|
|
var localized string Custom;
|
|
var localized string Error;
|
|
var localized string NoneText;
|
|
var localized string SwitchText;
|
|
var localized string DeleteText;
|
|
var localized string NewText;
|
|
var localized string Edit;
|
|
|
|
var localized string WaitTitle;
|
|
var localized string MapChanging;
|
|
var localized string MapChangingTo;
|
|
|
|
var localized string AccessDeniedText;
|
|
var localized string ErrorAuthenticating;
|
|
var localized string NoPrivs;
|
|
|
|
var localized string LoadingGames, LoadingMaps, Initialized;
|
|
|
|
var bool bDebug;
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Initialization
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
event Init()
|
|
{
|
|
Super.Init();
|
|
|
|
if (SpectatorType != None)
|
|
Spectator = Level.Spawn(SpectatorType);
|
|
else
|
|
Spectator = Level.Spawn(class'UTServerAdminSpectator');
|
|
|
|
if (Spectator != None)
|
|
Spectator.Server = self;
|
|
|
|
// won't change as long as the server is up and the map hasnt changed
|
|
LoadMaps();
|
|
LoadGameTypes();
|
|
LoadMutators();
|
|
LoadQueryHandlers();
|
|
|
|
ReplaceText( Initialized, "%class%", string(Class) );
|
|
ReplaceText( Initialized, "%port%", string(WebServer.ListenPort) );
|
|
Log(Initialized,'WebAdmin');
|
|
}
|
|
|
|
function LoadMaps()
|
|
{
|
|
log(LoadingMaps,'WebAdmin');
|
|
class'CacheManager'.static.GetMaplist(AllMaps);
|
|
}
|
|
|
|
function LoadGameTypes()
|
|
{
|
|
local int i;
|
|
|
|
Log(LoadingGames,'WebAdmin');
|
|
|
|
// reinitialize list if needed
|
|
AGameType = New(None) class'SortedStringArray';
|
|
|
|
class'CacheManager'.static.GetGameTypeList(AllGames);
|
|
for (i = 0; i < AllGames.Length; i++)
|
|
AGameType.Add( AllGames[i].ClassName, AllGames[i].GameName );
|
|
}
|
|
|
|
function LoadMutators()
|
|
{
|
|
local Mutator M;
|
|
local int i, id;
|
|
|
|
AExcMutators = New(None) class'StringArray';
|
|
AIncMutators = New(None) class'SortedStringArray';
|
|
|
|
if ( Level.IsDemoBuild() )
|
|
return;
|
|
|
|
// Load All mutators
|
|
class'CacheManager'.static.GetMutatorList(AllMutators);
|
|
for (i = 0; i<AllMutators.Length; i++)
|
|
AExcMutators.Add(string(i), AllMutators[i].ClassName);
|
|
|
|
// Check Current Mutators
|
|
for (M = Level.Game.BaseMutator; M != None; M = M.NextMutator)
|
|
{
|
|
if (M.bUserAdded)
|
|
{
|
|
id = AExcMutators.FindTagId(String(M.Class));
|
|
if (id >= 0)
|
|
AIncMutators.Add(AExcMutators.GetItem(id), AllMutators[id].FriendlyName);
|
|
else
|
|
log("Unknown Mutator in use: "@M.Class,'WebAdmin');
|
|
}
|
|
}
|
|
}
|
|
|
|
function LoadQueryHandlers()
|
|
{
|
|
local int i, j;
|
|
local xWebQueryHandler QH;
|
|
local class<xWebQueryHandler> QHC;
|
|
|
|
LoadSkins();
|
|
for (i=0; i<QueryHandlerClasses.Length; i++)
|
|
{
|
|
QHC = class<xWebQueryHandler>(DynamicLoadObject(QueryHandlerClasses[i],class'Class'));
|
|
|
|
// skip invalid classes
|
|
if (QHC != None)
|
|
{
|
|
// Make sure we dont have duplicate instance of the same class
|
|
for (j=0;j<QueryHandlers.Length; j++)
|
|
{
|
|
if (QueryHandlers[j].Class == QHC)
|
|
{
|
|
QHC = None;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (QHC != None)
|
|
{
|
|
QH = new(self) QHC;
|
|
if (QH != None)
|
|
{
|
|
if (QH.Init())
|
|
{
|
|
QueryHandlers.Length = QueryHandlers.Length+1;
|
|
QueryHandlers[QueryHandlers.Length - 1] = QH;
|
|
}
|
|
else Log("WebQueryHandler:"@QHC@"could not be initialized",'WebAdmin');
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
log("Invalid QueryHandlerClass:"$QueryHandlerClasses[i]$". Removing invalid entry.",'WebAdmin');
|
|
QueryHandlerClasses.Remove(i,1);
|
|
SaveConfig();
|
|
}
|
|
}
|
|
}
|
|
|
|
function string SetGamePI(optional string GameType)
|
|
{
|
|
local class<Info> TmpClass;
|
|
local array<class<Info> > InfoClasses;
|
|
local int i;
|
|
|
|
if (GameType == "")
|
|
{
|
|
InfoClasses[0] = Level.Game.Class;
|
|
GameType = string(Level.Game.Class);
|
|
}
|
|
|
|
else
|
|
{
|
|
TmpClass = class<Info>(DynamicLoadObject(GameType, class'Class'));
|
|
if (TmpClass == None)
|
|
{
|
|
Log("Error loading gametype"@GameType,'WebAdmin');
|
|
return "";
|
|
}
|
|
|
|
InfoClasses[0] = TmpClass;
|
|
}
|
|
|
|
if (GamePI == None)
|
|
GamePI = new(None) class'PlayInfo';
|
|
|
|
if (Level.Game.AccessControl != None)
|
|
InfoClasses[1] = Level.Game.AccessControl.Class;
|
|
|
|
while (i < AIncMutators.Count())
|
|
{
|
|
TmpClass = class<Info>(DynamicLoadObject(AllMutators[int(AIncMutators.GetItem(i++))].ClassName, class'Class'));
|
|
if (TmpClass != None)
|
|
InfoClasses[InfoClasses.Length] = TmpClass;
|
|
}
|
|
|
|
GamePI.Init( InfoClasses );
|
|
return GameType;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// List Initialization & Generation
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
// Returns a list of all maps for this gametype
|
|
function StringArray ReloadExcludeMaps(String GameType)
|
|
{
|
|
local int i, GameIndex;
|
|
local string MapPrefix;
|
|
local StringArray AMaps;
|
|
local array<CacheManager.MapRecord> MapRecords;
|
|
|
|
if ( GameType == "" )
|
|
GameType = string(Level.Game.Class);
|
|
|
|
GameIndex = GetGameCacheIndex(GameType);
|
|
if ( GameIndex == -1 )
|
|
{
|
|
Warn("Could not load gametype"@GameType@"for maplist.");
|
|
return AMaps;
|
|
}
|
|
|
|
AMaps = New(None) class'SortedStringArray';
|
|
MapPrefix = AllGames[GameIndex].MapPrefix;
|
|
|
|
class'CacheManager'.static.GetMapList(MapRecords, MapPrefix);
|
|
for ( i = 0; i < MapRecords.Length; i++ )
|
|
if ( ValidMap(MapRecords[i].MapName) )
|
|
{
|
|
if ( bDebug )
|
|
log("Adding entry to exclude maplist Item '"$i$"' Tag '"$MapRecords[i].MapName$"'");
|
|
|
|
AMaps.Add(MapRecords[i].MapName, MapRecords[i].MapName, True);
|
|
}
|
|
|
|
return AMaps;
|
|
}
|
|
|
|
// Returns a list of active maps for this gametype
|
|
function StringArray ReloadIncludeMaps(StringArray ExMaps, int GameIndex, int MapListIndex)
|
|
{
|
|
local int i, id;
|
|
local string MapName;
|
|
local array<string> Maps;
|
|
local StringArray Arr;
|
|
|
|
Arr = new(None) class'StringArray';
|
|
Maps = Level.Game.MaplistHandler.GetMapList(GameIndex, MapListIndex);
|
|
|
|
for ( i = 0; i < Maps.Length; i++ )
|
|
{
|
|
MapName = class'MaplistRecord'.static.GetBaseMapName(Maps[i]);
|
|
Arr.Add( MapName $ Maps[i], MapName, True );
|
|
|
|
if ( bDebug )
|
|
log("Adding map entry to include list Item '"$MapName $ Maps[i]$"' Tag '"$MapName$"'");
|
|
|
|
// Always add the maps that are in the existing maplist, even if they aren't valid maps
|
|
id = ExMaps.FindTagId(MapName);
|
|
if ( id != -1 )
|
|
ExMaps.Remove(id);
|
|
}
|
|
|
|
return Arr;
|
|
}
|
|
|
|
// Only 'Mutators' has value when function is called
|
|
function CreateFullMutatorList(out StringArray Mutators, out StringArray GroupsOnly)
|
|
{
|
|
local StringArray Grouped;
|
|
local int i,j,z;
|
|
local string GrpName;
|
|
local string thisgroup, nextgroup;
|
|
|
|
// This class provides a specialized sorting function
|
|
// since mutators may be grouped - mutators in the same groups
|
|
// are not allowed to be selected together
|
|
|
|
Grouped = new(None) class'SortedStringArray';
|
|
|
|
// Create array sorted on GroupName...this allows to flag
|
|
// the mutator for a different .inc file (radio instead of checkbox)
|
|
for (i = 0; i < Mutators.Count(); i++)
|
|
{
|
|
j = int(Mutators.GetItem(i));
|
|
|
|
// If the mutator author forgot to configure a group name for the mutator,
|
|
// generate a groupname "Z" + number, so that every mutator without group name
|
|
// has its own group, and isn't grouped together
|
|
if (AllMutators[j].GroupName == "")
|
|
GrpName = "Z" $ string (z++);
|
|
|
|
else GrpName = AllMutators[j].GroupName;
|
|
|
|
Grouped.Add(string(j),GrpName $ "." $ AllMutators[j].FriendlyName $ AllMutators[j].ClassName);
|
|
}
|
|
|
|
// Move all grouped mutators to GroupsOnly StringArray for sorting by friendly name
|
|
for (i = 0; i < Grouped.Count(); i++)
|
|
{
|
|
thisgroup = AllMutators[int(Grouped.GetItem(i))].GroupName;
|
|
nextgroup = "";
|
|
|
|
if (thisgroup == "") continue;
|
|
if (i+1 < Grouped.Count())
|
|
nextgroup = AllMutators[int(Grouped.GetItem(i+1))].GroupName;
|
|
|
|
if (thisgroup ~= nextgroup)
|
|
{
|
|
j=i;
|
|
while(nextgroup ~= thisgroup && j < Grouped.Count())
|
|
{
|
|
GroupsOnly.MoveFromId(Grouped, Grouped.FindItemId(Grouped.GetItem(j)));
|
|
thisgroup = nextgroup;
|
|
if (j+1 == Grouped.Count())
|
|
nextgroup="";
|
|
else nextgroup = AllMutators[int(Grouped.GetItem(j+1))].GroupName;
|
|
}
|
|
|
|
if (j < Grouped.Count())
|
|
GroupsOnly.MoveFromId(Grouped,Grouped.FindItemId(Grouped.GetItem(j)));
|
|
|
|
i=-1;
|
|
}
|
|
}
|
|
|
|
// Move all non-grouped mutators back to Mutators StringArray
|
|
// for re-sorting by Friendly Name
|
|
Mutators.Reset();
|
|
for (i=0;i<Grouped.Count();i++)
|
|
{
|
|
j=int(Grouped.GetItem(i));
|
|
Mutators.Add(string(j),AllMutators[j].FriendlyName);
|
|
}
|
|
Grouped.Reset();
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Server Management & Control
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function LoadSkins()
|
|
{
|
|
local int i;
|
|
local string S;
|
|
local class<WebSkin> SkinClass;
|
|
|
|
Skins = new(None) class'StringArray';
|
|
S = Level.GetNextInt("XWebAdmin.WebSkin", i++);
|
|
while (S != "")
|
|
{
|
|
SkinClass = class<WebSkin>(DynamicLoadObject(S, class'Class'));
|
|
if (SkinClass != None)
|
|
{
|
|
Skins.Add(Level.GetItemName(string(SkinClass)), SkinClass.default.DisplayName);
|
|
WebSkins[WebSkins.Length] = SkinClass;
|
|
}
|
|
S = Level.GetNextInt("XWebAdmin.WebSkin", i++);
|
|
}
|
|
ApplySkinSettings();
|
|
}
|
|
|
|
function ApplySkinSettings()
|
|
{
|
|
local int i;
|
|
|
|
if (Skins == None)
|
|
return;
|
|
|
|
if (CurrentSkin != None && Level.GetItemName(string(CurrentSkin.Class)) ~= ActiveSkin)
|
|
return;
|
|
|
|
i = Skins.FindItemId(ActiveSkin);
|
|
if (i < 0)
|
|
CurrentSkin = new(None) class'XWebAdmin.ROOstSkin';
|
|
else CurrentSkin = new(None) WebSkins[i];
|
|
|
|
if (CurrentSkin != None)
|
|
CurrentSkin.Init(Self);
|
|
}
|
|
|
|
function ServerChangeMap(WebRequest Request, WebResponse Response, string MapName, string GameType)
|
|
{
|
|
local int i;
|
|
local bool bConflict;
|
|
local string Conflicts, Str, ShortName, Muts;
|
|
|
|
|
|
if (Level.NextURL != "")
|
|
{
|
|
ShowMessage(Response, WaitTitle, MapChanging);
|
|
}
|
|
|
|
if (Request.GetVariable("Save", "") != "")
|
|
{
|
|
// All we need to do is override settings as required
|
|
for (i = 0; i<GamePI.Settings.Length; i++)
|
|
{
|
|
ShortName = Level.GetItemName(GamePI.Settings[i].SettingName);
|
|
|
|
if (Request.GetVariable(GamePI.Settings[i].SettingName, "") != "")
|
|
Level.UpdateURL(ShortName, GamePI.Settings[i].Value, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bConflict = false;
|
|
Conflicts = "";
|
|
|
|
// Make sure we have a GamePI with the right GameType selected
|
|
GameType = SetGamePI(GameType);
|
|
|
|
// Check each parameter and see if it conflicts with the settings on the command line
|
|
for (i = 0; i<GamePI.Settings.Length; i++)
|
|
{
|
|
// Hack to get around "AdminName bug"
|
|
if (HasURLOption(GamePI.Settings[i].SettingName, Str) && !(GamePI.Settings[i].Value ~= Str) && GamePI.Settings[i].SettingName != "GameReplicationInfo.AdminName")
|
|
{
|
|
// We have a conflicting setting, prepare a table row for it.
|
|
Response.Subst("SettingName", GamePI.Settings[i].SettingName);
|
|
Response.Subst("SettingText", GamePI.Settings[i].DisplayName);
|
|
Response.Subst("DefVal", GamePI.Settings[i].Value);
|
|
Response.Subst("URLVal", Str);
|
|
Response.Subst("MapName", MapName);
|
|
Response.Subst("GameType", GameType);
|
|
Conflicts = Conflicts $ WebInclude(RestartPage$"_row");//skinme
|
|
bConflict = true;
|
|
}
|
|
}
|
|
|
|
if (bConflict)
|
|
{
|
|
// Conflicts exist .. show the RestartPage
|
|
Response.Subst("Conflicts", Conflicts);
|
|
Response.Subst("PostAction", RestartPage);
|
|
Response.Subst("Section", "Restart Conflicts");
|
|
Response.Subst("SubmitValue", Accept);
|
|
|
|
ShowPage(Response, RestartPage);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Muts = UsedMutators();
|
|
if (Muts != "")
|
|
Muts = "?Mutator=" $ Muts;
|
|
Level.ServerTravel(MapName$"?Game="$GameType$Muts, false);
|
|
ShowMessage(Response, WaitTitle, MapChanging);
|
|
}
|
|
|
|
// Save custom maplist
|
|
function UpdateCustomMapList(int GameIndex, int Index, string NewName)
|
|
{
|
|
if ( !(Level.Game.MaplistHandler.GetMapListTitle(GameIndex, Index) == NewName) )
|
|
Index = Level.Game.MaplistHandler.RenameList(GameIndex, Index, NewName);
|
|
|
|
if ( Level.Game.MaplistHandler.GetActiveList(GameIndex) == Index )
|
|
Level.Game.MaplistHandler.ApplyMaplist(GameIndex, Index);
|
|
else Level.Game.MaplistHandler.SaveMapList(GameIndex, Index);
|
|
}
|
|
|
|
// Called at end of game
|
|
function CleanupApp()
|
|
{
|
|
local int i;
|
|
|
|
if (Spectator != None)
|
|
Spectator = None;
|
|
|
|
for (i = 0; i < QueryHandlers.Length; i++)
|
|
QueryHandlers[i].Cleanup();
|
|
|
|
// In case a query is hung
|
|
if (Resp != None)
|
|
CleanupQuery();
|
|
|
|
Super.CleanupApp(); // Always call Super.CleanupApp();
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Webpage Query Response Handling
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function bool PreQuery(WebRequest Request, WebResponse Response)
|
|
{
|
|
local bool bResult;
|
|
local int i;
|
|
|
|
if (Level == None || Level.Game == None || Level.Game.AccessControl == None)
|
|
{
|
|
ShowMessage(Response, Error, ErrorAuthenticating);
|
|
return false;
|
|
}
|
|
|
|
// Prevent admin credentials from being stored across threads
|
|
// Also ensure that hanging connections are closed before beginning another Query()
|
|
if (Resp != None)
|
|
CleanupQuery();
|
|
|
|
if (Spectator == None)
|
|
{
|
|
if (SpectatorType != None)
|
|
Spectator = Level.Spawn(SpectatorType);
|
|
else Spectator = Level.Spawn(class'UTServerAdminSpectator');
|
|
|
|
if (Spectator != None)
|
|
Spectator.Server = self;
|
|
}
|
|
|
|
if (Spectator == None)
|
|
{
|
|
ShowMessage(Response, Error, ErrorAuthenticating);
|
|
return false;
|
|
}
|
|
|
|
// Check authentication:
|
|
if (!Level.Game.AccessControl.AdminLogin(Spectator, Request.Username, Request.Password))
|
|
{
|
|
Response.FailAuthentication(AdminRealm);
|
|
return false;
|
|
}
|
|
|
|
CurAdmin = Level.Game.AccessControl.GetLoggedAdmin(Spectator);
|
|
if (CurAdmin == None)
|
|
{
|
|
ShowMessage(Response, Error, ErrorAuthenticating);
|
|
Level.Game.AccessControl.AdminLogout(Spectator);
|
|
return false;
|
|
}
|
|
|
|
Resp = Response;
|
|
bResult = True;
|
|
for (i=0; i<QueryHandlers.Length; i++)
|
|
{
|
|
if (!QueryHandlers[i].PreQuery(Request, Response))
|
|
bResult = False;
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
event Query(WebRequest Request, WebResponse Response)
|
|
{
|
|
local int i;
|
|
|
|
Response.Subst("BugAddress", "webadminbugs"$Level.EngineVersion$"@epicgames.com");
|
|
Response.Subst("CSS", SiteCSSFile);
|
|
Response.Subst("BODYBG", SiteBG);
|
|
|
|
// Match query function. checks URI and calls appropriate input/output function
|
|
if (CurrentSkin != None && CurrentSkin.SpecialQuery.Length > 0)
|
|
{
|
|
for (i = 0; i < CurrentSkin.SpecialQuery.Length; i++)
|
|
{
|
|
if (CurrentSkin.SpecialQuery[i] ~= Mid(Request.URI,1))
|
|
{
|
|
if (CurrentSkin.HandleSpecialQuery(Request, Response))
|
|
return;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check whether we're supposed to handle this query
|
|
switch (Mid(Request.URI, 1))
|
|
{
|
|
case "":
|
|
case RootFrame: QueryRootFrame(Request, Response); return;
|
|
case HeaderPage: QueryHeaderPage(Request, Response); return;
|
|
case RestartPage: if (!MapIsChanging()) QuerySubmitRestartPage(Request, Response); return;
|
|
case SiteCSSFile: Response.SendCachedFile( Path $ SkinPath $ "/" $ Mid(Request.URI, 1), "text/css"); return;
|
|
}
|
|
|
|
// If not, allow each query handler a chance to process this query. Show error message if no query handlers
|
|
// were able to handle the query
|
|
for (i=0; i<QueryHandlers.Length; i++)
|
|
if (QueryHandlers[i].Query(Request, Response))
|
|
return;
|
|
|
|
ShowMessage(Response, Error, "Page not found!");
|
|
return;
|
|
}
|
|
|
|
event PostQuery(WebRequest Request, WebResponse Response)
|
|
{
|
|
local int i;
|
|
|
|
for (i=0; i<QueryHandlers.Length; i++)
|
|
if (!QueryHandlers[i].PostQuery(Request, Response))
|
|
Response.Connection.bDelayCleanup = True;
|
|
|
|
if (Response.Connection.IsHanging())
|
|
return;
|
|
|
|
CleanupQuery();
|
|
}
|
|
|
|
function CleanupQuery()
|
|
{
|
|
if (Resp != None && Resp.Connection.IsHanging())
|
|
Resp.Connection.Timer();
|
|
Resp = None;
|
|
CurAdmin = None;
|
|
Level.Game.AccessControl.AdminLogout(Spectator);
|
|
}
|
|
|
|
// Generates the HTML for the main root frame
|
|
function QueryRootFrame(WebRequest Request, WebResponse Response)
|
|
{
|
|
local String GroupPage;
|
|
|
|
if (QueryHandlers.Length > 0)
|
|
GroupPage = QueryHandlers[0].DefaultPage;
|
|
|
|
if (Request.GetVariable("ChangeSkin") != "")
|
|
{
|
|
ActiveSkin = Request.GetVariable("WebSkin", ActiveSkin);
|
|
ApplySkinSettings();
|
|
SaveConfig();
|
|
}
|
|
GroupPage = Request.GetVariable("Group", GroupPage);
|
|
|
|
Response.Subst("HeaderURI", HeaderPage$"?Group="$GroupPage);
|
|
Response.Subst("BottomURI", GroupPage);
|
|
Response.Subst("ServerName", class'GameReplicationInfo'.default.ServerName);
|
|
|
|
ShowFrame(Response, RootFrame);
|
|
}
|
|
|
|
// Generates the HTML for the top frame, which displays the available areas of webadmin and webadmin skin selector
|
|
function QueryHeaderPage(WebRequest Request, WebResponse Response)
|
|
{
|
|
local int i;
|
|
local string menu, GroupPage, Dis, CurPageTitle;
|
|
|
|
Response.Subst("AdminName", CurAdmin.UserName);
|
|
Response.Subst("HeaderColSpan", "2");
|
|
|
|
if (QueryHandlers.Length > 0)
|
|
{
|
|
GroupPage = Request.GetVariable("Group", QueryHandlers[0].DefaultPage);
|
|
// We build a multi-column table for each QueryHandler
|
|
menu = "";
|
|
CurPageTitle = "";
|
|
for (i=0; i<QueryHandlers.Length; i++)
|
|
{
|
|
if (QueryHandlers[i].DefaultPage == GroupPage)
|
|
CurPageTitle = QueryHandlers[i].Title;
|
|
|
|
Dis = "";
|
|
if (QueryHandlers[i].NeededPrivs != "" && !CanPerform(QueryHandlers[i].NeededPrivs))
|
|
Dis = "d";
|
|
|
|
Response.Subst("MenuLink", RootFrame$"?Group="$QueryHandlers[i].DefaultPage);
|
|
Response.Subst("MenuTitle", QueryHandlers[i].Title);
|
|
menu = menu$WebInclude(HeaderPage$"_item"$Dis);//skinme
|
|
}
|
|
Response.Subst("Location", CurPageTitle);
|
|
Response.Subst("HeaderMenu", menu);
|
|
}
|
|
|
|
if ( CanPerform("Xs") )
|
|
{
|
|
Response.Subst("HeaderColSpan", "3");
|
|
Response.Subst("SkinSelect", Select("WebSkin", GenerateSkinSelect()));
|
|
Response.Subst("WebSkinSelect", WebInclude(SkinSelectInclude));
|
|
}
|
|
// Set URIs
|
|
ShowPage(Response, HeaderPage);
|
|
}
|
|
|
|
function QueryRestartPage(WebRequest Request, WebResponse Response)
|
|
{
|
|
if ( CanPerform("Mr|Mt|Mm|Ms|Mu|Ml") )
|
|
ServerChangeMap(Request, Response, Level.GetURLMap(), String(Level.Game.Class));
|
|
}
|
|
|
|
function QuerySubmitRestartPage(WebRequest Request, WebResponse Response)
|
|
{
|
|
if ( CanPerform("Mr|Mt|Mm|Ms|Mu|Ml") )
|
|
ServerChangeMap(Request, Response, Request.GetVariable("MapName"), Request.GetVariable("GameType"));
|
|
}
|
|
|
|
// Generates HTML indicating that the admin doesn't have the priviledges required to view this page
|
|
function AccessDenied(WebResponse Response)
|
|
{
|
|
ShowMessage(Response, AccessDeniedText, NoPrivs);
|
|
}
|
|
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// HTML Generators for combo lists
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
// HTML for gametype combo options
|
|
function string GenerateGameTypeOptions(String CurrentGameType)
|
|
{
|
|
local int i;
|
|
local string SelectedStr, OptionStr;
|
|
|
|
for (i=0; i < AGameType.Count(); i++)
|
|
{
|
|
if (CurrentGameType ~= AGameType.GetItem(i))
|
|
SelectedStr = " selected";
|
|
else
|
|
SelectedStr = "";
|
|
|
|
OptionStr = OptionStr$"<option value=\""$AGameType.GetItem(i)$"\""$SelectedStr$">"$AGameType.GetTag(i)$"</option>";
|
|
}
|
|
return OptionStr;
|
|
}
|
|
|
|
// HTML for active mutator selection list
|
|
function string GenerateMutatorOptions(String SelectedMutators)
|
|
{
|
|
local int i, j;
|
|
local string SelectedStr, OptionStr;
|
|
local array<string> MutatorList;
|
|
local StringArray SortedMutatorList;
|
|
local bool bNoMutators;
|
|
|
|
Split( SelectedMutators, ",", MutatorList);
|
|
|
|
SortedMutatorList = new(None) class'SortedStringArray';
|
|
|
|
for(i=0; i < AllMutators.Length; i++)
|
|
SortedMutatorList.Add(AllMutators[i].ClassName,AllMutators[i].FriendlyName);
|
|
|
|
// Search mutator list for NONE, if found then no other mutators can be selected.
|
|
SelectedStr = "";
|
|
for( j=0; j < MutatorList.Length; j++)
|
|
{
|
|
if( MutatorList[j] ~= "NONE" )
|
|
{
|
|
SelectedStr = " selected";
|
|
bNoMutators = true;
|
|
break;
|
|
}
|
|
}
|
|
// add None options - for unloading all mutators
|
|
OptionStr = "<option value=\"NONE\"" $ SelectedStr $ ">" $ NoneText $ "</option>";
|
|
|
|
|
|
for(i=0; i < SortedMutatorList.Count(); i++)
|
|
{
|
|
SelectedStr = "";
|
|
if( !bNoMutators ) // dont select other mutators if none selected
|
|
{
|
|
for( j=0; j < MutatorList.Length; j++)
|
|
{
|
|
if( MutatorList[j] ~= SortedMutatorList.GetItem(i) )
|
|
{
|
|
SelectedStr = " selected";
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
OptionStr $= "<option value=\"" $ SortedMutatorList.GetItem(i) $ "\"" $ SelectedStr $ ">" $
|
|
SortedMutatorList.GetTag(i) $ "</option>";
|
|
}
|
|
SortedMutatorList.Reset();
|
|
return OptionStr;
|
|
}
|
|
|
|
// HTML for maplist selection options
|
|
function string GenerateMapListOptions(string GameType, int Active)
|
|
{
|
|
local int i, idx;
|
|
local array<string> Ar;
|
|
local string Result, selected;
|
|
|
|
idx = Level.Game.MaplistHandler.GetGameIndex(GameType);
|
|
Ar = Level.Game.MaplistHandler.GetMapListNames(idx);
|
|
for (i = 0; i < Ar.Length; i++)
|
|
{
|
|
selected =Eval(i == Active, " selected", "");
|
|
Result $= "<option value=\"" $ string(i) $ "\"" $ selected $ ">" $ Ar[i] $ "</option>";
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
// HTML for the dual map selection lists
|
|
function string GenerateMapListSelect(StringArray MapList, StringArray MovedMaps)
|
|
{
|
|
local int i;
|
|
local String ResponseStr, SelectedStr;
|
|
|
|
if (MapList.Count() == 0)
|
|
return "<option value=\"\">***"@NoneText@"***</option>";
|
|
|
|
for (i = 0; i<MapList.Count(); i++)
|
|
{
|
|
if ( ValidMap(MapList.GetTag(i)))
|
|
{
|
|
SelectedStr = "";
|
|
if (MovedMaps != None && MovedMaps.FindTagId(MapList.GetTag(i)) >= 0)
|
|
SelectedStr = " selected";
|
|
ResponseStr = ResponseStr$"<option value=\""$MapList.GetItem(i)$"\""$SelectedStr$">"$MapList.GetTag(i)$"</option>";
|
|
}
|
|
}
|
|
|
|
return ResponseStr;
|
|
}
|
|
|
|
// HTML for webadmin skin options
|
|
function string GenerateSkinSelect()
|
|
{
|
|
local string S, selectedstring;
|
|
local int i;
|
|
|
|
if (Skins.Count() == 0)
|
|
return "<option value=\"\">***"@NoneText@"***</option>";
|
|
|
|
for (i = 0; i < Skins.Count(); i++)
|
|
{
|
|
SelectedString = Eval(CurrentSkin != None && Level.GetItemName(string(CurrentSkin.Class)) ~= Skins.GetItem(i), " selected", "");
|
|
S = S $ "<option value=\""$Skins.GetItem(i)$"\""$SelectedString$">"$Skins.GetTag(i)$"</option>";
|
|
}
|
|
|
|
return S;
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// HTML helpers
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
// Replaces special characters in a regular string with HTML
|
|
static function string HtmlEncode(string src)
|
|
{
|
|
local int i;
|
|
|
|
for (i = 0; i < default.SpecialChars.Length; i++)
|
|
src = Repl(src, default.SpecialChars[i].Plain, default.SpecialChars[i].Coded);
|
|
|
|
return src;
|
|
}
|
|
|
|
// Replaces HTML special characters with regular text
|
|
static function string HtmlDecode(string src)
|
|
{
|
|
local int i;
|
|
|
|
for (i = 0; i < default.SpecialChars.Length; i++)
|
|
src = Repl(src, default.SpecialChars[i].Coded, default.SpecialChars[i].Plain);
|
|
|
|
return src;
|
|
}
|
|
|
|
// Make the specified string's length equal to MaxWidth by adding PadString to the beginning of the string
|
|
function string PadLeft( string Text, int MaxWidth, optional string PadString)
|
|
{
|
|
local String OutStr;
|
|
|
|
if (PadString == "")
|
|
PadString = " ";
|
|
|
|
for (OutStr = Text; Len(OutStr) < MaxWidth; OutStr = PadString $ OutStr);
|
|
|
|
return Right(OutStr, MaxWidth); // in case PadStr is more than one character
|
|
}
|
|
|
|
// Make the specified string's length equal to MaxWidth by adding PadString to the end of the string
|
|
function string PadRight(string Text, int MaxWidth, optional string PadString)
|
|
{
|
|
local string OutStr;
|
|
|
|
if (PadString == "")
|
|
PadString = " ";
|
|
|
|
for (OutStr = Text; Len(OutStr) < MaxWidth; OutStr $= PadString);
|
|
|
|
return Left(OutStr, MaxWidth);
|
|
}
|
|
|
|
// Adds the HTML necessary for showing the current map's name
|
|
function MapTitle(WebResponse Response)
|
|
{
|
|
local string str, smap;
|
|
|
|
str = Level.Game.GameReplicationInfo.GameName$" in ";
|
|
if (Level.Title ~= "untitled")
|
|
{
|
|
smap = Level.GetURLMap();
|
|
if (Right(smap, 4) ~= ".ut2")
|
|
str $= Left(smap, Len(smap) - 4);
|
|
else
|
|
str $= smap;
|
|
}
|
|
else
|
|
str $= Level.Title;
|
|
|
|
Response.Subst("SubTitle", str);
|
|
}
|
|
|
|
function bool MapIsChanging()
|
|
{
|
|
if (Level.NextURL != "")
|
|
{
|
|
ShowMessage(Resp, WaitTitle, MapChanging);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Converts the specified text into a hyperlink
|
|
function string HyperLink(string url, string text, bool bEnabled, optional string target)
|
|
{
|
|
local string hlink;
|
|
|
|
if (bEnabled)
|
|
{
|
|
hlink = "<a href='"$url$"'";
|
|
if (target != "")
|
|
hlink = hlink@"target='"$target$"'";
|
|
hlink = hlink$">"$text$"</a>";
|
|
return hlink;
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// Loads an .inc file into the current WebResponse object
|
|
function string WebInclude(string file)
|
|
{
|
|
local string S;
|
|
if (CurrentSkin != None)
|
|
{
|
|
S = CurrentSkin.HandleWebInclude(Resp, file);
|
|
if (S != "") return S;
|
|
}
|
|
return Resp.LoadParsedUHTM(Path $ SkinPath $ "/" $ file $ ".inc");
|
|
}
|
|
|
|
function bool ShowFrame(WebResponse Response, string Page)
|
|
{
|
|
if (CurrentSkin != None && CurrentSkin.HandleHTM(Response, Page))
|
|
return true;
|
|
|
|
Response.IncludeUHTM( Path $ SkinPath $ "/" $ Page $ htm);
|
|
return true;
|
|
}
|
|
|
|
function bool ShowPage(WebResponse Response, string Page)
|
|
{
|
|
if (CurrentSkin != None && CurrentSkin.HandleHTM(Response, Page))
|
|
{
|
|
Response.ClearSubst();
|
|
return true;
|
|
}
|
|
Response.IncludeUHTM( Path $ SkinPath $ "/" $ Page $ htm);
|
|
Response.ClearSubst();
|
|
return true;
|
|
}
|
|
|
|
function StatusError(WebResponse Response, string Message)
|
|
{
|
|
if (Left(Message,1) == "@")
|
|
Message = Mid(Message,1);
|
|
|
|
Response.Subst("Status", "<font color='"$StatusErrorColor$"'>"$Message$"</font><br>");
|
|
}
|
|
|
|
function StatusOk(WebResponse Response, string Message)
|
|
{
|
|
Response.Subst("Status", "<font color='"$StatusOKColor$"'>"$Message$"</font>");
|
|
}
|
|
|
|
function bool StatusReport(WebResponse Response, string ErrorMessage, string SuccessMessage)
|
|
{
|
|
if (ErrorMessage == "")
|
|
StatusOk(Response, SuccessMessage);
|
|
else
|
|
StatusError(Response, ErrorMessage);
|
|
|
|
return ErrorMessage=="";
|
|
}
|
|
|
|
function ShowMessage(WebResponse Response, string Title, string Message)
|
|
{
|
|
if (CurrentSkin != None && CurrentSkin.HandleMessagePage(Response, Title, Message))
|
|
return;
|
|
|
|
Response.Subst("Section", Title);
|
|
Response.Subst("Message", Message);
|
|
Response.IncludeUHTM(Path $ SkinPath $ "/" $ MessagePage $ htm);
|
|
}
|
|
|
|
// Framed message page (eliminates double headers)
|
|
function ShowFramedMessage(WebResponse Response, string Message, bool bIsErrorMsg)
|
|
{
|
|
if (CurrentSkin != None && CurrentSkin.HandleFrameMessage(Response, Message, bIsErrorMsg))
|
|
return;
|
|
|
|
if (bIsErrorMsg)
|
|
StatusError(Response,Message);
|
|
else Response.Subst("Message", Message);
|
|
Response.IncludeUHTM(Path $ SkinPath $ "/" $ FramedMessagePage $ htm);
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Form control HTML helpers
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
// Creates a checkbox, optionally greying it out (disabled)
|
|
function string Checkbox(string tag, bool bChecked, optional bool bDisabled)
|
|
{
|
|
Resp.Subst("CheckName", tag);
|
|
Resp.Subst("Checked", Eval(bChecked, " checked", ""));
|
|
Resp.Subst("Disabled", Eval(bDisabled, " disabled", ""));
|
|
|
|
return WebInclude(CheckboxInclude);
|
|
}
|
|
|
|
function string Hidden(string Tag, string Value)
|
|
{
|
|
Resp.Subst("HiddenName", Tag);
|
|
Resp.Subst("HiddenValue", Value);
|
|
|
|
return WebInclude(HiddenInclude);
|
|
}
|
|
|
|
// Creates a submit button
|
|
function string SubmitButton(string SubmitButtonName, string SubmitButtonValue)
|
|
{
|
|
Resp.Subst("SubmitName", SubmitButtonName);
|
|
Resp.Subst("SubmitValue", SubmitButtonValue);
|
|
|
|
return WebInclude(SubmitButtonInclude);
|
|
}
|
|
|
|
function string ResetButton(string ResetButtonName, string ResetButtonValue)
|
|
{
|
|
Resp.Subst("ResetName", ResetButtonName);
|
|
Resp.Subst("ResetValue", ResetButtonValue);
|
|
|
|
return WebInclude(ResetButtonInclude);
|
|
}
|
|
|
|
// Creates a textbox, optionally providing a default value
|
|
function string TextBox(string TextName, coerce string Size, coerce string MaxLength, optional string DefaultValue)
|
|
{
|
|
Resp.Subst("TextName", TextName);
|
|
if (int(Size) > 0) Resp.Subst("TextSize", "size=\""$Size$"\"");
|
|
else Resp.Subst("TextSize", "");
|
|
if (int(MaxLength) > 0) Resp.Subst("TextLength", "maxlength=\""$MaxLength$"\"");
|
|
else Resp.Subst("TextLength", "");
|
|
Resp.Subst("TextValue", DefaultValue);
|
|
|
|
return WebInclude(TextboxInclude);
|
|
}
|
|
|
|
function string RadioButton(string Group, string Value, bool bSelected)
|
|
{
|
|
Resp.Subst("RadioGroup", Group);
|
|
Resp.Subst("RadioValue", Value);
|
|
Resp.Subst("Selected", Eval(bSelected, " checked", ""));
|
|
|
|
return WebInclude(RadioButtonInclude);
|
|
}
|
|
|
|
function string Select(string SelectName, string SelectOptions)
|
|
{
|
|
Resp.Subst("SelectName", SelectName);
|
|
Resp.Subst("ListOptions", SelectOptions);
|
|
|
|
return WebInclude(SelectInclude);
|
|
}
|
|
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
// Accessor / Utility Functions
|
|
// =====================================================================================================================
|
|
// =====================================================================================================================
|
|
|
|
function int GetGameCacheIndex( string GameClass )
|
|
{
|
|
local int i;
|
|
|
|
for ( i = 0; i < AllGames.Length; i++ )
|
|
if ( AllGames[i].ClassName ~= GameClass )
|
|
return i;
|
|
|
|
return -1;
|
|
}
|
|
|
|
// Returns whether this map is a valid map
|
|
function bool ValidMap(string MapURL)
|
|
{
|
|
local int i;
|
|
local string MapName;
|
|
|
|
if ( Left(Caps(MapName),4) ~= "TUT-" )
|
|
return false;
|
|
|
|
MapName = class'MaplistRecord'.static.GetBaseMapName(MapURL);
|
|
for ( i = 0; i < AllMaps.Length; i++ )
|
|
{
|
|
if ( AllMaps[i].MapName ~= MapName )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function FormatMapName(out string FullName, out string ShortName)
|
|
{
|
|
local string ext;
|
|
ext = ".ut2";
|
|
|
|
if (FullName == "" && ShortName == "") return;
|
|
|
|
if (FullName != "" && ShortName == "")
|
|
{
|
|
if (Right(FullName, 4) ~= ext)
|
|
ShortName = Left(FullName, Len(FullName) - 4);
|
|
|
|
else
|
|
{
|
|
ShortName = FullName;
|
|
FullName = FullName $ ext;
|
|
}
|
|
}
|
|
|
|
else if (FullName == "" && ShortName != "")
|
|
{
|
|
if (Right(ShortName,4) ~= ext)
|
|
{
|
|
FullName = ShortName;
|
|
ShortName = Left(ShortName, Len(ShortName) - 4);
|
|
}
|
|
|
|
else
|
|
FullName = ShortName $ ext;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (!(Right(FullName,4) ~= ext))
|
|
FullName = FullName $ ext;
|
|
|
|
ShortName = Left(FullName, Len(FullName) - 4);
|
|
}
|
|
}
|
|
|
|
function String UsedMutators()
|
|
{
|
|
local int i;
|
|
local String OutStr;
|
|
|
|
while (i < AIncMutators.Count())
|
|
{
|
|
if (OutStr != "") OutStr $= ",";
|
|
OutStr $= AllMutators[int(AIncMutators.GetItem(i++))].ClassName;
|
|
}
|
|
|
|
return OutStr;
|
|
}
|
|
|
|
function bool HasURLOption(string ParamName, out string Value)
|
|
{
|
|
local string Param;
|
|
local int i;
|
|
|
|
Param = ParamName;
|
|
while (true)
|
|
{
|
|
i = Instr(Param, ".");
|
|
if (i < 0)
|
|
break;
|
|
|
|
Param = Mid(Param, i+1);
|
|
}
|
|
|
|
Value = Level.GetUrlOption(Param);
|
|
return Value != "";
|
|
}
|
|
|
|
function string NextPriv(out string PrivString)
|
|
{
|
|
local int pos;
|
|
local string Priv;
|
|
|
|
pos = Instr(PrivString, "|");
|
|
if (pos < 0)
|
|
pos = Len(PrivString);
|
|
EatStr(Priv, PrivString, Pos);
|
|
if (PrivString != "") // Remove leading pipe char
|
|
PrivString = Mid(PrivString, 1);
|
|
|
|
return Priv;
|
|
}
|
|
|
|
function bool CanPerform(string privs)
|
|
{
|
|
local string priv;
|
|
|
|
priv = NextPriv(privs);
|
|
while (priv != "")
|
|
{
|
|
if (Level.Game.AccessControl.AllowPriv(priv) && CurAdmin.HasPrivilege(priv))
|
|
return true;
|
|
|
|
priv = NextPriv(privs);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
defaultproperties
|
|
{
|
|
SpectatorType=class'XWebAdmin.UTServerAdminSpectator'
|
|
DefaultWebSkinClass=class'XWebAdmin.ROOstSkin'
|
|
QueryHandlerClasses(0)="XWebAdmin.xWebQueryCurrent"
|
|
QueryHandlerClasses(1)="XWebAdmin.xWebQueryDefaults"
|
|
QueryHandlerClasses(2)="XWebAdmin.xWebQueryAdmins"
|
|
RootFrame="rootframe"
|
|
HeaderPage="mainmenu"
|
|
MessagePage="message"
|
|
RestartPage="server_restart"
|
|
DefaultBG="#aaaaaa"
|
|
HighlightedBG="#3a7c8c"
|
|
SiteBG="#411b17"//"#243954"
|
|
AdminRealm="KF Remote Admin Server"
|
|
htm=".htm"
|
|
SpecialChars(0)=(Plain="&",Coded="&")
|
|
SpecialChars(1)=(Plain="\"",Coded=""")
|
|
SpecialChars(2)=(Plain=" ",Coded=" ")
|
|
SpecialChars(3)=(Plain="<",Coded="<")
|
|
SpecialChars(4)=(Plain=">",Coded=">")
|
|
SpecialChars(5)=(Plain="©",Coded="©")
|
|
SpecialChars(6)=(Plain="™",Coded="™")
|
|
SpecialChars(7)=(Plain="®",Coded="®")
|
|
StatusOKColor="#33cc66"
|
|
StatusErrorColor="Yellow"
|
|
|
|
Accept="Accept"
|
|
Deny="Deny"
|
|
Update="Update"
|
|
Custom="Custom"
|
|
Error="Error"
|
|
NoneText="None"
|
|
SwitchText="Switch"
|
|
DeleteText="Delete"
|
|
NewText="New"
|
|
Edit="Edit"
|
|
AccessDeniedText="Access Denied"
|
|
ErrorAuthenticating="Exception Occurred During Authentication!"
|
|
NoPrivs="Your privileges are not sufficient to view this page."
|
|
WaitTitle="Please Wait"
|
|
MapChanging="The server is now switching maps. Please allow 10 - 15 seconds while the server changes maps."
|
|
MapChangingTo="The server is now switching to map '%MapName%'. Please allow 10-15 seconds while the server changes maps."
|
|
|
|
LoadingGames="Loading Game Types"
|
|
LoadingMaps="Loading Available Maps"
|
|
Initialized="%class% Initialized on port %port%"
|
|
|
|
FramedMessagePage="frame_message"
|
|
SiteCSSFile="ROOst.css"
|
|
CheckboxInclude="checkbox"
|
|
TextboxInclude="textbox"
|
|
RadioButtonInclude="radio_button"
|
|
SelectInclude="select"
|
|
SubmitButtonInclude="submit_button"
|
|
ResetButtonInclude="reset_button"
|
|
HiddenInclude="hidden"
|
|
SkinSelectInclude="mainmenu_items"
|
|
|
|
CellLeft="cell_left"
|
|
CellCenter="cell_center"
|
|
CellRight="cell_right"
|
|
CellColSpan="cell_colspan"
|
|
NowrapLeft="cell_left_nowrap"
|
|
NowrapCenter="cell_center_nowrap"
|
|
NowrapRight="cell_right_nowrap"
|
|
RowLeft="row_left"
|
|
RowCenter="row_center"
|
|
|
|
bDebug=False
|
|
}
|