Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plugin loader #5

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ICityWebMod.cs" />
<Compile Include="IWebServer.cs" />
<Compile Include="RequestHandlerBase.cs" />
<Compile Include="ResponseFormatters\HtmlResponseFormatter.cs" />
Expand All @@ -52,6 +53,7 @@
<Compile Include="ResponseFormatters\PlainTextResponseFormatter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RestfulRequestHandlerBase.cs" />
<Compile Include="TemplateHelper.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
38 changes: 38 additions & 0 deletions CityWebServer.Extensibility/ICityWebMod.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CityWebServer.Extensibility
{
public interface ICityWebMod
{
/// <summary>
/// The name of this mod. Shown in the index page/nav menu as a link.
/// </summary>
String ModName { get; }

/// <summary>
/// The author of this mod.
/// </summary>
String ModAuthor { get; }

/// <summary>
/// The ID/slug for this mod.
/// </summary>
/// <remarks>
/// This must be unique across all CityWebMods, and is used as the root for all request handlers provided by this mod.
/// </remarks>
String ModID { get; }

/// <summary>
/// Whether this mod should show up in the top menu.
/// </summary>
Boolean TopMenu { get; }

/// <summary>
/// Called when the server first starts and is registering handlers. Returns an enumerable collection of IRequestHandler objects.
/// </summary>
List<IRequestHandler> GetHandlers(IWebServer server);
}
}
9 changes: 2 additions & 7 deletions CityWebServer.Extensibility/IRequestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,6 @@ public interface IRequestHandler
{
IWebServer Server { get; }

/// <summary>
/// Gets a unique identifier for this handler. Only one handler can be loaded with a given identifier.
/// </summary>
Guid HandlerID { get; }

/// <summary>
/// Gets the priority of this request handler. A request will be handled by the request handler with the lowest priority.
/// </summary>
Expand All @@ -41,11 +36,11 @@ public interface IRequestHandler
/// <summary>
/// Returns a value that indicates whether this handler is capable of servicing the given request.
/// </summary>
Boolean ShouldHandle(HttpListenerRequest request);
Boolean ShouldHandle(HttpListenerRequest request, String slug);

/// <summary>
/// Handles the specified request. The method should not close the stream.
/// </summary>
IResponseFormatter Handle(HttpListenerRequest request);
IResponseFormatter Handle(HttpListenerRequest request, String slug, String wwwroot);
}
}
24 changes: 21 additions & 3 deletions CityWebServer.Extensibility/IWebServer.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
namespace CityWebServer.Extensibility
using System;
using System.Collections.Generic;

namespace CityWebServer.Extensibility
{
public interface IWebServer
{
/// <summary>
/// Gets an array containing all currently registered request handlers.
/// Gets an array containing all currently registered CityWebMods.
/// </summary>
IRequestHandler[] RequestHandlers { get; }
ICityWebMod[] Mods { get; }

/// <summary>
/// Gets the name of the current city.
/// </summary>
String CityName { get; }

/// <summary>
/// Gets all of the content in the log buffer.
/// </summary>
List<String> LogLines { get; }

/// <summary>
/// Gets the base static file path for the server.
/// </summary>
String WebRoot { get; }
}
}
43 changes: 26 additions & 17 deletions CityWebServer.Extensibility/RequestHandlerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,41 @@ protected void OnLogMessage(String message)
#endregion ILogAppender Implementation

protected readonly IWebServer _server;
protected Guid _handlerID;
protected int _priority;
protected String _name;
protected String _author;
protected int _priority = 100;
protected String _name = null;
protected String _author = null;
protected String _mainPath;

private RequestHandlerBase()
{
}

protected RequestHandlerBase(IWebServer server, Guid handlerID, String name, String author, int priority, String mainPath)
protected RequestHandlerBase(IWebServer server, String mainPath)
{
_server = server;
_mainPath = mainPath;
}

protected RequestHandlerBase(IWebServer server, String name, String author, int priority, String mainPath)
{
_server = server;
_handlerID = handlerID;
_name = name;
_author = author;
_priority = priority;
_mainPath = mainPath;
}

public RequestHandlerBase(IWebServer server)
{
_server = server;
_mainPath = null;
}

/// <summary>
/// Gets the server that is currently servicing this instance.
/// </summary>
public virtual IWebServer Server { get { return _server; } }

/// <summary>
/// Gets a unique identifier for this handler. Only one handler can be loaded with a given identifier.
/// </summary>
public virtual Guid HandlerID { get { return _handlerID; } }

