Skip to content

Commit

Permalink
Fix conflicts after merge
Browse files Browse the repository at this point in the history
  • Loading branch information
Mantas Janulionis committed Jul 12, 2016
1 parent 748ddef commit 762443b
Show file tree
Hide file tree
Showing 15 changed files with 246 additions and 19 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,4 @@ npm-debug.log
*.sass-cache
*.idea
Modules/BetterCms.Module.Root/Scripts/bcms.knockout-3.3.0.min.js
/.vs/config
26 changes: 24 additions & 2 deletions BetterCms.Core/Environment/Host/DefaultCmsHost.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Web;

using BetterCms.Core.Services;

using BetterModules.Core.DataAccess.DataContext.Migrations;
using BetterModules.Core.Web.Environment.Host;
using BetterModules.Core.Web.Modules.Registration;
Expand All @@ -8,16 +10,18 @@ namespace BetterCms.Core.Environment.Host
{
public class DefaultCmsHost : DefaultWebApplicationHost, ICmsHost
{
public DefaultCmsHost(IWebModulesRegistration modulesRegistration, IMigrationRunner migrationRunner)
private readonly IRedirectControl redirectControl;

public DefaultCmsHost(IWebModulesRegistration modulesRegistration, IMigrationRunner migrationRunner, IRedirectControl redirectControl)
: base(modulesRegistration, migrationRunner)
{
this.redirectControl = redirectControl;
}

/// <summary>
/// Called when the host application authenticates a web request.
/// </summary>
/// <param name="application"></param>
/// <exception cref="System.NotImplementedException"></exception>
public override void OnAuthenticateRequest(HttpApplication application)
{
// Impersonates user as anonymous, if requested
Expand All @@ -28,5 +32,23 @@ public override void OnAuthenticateRequest(HttpApplication application)

base.OnAuthenticateRequest(application);
}

/// <summary>
/// Called when the lifecyle of the request begins.
/// </summary>
/// <param name="application">The application.</param>
public override void OnBeginRequest(HttpApplication application)
{
var sourceUrl = application.Request.RawUrl;
if (redirectControl != null && string.IsNullOrEmpty(application.Request.CurrentExecutionFilePathExtension))
{
var redirectUrl = redirectControl.FindRedirect(sourceUrl);
if (!string.IsNullOrEmpty(redirectUrl))
{
application.Response.RedirectPermanent(redirectUrl);
}
}
base.OnBeginRequest(application);
}
}
}
15 changes: 15 additions & 0 deletions BetterCms.Core/Services/IRedirectControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace BetterCms.Core.Services
{
/// <summary>
/// Interface designed to control redirects
/// </summary>
public interface IRedirectControl
{
/// <summary>
/// Finds the redirect.
/// </summary>
/// <param name="source">The source url.</param>
/// <returns>Destination url</returns>
string FindRedirect(string source);
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -1374,4 +1374,7 @@
<data name="EditPageContent_AdvancedOptionsTab_Title" xml:space="preserve">
<value>Javascript &amp; CSS</value>
</data>
<data name="Redirect_InvalidUrl_Message" xml:space="preserve">
<value>The URL is invalid. It either contains restricted symbols or is not properly formed </value>
</data>
</root>
7 changes: 6 additions & 1 deletion Modules/BetterCms.Module.Pages/PagesConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ public static class PagesConstants
/// The intenal URL regular expression (used in page naming)
/// </summary>
// NOTE: After changing this regexp please run BetterCms.Test.Module.Pages.ServiceTests.UrlServiceTests test.
public const string InternalUrlRegularExpression = @"^\/?([^\\:?#[\]@!$&'()*+,;=""<> \/%]{1,260}\/)*([^\\:?#[\]@!$&'()*+,;=""<> \/%]{1,260})?$";
public const string InternalUrlRegularExpression = @"^\/?([^\\:?#[\]@!$&'()*+.,;=""<> \/%]{1,260}\/)*([^\\:?#[\]@!$&'()*+.,;=""<> \/%]{1,260})?$";

/// <summary>
/// The internal URL regular expression used for redirects
/// </summary>
public const string InternalUrlWithQueryStringRegularExpression = @"^\/?([^\\:?#[\]@!$&'()*+.,;=""<> \/%]{1,260}\/)*([^\\:?#[\]@!$&'()*+.,;=""<> \/%]{1,260})?((\?|#).*)?$";

/// <summary>
/// The external URL regular expression (used in sitemaps, external URLs)
Expand Down
5 changes: 4 additions & 1 deletion Modules/BetterCms.Module.Pages/PagesModuleDescriptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using BetterCms.Core.DataContracts.Enums;
using BetterCms.Core.Modules;
using BetterCms.Core.Modules.Projections;

using BetterCms.Core.Services;
using BetterCms.Events;

using BetterCms.Module.Pages.Accessors;
Expand All @@ -31,6 +31,7 @@
using BetterCms.Module.Root.ViewModels.Cms;

using BetterModules.Core.Modules.Registration;
using BetterModules.Events;

namespace BetterCms.Module.Pages
{
Expand Down Expand Up @@ -124,6 +125,7 @@ public PagesModuleDescriptor(ICmsConfiguration configuration)
CategoryAccessors.Register<PageCategoryAccessor>();

RootEvents.Instance.PageRetrieved += Events_PageRetrieved;
// BetterModules.Events.WebCoreEvents.Instance.

RegisterRenderingPageProperties();
}
Expand Down Expand Up @@ -229,6 +231,7 @@ public override void RegisterModuleTypes(ModuleRegistrationContext context, Cont
containerBuilder.RegisterType<DefaultDraftService>().AsImplementedInterfaces().InstancePerLifetimeScope();
containerBuilder.RegisterType<DefaultPageListService>().As<IPageListService>().InstancePerLifetimeScope();
containerBuilder.RegisterType<DefaultUntranslatedPageListService>().As<IUntranslatedPageListService>().InstancePerLifetimeScope();
containerBuilder.RegisterType<DefaultRedirectControl>().As<IRedirectControl>().InstancePerLifetimeScope();

// Registering root module, because root module register the last one, and this one should be before users module
containerBuilder.RegisterType<EmptyUserProfileUrlResolver>().As<IUserProfileUrlResolver>().InstancePerLifetimeScope();
Expand Down
73 changes: 73 additions & 0 deletions Modules/BetterCms.Module.Pages/Services/DefaultRedirectControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;

using BetterCms.Core.Services;
using BetterCms.Module.Pages.Models;

using BetterModules.Core.Web.Services.Caching;
using BetterModules.Events;

namespace BetterCms.Module.Pages.Services
{
/// <summary>
/// Class responsible for controlling redirect strategy
/// </summary>
public class DefaultRedirectControl : IRedirectControl
{
private readonly IRedirectService redirectService;

private readonly ICacheService cacheService;

private readonly ICmsConfiguration cmsConfiguration;

private readonly IUrlService urlService;

private string cacheKey = "CMS_redirect_control_all";

public DefaultRedirectControl(IRedirectService redirectService, ICacheService cacheService, ICmsConfiguration cmsConfiguration, IUrlService urlService)
{
this.redirectService = redirectService;
this.cacheService = cacheService;
this.cmsConfiguration = cmsConfiguration;
this.urlService = urlService;

Events.PageEvents.Instance.RedirectCreated += InvalidateCache;
Events.PageEvents.Instance.RedirectUpdated += InvalidateCache;
Events.PageEvents.Instance.RedirectDeleted += InvalidateCache;
}

/// <summary>
/// Finds the redirect.
/// </summary>
/// <param name="source">The source url.</param>
/// <returns>
/// Destination url
/// </returns>
public string FindRedirect(string source)
{
string redirectDestinationUrl = null;
var useCache = cmsConfiguration.Cache.Enabled;
if (urlService.ValidateInternalUrl(source))
{
source = urlService.FixUrl(source);
}
if (useCache)
{
var redirects = cacheService.Get(cacheKey, cmsConfiguration.Cache.Timeout, () => redirectService.GetAllRedirects());
redirectDestinationUrl = redirects.Where(x => x.PageUrl.Equals(source, StringComparison.InvariantCultureIgnoreCase)).Select(x => x.RedirectUrl).FirstOrDefault();
}
else
{
return redirectService.GetRedirect(source);
}

return redirectDestinationUrl;
}

private void InvalidateCache(SingleItemEventArgs<Redirect> args)
{
cacheService.Remove(cacheKey);
}
}
}
42 changes: 31 additions & 11 deletions Modules/BetterCms.Module.Pages/Services/DefaultRedirectService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ private void RecursiveCircularLoop(IList<Redirect> redirects, string redirectUrl
/// Gets the list with all redirects.
/// </summary>
/// <returns>The list with all redirectss</returns>
private IList<Redirect> GetAllRedirects()
public IList<Redirect> GetAllRedirects()
{
var query = unitOfWork
.Session
Expand Down Expand Up @@ -200,26 +200,46 @@ public string GetRedirect(string url)

public Redirect SaveRedirect(ViewModels.SiteSettings.SiteSettingRedirectViewModel model, bool createIfNotExists = false)
{
var isRedirectInternal = urlService.ValidateInternalUrl(model.RedirectUrl);
if (!isRedirectInternal && urlService.ValidateInternalUrl(urlService.FixUrl(model.RedirectUrl)))
bool isDestinationInternal;
bool isDestinationInternalWithQueryString = false;
isDestinationInternal = urlService.ValidateInternalUrl(model.RedirectUrl);
if (!isDestinationInternal && urlService.ValidateInternalUrl(urlService.FixUrl(model.RedirectUrl)))
{
isRedirectInternal = true;
isDestinationInternal = true;
}

model.PageUrl = urlService.FixUrl(model.PageUrl);
if (isRedirectInternal)
if (urlService.ValidateInternalUrlWithQueryString(model.RedirectUrl))
{
model.RedirectUrl = urlService.FixUrl(model.RedirectUrl);
isDestinationInternalWithQueryString = true;
}

// Validate request
if (!urlService.ValidateInternalUrl(model.PageUrl))
// Validate redirect source field
var isSourceUrlInternal = urlService.ValidateInternalUrl(model.PageUrl);
var isSourceUrlInternalWithQueryString = urlService.ValidateInternalUrlWithQueryString(model.PageUrl);
if (isSourceUrlInternal)
{
model.PageUrl = urlService.FixUrl(model.PageUrl);
}
else if (isSourceUrlInternalWithQueryString)
{
model.PageUrl = urlService.FixUrlFront(model.PageUrl);
}
else
{
var message = PagesGlobalization.SaveRedirect_InvalidPageUrl_Message;
var logMessage = string.Format("Invalid page url {0}.", model.PageUrl);
throw new ValidationException(() => message, logMessage);
}
if (!urlService.ValidateExternalUrl(model.RedirectUrl))

if (isDestinationInternal)
{
model.RedirectUrl = urlService.FixUrl(model.RedirectUrl);
}
else if (isDestinationInternalWithQueryString)
{
model.RedirectUrl = urlService.FixUrlFront(model.RedirectUrl);
}
else if (!urlService.ValidateExternalUrl(model.RedirectUrl))
{
var message = PagesGlobalization.SaveRedirect_InvalidRedirectUrl_Message;
var logMessage = string.Format("Invalid redirect url {0}.", model.RedirectUrl);
Expand All @@ -233,7 +253,7 @@ public Redirect SaveRedirect(ViewModels.SiteSettings.SiteSettingRedirectViewMode
var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, model.PageUrl);
throw new ValidationException(() => patternsValidationMessage, logMessage);
}
if (isRedirectInternal && !urlService.ValidateUrlPatterns(model.RedirectUrl, out patternsValidationMessage, PagesGlobalization.SaveRedirect_RedirectUrl_Name))
if ((isDestinationInternal || isDestinationInternalWithQueryString) && !urlService.ValidateUrlPatterns(model.RedirectUrl, out patternsValidationMessage, PagesGlobalization.SaveRedirect_RedirectUrl_Name))
{
var logMessage = string.Format("{0}. URL: {1}.", patternsValidationMessage, model.PageUrl);
throw new ValidationException(() => patternsValidationMessage, logMessage);
Expand Down
28 changes: 27 additions & 1 deletion Modules/BetterCms.Module.Pages/Services/DefaultUrlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,17 @@ public bool ValidateInternalUrl(string url)
{
return Regex.IsMatch(url, PagesConstants.InternalUrlRegularExpression);
}


/// <summary>
/// Validates the internal URL with query string.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>true, if url is valid for internal use</returns>
public bool ValidateInternalUrlWithQueryString(string url)
{
return Regex.IsMatch(url, PagesConstants.InternalUrlWithQueryStringRegularExpression) && Uri.IsWellFormedUriString(url, UriKind.Relative);
}

/// <summary>
/// Validates the internal URL.
/// </summary>
Expand Down Expand Up @@ -239,6 +249,22 @@ public string FixUrl(string url)
return url;
}

public string FixUrlFront(string url)
{
if (!string.IsNullOrWhiteSpace(url))
{
if (url.Trim() == "/")
{
return url;
}
if (!url.StartsWith("/", StringComparison.Ordinal))
{
url = string.Concat("/", url);
}
}
return url;
}

/// <summary>
/// Validates the URL patterns.
/// </summary>
Expand Down
8 changes: 8 additions & 0 deletions Modules/BetterCms.Module.Pages/Services/IRedirectService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;

using BetterCms.Module.Pages.Models;
using BetterCms.Module.Pages.ViewModels.SiteSettings;
Expand Down Expand Up @@ -67,5 +68,12 @@ public interface IRedirectService
/// <param name="version">The version.</param>
/// <returns>Deletion result</returns>
bool DeleteRedirect(Guid id, int version);


/// <summary>
/// Gets all redirects.
/// </summary>
/// <returns>List of redirects</returns>
IList<Redirect> GetAllRedirects();
}
}
9 changes: 9 additions & 0 deletions Modules/BetterCms.Module.Pages/Services/IUrlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ public interface IUrlService
/// <param name="url">The URL.</param>
/// <returns>true, if url is valid for internal use (without http:// and other prefixes and any suffixes, such as ?# etc.)</returns>
bool ValidateInternalUrl(string url);

/// <summary>
/// Validates the internal URL with query string.
/// </summary>
/// <param name="url">The URL.</param>
/// <returns>true, if url is valid for internal use</returns>
bool ValidateInternalUrlWithQueryString(string url);

/// <summary>
/// Validates the external URL.
Expand All @@ -42,5 +49,7 @@ public interface IUrlService
/// <param name="url">The URL.</param>
/// <returns>Fixed url</returns>
string FixUrl(string url);

string FixUrlFront(string url);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class SiteSettingRedirectViewModel : IEditableGridItem
[AllowHtml]
[DisallowHtml(ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_DisallowHtml_PageUrl_Message")]
[Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_RequiredAttribute_Message")]
[RegularExpression(PagesConstants.InternalUrlRegularExpression, ErrorMessageResourceType = typeof(PagesGlobalization), ErrorMessageResourceName = "PageProperties_PageUrl_InvalidMessage")]
[RegularExpression(PagesConstants.InternalUrlWithQueryStringRegularExpression, ErrorMessageResourceType = typeof(PagesGlobalization), ErrorMessageResourceName = "Redirect_InvalidUrl_Message")]
[StringLength(MaxLength.Url, ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_StringLengthAttribute_Message")]
public string PageUrl { get; set; }

Expand All @@ -51,7 +51,7 @@ public class SiteSettingRedirectViewModel : IEditableGridItem
[AllowHtml]
[DisallowHtml(ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_DisallowHtml_RedirectUrl_Message")]
[Required(AllowEmptyStrings = false, ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_RequiredAttribute_Message")]
[RegularExpression(PagesConstants.ExternalUrlRegularExpression, ErrorMessageResourceType = typeof(PagesGlobalization), ErrorMessageResourceName = "RedirectUrl_InvalidMessage")]
[RegularExpression(PagesConstants.ExternalUrlRegularExpression, ErrorMessageResourceType = typeof(PagesGlobalization), ErrorMessageResourceName = "Redirect_InvalidUrl_Message")]
[StringLength(MaxLength.Url, ErrorMessageResourceType = typeof(RootGlobalization), ErrorMessageResourceName = "Validation_StringLengthAttribute_Message")]
public string RedirectUrl { get; set; }

Expand Down
Loading

0 comments on commit 762443b

Please sign in to comment.