diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailSettingsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailSettingsPartDriver.cs index 6f235f843..ce9127e9a 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailSettingsPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/AuditTrailSettingsPartDriver.cs @@ -48,7 +48,8 @@ namespace Orchard.AuditTrail.Drivers { }; var viewModel = new AuditTrailSettingsViewModel { - Categories = categoriesQuery.ToList() + Categories = categoriesQuery.ToList(), + EnableClientIpAddressLogging = part.EnableClientIpAddressLogging }; // Update the settings as we may have added new settings. @@ -58,12 +59,13 @@ namespace Orchard.AuditTrail.Drivers { var eventsDictionary = _auditTrailManager.DescribeProviders().Describe().SelectMany(x => x.Events).ToDictionary(x => x.Event); if (updater.TryUpdateModel(viewModel, Prefix, null, null)) { foreach (var eventSettingViewModel in viewModel.Categories.SelectMany(x => x.Events)) { - var eventSetting = eventSettings.FirstOrDefault(x => x.EventName == eventSettingViewModel.Event); + var eventSetting = eventSettings.First(x => x.EventName == eventSettingViewModel.Event); var descriptor = eventsDictionary[eventSetting.EventName]; eventSetting.IsEnabled = eventSettingViewModel.IsEnabled || descriptor.IsMandatory; } part.EventSettings = eventSettings; + part.EnableClientIpAddressLogging = viewModel.EnableClientIpAddressLogging; } } diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/ClientIpAddressSettingsPartDriver.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/ClientIpAddressSettingsPartDriver.cs new file mode 100644 index 000000000..907ec9dc5 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Drivers/ClientIpAddressSettingsPartDriver.cs @@ -0,0 +1,31 @@ +using Orchard.AuditTrail.Models; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Drivers; +using Orchard.Security; + +namespace Orchard.AuditTrail.Drivers { + public class ClientIpAddressSettingsPartDriver : ContentPartDriver { + private readonly IAuthorizer _authorizer; + + public ClientIpAddressSettingsPartDriver(IAuthorizer authorizer) { + _authorizer = authorizer; + } + + protected override DriverResult Editor(ClientIpAddressSettingsPart part, dynamic shapeHelper) { + return Editor(part, null, shapeHelper); + } + + protected override DriverResult Editor(ClientIpAddressSettingsPart part, IUpdateModel updater, dynamic shapeHelper) { + if (!_authorizer.Authorize(Permissions.ManageClientIpAddressSettings)) + return null; + + return ContentShape("Parts_ClientIpAddressSettings_Edit", () => { + if (updater != null) { + updater.TryUpdateModel(part, Prefix, null, null); + } + + return shapeHelper.EditorTemplate(TemplateName: "Parts.ClientIpAddressSettings", Model: part, Prefix: Prefix); + }).OnGroup("Audit Trail"); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/AuditTrailSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/AuditTrailSettingsPartHandler.cs index 2d5655b21..657de865e 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/AuditTrailSettingsPartHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/AuditTrailSettingsPartHandler.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; +using System.Collections.Generic; using Orchard.AuditTrail.Models; using Orchard.AuditTrail.Providers.AuditTrail; using Orchard.AuditTrail.Services; @@ -9,7 +6,6 @@ using Orchard.Caching; using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; using Orchard.Localization; -using Orchard.Logging; namespace Orchard.AuditTrail.Handlers { public class AuditTrailSettingsPartHandler : ContentHandler { diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/ClientIpAddressSettingsPartHandler.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/ClientIpAddressSettingsPartHandler.cs new file mode 100644 index 000000000..7356929bf --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Handlers/ClientIpAddressSettingsPartHandler.cs @@ -0,0 +1,17 @@ +using Orchard.AuditTrail.Models; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Handlers; +using Orchard.Localization; + +namespace Orchard.AuditTrail.Handlers { + public class ClientIpAddressSettingsPartHandler : ContentHandler { + + public ClientIpAddressSettingsPartHandler() { + Filters.Add(new ActivatingFilter("Site")); + OnGetContentItemMetadata((context, part) => context.Metadata.EditorGroupInfo.Add(new GroupInfo(T("Audit Trail")))); + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs index 30b109b82..f431bfcf4 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Migrations.cs @@ -17,13 +17,20 @@ namespace Orchard.AuditTrail { .Column("EventData", c => c.Unlimited()) .Column("EventFilterKey", c => c.WithLength(16)) .Column("EventFilterData", c => c.WithLength(256)) - .Column("Comment", c => c.Unlimited())); + .Column("Comment", c => c.Unlimited()) + .Column("ClientIpAddress", c => c.WithLength(46))); ContentDefinitionManager.AlterPartDefinition("AuditTrailPart", part => part .Attachable() .WithDescription("Enables the user to enter a comment about the change when saving a content item.")); - return 1; + return 2; + } + + public int UpdateFrom1() { + SchemaBuilder.AlterTable("AuditTrailEventRecord", table => table + .AddColumn("ClientIpAddress", c => c.WithLength(46))); + return 2; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailEventRecord.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailEventRecord.cs index 77530e2de..9b9dbcedb 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailEventRecord.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailEventRecord.cs @@ -17,5 +17,6 @@ namespace Orchard.AuditTrail.Models { [StringLengthMax] public virtual string Comment { get; set; } + public virtual string ClientIpAddress { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailSettingsPart.cs index 012f47134..7554f81c1 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailSettingsPart.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/AuditTrailSettingsPart.cs @@ -10,5 +10,10 @@ namespace Orchard.AuditTrail.Models { get { return _eventProviderSettingsField.Value; } set { _eventProviderSettingsField.Value = value; } } + + public bool EnableClientIpAddressLogging { + get { return this.Retrieve(x => x.EnableClientIpAddressLogging); } + set { this.Store(x => x.EnableClientIpAddressLogging, value); } + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/ClientIpAddressSettingsPart.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/ClientIpAddressSettingsPart.cs new file mode 100644 index 000000000..3a8ae42d7 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Models/ClientIpAddressSettingsPart.cs @@ -0,0 +1,16 @@ +using Orchard.ContentManagement; + +namespace Orchard.AuditTrail.Models { + public class ClientIpAddressSettingsPart : ContentPart { + + public bool EnableClientIpAddressHeader { + get { return this.Retrieve(x => x.EnableClientIpAddressHeader); } + set { this.Store(x => x.EnableClientIpAddressHeader, value); } + } + + public string ClientIpAddressHeaderName { + get { return this.Retrieve(x => x.ClientIpAddressHeaderName, "X-Forwarded-For"); } + set { this.Store(x => x.ClientIpAddressHeaderName, value); } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj b/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj index c0c9cc7e6..b86b1e33c 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Orchard.AuditTrail.csproj @@ -184,8 +184,10 @@ + + @@ -197,11 +199,14 @@ + + + @@ -294,7 +299,9 @@ - + + + 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs index 43008157d..e381cce47 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Permissions.cs @@ -7,6 +7,7 @@ namespace Orchard.AuditTrail { public static readonly Permission ViewAuditTrail = new Permission { Description = "View audit trail", Name = "ViewAuditTrail" }; public static readonly Permission ManageAuditTrailSettings = new Permission { Description = "Manage audit trail settings", Name = "ManageAuditTrailSettings" }; public static readonly Permission ImportAuditTrail = new Permission { Description = "Import audit trail", Name = "ImportAuditTrail" }; + public static readonly Permission ManageClientIpAddressSettings = new Permission { Description = "Manage client IP address settings", Name = "ManageClientIpAddressSettings" }; public virtual Feature Feature { get; set; } @@ -14,6 +15,7 @@ namespace Orchard.AuditTrail { yield return ViewAuditTrail; yield return ManageAuditTrailSettings; yield return ImportAuditTrail; + yield return ManageClientIpAddressSettings; } public IEnumerable GetDefaultStereotypes() { @@ -22,7 +24,8 @@ namespace Orchard.AuditTrail { Permissions = new[] { ViewAuditTrail, ManageAuditTrailSettings, - /* Not even an administrator will get the ImportAuditTrail permission. */ + ManageClientIpAddressSettings, + /* Not even an administrator will get the ImportAuditTrail permission by default. */ } }; } diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info b/src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info index 72e74a5cc..d5826b8ae 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Placement.info @@ -3,7 +3,8 @@ Parts_AuditTrail_Link="Content:after.2" Parts_AuditTrail="Content:after.3" Parts_AuditTrailSettings_Edit="Content:0" - Parts_AuditTrailTrimmingSettings_Edit="Content:1"/> + Parts_AuditTrailTrimmingSettings_Edit="Content:3" + Parts_ClientIpAddressSettings_Edit="Content:2"/> diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/AuditTrailManager.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/AuditTrailManager.cs index ba56acd78..1d0ac6566 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/AuditTrailManager.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/AuditTrailManager.cs @@ -26,6 +26,7 @@ namespace Orchard.AuditTrail.Services { private readonly ISiteService _siteService; private readonly ISignals _signals; private readonly IShapeFactory _shapeFactory; + private readonly IClientIpAddressProvider _clientIpAddressProvider; public AuditTrailManager( IRepository auditTrailRepository, @@ -36,7 +37,8 @@ namespace Orchard.AuditTrail.Services { ICacheManager cacheManager, ISiteService siteService, ISignals signals, - IShapeFactory shapeFactory) { + IShapeFactory shapeFactory, + IClientIpAddressProvider clientIpAddressProvider) { _auditTrailRepository = auditTrailRepository; _providers = providers; @@ -47,6 +49,7 @@ namespace Orchard.AuditTrail.Services { _siteService = siteService; _signals = signals; _shapeFactory = shapeFactory; + _clientIpAddressProvider = clientIpAddressProvider; } public IPageOfItems GetRecords( @@ -151,7 +154,8 @@ namespace Orchard.AuditTrail.Services { EventData = _serializer.Serialize(context.EventData), EventFilterKey = context.EventFilterKey, EventFilterData = context.EventFilterData, - Comment = context.Comment + Comment = context.Comment, + ClientIpAddress = GetClientIpAddress() }; _auditTrailRepository.Create(record); @@ -161,18 +165,6 @@ namespace Orchard.AuditTrail.Services { }; } - private bool IsEventEnabled(AuditTrailEventDescriptor eventDescriptor) { - if (eventDescriptor.IsMandatory) - return true; - - var settingsDictionary = _cacheManager.Get("AuditTrail.EventSettings", context => { - context.Monitor(_signals.When("AuditTrail.EventSettings")); - return _siteService.GetSiteSettings().As().EventSettings.ToDictionary(x => x.EventName); - }); - var setting = settingsDictionary.ContainsKey(eventDescriptor.Event) ? settingsDictionary[eventDescriptor.Event] : default(AuditTrailEventSetting); - return setting != null ? setting.IsEnabled : eventDescriptor.IsEnabledByDefault; - } - public IEnumerable DescribeCategories() { var context = DescribeProviders(); return context.Describe(); @@ -245,5 +237,26 @@ namespace Orchard.AuditTrail.Services { } return Enumerable.Empty(); } + + private string GetClientIpAddress() { + var settings = _siteService.GetSiteSettings().As(); + + if (!settings.EnableClientIpAddressLogging) + return null; + + return _clientIpAddressProvider.GetClientIpAddress(); + } + + private bool IsEventEnabled(AuditTrailEventDescriptor eventDescriptor) { + if (eventDescriptor.IsMandatory) + return true; + + var settingsDictionary = _cacheManager.Get("AuditTrail.EventSettings", context => { + context.Monitor(_signals.When("AuditTrail.EventSettings")); + return _siteService.GetSiteSettings().As().EventSettings.ToDictionary(x => x.EventName); + }); + var setting = settingsDictionary.ContainsKey(eventDescriptor.Event) ? settingsDictionary[eventDescriptor.Event] : default(AuditTrailEventSetting); + return setting != null ? setting.IsEnabled : eventDescriptor.IsEnabledByDefault; + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/DefaultClientIpAddressProvider.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/DefaultClientIpAddressProvider.cs new file mode 100644 index 000000000..755ec9030 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/DefaultClientIpAddressProvider.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Orchard.AuditTrail.Models; +using Orchard.ContentManagement; + +namespace Orchard.AuditTrail.Services { + public class DefaultClientIpAddressProvider : IClientIpAddressProvider { + private readonly IWorkContextAccessor _wca; + public DefaultClientIpAddressProvider(IWorkContextAccessor wca) { + _wca = wca; + } + + public string GetClientIpAddress() { + var workContext = _wca.GetContext(); + var settings = workContext.CurrentSite.As(); + var address = workContext.HttpContext.Request.UserHostAddress; + + if (settings.EnableClientIpAddressHeader && !String.IsNullOrWhiteSpace(settings.ClientIpAddressHeaderName)) { + var headerName = settings.ClientIpAddressHeaderName.Trim(); + var customAddresses = ParseAddresses(workContext.HttpContext.Request.Headers[headerName]).ToArray(); + + if (customAddresses.Any()) + address = customAddresses.First(); + } + + return address; + } + + private static IEnumerable ParseAddresses(string value) { + return !String.IsNullOrWhiteSpace(value) + ? value.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim()) + : Enumerable.Empty(); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IClientIpAddressProvider.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IClientIpAddressProvider.cs new file mode 100644 index 000000000..65022a281 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Services/IClientIpAddressProvider.cs @@ -0,0 +1,5 @@ +namespace Orchard.AuditTrail.Services { + public interface IClientIpAddressProvider : IDependency { + string GetClientIpAddress(); + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs b/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs index 12711762d..0176bfae6 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/ViewModels/AuditTrailSettingsViewModel.cs @@ -3,5 +3,6 @@ using System.Collections.Generic; namespace Orchard.AuditTrail.ViewModels { public class AuditTrailSettingsViewModel { public IList Categories { get; set; } + public bool EnableClientIpAddressLogging { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Detail.cshtml b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Detail.cshtml index bc4637cf2..8b15a0222 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Detail.cshtml +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Detail.cshtml @@ -1,9 +1,12 @@ -@model Orchard.AuditTrail.ViewModels.AuditTrailDetailsViewModel +@using Orchard.AuditTrail.Models +@using Orchard.ContentManagement +@model Orchard.AuditTrail.ViewModels.AuditTrailDetailsViewModel @{ Style.Include("audittrail-display.css"); var record = Model.Record; var descriptor = Model.Descriptor; + var enablieClientIpAddressLogging = WorkContext.CurrentSite.As().EnableClientIpAddressLogging; Layout.Title = T("Audit Trail Event"); } @@ -12,6 +15,10 @@ @T("Category:") @descriptor.CategoryDescriptor.Name
@T("Timestamp:") @Display.DateTime(DateTimeUtc: record.CreatedUtc)
@T("Username:") @record.UserName + @if (enablieClientIpAddressLogging) { +
+ @T("Client IP Address:") @record.ClientIpAddress + }
@Display(Model.DetailsShape) diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Index.cshtml index d9b67168e..2d1efb3fc 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/Admin/Index.cshtml @@ -1,5 +1,7 @@ @using Orchard.AuditTrail.Helpers +@using Orchard.AuditTrail.Models @using Orchard.AuditTrail.Services.Models +@using Orchard.ContentManagement @model Orchard.AuditTrail.ViewModels.AuditTrailViewModel @{ Style.Include("audittrail-display.css"); @@ -10,6 +12,7 @@ new SelectListItem {Text = T("Category (alphabetical)").Text, Value = AuditTrailOrderBy.CategoryAscending.ToString(), Selected = Model.OrderBy == AuditTrailOrderBy.CategoryAscending}, new SelectListItem {Text = T("Event name (alphabetical)").Text, Value = AuditTrailOrderBy.EventAscending.ToString(), Selected = Model.OrderBy == AuditTrailOrderBy.EventAscending}, }; + var enablieClientIpAddressLogging = WorkContext.CurrentSite.As().EnableClientIpAddressLogging; Layout.Title = T("Audit Trail"); } @@ -38,6 +41,9 @@ @T("Category") @T("Event") @T("User") + @if (enablieClientIpAddressLogging) { + @T("Client IP Address") + } @T("Timestamp") @T("Summary") @T("Comment") @@ -50,6 +56,9 @@ @record.CategoryDescriptor.Name @record.EventDescriptor.Name @record.Record.UserName + @if (enablieClientIpAddressLogging) { + @record.Record.ClientIpAddress + } @Display.DateTime(DateTimeUtc: record.Record.CreatedUtc) @Display(record.SummaryShape) @Html.Raw(record.Record.Comment.NewlinesToHtml()) diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.AuditTrailSettings.cshtml b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.AuditTrailSettings.cshtml index bf2f61404..e6035e85f 100644 --- a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.AuditTrailSettings.cshtml +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.AuditTrailSettings.cshtml @@ -46,7 +46,15 @@ j++; } - i++; + i++; } +
+
+