/// <summary>
/// Gets the priority of this request handler. A request will be handled by the request handler with the lowest priority.
/// </summary>
Expand All @@ -70,23 +75,27 @@ protected RequestHandlerBase(IWebServer server, Guid handlerID, String name, Str
/// <summary>
/// Gets the absolute path to the main page for this request handler. Your class is responsible for handling requests at this path.
/// </summary>
/// <remarks>
/// When set to a value other than <c>null</c>, the Web Server will show this url as a link on the home page.
/// </remarks>
public virtual String MainPath { get { return _mainPath; } }

/// <summary>
/// Returns a value that indicates whether this handler is capable of servicing the given request.
/// </summary>
public virtual Boolean ShouldHandle(HttpListenerRequest request)
public virtual Boolean ShouldHandle(HttpListenerRequest request, String slug)
{
return (request.Url.AbsolutePath.Equals(_mainPath, StringComparison.OrdinalIgnoreCase));
if (slug == null)
{
return request.Url.AbsolutePath.Equals(_mainPath, StringComparison.OrdinalIgnoreCase);
}
else
{
return request.Url.AbsolutePath.Equals(String.Format("/{0}{1}", slug, _mainPath), StringComparison.OrdinalIgnoreCase);
}
}

/// <summary>
/// Handles the specified request. The method should not close the stream.
/// </summary>
public abstract IResponseFormatter Handle(HttpListenerRequest request);
public abstract IResponseFormatter Handle(HttpListenerRequest request, String slug, String wwwroot);

/// <summary>
/// Returns a response in JSON format.
Expand Down
18 changes: 5 additions & 13 deletions CityWebServer.Extensibility/RestfulRequestHandlerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,19 @@ namespace CityWebServer.Extensibility
{
public abstract class RestfulRequestHandlerBase : RequestHandlerBase
{
public RestfulRequestHandlerBase(IWebServer server, Guid handlerID, String name, String author, int priority, String mainPath)
: base(server, handlerID, name, author, priority, mainPath)
public RestfulRequestHandlerBase(IWebServer server, String mainPath)
: base(server, mainPath)
{
}

public override Guid HandlerID { get { return _handlerID; } }

public override int Priority { get { return _priority; } }

public override string Name { get { return _name; } }

public override string Author { get { return _author; } }

public override string MainPath { get { return _mainPath; } }

public override bool ShouldHandle(HttpListenerRequest request)
public override bool ShouldHandle(HttpListenerRequest request, String slug)
{
return (request.Url.AbsolutePath.StartsWith(_mainPath, StringComparison.OrdinalIgnoreCase));
return (request.Url.AbsolutePath.StartsWith(String.Format("/{0}{1}", slug, _mainPath), StringComparison.OrdinalIgnoreCase));
}

public override IResponseFormatter Handle(HttpListenerRequest request)
public override IResponseFormatter Handle(HttpListenerRequest request, string slug, string wwwroot)
{
switch (request.HttpMethod)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,19 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using CityWebServer.Extensibility;
using ColossalFramework.Plugins;

namespace CityWebServer.Helpers
namespace CityWebServer.Extensibility
{
public static class TemplateHelper
{
/// <summary>
/// Gets the full path of the directory that contains this assembly.
/// </summary>
public static String GetModPath()
{
var modPaths = PluginManager.instance.GetPluginsInfo().Select(obj => obj.modPath);

foreach (var path in modPaths)
{
var indexPath = Path.Combine(path, "index.html");
if (File.Exists(indexPath))
{
return indexPath;
}
}
return null;
}

/// <summary>
/// Gets the full content of a template.
/// </summary>
public static String GetTemplate(String template)
public static String GetTemplate(String template, String tmplPath)
{
// Templates seem like something we shouldn't handle internally.
// Perhaps we should force request handlers to implement their own templating if they so desire, and maintain a more "API" approach within the core.
String modPath = GetModPath();
String templatePath = Path.Combine(modPath, "wwwroot");
String specifiedTemplatePath = String.Format("{0}{1}{2}.html", templatePath, Path.DirectorySeparatorChar, template);
String specifiedTemplatePath = String.Format("{0}{1}{2}.html", tmplPath, Path.DirectorySeparatorChar, template);

if (File.Exists(specifiedTemplatePath))
{
Expand All @@ -57,11 +35,11 @@ public static String GetTemplate(String template)
/// <remarks>
/// The value of <paramref name="template"/> should not include the file extension.
/// </remarks>
public static String PopulateTemplate(String template, Dictionary<String, String> tokenReplacements)
public static String PopulateTemplate(String template, String tmplPath, Dictionary<String, String> tokenReplacements)
{
try
{
String templateContents = GetTemplate(template);
String templateContents = GetTemplate(template, tmplPath);
foreach (var tokenReplacement in tokenReplacements)
{
templateContents = templateContents.Replace(tokenReplacement.Key, tokenReplacement.Value);
Expand All @@ -70,28 +48,25 @@ public static String PopulateTemplate(String template, Dictionary<String, String
}
catch (Exception ex)
{
IntegratedWebServer.LogMessage(ex.ToString());
// IntegratedWebServer.LogMessage(ex.ToString());
return tokenReplacements["#PAGEBODY#"];
}
}

/// <summary>
/// Gets a dictionary that contains standard replacement tokens using the specified values.
/// </summary>
public static Dictionary<String, String> GetTokenReplacements(String cityName, String title, List<IRequestHandler> handlers, String body)
public static Dictionary<String, String> GetTokenReplacements(String cityName, String title, ICityWebMod[] mods, String body)
{
var orderedHandlers = handlers.OrderBy(obj => obj.Priority).ThenBy(obj => obj.Name);
var handlerLinks = orderedHandlers.Select(obj => String.Format("<li><a href='{0}'>{1}</a></li>", obj.MainPath, obj.Name)).ToArray();
var handlerLinks = mods.Select(obj => obj.TopMenu ? String.Format("<li><a href='/{0}/'>{1}</a></li>", obj.ModID, obj.ModName) : "").ToArray();
String nav = String.Join(Environment.NewLine, handlerLinks);

return new Dictionary<String, String>
{
{ "#PAGETITLE#", title },
{ "#NAV#", nav},
{ "#CSS#", ""}, // Moved directly into the template.
{ "#PAGEBODY#", body},
{ "#CITYNAME#", cityName},
{ "#JS#", ""}, // Moved directly into the template.
};
}
}
Expand Down
8 changes: 7 additions & 1 deletion CityWebServer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityWebServer", "CityWebSer
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CityWebServer.Extensibility", "CityWebServer.Extensibility\CityWebServer.Extensibility.csproj", "{DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebServerExtension", "SampleWebServerExtension\SampleWebServerExtension.csproj", "{7DF15DF6-C475-4866-9111-F5150C1336E1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleNakedHandlerMod", "SampleNakedHandlerMod\SampleNakedHandlerMod.csproj", "{671CB9E5-3EFD-4466-935E-1BF58470296F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleCityWebMod", "SampleCityWebMod\SampleCityWebMod.csproj", "{7DF15DF6-C475-4866-9111-F5150C1336E1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand All @@ -23,6 +25,10 @@ Global
{DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB96EFB4-FA45-4ACC-8D51-7ED37065CC79}.Release|Any CPU.Build.0 = Release|Any CPU
{671CB9E5-3EFD-4466-935E-1BF58470296F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{671CB9E5-3EFD-4466-935E-1BF58470296F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{671CB9E5-3EFD-4466-935E-1BF58470296F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{671CB9E5-3EFD-4466-935E-1BF58470296F}.Release|Any CPU.Build.0 = Release|Any CPU
{7DF15DF6-C475-4866-9111-F5150C1336E1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7DF15DF6-C475-4866-9111-F5150C1336E1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7DF15DF6-C475-4866-9111-F5150C1336E1}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand Down
7 changes: 6 additions & 1 deletion CityWebServer/CityWebServer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,16 @@
<ItemGroup>
<Compile Include="Helpers\ApacheMimeTypes.cs" />
<Compile Include="Helpers\CitizenExtensions.cs" />
<Compile Include="Helpers\UserMod.cs" />
<Compile Include="RequestHandlers\APICWM.cs" />
<Compile Include="RequestHandlers\HandlerCWM.cs" />
<Compile Include="RequestHandlers\RootCWM.cs" />
<Compile Include="RequestHandlers\LogCWM.cs" />
<Compile Include="Helpers\CityWebMod.cs" />
<Compile Include="Helpers\ConfigurationHelper.cs" />
<Compile Include="Helpers\DistrictExtensions.cs" />
<Compile Include="Helpers\EnumExtensions.cs" />
<Compile Include="Helpers\NameValueCollectionExtensions.cs" />
<Compile Include="Helpers\TemplateHelper.cs" />
<Compile Include="Models\ChirperMessage.cs" />
<Compile Include="Models\CityInfo.cs" />
<Compile Include="Models\DistrictInfo.cs" />
Expand Down
2 changes: 1 addition & 1 deletion CityWebServer/Helpers/ApacheMimeTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using System.Linq;

// Source: https://raw.githubusercontent.com/cymen/ApacheMimeTypesToDotNet/master/ApacheMimeTypes.cs
namespace ApacheMimeTypes
namespace CityWebServer.Helpers
{
internal class Apache
{
Expand Down
Loading