@T("Client IP address logging")

+
+ @Html.CheckBoxFor(m => m.EnableClientIpAddressLogging) + @Html.LabelFor(m => m.EnableClientIpAddressLogging, T("Enable client IP address logging").Text, new { @class = "forcheckbox" }) + @T("When enabled, the client IP address will be recorded as part of an audit trail event.") +
\ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.ClientIpAddressSettings.cshtml b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.ClientIpAddressSettings.cshtml new file mode 100644 index 000000000..ed0f2361b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AuditTrail/Views/EditorTemplates/Parts.ClientIpAddressSettings.cshtml @@ -0,0 +1,16 @@ +@model Orchard.AuditTrail.Models.ClientIpAddressSettingsPart +@{ + Script.Require("ShapesBase"); +} +
+
+ @Html.CheckBoxFor(m => m.EnableClientIpAddressHeader) + @Html.LabelFor(m => m.EnableClientIpAddressHeader, T("Enable client IP address header").Text, new { @class = "forcheckbox" }) + @T("Enable this when running behind a reverse proxy server.") +
+
+ @Html.LabelFor(m => m.ClientIpAddressHeaderName, T("Client IP address header name")) + @Html.TextBoxFor(m => m.ClientIpAddressHeaderName, new { @class = "text medium" }) + @T("The client IP address header name to check for. Ususally this is the X-Forwarded-For header.") +
+