\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html
index a12358d5e..db3f65f78 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html
@@ -1,22 +1,27 @@
-
-
-
+
+
+
+
-
-
+
+
+
-
-
+
+
+
-
-
+
+
+
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html
index a6386f372..97e08843b 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html
@@ -1,18 +1,22 @@
-
-
-
+
+
+
+
-
-
+
+
+
-
-
+
+
+
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+6+3.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+6+3.html
index 2ee40381e..a0d9f0538 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+6+3.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+6+3.html
@@ -1,14 +1,17 @@
-
-
-
+
+
+
+
-
-
+
+
+
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html
index 2001037bb..7e9733a7b 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html
@@ -1,10 +1,12 @@
-
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html
index b1477e9ba..f4b2485f3 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html
@@ -1,10 +1,12 @@
-
-
-
+
+
+
+
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html
index fc544183d..d392b690b 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html
@@ -1,10 +1,11 @@
-
-
-
+
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html
index 916ce1cb2..5fa5e715b 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html
@@ -1,10 +1,12 @@
-
-
-
+
+
+
+
-
-
+
+
+
-
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html
index 1aed765ba..c43e44cc8 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html
@@ -1,10 +1,11 @@
-
-
-
+
+
+
+
-
-
+
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/IInitializer.cs b/Telerik.Sitefinity.Frontend/IInitializer.cs
new file mode 100644
index 000000000..088066e9c
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/IInitializer.cs
@@ -0,0 +1,18 @@
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Exposes methods for initializig and reverting the initialization (uninitializing).
+ ///
+ internal interface IInitializer
+ {
+ ///
+ /// Initializes this instance.
+ ///
+ void Initialize();
+
+ ///
+ /// Uninitializes this instance.
+ ///
+ void Uninitialize();
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/InlineEditing/HtmlProcessor.cs b/Telerik.Sitefinity.Frontend/InlineEditing/HtmlProcessor.cs
index 21407ba96..02c42dec8 100644
--- a/Telerik.Sitefinity.Frontend/InlineEditing/HtmlProcessor.cs
+++ b/Telerik.Sitefinity.Frontend/InlineEditing/HtmlProcessor.cs
@@ -3,6 +3,7 @@
using System.Linq;
using System.Web.Mvc;
using Telerik.Sitefinity.Frontend.InlineEditing.Attributes;
+using Telerik.Sitefinity.Services;
namespace Telerik.Sitefinity.Frontend.InlineEditing
{
@@ -11,6 +12,23 @@ namespace Telerik.Sitefinity.Frontend.InlineEditing
///
public class HtmlProcessor
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public HtmlProcessor()
+ : this(SystemManager.IsInlineEditingMode)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Value indicating whether HTML should be generated for inline editing.
+ public HtmlProcessor(bool isInlineEditing)
+ {
+ this.isInlineEditingMode = isInlineEditing;
+ }
+
///
/// Creates a region which has the required by the InlineEditing attributes.
///
@@ -21,17 +39,24 @@ public class HtmlProcessor
///
public virtual HtmlRegion CreateInlineEditingRegion(TextWriter writer, string providerName, string type, Guid id)
{
- string htmlTagType = "div";
- var tagBuilder = new TagBuilder(htmlTagType);
- tagBuilder.Attributes.Add("data-sf-provider", providerName);
- tagBuilder.Attributes.Add("data-sf-type", type);
+ if (this.isInlineEditingMode)
+ {
+ string htmlTagType = "div";
+ var tagBuilder = new TagBuilder(htmlTagType);
+ tagBuilder.Attributes.Add("data-sf-provider", providerName);
+ tagBuilder.Attributes.Add("data-sf-type", type);
- if (id != Guid.Empty)
- tagBuilder.Attributes.Add("data-sf-id", id.ToString());
+ if (id != Guid.Empty)
+ tagBuilder.Attributes.Add("data-sf-id", id.ToString());
- writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
+ writer.Write(tagBuilder.ToString(TagRenderMode.StartTag));
- return new HtmlRegion(writer, htmlTagType);
+ return new HtmlRegion(writer, htmlTagType);
+ }
+ else
+ {
+ return new HtmlRegion(writer, string.Empty);
+ }
}
///
@@ -51,7 +76,7 @@ public virtual string GetStringContent(object model, string propName)
var fieldInfoAttr = propattr.OfType().FirstOrDefault();
- if (fieldInfoAttr == null)
+ if (fieldInfoAttr == null || !this.isInlineEditingMode)
{
htmlString = propValue.ToString();
}
@@ -63,6 +88,8 @@ public virtual string GetStringContent(object model, string propName)
return htmlString;
}
+ private bool isInlineEditingMode;
+
public const string InlineEditingHtmlWrapper = "
{2}
";
}
}
diff --git a/Telerik.Sitefinity.Frontend/InterfaceMappings.cs b/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
index db125b637..147979f5c 100644
--- a/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
+++ b/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
@@ -11,6 +11,8 @@ public class InterfaceMappings : NinjectModule
public override void Load()
{
Bind().To();
+ Bind().To();
+ Bind().To();
}
}
}
diff --git a/Telerik.Sitefinity.Frontend/Modules/Comments/ReviewNotificationStrategy.cs b/Telerik.Sitefinity.Frontend/Modules/Comments/ReviewNotificationStrategy.cs
new file mode 100644
index 000000000..8c537930a
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Modules/Comments/ReviewNotificationStrategy.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Linq;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Services.Comments;
+using Telerik.Sitefinity.Services.Comments.Impl.Notifications;
+using Telerik.Sitefinity.Services.Comments.Notifications;
+using Telerik.Sitefinity.Services.Notifications;
+
+namespace Telerik.Sitefinity.Frontend.Modules.Comments
+{
+ ///
+ /// Notification strategy that is responsible for notifications for objects..
+ ///
+ internal class ReviewNotificationStrategy : CommentNotificationsStrategy, ICommentNotificationsStrategy
+ {
+ ///
+ /// Gets the message template unique identifier for the specified event.
+ ///
+ /// The event.
+ protected override IMessageTemplateRequest GetMessageTemplate(ICommentEvent @event)
+ {
+ if (@event == null || @event.Item == null)
+ return null;
+
+ IMessageTemplateRequest messageTemplate;
+ IComment comment = @event.Item;
+ ICommentService cs = SystemManager.GetCommentsService();
+ IThread thread = cs.GetThread(comment.ThreadKey);
+
+ if (this.IsReviewThread(thread))
+ {
+ messageTemplate = this.GetNewReviewMessageTemplate();
+ }
+ else
+ {
+ messageTemplate = this.GetNewCommentMessageTemplate();
+ }
+
+ return messageTemplate;
+ }
+
+ private bool IsReviewThread(IThread thread)
+ {
+ var isReview = false;
+
+ if (thread.Behavior.IsNullOrEmpty())
+ {
+ isReview = thread.Key.EndsWith("_review", StringComparison.Ordinal);
+ }
+ else
+ {
+ isReview = thread.Behavior == "review";
+ }
+
+ return isReview;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js
new file mode 100644
index 000000000..a167d157b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js
@@ -0,0 +1,90 @@
+Type.registerNamespace("Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI");
+
+Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor = function(element){
+ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.initializeBase(this, [element]);
+
+ this._containerToHide = {};
+ this._mvcTypes = null;
+ this._elementIdsToBeHiddenIfMvc = null;
+};
+
+Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.prototype = {
+
+ initialize: function () {
+ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "initialize");
+
+ this._containerToHide = this.getInsertSectionContainer();
+ this._containerToHide.hide();
+ },
+
+ dispose: function () {
+ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "dispose");
+ },
+
+ _valueChangedHandler: function (sender, args) {
+
+ if (!this._isMvcController(sender.target.value)) {
+ this._dataBindPropertiesItemsList(this._commonPropertiesItemsList);
+ this._toggleVisibilityOfElements(true);
+ } else {
+ this._toggleVisibilityOfElements(false);
+ }
+
+ this._setModuleTitle();
+ this._bindFields();
+ },
+ // called when data binding was successful
+ _dataBindSuccess: function (sender, result) {
+ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "_dataBindSuccess", [sender, result]);
+
+ if (sender._isMvcController(result.Item.ControlType)) {
+ sender._toggleVisibilityOfElements(false);
+ } else {
+ sender._toggleVisibilityOfElements(true);
+ }
+ },
+
+ _isMvcController: function (item) {
+ var len = item.indexOf("-");
+
+ if (len > -1) {
+ var mvcControllerType = item.substring(0, len);
+ var mvcControllerTypesArray = JSON.parse(this.get_mvcTypes());
+
+ for (var i = 0; i < mvcControllerTypesArray.length; i++)
+ if (mvcControllerTypesArray[i] == mvcControllerType)
+ return true;
+ }
+
+ return false;
+ },
+
+ _toggleVisibilityOfElements: function(visible){
+ var elements = JSON.parse(this.get_elementIdsToBeHiddenIfMvc());
+
+ for (var i = 0; i < elements.length; i++) {
+ var uiElement = jQuery('#' + elements[i]);
+ uiElement.toggle(visible);
+ }
+
+ this._containerToHide.toggle(visible);
+ },
+
+ get_mvcTypes: function () {
+ return this._mvcTypes;
+ },
+
+ set_mvcTypes: function (value) {
+ this._mvcTypes = value;
+ },
+
+ get_elementIdsToBeHiddenIfMvc: function () {
+ return this._elementIdsToBeHiddenIfMvc;
+ },
+
+ set_elementIdsToBeHiddenIfMvc: function (value) {
+ this._elementIdsToBeHiddenIfMvc = value;
+ }
+}
+
+Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.registerClass("Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor", Telerik.Sitefinity.Modules.ControlTemplates.Web.UI.ControlTemplateEditor);
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs
new file mode 100644
index 000000000..ab0b5c5b3
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Script.Serialization;
+using System.Web.UI;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Modules.ControlTemplates.Web.UI;
+using Telerik.Sitefinity.Web.UI;
+
+namespace Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI
+{
+ ///
+ /// Override the original control template editor for Mvc based widgets
+ ///
+ internal class MvcControlTemplateEditor : ControlTemplateEditor
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MvcControlTemplateEditor() : base()
+ {
+ }
+
+ ///
+ public override string ClientComponentType
+ {
+ get
+ {
+ return typeof(MvcControlTemplateEditor).FullName;
+ }
+ }
+
+ ///
+ public override IEnumerable GetScriptReferences()
+ {
+ var scripts = new List(base.GetScriptReferences());
+
+ scripts.Add(new ScriptReference(MvcControlTemplateEditor.MvcControlTemplateEditorScript, typeof(MvcControlTemplateEditor).Assembly.FullName));
+
+ return scripts;
+ }
+
+ ///
+ /// Gets Mvc controllers fullnames for current Templatable Controls.
+ ///
+ /// List of .
+ public override IEnumerable GetScriptDescriptors()
+ {
+ var descriptor = (ScriptComponentDescriptor)base.GetScriptDescriptors().Last();
+
+ var mvcTypesFullNames = Telerik.Sitefinity.Modules.ControlTemplates.ControlTemplates.GetTemplatableControls()
+ .Where(c => c.Value != null && c.Value.DataItemType != null)
+ .Where(c => FrontendManager.ControllerFactory.IsController(c.Value.DataItemType))
+ .Select(c => c.Value.DataItemType.FullName);
+
+ descriptor.AddProperty("mvcTypes", new JavaScriptSerializer().Serialize(mvcTypesFullNames));
+
+ descriptor.AddProperty("elementIdsToBeHiddenIfMvc", new JavaScriptSerializer().Serialize(this.GetRestoreButtonsClientIds()));
+
+ return new ScriptDescriptor[] { descriptor };
+ }
+
+ ///
+ /// Gets the restore buttons client ids.
+ ///
+ private List GetRestoreButtonsClientIds()
+ {
+ var returnResult = new List();
+
+ var topButton = BottomCommandBar.Commands.OfType()
+ .SingleOrDefault(x => x.CommandName == "restoreTemplate");
+
+ if (topButton != null)
+ returnResult.Add(topButton.ButtonClientId);
+
+ var bottomButton = TopCommandBar.Commands.OfType()
+ .SingleOrDefault(x => x.CommandName == "restoreTemplate");
+
+ if (bottomButton != null)
+ returnResult.Add(bottomButton.ButtonClientId);
+
+ return returnResult;
+ }
+
+ internal const string MvcControlTemplateEditorScript = "Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.Scripts.MvcControlTemplateEditor.js";
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/CompositePrecompiledMvcEngineWrapper.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/CompositePrecompiledMvcEngineWrapper.cs
new file mode 100644
index 000000000..6432268f4
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/CompositePrecompiledMvcEngineWrapper.cs
@@ -0,0 +1,247 @@
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Security.Cryptography;
+using System.Web.Caching;
+using System.Web.Hosting;
+using System.Web.Mvc;
+using RazorGenerator.Mvc;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Configuration;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
+{
+ ///
+ /// View engine that serves precompiled views from several assemblies.
+ ///
+ internal class CompositePrecompiledMvcEngineWrapper : CompositePrecompiledMvcEngine
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The view assemblies.
+ public CompositePrecompiledMvcEngineWrapper(params PrecompiledViewAssemblyWrapper[] viewAssemblies)
+ : this(viewAssemblies, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The view assemblies.
+ /// The view page activator.
+ public CompositePrecompiledMvcEngineWrapper(IEnumerable viewAssemblies, IViewPageActivator viewPageActivator)
+ : this(viewAssemblies, viewPageActivator, string.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The view assemblies.
+ /// The view page activator.
+ /// Name of the package.
+ public CompositePrecompiledMvcEngineWrapper(IEnumerable viewAssemblies, IViewPageActivator viewPageActivator, string packageName)
+ : base(viewAssemblies.Select(a => a.PrecompiledViewAssembly), viewPageActivator)
+ {
+ this.precompiledAssemblies = viewAssemblies.ToArray();
+ this.packageName = packageName;
+ }
+
+ ///
+ /// Gets the name of the package.
+ ///
+ ///
+ /// The name of the package.
+ ///
+ public string PackageName
+ {
+ get
+ {
+ return this.packageName;
+ }
+ }
+
+ ///
+ /// Creates a new instance that is a clone of this one.
+ ///
+ /// The new instance.
+ public CompositePrecompiledMvcEngineWrapper Clone()
+ {
+ return new CompositePrecompiledMvcEngineWrapper(this.precompiledAssemblies, null, this.PackageName);
+ }
+
+ ///
+ /// Computes the hash of the data in the given stream in the expected format for the precompiled assemblies to compare resources.
+ ///
+ /// The stream.
+ /// Base64 string of the MD5 hash of the data in the stream.
+ internal static string ComputeHash(Stream stream)
+ {
+ using (var algorithm = SHA256.Create())
+ {
+ return Convert.ToBase64String(algorithm.ComputeHash(stream));
+ }
+ }
+
+ internal IEnumerable GetViews(IEnumerable viewLocations)
+ {
+ var views = new List();
+ if (Config.Get().DisablePrecompilation)
+ return views;
+
+ foreach (var viewLocation in viewLocations)
+ {
+ foreach (var asm in this.precompiledAssemblies)
+ {
+ var precompiledViews = asm.GetViews(viewLocation);
+ if (precompiledViews != null)
+ {
+ views.AddRange(precompiledViews);
+ }
+ }
+ }
+
+ return views;
+ }
+
+ ///
+ /// Files the exists.
+ ///
+ /// The controller context.
+ /// The virtual path.
+ ///
+ protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
+ {
+ return !Config.Get().DisablePrecompilation && base.FileExists(controllerContext, virtualPath) && (Config.Get().AlwaysUsePrecompiledVersion || this.ShouldServe(controllerContext, virtualPath));
+ }
+
+ ///
+ /// Creates the specified view by using the controller context, path of the view, and path of the master view.
+ ///
+ /// The controller context.
+ /// The path of the view.
+ /// The path of the master view.
+ ///
+ /// A reference to the view.
+ ///
+ protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
+ {
+ this.LogPrecompiledViewUsage(controllerContext, viewPath);
+ return base.CreateView(controllerContext, viewPath, masterPath);
+ }
+
+ ///
+ /// Creates the specified partial view by using the specified controller context.
+ ///
+ /// The controller context.
+ /// The partial path for the new partial view.
+ ///
+ /// A reference to the partial view.
+ ///
+ protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
+ {
+ this.LogPrecompiledViewUsage(controllerContext, partialPath);
+ return base.CreatePartialView(controllerContext, partialPath);
+ }
+
+ private void LogPrecompiledViewUsage(ControllerContext controllerContext, string viewPath)
+ {
+ if (Config.Get().LogPrecompiledViewUsage && controllerContext != null && controllerContext.Controller != null && controllerContext.RouteData != null)
+ {
+ var controllerName = controllerContext.Controller.GetType().Name;
+ var actionName = controllerContext.RouteData.Values["action"];
+ var message = string.Format("Precompiled view used for controller: \"{0}\", action: \"{1}\", ViewPath: {2}", controllerName, actionName, viewPath);
+ Log.Write(message, TraceEventType.Information);
+ }
+ }
+
+ // If for any of the location we find newer version of the file outside the precompiled assebly we add "servePrecompiled" flag in the RotueData.
+ // This way we ensure that view with bigger prority will be served instead of the first view found from the precompiled view engine.
+ // See #299642
+ private bool ShouldServe(ControllerContext controllerContext, string virtualPath)
+ {
+ if (controllerContext.RouteData.Values.ContainsKey("servePrecompiled") && controllerContext.RouteData.Values["servePrecompiled"].ToString() == "false")
+ return false;
+
+ string precompiledFileHash = null;
+ foreach (var asm in this.precompiledAssemblies)
+ {
+ precompiledFileHash = asm.GetFileHash(virtualPath);
+ if (precompiledFileHash != null)
+ break;
+ }
+
+ if (precompiledFileHash == null)
+ return true;
+
+ string virtualResourceHash = null;
+ if (precompiledFileHash != null)
+ {
+ virtualResourceHash = this.VirtualResourceHash(virtualPath);
+ }
+
+ var shouldServe = virtualResourceHash == null || virtualResourceHash == precompiledFileHash;
+
+ if (!shouldServe)
+ {
+ controllerContext.RouteData.Values.Add("servePrecompiled", "false");
+ }
+
+ return shouldServe;
+ }
+
+ private string VirtualResourceHash(string virtualPath)
+ {
+ CompositePrecompiledMvcEngineWrapper.HashCacheRecord hashRecord;
+ if (CompositePrecompiledMvcEngineWrapper.HashCache.ContainsKey(virtualPath))
+ {
+ hashRecord = CompositePrecompiledMvcEngineWrapper.HashCache[virtualPath];
+ if (hashRecord.Dependency == null || !hashRecord.Dependency.HasChanged)
+ return hashRecord.Hash;
+ }
+
+ lock (CompositePrecompiledMvcEngineWrapper.HashCache)
+ {
+ if (HostingEnvironment.VirtualPathProvider.FileExists(virtualPath))
+ {
+ using (var stream = HostingEnvironment.VirtualPathProvider.GetFile(virtualPath).Open())
+ {
+ hashRecord = new CompositePrecompiledMvcEngineWrapper.HashCacheRecord()
+ {
+ Hash = CompositePrecompiledMvcEngineWrapper.ComputeHash(stream),
+ Dependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, new object[0], DateTime.UtcNow)
+ };
+ }
+ }
+ else
+ {
+ hashRecord = new CompositePrecompiledMvcEngineWrapper.HashCacheRecord()
+ {
+ Hash = null,
+ Dependency = null
+ };
+ }
+
+ CompositePrecompiledMvcEngineWrapper.HashCache[virtualPath] = hashRecord;
+ }
+
+ return hashRecord.Hash;
+ }
+
+ private class HashCacheRecord
+ {
+ public string Hash { get; set; }
+
+ public CacheDependency Dependency { get; set; }
+ }
+
+ private static readonly ConcurrentDictionary HashCache = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
+
+ private readonly PrecompiledViewAssemblyWrapper[] precompiledAssemblies;
+ private readonly string packageName;
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs
index eb01e3adc..c3941a4e4 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs
@@ -1,15 +1,20 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Reflection;
using System.Web.Mvc;
using Telerik.Sitefinity.Frontend.Mvc.Helpers.ViewModels;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
+using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.Web.UI;
namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
{
///
/// This class represents controller for pager widget.
///
+ [Localization(typeof(ContentPagerResources))]
+ [ControllerMetadata(IsTemplatableControl = false)]
public class ContentPagerController : Controller
{
///
@@ -18,12 +23,112 @@ public class ContentPagerController : Controller
/// The current page.
/// The total pages count.
/// The template of the URL used for redirecting.
+ /// The display count of the pages.
///
- public PartialViewResult Index(int currentPage, int totalPagesCount, string redirectUrlTemplate)
+ [OutputCache(Duration = 1)]
+ public PartialViewResult Index(int currentPage, int totalPagesCount, string redirectUrlTemplate, int displayCount = 10)
{
- var model = new PagerViewModel(currentPage, totalPagesCount, redirectUrlTemplate);
+ var model = new PagerViewModel(currentPage, totalPagesCount, redirectUrlTemplate, displayCount);
+
+ // Build the pager
+ int startIndex = 1;
+ if (model.CurrentPage > model.DisplayCount)
+ {
+ if (model.CurrentPage <= 0)
+ model.CurrentPage = 1;
+
+ startIndex = ((int)Math.Floor((double)(model.CurrentPage - 1) / model.DisplayCount) * model.DisplayCount) + 1;
+ }
+
+ int endIndex = Math.Min(model.TotalPagesCount, (startIndex + model.DisplayCount) - 1);
+
+ // Check to see if we need a Previous Button Node
+ if (startIndex > model.DisplayCount)
+ {
+ model.PreviousNode = new Pager.PagerNumericItem(Math.Max(startIndex - 1, 1));
+ }
+ else
+ {
+ model.PreviousNode = null;
+ }
+
+ for (int i = startIndex; i <= endIndex; i++)
+ {
+ var text = string.Format(model.RedirectUrlTemplate, i);
+ Pager.PagerNumericItem pagerNumericItem = new Pager.PagerNumericItem(i, text);
+ model.PagerNodes.Add(pagerNumericItem);
+ }
+
+ // Check to see if we need a Next Button Node
+ if (endIndex < model.TotalPagesCount)
+ {
+ model.NextNode = new Pager.PagerNumericItem(endIndex + 1);
+ }
+ else
+ {
+ model.NextNode = null;
+ }
+
+ this.TryStorePaginationUrls(model);
return this.PartialView("Pager", model);
}
+
+ internal static object GetPaginationUrls(string nextUrl, string previousUrl)
+ {
+ var paginationUrlsType = Type.GetType("Telerik.Sitefinity.ContentLocations.PaginationUrls, Telerik.Sitefinity");
+ var instance = Activator.CreateInstance(paginationUrlsType, nonPublic: true);
+ paginationUrlsType.GetProperty("PreviousUrl").SetValue(instance, previousUrl);
+ paginationUrlsType.GetProperty("NextUrl").SetValue(instance, nextUrl);
+
+ return instance;
+ }
+
+ internal static MethodInfo GetTryStorePaginationUrlsMethod()
+ {
+ var canonicalUrlExtensions = Type.GetType("Telerik.Sitefinity.ContentLocations.CanonicalUrlPageExtensions, Telerik.Sitefinity");
+ return canonicalUrlExtensions.GetMethod("TryStorePaginationUrls", BindingFlags.Static | BindingFlags.NonPublic);
+ }
+
+ private static string PageNodeUrl(Pager.PagerNumericItem node, string template)
+ {
+ if (node == null)
+ return null;
+ else
+ return RouteHelper.ResolveUrl(string.Format(template, node.PageNumber), UrlResolveOptions.Absolute);
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "paginationUrls")]
+ private void TryStorePaginationUrls(PagerViewModel model)
+ {
+ string nextUrl;
+ var highlightedIndex = model.CurrentPage % model.DisplayCount;
+ if (highlightedIndex == 0)
+ {
+ highlightedIndex = model.DisplayCount;
+ }
+
+ if (model.CurrentPage > 0 && model.CurrentPage < model.TotalPagesCount)
+ if (highlightedIndex == model.DisplayCount)
+ nextUrl = ContentPagerController.PageNodeUrl(model.NextNode, model.RedirectUrlTemplate);
+ else
+ nextUrl = ContentPagerController.PageNodeUrl(model.PagerNodes[highlightedIndex], model.RedirectUrlTemplate);
+ else
+ nextUrl = null;
+
+ string previousUrl;
+ if (model.CurrentPage > 1 && model.CurrentPage <= model.TotalPagesCount)
+ if (highlightedIndex > 1)
+ previousUrl = ContentPagerController.PageNodeUrl(model.PagerNodes[highlightedIndex - 2], model.RedirectUrlTemplate);
+ else
+ previousUrl = ContentPagerController.PageNodeUrl(model.PreviousNode, model.RedirectUrlTemplate);
+ else
+ previousUrl = null;
+
+ var paginationUrls = ContentPagerController.GetPaginationUrls(nextUrl, previousUrl);
+
+ var page = this.HttpContext.Handler.GetPageHandler();
+ ContentPagerController.GetTryStorePaginationUrlsMethod().Invoke(null, new object[] { page, paginationUrls });
+ }
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs
index e5ecdf35e..67be1f5a7 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs
@@ -1,10 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Web.Mvc;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
-using Telerik.Sitefinity.Frontend.Mvc.Models;
-using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using System;
+using System.Collections.Generic;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using System.Web.UI;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
+using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using Telerik.Sitefinity.Modules;
+using Telerik.Sitefinity.Modules.Forms;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Services;
namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
{
@@ -13,19 +21,28 @@ namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
///
[Localization(typeof(DesignerResources))]
[RequestBackendUserAuthentication]
+ [ControllerMetadataAttribute(IsTemplatableControl = false)]
public class DesignerController : Controller
- {
+ {
///
/// Returns the designer view which handles the property editing for a particular widget.
/// If there is custom designer for the particular widget it will be retrieved, otherwise it will fallback to the default designer.
/// The default designer is located under .
///
/// The name of the widget.
- public virtual ActionResult Master(string widgetName)
- {
+ /// The name of the widget dynamic module (if any). Default value is null.
+ public virtual ActionResult Master(string widgetName, string moduleName = null)
+ {
+ this.GetHttpContext().Items[SystemManager.IsBackendRequestKey] = true;
+
+ var controlId = this.Request != null ? this.Request["controlId"] ?? Guid.Empty.ToString() : Guid.Empty.ToString();
+ var mediaType = this.Request != null ? this.Request["mediaType"] ?? DesignMediaType.Page.ToString() : DesignMediaType.Page.ToString();
+
this.ViewBag.ControlName = widgetName;
+ this.ViewBag.ControlId = controlId;
+ this.ViewBag.MediaType = mediaType;
- var model = this.GetModel(widgetName);
+ var model = this.GetModel(widgetName, Guid.Parse(controlId), moduleName, (DesignMediaType)Enum.Parse(typeof(DesignMediaType), mediaType));
return this.View(DesignerController.DefaultView, model);
}
@@ -39,28 +56,107 @@ public virtual ActionResult Master(string widgetName)
///
/// View cannot be found on the searched locations.
public new virtual ActionResult View(string widgetName, string viewType)
- {
- var viewName = DesignerController.DesignerViewTemplate.Arrange(viewType);
+ {
+ this.GetHttpContext().Items[SystemManager.IsBackendRequestKey] = true;
+
+ var viewName = DesignerController.DesignerViewTemplate.Arrange(viewType);
+ var mediaType = this.Request != null ? this.Request["mediaType"] ?? DesignMediaType.Page.ToString() : DesignMediaType.Page.ToString();
+ var designMediaType = (DesignMediaType)Enum.Parse(typeof(DesignMediaType), mediaType);
+
+ var model = this.GetViewModel(designMediaType);
- return this.PartialView(viewName);
+ // Passing the DesignerModel to the view model
+ var controlIdParam = this.GetControlIdParam();
+ if (controlIdParam.HasValue)
+ {
+ ViewBag.DesignerModel = this.GetModel(widgetName, controlIdParam.Value, designMediaType: designMediaType);
+ }
+
+ return this.PartialView(viewName, model);
+ }
+
+ ///
+ /// Gets the HTTP context.
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ protected virtual HttpContextBase GetHttpContext()
+ {
+ return this.HttpContext;
}
+ private Guid? GetControlIdParam()
+ {
+ if (this.Request == null)
+ return null;
+
+ var controlIdParam = this.Request["controlId"];
+ if (controlIdParam == null)
+ return null;
+
+ Guid controlIdParamAsGuid;
+ if (!Guid.TryParse(controlIdParam.ToString(), out controlIdParamAsGuid))
+ return null;
+
+ return controlIdParamAsGuid;
+ }
+
///
/// Gets the model of the designer.
///
- private IDesignerModel GetModel(string widgetName)
- {
+ private IDesignerModel GetModel(string widgetName, Guid controlId, string moduleName = null, DesignMediaType designMediaType = DesignMediaType.Page)
+ {
+ var viewFilesMappgings = new Dictionary();
+
var constructorParameters = new Dictionary
- {
- { "views", this.GetPartialViews() },
- { "viewLocations", this.GetPartialViewLocations() },
- { "widgetName", widgetName },
- { "preselectedView", this.Request != null ? this.Request["view"] : null }
- };
+ {
+ { "views", this.GetPartialViews(ref viewFilesMappgings, moduleName) },
+ { "viewLocations", this.GetPartialViewLocations() },
+ { "widgetName", widgetName },
+ { "controlId", controlId },
+ { "preselectedView", this.Request != null ? this.Request["view"] : null },
+ { "viewFilesMappings", viewFilesMappgings },
+ { "mediaType", designMediaType }
+ };
return ControllerModelFactory.GetModel(typeof(DesignerController), constructorParameters);
}
+ private Control GetViewModel(DesignMediaType designMediaType)
+ {
+ var controlIdParam = this.GetControlIdParam();
+
+ if (!controlIdParam.HasValue)
+ return null;
+
+ var controlId = controlIdParam.Value;
+ var manager = this.GetControlManager(designMediaType);
+ var viewModel = manager.LoadControl(manager.GetControl(controlId));
+
+ var widgetProxy = viewModel as MvcWidgetProxy;
+ if (widgetProxy != null)
+ {
+ if (widgetProxy.Controller.RouteData == null)
+ widgetProxy.Controller.ControllerContext = new ControllerContext(new RequestContext(this.HttpContext, new RouteData()), widgetProxy.Controller);
+
+ widgetProxy.Controller.RouteData.Values["controller"] = widgetProxy.WidgetName;
+ }
+
+ return viewModel;
+ }
+
+ private IControlManager GetControlManager(DesignMediaType designMediaType)
+ {
+ if (designMediaType == DesignMediaType.Form)
+ {
+ return FormsManager.GetManager();
+ }
+ else
+ {
+ return PageManager.GetManager();
+ }
+ }
+
private const string DefaultView = "Designer";
private const string DesignerViewTemplate = "DesignerView.{0}";
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/GenericController.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/GenericController.cs
index ebda8af40..ea07d709c 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Controllers/GenericController.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/GenericController.cs
@@ -1,9 +1,11 @@
using System.Web.Mvc;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
{
// Just a generic controller for the view resolving purposes
- public class GenericController : Controller
+ [ControllerMetadataAttribute(IsTemplatableControl = false)]
+ internal class GenericController : Controller
{
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/GridDesignerController.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/GridDesignerController.cs
new file mode 100644
index 000000000..62d778a6c
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/GridDesignerController.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
+using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
+{
+ ///
+ /// This class contains logic for resolving the views of the MVC designer of a grid.
+ ///
+ [Localization(typeof(GridDesignerResources))]
+ [RequestBackendUserAuthentication]
+ public class GridDesignerController : Controller
+ {
+ ///
+ /// Returns the designer view which handles the property editing for a particular grid.
+ /// If there is custom designer for the particular widget it will be retrieved, otherwise it will fallback to the default designer.
+ /// The default designer is located under .
+ ///
+ /// The name of the widget.
+ public virtual ActionResult Master(string widgetName)
+ {
+ this.GetHttpContext().Items[SystemManager.IsBackendRequestKey] = true;
+
+ var controlId = this.Request != null ? this.Request["controlId"] ?? Guid.Empty.ToString() : Guid.Empty.ToString();
+ var gridTitle = this.Request != null ? this.Request["gridTitle"] ?? Guid.Empty.ToString() : Guid.Empty.ToString();
+
+ var model = this.GetModel(gridTitle, controlId);
+
+ return this.View(GridDesignerController.DefaultView, model);
+ }
+
+ ///
+ /// Gets the HTTP context.
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ protected virtual HttpContextBase GetHttpContext()
+ {
+ return this.HttpContext;
+ }
+
+ ///
+ /// Gets the model of the designer.
+ ///
+ private IGridDesignerModel GetModel(string gridTitle, string controlId)
+ {
+ var constructorParameters = new Dictionary
+ {
+ { "gridTitle", gridTitle },
+ { "controlId", controlId }
+ };
+
+ return ControllerModelFactory.GetModel(typeof(GridDesignerController), constructorParameters);
+ }
+
+ private const string DefaultView = "Designer";
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/PersonalizationDesignerController.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/PersonalizationDesignerController.cs
new file mode 100644
index 000000000..120318c3c
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/PersonalizationDesignerController.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
+using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using Telerik.Sitefinity.Localization;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
+{
+ ///
+ /// This class contains logic for resolving the views of the MVC designer of a grid.
+ ///
+ [Localization(typeof(PersonalizationDesignerResources))]
+ [RequestBackendUserAuthentication]
+ public class PersonalizationDesignerController : Controller
+ {
+ ///
+ /// Returns the designer view which handles the property editing for a particular grid.
+ /// If there is custom designer for the particular widget it will be retrieved, otherwise it will fallback to the default designer.
+ /// The default designer is located under .
+ ///
+ public virtual ActionResult Master()
+ {
+ this.GetHttpContext().Items[SystemManager.IsBackendRequestKey] = true;
+
+ var title = Res.Get().PersonalizationDialogCaption;
+ var model = this.GetModel();
+ model.Title = title;
+
+ return this.View(PersonalizationDesignerController.DefaultView, model);
+ }
+
+ ///
+ /// Gets the HTTP context.
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")]
+ protected virtual HttpContextBase GetHttpContext()
+ {
+ return this.HttpContext;
+ }
+
+ ///
+ /// Gets the model of the designer.
+ ///
+ private IPersonalizationDesignerModel GetModel()
+ {
+ return ControllerModelFactory.GetModel(typeof(PersonalizationDesignerController));
+ }
+
+ private const string DefaultView = "Personalization";
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Controllers/PrecompiledViewAssemblyWrapper.cs b/Telerik.Sitefinity.Frontend/Mvc/Controllers/PrecompiledViewAssemblyWrapper.cs
new file mode 100644
index 000000000..522949152
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/PrecompiledViewAssemblyWrapper.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using RazorGenerator.Mvc;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
+{
+ ///
+ /// This class wraps a PrecompiledViewAssembly to
+ ///
+ internal class PrecompiledViewAssemblyWrapper
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The assembly.
+ /// The package.
+ public PrecompiledViewAssemblyWrapper(Assembly assembly, string package)
+ {
+ this.assembly = assembly;
+ this.basePath = package == null ? "~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(assembly) : "~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(PrecompiledViewAssemblyWrapper));
+ this.precompiledViewAssembly = new PrecompiledViewAssembly(assembly, this.basePath)
+ {
+ UsePhysicalViewsIfNewer = false
+ };
+
+ this.embeddedResourceHashes = new Dictionary(StringComparer.OrdinalIgnoreCase);
+ foreach (var name in this.assembly.GetManifestResourceNames())
+ {
+ this.embeddedResourceHashes[name] = this.ComputeHashForResource(name);
+ }
+ }
+
+ ///
+ /// Gets the precompiled view assembly.
+ ///
+ ///
+ /// The precompiled view assembly.
+ ///
+ public PrecompiledViewAssembly PrecompiledViewAssembly
+ {
+ get
+ {
+ return this.precompiledViewAssembly;
+ }
+ }
+
+ ///
+ /// Gets the hash of the resource on the virtual path.
+ ///
+ /// The virtual path of the resource.
+ /// Hash of the resource.
+ public string GetFileHash(string virtualPath)
+ {
+ if (virtualPath.StartsWith(this.basePath, StringComparison.OrdinalIgnoreCase))
+ {
+ var relativePath = virtualPath.Right(virtualPath.Length - this.basePath.Length);
+ var relativePathWithoutSpaces = Regex.Replace(relativePath, @"[ \-]", "_").Replace('/', '.');
+ var resourcePathWithoutSpaces = this.assembly.GetName().Name + "." + relativePathWithoutSpaces;
+ var relativePathWithSpaces = Regex.Replace(relativePath, @"[\-]", "_").Replace('/', '.');
+ var resourcePathWithSpaces = this.assembly.GetName().Name + "." + relativePathWithSpaces;
+
+ if (this.embeddedResourceHashes.ContainsKey(resourcePathWithoutSpaces))
+ {
+ return this.embeddedResourceHashes[resourcePathWithoutSpaces];
+ }
+ else if (this.embeddedResourceHashes.ContainsKey(resourcePathWithSpaces))
+ {
+ return this.embeddedResourceHashes[resourcePathWithSpaces];
+ }
+ }
+
+ return null;
+ }
+
+ public IEnumerable GetViews(string virtualFolderPath)
+ {
+ if (virtualFolderPath.StartsWith(this.basePath, StringComparison.OrdinalIgnoreCase))
+ {
+ var relativePath = virtualFolderPath.Right(virtualFolderPath.Length - this.basePath.Length);
+ var relativePathWithSpaces = Regex.Replace(relativePath, @"[\-]", "_").Replace('/', '.');
+ var resourcePathWithSpaces = this.assembly.GetName().Name + "." + relativePathWithSpaces;
+
+ var resources = this.embeddedResourceHashes.Keys
+ .Where(resourceKey => resourceKey.ToUpperInvariant().Contains(resourcePathWithSpaces.ToUpperInvariant()))
+ .Select(key => Path.GetFileNameWithoutExtension(Regex.Replace(key, resourcePathWithSpaces, string.Empty, RegexOptions.IgnoreCase)));
+
+ return resources;
+ }
+
+ return new List();
+ }
+
+ private string ComputeHashForResource(string resourceName)
+ {
+ using (var stream = this.assembly.GetManifestResourceStream(resourceName))
+ {
+ return CompositePrecompiledMvcEngineWrapper.ComputeHash(stream);
+ }
+ }
+
+ private readonly PrecompiledViewAssembly precompiledViewAssembly;
+ private readonly Assembly assembly;
+ private readonly string basePath;
+ private readonly Dictionary embeddedResourceHashes;
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/AspHtmlHelperExtensions.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/AspHtmlHelperExtensions.cs
new file mode 100644
index 000000000..6c74cd062
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/AspHtmlHelperExtensions.cs
@@ -0,0 +1,144 @@
+// It is important to keep the namespace. This way these extensions take precedence over the default System.Web.Mvc.Html.PartialExtensions.
+namespace ASP
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Text;
+ using System.Web.Mvc;
+ using Telerik.Sitefinity.Services;
+
+ ///
+ /// extensions methods for rendering partial views.
+ ///
+ public static class PartialExtensions
+ {
+ ///
+ /// Renders the partial view with the specified name.
+ ///
+ /// The HTML helper.
+ /// Name of the partial view.
+ /// Rendered partial view.
+ public static System.Web.Mvc.MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName)
+ {
+ return PartialExtensions.Partial(htmlHelper, partialViewName, null, htmlHelper.ViewData);
+ }
+
+ ///
+ /// Renders the partial view with the specified name.
+ ///
+ /// The HTML helper.
+ /// Name of the partial view.
+ /// The view data.
+ ///
+ /// Rendered partial view.
+ ///
+ public static System.Web.Mvc.MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData)
+ {
+ return PartialExtensions.Partial(htmlHelper, partialViewName, null, viewData);
+ }
+
+ ///
+ /// Renders the partial view with the specified name.
+ ///
+ /// The HTML helper.
+ /// Name of the partial view.
+ /// The model.
+ ///
+ /// Rendered partial view.
+ ///
+ public static System.Web.Mvc.MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model)
+ {
+ return PartialExtensions.Partial(htmlHelper, partialViewName, model, htmlHelper.ViewData);
+ }
+
+ ///
+ /// Renders the partial view with the specified name.
+ ///
+ /// The HTML helper.
+ /// Name of the partial view.
+ /// The model.
+ /// The view data.
+ ///
+ /// Rendered partial view.
+ ///
+ public static System.Web.Mvc.MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData)
+ {
+ using (var writer = new StringWriter(CultureInfo.CurrentCulture))
+ {
+ PartialExtensions.RenderPartialInternal(htmlHelper, partialViewName, viewData, model, writer);
+ return MvcHtmlString.Create(writer.ToString());
+ }
+ }
+
+ /// Renders the specified partial view by using the specified HTML helper.
+ /// The HTML helper.
+ /// The name of the partial view
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName)
+ {
+ PartialExtensions.RenderPartialInternal(htmlHelper, partialViewName, htmlHelper.ViewData, null, htmlHelper.ViewContext.Writer);
+ }
+
+ /// Renders the specified partial view, replacing its ViewData property with the specified object.
+ /// The HTML helper.
+ /// The name of the partial view.
+ /// The view data.
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData)
+ {
+ PartialExtensions.RenderPartialInternal(htmlHelper, partialViewName, viewData, null, htmlHelper.ViewContext.Writer);
+ }
+
+ /// Renders the specified partial view, passing it a copy of the current object, but with the Model property set to the specified model.
+ /// The HTML helper.
+ /// The name of the partial view.
+ /// The model.
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model)
+ {
+ PartialExtensions.RenderPartialInternal(htmlHelper, partialViewName, htmlHelper.ViewData, model, htmlHelper.ViewContext.Writer);
+ }
+
+ /// Renders the specified partial view, replacing the partial view's ViewData property with the specified object and setting the Model property of the view data to the specified model.
+ /// The HTML helper.
+ /// The name of the partial view.
+ /// The model for the partial view.
+ /// The view data for the partial view.
+ public static void RenderPartial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData)
+ {
+ PartialExtensions.RenderPartialInternal(htmlHelper, partialViewName, viewData, model, htmlHelper.ViewContext.Writer);
+ }
+
+ private static void RenderPartialInternal(HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer)
+ {
+ ViewDataDictionary newViewData;
+ if (model == null)
+ {
+ newViewData = viewData == null ? new ViewDataDictionary(htmlHelper.ViewData) : new ViewDataDictionary(viewData);
+ }
+ else
+ {
+ newViewData = viewData == null ? new ViewDataDictionary(model) : new ViewDataDictionary(viewData) { Model = model };
+ }
+
+ var controller = htmlHelper.ViewContext.Controller as Controller;
+ var viewEngineCollection = controller != null ? controller.ViewEngineCollection : ViewEngines.Engines;
+ var newViewContext = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, newViewData, htmlHelper.ViewContext.TempData, writer);
+ var result = viewEngineCollection.FindPartialView(newViewContext, partialViewName);
+ if (result.View != null)
+ {
+ result.View.Render(newViewContext, writer);
+ }
+ else
+ {
+ var locationsText = new StringBuilder();
+ foreach (string location in result.SearchedLocations)
+ {
+ locationsText.AppendLine();
+ locationsText.Append(location);
+ }
+
+ throw new InvalidOperationException("The partial view '{0}' was not found or no view engine supports the searched locations. The following locations were searched: {1}".Arrange(partialViewName, locationsText));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/CommentsHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/CommentsHelpers.cs
new file mode 100644
index 000000000..f3eacb5a7
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/CommentsHelpers.cs
@@ -0,0 +1,226 @@
+using System;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Mvc.Html;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.DynamicModules.Builder;
+using Telerik.Sitefinity.DynamicModules.Builder.Model;
+using Telerik.Sitefinity.DynamicModules.Model;
+using Telerik.Sitefinity.GenericContent.Model;
+using Telerik.Sitefinity.Model;
+using Telerik.Sitefinity.Modules.Comments;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Web.UI;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// Holds helpers related to Comments widget
+ ///
+ public static class CommentsHelpers
+ {
+ ///
+ /// Returns the CommentsCount widget if exist, else render error message
+ ///
+ /// The helper.
+ /// The navigate URL.
+ /// The commented item.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
+ public static MvcHtmlString CommentsCount(this HtmlHelper helper, string navigateUrl, IDataItem item)
+ {
+ if (item == null)
+ return MvcHtmlString.Empty;
+
+ var contentItem = item as Content;
+ bool? allowComments = contentItem != null ? contentItem.AllowComments : null;
+
+ if (NewslettersHelper.IsNewsletter())
+ {
+ allowComments = false;
+ }
+
+ var threadKey = ControlUtilities.GetLocalizedKey(item.Id, null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(item.GetType().FullName));
+ var itemTypeFullName = item.GetType().FullName;
+
+ return CommentsHelpers.CommentsCount(helper, navigateUrl, threadKey, itemTypeFullName, allowComments);
+ }
+
+ ///
+ /// Returns the CommentsCount widget if exist, else render error message
+ ///
+ /// The HTML helper.
+ /// The navigate URL.
+ /// The thread key.
+ /// Type of the thread.
+ /// if not null this value will override the configuration for allowing comments.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
+ public static MvcHtmlString CommentsCount(this HtmlHelper helper, string navigateUrl, string threadKey, string threadType, bool? allowComments = null)
+ {
+ if (SystemManager.GetModule("Comments") == null || string.IsNullOrEmpty(threadKey))
+ return MvcHtmlString.Empty;
+
+ if (string.IsNullOrEmpty(navigateUrl))
+ {
+ navigateUrl = "#comments-" + threadKey;
+ }
+
+ var controllerName = threadKey.EndsWith(ReviewsSuffix, StringComparison.Ordinal) ? CommentsHelpers.ReviewsControllerName : CommentsHelpers.CommentsControllerName;
+
+ MvcHtmlString result;
+ try
+ {
+ result = helper.Action(CommentsHelpers.CountActionName, controllerName, new { NavigateUrl = navigateUrl, ThreadKey = threadKey, ThreadType = threadType, AllowComments = allowComments });
+ }
+ catch (HttpException)
+ {
+ result = MvcHtmlString.Empty;
+ }
+ catch (NullReferenceException)
+ {
+ //// Telerik.Sitefinity.Mvc.SitefinityMvcRoute GetOrderedParameters() on line 116 controllerType.GetMethods() throws null reference exception (controllerType is null).
+ result = MvcHtmlString.Empty;
+ }
+
+ return result;
+ }
+
+ ///
+ /// Renders comments list.
+ ///
+ /// The helper.
+ /// The item.
+ public static MvcHtmlString CommentsList(this HtmlHelper helper, IDataItem item)
+ {
+ var title = CommentsHelpers.GetTitle(item);
+ return CommentsHelpers.GetCommentsList(helper, item, title);
+ }
+
+ ///
+ /// Renders comments list.
+ ///
+ /// The helper.
+ /// The item.
+ /// The title.
+ public static MvcHtmlString CommentsList(this HtmlHelper helper, IDataItem item, string title)
+ {
+ return CommentsHelpers.GetCommentsList(helper, item, title);
+ }
+
+ private static MvcHtmlString GetCommentsList(HtmlHelper helper, IDataItem item, string title)
+ {
+ if (SystemManager.GetModule("Comments") == null || item == null)
+ {
+ return MvcHtmlString.Empty;
+ }
+
+ var itemTypeFullName = item.GetType().FullName;
+ var itemProviderName = item.GetProviderName();
+
+ var itemThreadKey = ControlUtilities.GetLocalizedKey(item.Id, null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(itemTypeFullName));
+ var itemGroupKey = ControlUtilities.GetUniqueProviderKey(GetDataSourceName(item), itemProviderName);
+
+ var routeDictionary = new System.Web.Routing.RouteValueDictionary()
+ {
+ { "AllowComments", GetAllowComments(item) },
+ { "ThreadKey", itemThreadKey },
+ { "ThreadTitle", title },
+ { "ThreadType", itemTypeFullName },
+ { "GroupKey", itemGroupKey },
+ { "DataSource", itemProviderName }
+ };
+
+ var controllerName = itemThreadKey.EndsWith(ReviewsSuffix, StringComparison.Ordinal) ? CommentsHelpers.ReviewsControllerName : CommentsHelpers.CommentsControllerName;
+
+ MvcHtmlString result;
+ try
+ {
+ result = helper.Action(CommentsHelpers.IndexActionName, controllerName, routeDictionary);
+ }
+ catch (HttpException)
+ {
+ result = MvcHtmlString.Empty;
+ }
+ catch (NullReferenceException)
+ {
+ //// Telerik.Sitefinity.Mvc.SitefinityMvcRoute GetOrderedParameters() on line 116 controllerType.GetMethods() throws null reference exception (controllerType is null).
+ result = MvcHtmlString.Empty;
+ }
+
+ return result;
+ }
+
+ private static string GetDataSourceName(IDataItem item)
+ {
+ string dataSourceName = null;
+
+ if (item != null && item is DynamicContent)
+ {
+ var itemType = item.GetType();
+
+ var dynamicContentType = ModuleBuilderManager.GetActiveTypes()
+ .FirstOrDefault(t => t.TypeNamespace == itemType.Namespace && t.TypeName == itemType.Name);
+
+ if (dynamicContentType != null)
+ {
+ dataSourceName = dynamicContentType.ModuleName;
+ }
+ }
+ else if (item != null)
+ {
+ Type managerType;
+ if (ManagerBase.TryGetMappedManagerType(item.GetType(), out managerType))
+ {
+ dataSourceName = managerType.FullName;
+ }
+ }
+
+ return dataSourceName;
+ }
+
+ private static string GetTitle(IDataItem item)
+ {
+ string title = null;
+
+ if (item != null)
+ {
+ var hasTitleItem = item as IHasTitle;
+ if (hasTitleItem != null)
+ {
+ title = hasTitleItem.GetTitle();
+ }
+ }
+
+ return title;
+ }
+
+ private static bool? GetAllowComments(IDataItem item)
+ {
+ bool? allowComments = null;
+
+ if (item != null)
+ {
+ var contentItem = item as Content;
+ if (contentItem != null)
+ {
+ allowComments = contentItem.AllowComments;
+ }
+ }
+
+ if (NewslettersHelper.IsNewsletter())
+ {
+ allowComments = false;
+ }
+
+ return allowComments;
+ }
+
+ private const string ReviewsSuffix = "_review";
+
+ private const string CommentsControllerName = "Comments";
+ private const string ReviewsControllerName = "Reviews";
+ private const string IndexActionName = "Index";
+ private const string CountActionName = "Count";
+ }
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ContentDataItemResolver.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ContentDataItemResolver.cs
new file mode 100644
index 000000000..b99740b59
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ContentDataItemResolver.cs
@@ -0,0 +1,289 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.ContentLocations;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.DynamicModules;
+using Telerik.Sitefinity.DynamicModules.Model;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
+using Telerik.Sitefinity.GenericContent.Model;
+using Telerik.Sitefinity.Lifecycle;
+using Telerik.Sitefinity.Model;
+using Telerik.Sitefinity.Modules.GenericContent;
+using Telerik.Sitefinity.Mvc.Proxy.Security;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+using Telerik.Sitefinity.Web;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// The class contains helper methods related to resolving detail content items operations.
+ ///
+ public class ContentDataItemResolver
+ {
+ ///
+ /// Gets the detail content item.
+ ///
+ /// The controller.
+ /// Contains information about the route parameters.
+ public IDataItem GetItemByController(ControllerBase controller, string[] routeParams)
+ {
+ string redirectUrl = null;
+ IDataItem item = null;
+
+ var url = RouteHelper.GetUrlParameterString(routeParams);
+
+ var itemType = this.GetItemType(controller);
+ if (itemType != null)
+ {
+ var providerNames = this.GetProviderNames(controller, itemType);
+ foreach (var providerName in providerNames)
+ {
+ item = this.GetItemByUrl(url, itemType, providerName, out redirectUrl);
+ if (item != null)
+ {
+ return item;
+ }
+ }
+ }
+
+ return item;
+ }
+
+ ///
+ /// Gets the content item by URL.
+ ///
+ /// The URL.
+ /// The content type of the item.
+ /// The provider of the item
+ /// The redirect URL.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
+ public IDataItem GetItemByUrl(string url, Type itemType, string provider, out string redirectUrl)
+ {
+ var manager = this.ResolveManager(itemType, provider);
+ if (manager == null)
+ {
+ redirectUrl = null;
+ return null;
+ }
+
+ var itemIdFromQueryParam = (SystemManager.CurrentHttpContext.Request.ParamsGet("sf-itemId") ?? SystemManager.CurrentHttpContext.Items["sf-itemId"]) as string;
+ if (!itemIdFromQueryParam.IsNullOrEmpty())
+ {
+ Guid itemIdGuid;
+ if (Guid.TryParse(itemIdFromQueryParam, out itemIdGuid))
+ {
+ redirectUrl = null;
+ return manager.GetItem(itemType, itemIdGuid) as IDataItem;
+ }
+ }
+
+ IDataItem item = null;
+ if (manager is IContentManager || manager is DynamicModuleManager)
+ {
+ try
+ {
+ item = this.GetItemFromUrl(manager, itemType, url, out redirectUrl);
+ }
+ catch (System.UnauthorizedAccessException e)
+ {
+ redirectUrl = null;
+ ItemAccessException access = new ItemAccessException(e.Message, e.InnerException);
+ access.Data["ItemType"] = itemType.Name;
+
+ throw access;
+ }
+
+ if (item != null)
+ {
+ var type = item.GetType();
+ if (!type.Equals(itemType) && !(type.Module != null && type.Module.ScopeName == "OpenAccessDynamic" && type.BaseType != null && type.BaseType.Equals(itemType)))
+ {
+ item = null;
+ }
+ }
+ }
+ else
+ {
+ item = ((IUrlProvider)manager.Provider).GetItemFromUrl(itemType, url, out redirectUrl);
+ }
+
+ var lifecycleItem = item as ILifecycleDataItem;
+ var lifecycleManager = manager as ILifecycleManager;
+ if (lifecycleItem != null && lifecycleManager != null)
+ {
+ ContentLifecycleStatus requestedStatus = ContentLocatableViewExtensions.GetRequestedItemStatus();
+ if (lifecycleItem.Status != requestedStatus)
+ {
+ item = lifecycleManager.Lifecycle.GetLive(lifecycleItem);
+ }
+
+ object requestedItem;
+ if (ContentLocatableViewExtensions.TryGetItemWithRequestedStatus(lifecycleItem, lifecycleManager, out requestedItem))
+ {
+ item = requestedItem as IDataItem;
+ }
+ }
+
+ return item;
+ }
+
+ private IDataItem GetItemFromUrl(IManager manager, Type itemType, string url, out string redirectUrl)
+ {
+ IDataItem item = null;
+ var isPublished = !this.IsPreviewRequested() || this.ResolveRequestedItemStatus() == ContentLifecycleStatus.Live;
+
+ if (manager is IContentManager)
+ {
+ var contentManager = manager as IContentManager;
+ item = contentManager.GetItemFromUrl(itemType, url, isPublished, out redirectUrl);
+ }
+ else
+ {
+ var dynamicManager = manager as DynamicModuleManager;
+ item = dynamicManager.Provider.GetItemFromUrl(itemType, url, isPublished, out redirectUrl);
+ }
+
+ return item;
+ }
+
+ ///
+ /// Gets the type of the content item for specified controller.
+ ///
+ /// The controller.
+ private Type GetItemType(ControllerBase controller)
+ {
+ var controllerType = controller.GetType();
+ Type contentType = null;
+ var detailsAction = new ReflectedControllerDescriptor(controllerType).FindAction(controller.ControllerContext, DetailActionParamsMapper.DefaultActionName);
+ if (detailsAction != null)
+ {
+ var contentParam = detailsAction.GetParameters().FirstOrDefault();
+ if (contentParam != null && contentParam.ParameterType.ImplementsInterface(typeof(IDataItem)))
+ {
+ if (typeof(DynamicContent) == contentParam.ParameterType)
+ {
+ var controllerName = this.GetControllerName(controller);
+ var dynamicContentType = controller.GetDynamicContentType(controllerName);
+ contentType = dynamicContentType != null ? TypeResolutionService.ResolveType(dynamicContentType.GetFullTypeName(), throwOnError: false) : null;
+ }
+ else
+ {
+ contentType = contentParam.ParameterType;
+ }
+ }
+ }
+
+ return contentType;
+ }
+
+ private string GetControllerName(ControllerBase controller)
+ {
+ var widgetName = (string)controller.ViewBag.WidgetName;
+
+ if (!string.IsNullOrEmpty(widgetName))
+ {
+ return widgetName;
+ }
+
+ var controllerName = controller.GetType().Name.Replace("Controller", "");
+ return controllerName;
+ }
+
+ ///
+ /// This method returns the requested item status based on content location url parameters.
+ ///
+ /// Copied from IContentLocatableView in Sitefinity. Should use it instead when it goes public.
+ /// Requested item status.
+ private ContentLifecycleStatus ResolveRequestedItemStatus()
+ {
+ var itemStatusParam = SystemManager.CurrentHttpContext.Request.ParamsGet("sf-lc-status") ?? SystemManager.CurrentHttpContext.Items["sf-lc-status"];
+ ContentLifecycleStatus status = ContentLifecycleStatus.Live;
+ if (itemStatusParam != null)
+ {
+ if (!Enum.TryParse(itemStatusParam as string, out status))
+ status = ContentLifecycleStatus.Live;
+ }
+
+ return status;
+ }
+
+ ///
+ /// Determines whether preview of the item is requested.
+ ///
+ /// Copied from IContentLocatableView in Sitefinity. Should use it instead when it goes public.
+ /// Whether preview is requested.
+ private bool IsPreviewRequested()
+ {
+ var actionParam = SystemManager.CurrentHttpContext.Request.ParamsGet("sf-content-action");
+ bool isPreview = actionParam != null && actionParam == "preview";
+ return isPreview;
+ }
+
+ ///
+ /// Resolves the manager.
+ ///
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ private IManager ResolveManager(Type itemType, string providerName)
+ {
+ IManager manager;
+
+ try
+ {
+ ManagerBase.TryGetMappedManager(itemType, providerName, out manager);
+ }
+ catch (Exception ex)
+ {
+ Log.Write(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Exception occurred in the routing functionality, details: {0}", ex));
+ manager = null;
+ }
+
+ return manager;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
+ private IEnumerable GetProviderNames(ControllerBase controller, Type contentType)
+ {
+ var providerNameProperty = controller.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(p => p.Name == "ProviderName" && p.PropertyType == typeof(string));
+
+ if (providerNameProperty != null)
+ {
+ return new string[1] { providerNameProperty.GetValue(controller, null) as string };
+ }
+ else
+ {
+ IManager manager;
+
+ try
+ {
+ ManagerBase.TryGetMappedManager(contentType, string.Empty, out manager);
+ }
+ catch (Exception ex)
+ {
+ Log.Write(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Exception occurred in the routing functionality, details: {0}", ex));
+ manager = null;
+ }
+
+ if (manager != null)
+ {
+ IList providers = manager.GetContextProviders().Select(p => p.Name).ToList();
+
+ if (!providers.Contains(manager.Provider.Name))
+ providers.Add(manager.Provider.Name);
+
+ return providers;
+ }
+ else
+ {
+ return new string[0];
+ }
+ }
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ControllerHelper.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ControllerHelper.cs
new file mode 100644
index 000000000..09880e752
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ControllerHelper.cs
@@ -0,0 +1,277 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Mvc.Proxy;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Security.Claims;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Web;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// This class contains helpers for working with controller related operations.
+ ///
+ public static class ControllerHelper
+ {
+ ///
+ /// Load controller model
+ ///
+ /// The widget id
+ /// The culture
+ /// Controller model
+ public static object LoadControllerModel(Guid widgetId, CultureInfo culture)
+ {
+ return LoadControllerModel(widgetId, culture, new Guid());
+ }
+
+ ///
+ /// Load controller model
+ ///
+ /// The widget id
+ /// The culture
+ /// The page id
+ /// Controller model
+ public static object LoadControllerModel(Guid widgetId, CultureInfo culture, Guid pageId)
+ {
+ if (widgetId == null || widgetId == Guid.Empty)
+ {
+ return null;
+ }
+
+ var pageManager = PageManager.GetManager();
+ ObjectData objectData = null;
+
+ if (pageId != Guid.Empty)
+ {
+ objectData = GetOverridingControlForPage(widgetId, pageId);
+ }
+
+ if (objectData == null)
+ {
+ objectData = pageManager.GetControls().SingleOrDefault(p => p.Id == widgetId);
+ }
+
+ if (objectData is PageDraftControl && ClaimsManager.IsBackendUser() == false)
+ {
+ return null;
+ }
+
+ object model = null;
+
+ if (objectData != null)
+ {
+ var mvcProxy = pageManager.LoadControl(objectData, culture) as MvcControllerProxy;
+ if (mvcProxy != null)
+ {
+ var controller = mvcProxy.Controller;
+ var controllerType = controller.GetType();
+ var modelProperty = controllerType.GetProperty(Model);
+ if (modelProperty != null)
+ {
+ model = modelProperty.GetValue(controller);
+ }
+ }
+ }
+
+ return model;
+ }
+
+ ///
+ /// Get controller widget id
+ ///
+ /// Widget Id
+ public static Guid GetWidgetId(Controller controller)
+ {
+ if (controller == null)
+ {
+ throw new ArgumentNullException("controller");
+ }
+
+ var pageManager = PageManager.GetManager();
+ var viewBagControlId = controller.ViewData[ControllerKey];
+ if (viewBagControlId == null)
+ return Guid.Empty;
+
+ var widgetId = Guid.Empty;
+ string controlId = (string)viewBagControlId;
+
+ // templates
+ if (controller.HttpContext.Items[IsTemplate] != null && (bool)controller.HttpContext.Items[IsTemplate] == true)
+ {
+ widgetId = GetTemplateWidgetId(pageManager, controller, controlId);
+ }
+ else
+ {
+ widgetId = GetPageWidgetId(pageManager, controlId);
+ }
+
+ return widgetId;
+ }
+
+ private static Guid GetTemplateWidgetId(PageManager pageManager, Controller controller, string controlId)
+ {
+ // check if action is after save or cancel in template
+ if (controller.HttpContext.Items[FormControlId] != null)
+ return (Guid)controller.HttpContext.Items[FormControlId];
+
+ var templateId = GetTemplateIdKey(controller.ControllerContext.HttpContext);
+ var versionId = GetVersionNumberKey(controller.ControllerContext.HttpContext);
+
+ if (templateId != null && versionId != null)
+ {
+ var template = pageManager.GetTemplate(new Guid(templateId));
+ if (template != null)
+ {
+ var versionManager = Telerik.Sitefinity.Versioning.VersionManager.GetManager();
+ TemplateDraft draft = new TemplateDraft();
+ versionManager.GetSpecificVersionByChangeId(draft, new Guid(versionId));
+
+ var control = GetControl(draft.Controls, controlId);
+ if (control != null)
+ return control.OriginalControlId;
+ }
+ }
+
+ // check if loaded in template
+ var templateData = controller.HttpContext.Items[TemplateDraftProxy] as TemplateDraftProxy;
+ if (templateData != null)
+ {
+ var template = pageManager.GetTemplate(templateData.ParentItemId);
+ if (template != null)
+ {
+ if (SystemManager.IsDesignMode || SystemManager.IsPreviewMode)
+ {
+ var control = GetControl(template.Drafts.FirstOrDefault(p => p.IsTempDraft).Controls, controlId);
+ if (control != null)
+ return control.Id;
+ }
+ else
+ {
+ var control = GetControl(template.Controls, controlId);
+ if (control != null)
+ return control.Id;
+ }
+ }
+ }
+
+ return Guid.Empty;
+ }
+
+ private static Guid GetPageWidgetId(PageManager pageManager, string controlId)
+ {
+ // pages
+ var pageId = SiteMapBase.GetCurrentNode().PageId;
+ var page = pageManager.GetPageData(pageId);
+
+ if (page.Template != null)
+ {
+ var templateControl = GetControl(page.Template.Controls, controlId);
+ if (templateControl != null)
+ return templateControl.Id;
+ }
+
+ if (SystemManager.IsDesignMode || SystemManager.IsPreviewMode)
+ {
+ var pageDraft = page.Drafts.FirstOrDefault(p => p.IsTempDraft);
+
+ // Draft, if page is created page template is null, only draft is available
+ if (page.Template == null && pageDraft.TemplateId != Guid.Empty)
+ {
+ var template = pageManager.GetTemplate(pageDraft.TemplateId);
+ if (template != null)
+ {
+ var templateControl = GetControl(template.Controls, controlId);
+ if (templateControl != null)
+ return templateControl.Id;
+ }
+ }
+
+ var control = GetControl(pageDraft.Controls, controlId);
+ if (control != null)
+ return control.Id;
+ }
+ else
+ {
+ var control = GetControl(page.Controls, controlId);
+ if (control != null)
+ return control.Id;
+ }
+
+ return Guid.Empty;
+ }
+
+ private static ControlData GetOverridingControlForPage(Guid controlId, Guid pageId)
+ {
+ var pageManager = PageManager.GetManager();
+ ControlData overridingControl = pageManager.GetControls().Where(c => c.Page.Id == pageId && (c.Id == controlId || c.BaseControlId == controlId)).FirstOrDefault();
+
+ if (overridingControl == null)
+ overridingControl = pageManager.GetControls().Where(c => c.Page.Id == pageId && (c.Id == controlId || c.BaseControlId == controlId)).FirstOrDefault();
+
+ if (overridingControl == null)
+ {
+ var pageDraft = pageManager.GetDrafts().Where(a => a.Id == pageId).FirstOrDefault();
+ if (pageDraft != null)
+ {
+ var iter = pageManager.GetTemplates().Where(c => c.Id == pageDraft.TemplateId).FirstOrDefault();
+
+ while (iter != null)
+ {
+ overridingControl = pageManager.GetControls().Where(c => c.Page.Id == iter.Id && (c.Id == controlId || c.BaseControlId == controlId)).FirstOrDefault();
+ if (overridingControl != null)
+ break;
+ iter = iter.ParentTemplate;
+ }
+ }
+ }
+
+ return overridingControl;
+ }
+
+ private static ControlData GetControl(IEnumerable controls, string controlId)
+ {
+ return controls.FirstOrDefault(p => p.Properties.FirstOrDefault(t => t.Name == IDParameter && controlId.EndsWith(t.Value)) != null);
+ }
+
+ private static string GetTemplateIdKey(HttpContextBase context)
+ {
+ var requestContext = context.Items[RouteHandler.RequestContextKey] as RequestContext ?? context.Request.RequestContext;
+ if (requestContext.RouteData.Values.ContainsKey(ItemId))
+ {
+ return requestContext.RouteData.Values[ItemId] as string;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private static string GetVersionNumberKey(HttpContextBase context)
+ {
+ var requestContext = context.Items[RouteHandler.RequestContextKey] as RequestContext ?? context.Request.RequestContext;
+ if (requestContext.RouteData.Values.ContainsKey(VersionNumber))
+ {
+ return requestContext.RouteData.Values[VersionNumber] as string;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ private const string TemplateDraftProxy = "TemplateDraftProxy";
+ private const string IsTemplate = "IsTemplate";
+ private const string FormControlId = "FormControlId";
+ private const string ControllerKey = "sf_cntrl_id";
+ private const string VersionNumber = "VersionNumber";
+ private const string ItemId = "itemId";
+ private const string IDParameter = "ID";
+ private const string Model = "Model";
+ }
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/CustomFieldsHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/CustomFieldsHelpers.cs
new file mode 100644
index 000000000..6026587cd
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/CustomFieldsHelpers.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Mvc;
+using System.Web.Script.Serialization;
+using Telerik.Sitefinity.Frontend.Mvc.Helpers.ViewModels;
+using Telerik.Sitefinity.Taxonomies;
+using Telerik.Sitefinity.Taxonomies.Model;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// This class contains helpers for working with custom fields.
+ ///
+ public static class CustomFieldsHelpers
+ {
+ ///
+ /// Gets the custom taxonomy fields.
+ ///
+ /// Type of the content.
+ ///
+ public static string GetTaxonomyFields(Type contentType)
+ {
+ var properties = OrganizerBase.GetPropertiesForType(contentType);
+ var taxonomyViewModelProperties = properties.Select(p => new CustomTaxonomyViewModel()
+ {
+ Id = p.TaxonomyId,
+ Name = p.Name,
+ TaxonomyType = p.TaxonomyType,
+ Title = CustomFieldsHelpers.GetClassificationPluralTitle(p.TaxonomyId, string.Empty)
+ });
+
+ var serialzier = new JavaScriptSerializer();
+ var serialziedProperties = serialzier.Serialize(taxonomyViewModelProperties);
+
+ return serialziedProperties;
+ }
+
+ ///
+ /// Gets the classification plural title.
+ ///
+ /// The taxa identifier.
+ /// Name of the provider.
+ ///
+ public static string GetClassificationPluralTitle(Guid taxaId, string providerName)
+ {
+ var taxonomyManager = TaxonomyManager.GetManager(providerName);
+ var taxa = taxonomyManager.GetTaxonomy(taxaId);
+
+ return taxa.Title;
+ }
+
+ ///
+ /// Gets the classification taxon name.
+ ///
+ /// The taxa identifier.
+ /// Name of the provider.
+ ///
+ public static string GetClassificationTaxonName(Guid taxaId, string providerName)
+ {
+ var taxonomyManager = TaxonomyManager.GetManager(providerName);
+ var taxa = taxonomyManager.GetTaxonomy(taxaId);
+
+ return taxa.TaxonName;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
index ba7193ee8..6bcf78465 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
@@ -1,106 +1,117 @@
-// It is important to keep the namespace. This way these extensions take precedence over the default System.Web.Mvc.Html.PartialExtensions.
-namespace ASP
-{
- using System;
- using System.Globalization;
- using System.IO;
- using System.Text;
- using System.Web.Mvc;
-
- ///
- /// extensions methods for rendering partial views.
- ///
- public static class PartialExtensions
- {
- ///
- /// Renders the partial view with the specified name.
- ///
- /// The HTML helper.
- /// Name of the partial view.
- /// Rendered partial view.
- public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName)
- {
- return PartialExtensions.Partial(htmlHelper, partialViewName, null, htmlHelper.ViewData);
- }
-
- ///
- /// Renders the partial view with the specified name.
- ///
- /// The HTML helper.
- /// Name of the partial view.
- /// The view data.
- ///
- /// Rendered partial view.
- ///
- public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData)
- {
- return PartialExtensions.Partial(htmlHelper, partialViewName, null, viewData);
- }
-
- ///
- /// Renders the partial view with the specified name.
- ///
- /// The HTML helper.
- /// Name of the partial view.
- /// The model.
- ///
- /// Rendered partial view.
- ///
- public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model)
- {
- return PartialExtensions.Partial(htmlHelper, partialViewName, model, htmlHelper.ViewData);
- }
-
- ///
- /// Renders the partial view with the specified name.
- ///
- /// The HTML helper.
- /// Name of the partial view.
- /// The model.
- /// The view data.
- ///
- /// Rendered partial view.
- ///
- public static MvcHtmlString Partial(this HtmlHelper htmlHelper, string partialViewName, object model, ViewDataDictionary viewData)
- {
- using (var writer = new StringWriter(CultureInfo.CurrentCulture))
- {
- PartialExtensions.RenderPartial(htmlHelper, partialViewName, viewData, model, writer);
- return MvcHtmlString.Create(writer.ToString());
- }
- }
-
- private static void RenderPartial(HtmlHelper htmlHelper, string partialViewName, ViewDataDictionary viewData, object model, TextWriter writer)
- {
- ViewDataDictionary newViewData;
- if (model == null)
- {
- newViewData = viewData == null ? new ViewDataDictionary(htmlHelper.ViewData) : new ViewDataDictionary(viewData);
- }
- else
- {
- newViewData = viewData == null ? new ViewDataDictionary(model) : new ViewDataDictionary(viewData) { Model = model };
- }
-
- var controller = htmlHelper.ViewContext.Controller as Controller;
- var viewEngineCollection = controller != null ? controller.ViewEngineCollection : ViewEngines.Engines;
- var newViewContext = new ViewContext(htmlHelper.ViewContext, htmlHelper.ViewContext.View, newViewData, htmlHelper.ViewContext.TempData, writer);
- var result = viewEngineCollection.FindPartialView(newViewContext, partialViewName);
- if (result.View != null)
- {
- result.View.Render(newViewContext, writer);
- }
- else
- {
- var locationsText = new StringBuilder();
- foreach (string location in result.SearchedLocations)
- {
- locationsText.AppendLine();
- locationsText.Append(location);
- }
-
- throw new InvalidOperationException("The partial view '{0}' was not found or no view engine supports the searched locations. The following locations were searched: {1}".Arrange(partialViewName, locationsText));
- }
- }
- }
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ using System.Collections.Generic;
+ using System.Web.Mvc;
+ using Sitefinity.Security.Sanitizers;
+ using Telerik.Sitefinity.Abstractions;
+ using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+ using Telerik.Sitefinity.Mvc.Rendering;
+ using Telerik.Sitefinity.Security.CSRF;
+ using Telerik.Sitefinity.Services;
+ using Telerik.Sitefinity.Utilities.TypeConverters;
+ using Telerik.Sitefinity.Web.UI;
+
+ ///
+ /// Helper method for HTML markup generation.
+ ///
+ public static class HtmlHelperExtensions
+ {
+ ///
+ /// Gets a unique element id with the given prefix.
+ ///
+ /// The HTML helper.
+ /// The prefix of the identifier.
+ /// Unique Id for an HTML element.
+ public static MvcHtmlString UniqueId(this HtmlHelper htmlHelper, string prefix)
+ {
+ if (!htmlHelper.ViewData.ContainsKey(HtmlHelperExtensions.ElementIdsKey))
+ {
+ htmlHelper.ViewData.Add(HtmlHelperExtensions.ElementIdsKey, new Dictionary());
+ }
+
+ var keysDictionary = (IDictionary)htmlHelper.ViewData[HtmlHelperExtensions.ElementIdsKey];
+ if (!keysDictionary.ContainsKey(prefix))
+ {
+ if (!SystemManager.CurrentHttpContext.Items.Contains(HtmlHelperExtensions.IdCountersKey))
+ {
+ SystemManager.CurrentHttpContext.Items[HtmlHelperExtensions.IdCountersKey] = new Dictionary();
+ }
+
+ var counters = (IDictionary)SystemManager.CurrentHttpContext.Items[HtmlHelperExtensions.IdCountersKey];
+ if (!counters.ContainsKey(prefix))
+ {
+ counters[prefix] = 0;
+ }
+
+ var counter = counters[prefix] + 1;
+ counters[prefix] = counter;
+ keysDictionary[prefix] = prefix + "-" + counter.ToString();
+ }
+
+ var uniqueId = keysDictionary[prefix];
+ return MvcHtmlString.Create(uniqueId);
+ }
+
+ ///
+ /// Sanitizes html string using the registered . This method should be used when you want to handle text that is supposed to be HTML, but comes from untrusted source.
+ ///
+ /// The HTML helper which this method is extending.
+ /// The HTML string to be sanitized.
+ ///
+ public static MvcHtmlString HtmlSanitize(this HtmlHelper htmlHelper, string html)
+ {
+ var sanitizedHtml = ControlUtilities.Sanitize(html);
+
+ return MvcHtmlString.Create(sanitizedHtml);
+ }
+
+ ///
+ /// Sanitizes url string using the registered . It will also attribute encode the string.
+ ///
+ /// The HTML helper which this method is extending.
+ /// The url string to be sanitized.
+ ///
+ public static MvcHtmlString UrlSanitize(this HtmlHelper htmlHelper, string url)
+ {
+ var sanitizedUrl = ControlUtilities.SanitizeUrl(url);
+
+ return MvcHtmlString.Create(sanitizedUrl);
+ }
+
+ ///
+ /// Adds an antiforgery markup and scripts to the view.
+ ///
+ /// The HTML helper which this method is extending.
+ /// The path to the token retrieval script.
+ ///
+ public static MvcHtmlString AddSitefinityAntiforgeryToken(this HtmlHelper htmlHelper, string retrievalScriptPath = null)
+ {
+ var antiForgery = ObjectFactory.IsTypeRegistered() ? ObjectFactory.Resolve() : null;
+ var antiForgeryEnabled = antiForgery != null && antiForgery.Enabled;
+ if (antiForgeryEnabled)
+ {
+ if (retrievalScriptPath == null)
+ {
+ var page = htmlHelper.ViewContext.HttpContext.Handler.GetPageHandler() ?? new PageProxy(null);
+ retrievalScriptPath = page.ClientScript.GetWebResourceUrl(TypeResolutionService.ResolveType("Telerik.Sitefinity.WebSecurity.WebSecurityModule"),
+ "Telerik.Sitefinity.WebSecurity.CSRF.TokenRetrieval.js");
+ }
+
+ htmlHelper.Script(retrievalScriptPath, "bottom", false);
+ var result =
+ $@"
+
+";
+
+ return MvcHtmlString.Create(result);
+ }
+ else
+ {
+ return MvcHtmlString.Empty;
+ }
+ }
+
+ private const string ElementIdsKey = "sf-element-ids";
+ private const string IdCountersKey = "sf-element-id-counters";
+ }
}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/HyperLinkHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HyperLinkHelpers.cs
new file mode 100644
index 000000000..f715d3919
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HyperLinkHelpers.cs
@@ -0,0 +1,269 @@
+using System;
+using System.Web;
+using System.Linq;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Data.Linq.Dynamic;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
+using Telerik.Sitefinity.Model;
+using Telerik.Sitefinity.Modules.Libraries;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Multisite;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Taxonomies.Model;
+using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.Web.DataResolving;
+using Telerik.Sitefinity.Web.UrlEvaluation;
+using System.Runtime.CompilerServices;
+using ServiceStack;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// Helper class that provides utility methods for resolving urls to a content pages.
+ ///
+ public static class HyperLinkHelpers
+ {
+ ///
+ /// Returns an absolute URL to a content item's details page.
+ ///
+ /// The item.
+ /// The details page id.
+ /// The absolute url of the details page.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetDetailPageUrl(IDataItem item, Guid detailsPageId)
+ {
+ string url = DataResolver.Resolve(item, "URL", null, detailsPageId.ToString());
+ return UrlPath.ResolveUrl(url, true);
+ }
+
+ ///
+ /// Gets the detail page URL for master/detail widgets.
+ ///
+ /// The item.
+ /// The details page identifier.
+ /// if set to true [open in same page].
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetDetailPageUrl(ItemViewModel item, Guid detailsPageId, bool openInSamePage)
+ {
+ string url;
+ if (openInSamePage)
+ {
+ var appRelativeUrl = DataResolver.Resolve(item.DataItem, "URL");
+ url = UrlPath.ResolveUrl(appRelativeUrl, true);
+ }
+ else
+ {
+ url = HyperLinkHelpers.GetDetailPageUrl(item.DataItem, detailsPageId);
+ }
+
+ return url;
+ }
+
+ ///
+ /// Gets the detail page URL for master/detail widgets.
+ ///
+ /// The item.
+ /// The details page identifier.
+ /// if set to true [open in same page].
+ /// Index of the item in collection.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetDetailPageUrl(ItemViewModel item, Guid detailsPageId, bool openInSamePage, int itemIndex)
+ {
+ string url;
+ if (openInSamePage)
+ {
+ var appRelativeUrl = DataResolver.Resolve(item.DataItem, "URL");
+ appRelativeUrl = appRelativeUrl + "?itemIndex=" + itemIndex;
+ url = UrlPath.ResolveUrl(appRelativeUrl, true);
+ }
+ else
+ {
+ url = HyperLinkHelpers.GetDetailPageUrl(item.DataItem, detailsPageId) + "?itemIndex=" + itemIndex;
+ }
+
+ return url;
+ }
+
+ ///
+ /// Gets the full detail page URL for master/detail widgets.
+ ///
+ /// The item.
+ /// The details page identifier.
+ /// if set to true [open in same page].
+ /// The URL key prefix.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetDetailPageUrl(ItemViewModel item, Guid detailsPageId, bool openInSamePage, string urlKeyPrefix)
+ {
+ string url;
+ if (openInSamePage)
+ {
+ url = DataResolver.Resolve(item.DataItem, "URL", urlKeyPrefix);
+ }
+ else
+ {
+ url = DataResolver.Resolve(item.DataItem, "URL", null, detailsPageId.ToString());
+ }
+
+ url = RemoveDoubleSlash(url);
+
+ return UrlPath.ResolveUrl(url, true);
+ }
+
+ ///
+ /// Gets the full detail page URL for master/detail widgets.
+ ///
+ /// The item.
+ /// The details page identifier.
+ /// if set to true [open in same page].
+ /// The URL key prefix.
+ /// Index of the item.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "3#"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetDetailPageUrl(ItemViewModel item, Guid detailsPageId, bool openInSamePage, string urlKeyPrefix, int itemIndex)
+ {
+ string url;
+ if (openInSamePage)
+ {
+ url = DataResolver.Resolve(item.DataItem, "URL", urlKeyPrefix);
+ }
+ else
+ {
+ url = DataResolver.Resolve(item.DataItem, "URL", null, detailsPageId.ToString());
+ }
+
+ url = url + "?itemIndex=" + itemIndex;
+ url = RemoveDoubleSlash(url);
+
+ return UrlPath.ResolveUrl(url, true);
+ }
+
+ ///
+ /// Gets the full page URL.
+ ///
+ /// The page identifier.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetFullPageUrl(Guid pageId)
+ {
+ if (pageId != Guid.Empty)
+ {
+ var siteMap = SitefinitySiteMap.GetCurrentProvider();
+
+ SiteMapNode node;
+ var sitefinitySiteMap = siteMap as SiteMapBase;
+ if (sitefinitySiteMap != null)
+ {
+ node = sitefinitySiteMap.FindSiteMapNodeFromKey(pageId.ToString(), false);
+ }
+ else
+ {
+ node = siteMap.FindSiteMapNodeFromKey(pageId.ToString());
+ }
+
+ if (node != null)
+ {
+ return UrlPath.ResolveUrl(node.Url, true);
+ }
+ else
+ {
+ // try to redirect to a page from a different site
+ var redirectPageNode = PageManager.GetManager().GetPageNode(pageId);
+ if (SystemManager.CurrentContext.CurrentSite.SiteMapRootNodeId != redirectPageNode.RootNodeId)
+ {
+ using (SiteRegion.FromSiteMapRoot(redirectPageNode.RootNodeId, SiteContextResolutionTypes.ByParam))
+ {
+ if (sitefinitySiteMap != null)
+ {
+ node = sitefinitySiteMap.FindSiteMapNodeFromKey(pageId.ToString(), false);
+ }
+ else
+ {
+ node = siteMap.FindSiteMapNodeFromKey(pageId.ToString());
+ }
+
+ if (node != null)
+ {
+ return UrlPath.ResolveUrl(node.Url, true);
+ }
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Builds the query string parameters for filtering by taxon.
+ ///
+ /// The taxon.
+ /// The URL key prefix.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "1#")]
+ public static string BuildTaxonQueryStringParams(ITaxon taxon, string urlKeyPrefix)
+ {
+ var evaluator = new TaxonomyEvaluator();
+ var taxonBuildOptions = TaxonBuildOptions.None;
+ string taxonRelativeUrl = null;
+
+ if (taxon.Taxonomy is HierarchicalTaxonomy)
+ {
+ taxonBuildOptions = TaxonBuildOptions.Hierarchical;
+ taxonRelativeUrl = (taxon as HierarchicalTaxon).FullUrl;
+ }
+ else if (taxon.Taxonomy is FlatTaxonomy)
+ {
+ taxonBuildOptions = TaxonBuildOptions.Flat;
+ taxonRelativeUrl = taxon.UrlName.Value;
+ }
+
+ var taxonQueryStringParams = evaluator.BuildUrl(taxon.Taxonomy.Name, taxonRelativeUrl, taxon.Taxonomy.Name, taxonBuildOptions, UrlEvaluationMode.QueryString, urlKeyPrefix);
+ return taxonQueryStringParams;
+ }
+
+ ///
+ /// Retrieves the thumbnail url for the video
+ ///
+ /// The librarires manager
+ /// The video id
+ ///
+ public static string GetVideoThumbnailUrl(LibrariesManager librariesManager, Guid videoId)
+ {
+ try
+ {
+ var video = librariesManager.GetVideo(videoId);
+
+ return video.ResolveThumbnailUrl();
+ }
+ catch (UnauthorizedAccessException ex)
+ {
+ Exceptions.HandleException(ex, ExceptionPolicyName.IgnoreExceptions);
+
+ return string.Empty;
+ }
+ }
+
+ private static string RemoveDoubleSlash(string url)
+ {
+ // If the url is absolute we should not replace first // with /. Ex: http://domain...
+ Uri result = null;
+ if (Uri.TryCreate(url, UriKind.Absolute, out result))
+ {
+ UriBuilder builder = new UriBuilder(url);
+ builder.Path = builder.Path.Replace("//", "/");
+
+ url = builder.Uri.ToString();
+ }
+ else
+ {
+ url = url.Replace("//", "/");
+ }
+
+ return url;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs
index e1acfb57f..73aace273 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs
@@ -1,6 +1,12 @@
using System;
+using System.Web;
using System.Web.Mvc;
+using System.Web.UI;
+using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.Frontend.InlineEditing;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+using Telerik.Sitefinity.Web;
namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
{
@@ -16,7 +22,7 @@ public static class InlineEditingHtmlHelpers
/// The object which contains the property.
/// Name of the property.
///
- public static System.Web.Mvc.MvcHtmlString TextField(this HtmlHelper helper, object model, string propName)
+ public static IHtmlString TextField(this HtmlHelper helper, object model, string propName)
{
var htmlProcessor = new HtmlProcessor();
var htmlString = htmlProcessor.GetStringContent(model, propName);
@@ -31,13 +37,22 @@ public static System.Web.Mvc.MvcHtmlString TextField(this HtmlHelper helper, obj
/// The property value.
/// Type of the field.
/// Html required for enabling inline editing.
- public static System.Web.Mvc.MvcHtmlString TextField(this HtmlHelper helper, string propName, string propValue, string fieldType)
+ public static IHtmlString TextField(this HtmlHelper helper, string propName, string propValue, string fieldType)
{
- var htmlString = string.Format(HtmlProcessor.InlineEditingHtmlWrapper, propName, fieldType, propValue);
-
+ string htmlString;
+
+ if (!SystemManager.IsInlineEditingMode)
+ {
+ htmlString = propValue;
+ }
+ else
+ {
+ htmlString = string.Format(HtmlProcessor.InlineEditingHtmlWrapper, propName, fieldType, propValue);
+ }
+
return new System.Web.Mvc.MvcHtmlString(htmlString);
}
-
+
///
/// HTML helper which adds an InlineEditing region. This should be added once at the top of the page, and the whole region will support InlineEditing.
///
@@ -49,16 +64,124 @@ public static System.Web.Mvc.MvcHtmlString TextField(this HtmlHelper helper, str
public static HtmlRegion InlineEditingRegion(
this HtmlHelper htmlHelper,
string providerName,
- string type,
+ string type,
Guid id)
{
var htmlProcessor = new HtmlProcessor();
-
return htmlProcessor.CreateInlineEditingRegion(
htmlHelper.ViewContext.Writer,
- providerName,
- type,
+ providerName,
+ type,
id);
}
+
+ ///
+ /// Renders InlineEditing attributes required for the Inline editing feature.
+ ///
+ /// Name of the provider.
+ /// The type.
+ /// The identifier.
+ /// The inline editing attributes.
+ public static IHtmlString InlineEditingAttributes(this HtmlHelper htmlHelper, string providerName, string type, Guid id)
+ {
+ if (!SystemManager.IsInlineEditingMode)
+ return htmlHelper.Raw(string.Empty);
+
+ if (string.IsNullOrEmpty(providerName))
+ {
+ providerName = GetProviderName(type);
+ }
+
+ var providerNameEncoded = providerName != null ? htmlHelper.Encode(providerName) : providerName;
+ var typeEncoded = htmlHelper.Encode(type);
+
+ if (id == Guid.Empty)
+ return htmlHelper.Raw("data-sf-provider='{0}' data-sf-type='{1}'".Arrange(providerNameEncoded, typeEncoded));
+ else
+ return htmlHelper.Raw("data-sf-provider='{0}' data-sf-type='{1}' data-sf-id='{2}'".Arrange(providerNameEncoded, typeEncoded, id.ToString("D")));
+ }
+
+ ///
+ /// Renders InlineEditing attributes for fields required for the Inline editing feature.
+ ///
+ /// The HTML helper.
+ /// Name of the field.
+ /// Type of the field.
+ ///
+ public static IHtmlString InlineEditingFieldAttributes(this HtmlHelper htmlHelper, string fieldName, string fieldType)
+ {
+ if (htmlHelper == null)
+ throw new ArgumentNullException("htmlHelper");
+
+ if (fieldName == null)
+ throw new ArgumentNullException("fieldName");
+
+ if (fieldType == null)
+ throw new ArgumentNullException("fieldType");
+
+ if (!SystemManager.IsInlineEditingMode)
+ return htmlHelper.Raw(string.Empty);
+
+ var fieldNameEncoded = htmlHelper.Encode(fieldName);
+ var fieldTypeEncoded = htmlHelper.Encode(fieldType);
+
+ return htmlHelper.Raw("data-sf-field='{0}' data-sf-ftype='{1}'".Arrange(fieldNameEncoded, fieldTypeEncoded));
+ }
+
+ ///
+ /// Returns if the inline editing section should be rendered.
+ ///
+ /// The HTML helper.
+ ///
+ public static bool ShouldRenderInlineEditing(this HtmlHelper htmlHelper)
+ {
+ var shouldRender = false;
+
+ if (!SitefinityContext.IsBackend && ControlExtensions.InlineEditingIsEnabled() && !SystemManager.CurrentHttpContext.Request.IsAjaxRequest())
+ {
+ const string SiteMapNodeKey = "ServedPageNode";
+
+ if (HttpContext.Current != null && HttpContext.Current.Items != null && HttpContext.Current.Items.Contains(SiteMapNodeKey))
+ {
+ var pageSiteNode = HttpContext.Current.Items[SiteMapNodeKey] as PageSiteNode;
+ if (pageSiteNode != null)
+ {
+ var firstPageDataNode = RouteHelper.GetFirstPageDataNode(pageSiteNode, true);
+ if (firstPageDataNode != null && firstPageDataNode.Framework == Pages.Model.PageTemplateFramework.Mvc)
+ {
+ shouldRender = true;
+ }
+ }
+ }
+ }
+
+ return shouldRender;
+ }
+
+ private static string GetProviderName(string type)
+ {
+ string providerName = null;
+
+ if (type != null)
+ {
+ var itemType = TypeResolutionService.ResolveType(type);
+ if (itemType != null)
+ {
+ Type managerType;
+ ManagerBase.TryGetMappedManagerType(itemType, out managerType);
+ if (managerType != null)
+ {
+ var currentSite = SystemManager.CurrentContext.CurrentSite;
+ var provider = currentSite.GetDefaultProvider(managerType.FullName);
+ if (provider != null)
+ {
+ providerName = provider.ProviderName;
+ }
+ }
+ }
+ }
+
+ return providerName;
+ }
}
}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/LayoutsHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/LayoutsHelpers.cs
index 56d9b31dc..cf8cd2dd9 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/LayoutsHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/LayoutsHelpers.cs
@@ -1,5 +1,8 @@
using System;
using System.Web.Mvc;
+using System.Web.UI;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Layouts;
namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
{
@@ -14,11 +17,53 @@ public static class LayoutsHelpers
/// The helper.
/// Name of the container.
///
- public static MvcHtmlString SfPlaceHolder(this HtmlHelper helper, string containerName = "Body")
+ public static System.Web.Mvc.MvcHtmlString SfPlaceHolder(this HtmlHelper helper, string containerName = LayoutsHelpers.PlaceHolderDefaultName)
{
- var htmlString = string.Format(System.Globalization.CultureInfo.InvariantCulture, "
", containerName);
+ var htmlString = LayoutsHelpers.ContentPlaceHolderMarkup(containerName);
- return new MvcHtmlString(htmlString);
+ return new System.Web.Mvc.MvcHtmlString(htmlString);
}
+
+ ///
+ /// Html helper which renders all script tags.
+ ///
+ /// The helper.
+ /// The name of the section.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper")]
+ public static System.Web.Mvc.MvcHtmlString Section(this HtmlHelper helper, string name)
+ {
+ if (helper.ViewContext.HttpContext != null)
+ {
+ SectionRenderer.MarkAvailability(helper.ViewContext.HttpContext.Handler.GetPageHandler(), name);
+ }
+
+ return new System.Web.Mvc.MvcHtmlString(LayoutsHelpers.SectionHtml.Arrange(name));
+ }
+
+ ///
+ /// Gets the ASPX markup of a content place holder.
+ ///
+ /// Name of the place holder.
+ ///
+ internal static string ContentPlaceHolderMarkup(string placeHolderName = LayoutsHelpers.PlaceHolderDefaultName)
+ {
+ return string.Format(System.Globalization.CultureInfo.InvariantCulture, "", placeHolderName);
+ }
+
+ ///
+ /// The section tag name.
+ ///
+ internal const string SectionTag = "FeatherSection";
+
+ ///
+ /// The section HTML markup.
+ ///
+ internal const string SectionHtml = "<" + LayoutsHelpers.SectionTag + " name=\"{0}\">" + LayoutsHelpers.SectionTag + ">";
+
+ ///
+ /// The content place holder default name.
+ ///
+ private const string PlaceHolderDefaultName = "Body";
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/LocalizationHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/LocalizationHelpers.cs
index 81a52a6a1..4d01f43c1 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/LocalizationHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/LocalizationHelpers.cs
@@ -23,6 +23,24 @@ public static string Resource(this HtmlHelper helper, string key, bool fallbackT
return LocalizationHelpers.Resource(controller, helper.ViewContext.RouteData, key, fallbackToKey);
}
+ ///
+ /// Get the resource string with the specified key from the resource files.
+ ///
+ /// The helper.
+ /// The key
+ /// The class to search in
+ public static string Resource(this HtmlHelper helper, string key, string className)
+ {
+ string result;
+
+ if (Res.TryGet(className, key, out result))
+ {
+ return result;
+ }
+
+ return "#ResourceNotFound: {0}, {1}#".Arrange(className, key);
+ }
+
///
/// Get the label with the specified key from the resource files.
///
@@ -45,7 +63,7 @@ public static string Label(this ViewPage page, string key)
var controller = LocalizationHelpers.GetController(page);
var resClass = LocalizationHelpers.FindResourceStringClassType(controller.GetType());
return Res.Get(resClass, key, System.Globalization.CultureInfo.InvariantCulture);
- }
+ }
///
/// Get the label with the specified key from the resource files.
@@ -65,7 +83,7 @@ private static string Resource(ControllerBase controller, RouteData routeData, s
{
var widgetResClass = LocalizationHelpers.FindResourceStringClassType(widget);
string res;
- if (widgetResClass != null && Res.TryGet(widgetResClass.Name, key, out res))
+ if (widgetResClass != null && Res.TryGet(widgetResClass.Name, key, null, out res))
{
return res;
}
@@ -73,7 +91,7 @@ private static string Resource(ControllerBase controller, RouteData routeData, s
}
string result;
- if (Res.TryGet(resClass.Name, key, out result))
+ if (Res.TryGet(resClass.Name, key, null, out result))
{
return result;
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/NewslettersHelper.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/NewslettersHelper.cs
new file mode 100644
index 000000000..8947cb693
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/NewslettersHelper.cs
@@ -0,0 +1,47 @@
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Modules.Newsletters;
+using Telerik.Sitefinity.Modules.Newsletters.Configuration;
+using Telerik.Sitefinity.Modules.Newsletters.Web;
+using Telerik.Sitefinity.Newsletters.Model;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// Helper methods for email campaigns.
+ ///
+ public static class NewslettersHelper
+ {
+ ///
+ /// Indicates whether the current request is for newsletter.
+ ///
+ /// True if the current request is for newsletter.
+ public static bool IsNewsletter()
+ {
+ var httpContext = SystemManager.CurrentHttpContext;
+
+ var newsLetterItem = httpContext.Items.Contains(NewslettersModule.IsNewsletter) ? (bool)httpContext.Items[NewslettersModule.IsNewsletter] : false;
+
+ var isNewsletter = newsLetterItem
+ || (httpContext.Request.UrlReferrer != null && httpContext.Request.UrlReferrer.AbsolutePath.ToLower().Contains("/sitefinity/sfnwslttrs/"))
+ || (httpContext.Request.Url != null && httpContext.Request.Url.AbsolutePath.ToLower().Contains("/sitefinity/sfnwslttrs/"));
+
+ return isNewsletter;
+ }
+
+ ///
+ /// Indicates whether the current request is for pure mvc newsletter.
+ ///
+ /// True if the current request is for mvc newsletter.
+ internal static bool IsMvcNewsletter()
+ {
+ var currentHttpContext = SystemManager.CurrentHttpContext;
+
+ return SystemManager.GetApplicationModule(NewslettersModule.ModuleName) != null
+ && !Config.Get().AllowHybridWidgets
+ && currentHttpContext != null
+ && currentHttpContext.Items.Contains(NewslettersRouteHandler.NewslettersMessageBodyType)
+ && (MessageBodyType)currentHttpContext.Items[NewslettersRouteHandler.NewslettersMessageBodyType] == MessageBodyType.InternalMvcPage;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/PagingHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/PagingHelpers.cs
new file mode 100644
index 000000000..89f2ccfd9
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/PagingHelpers.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Web;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Web;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// Helper methods for widget paging.
+ ///
+ public static class PagingHelpers
+ {
+ ///
+ /// Generate url template that suports paging.
+ ///
+ /// Target controller.
+ /// Base url for paging.
+ /// Query parameter sufix.
+ /// Url suffix.
+ public static string GeneratePagingTemplate(this ContentBaseController controller, string pageUrl, string urlKeyPrefix)
+ {
+ if (urlKeyPrefix.IsNullOrWhitespace())
+ {
+ return string.Concat(pageUrl, UrlHelpers.GetRedirectPagingUrl(), SystemManager.CurrentHttpContext.Request.QueryString.ToQueryString(true));
+ }
+
+ var key = string.Format(KeyFormat, urlKeyPrefix);
+ var queryParams = HttpUtility.ParseQueryString(SystemManager.CurrentHttpContext.Request.Url.Query);
+ queryParams[key] = "{0}";
+
+ var queryParamsRaw = HttpUtility.UrlDecode(queryParams.ToQueryString(true));
+ return string.Concat(SystemManager.CurrentHttpContext.Request.Url.LocalPath, queryParamsRaw);
+ }
+
+ ///
+ /// Check query params and update page if applicable.
+ ///
+ /// Target controller.
+ /// The page.
+ /// Prefix of page query parameter.
+ public static void UpdatePageFromQuery(this ContentBaseController controller, ref int? page, string urlKeyPrefix)
+ {
+ if (urlKeyPrefix.IsNullOrWhitespace()) return;
+
+ var key = string.Format(KeyFormat, urlKeyPrefix);
+ var pageString = SystemManager.CurrentHttpContext.Request.QueryStringGet(key);
+
+ int queryPage;
+ if (!int.TryParse(pageString, out queryPage))
+ {
+ queryPage = 1;
+ }
+ page = queryPage;
+ }
+
+ ///
+ /// Extract a valid page number.
+ ///
+ /// Target controller.
+ /// Nullable page number.
+ /// One (1) if page is null or less than one, otherwise page value.
+ public static int ExtractValidPage(this ContentBaseController controller, int? page)
+ {
+ if (!page.HasValue || page.Value < 1) return 1;
+
+ return page.Value;
+ }
+
+ private const string KeyFormat = "{0}page";
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs
index c5f859abc..8008231e8 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs
@@ -1,191 +1,979 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Reflection;
-using System.Text;
-using System.Web;
-using System.Web.Mvc;
-using System.Web.UI;
-using Telerik.Sitefinity.Configuration;
-using Telerik.Sitefinity.Frontend.Resources;
-using Telerik.Sitefinity.Modules.Pages;
-using Telerik.Sitefinity.Modules.Pages.Configuration;
-using Telerik.Sitefinity.Utilities.TypeConverters;
-using Telerik.Sitefinity.Web.Configuration;
-
-namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
-{
- ///
- /// This class contains helper methods for registering client resources.
- ///
- public static class ResourceHelper
- {
- ///
- /// Registers JavaScript reference and ensures that it loads maximum once for a page.
- ///
- ///
- /// This method uses directly the resource from the .
- /// In case you want to use embedded scripts from Sitefinity check .
- ///
- /// The path to the JavaScript file.
- /// OPTIONAL: Indicates whether to throw an exception if the JavaScript is already registered. By default the value is set to false.
- /// MvcHtmlString
- public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, bool throwException = false)
- {
- var context = helper.ViewContext.HttpContext;
-
- return ResourceHelper.RegisterResource(context, scriptPath, scriptPath, throwException);
- }
-
- ///
- /// Registers JavaScript reference and ensures that it loads maximum once for a page.
- ///
- ///
- /// This helper references the same resource existing in Sitefinity.
- ///
- /// The helper.
- /// The script reference.
- /// if set to true throw exception.
- ///
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")]
- public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, bool throwException = false)
- {
- if (ResourceHelper.TryConfigureScriptManager(scriptReference))
- return MvcHtmlString.Empty;
-
- var resourceKey = scriptReference.ToString();
- var context = helper.ViewContext.HttpContext;
- var resourceUrl = ResourceHelper.GetWebResourceUrl(scriptReference);
-
- if (string.IsNullOrEmpty(resourceUrl))
- throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Script reference for {0} is not found.", resourceKey));
-
- return ResourceHelper.RegisterResource(context, resourceKey, resourceUrl, throwException);
- }
-
- ///
- /// Tries the configure script manager.
- ///
- /// The script reference.
- ///
- private static bool TryConfigureScriptManager(ScriptRef scriptReference)
- {
- var page = HttpContext.Current.Handler as Page;
- ScriptManager scriptManager = null;
-
- if (page != null)
- {
- scriptManager = PageManager.ConfigureScriptManager(page, scriptReference, false);
- }
-
- if (scriptManager == null)
- return false;
- else
- return true;
- }
-
- ///
- /// Gets the web resource URL.
- ///
- /// The script reference.
- ///
- private static string GetWebResourceUrl(ScriptRef scriptReference)
- {
- var config = Config.Get().ScriptManager;
- var scriptConfig = config.ScriptReferences[scriptReference.ToString()];
- string resourceUrl = string.Empty;
-
- if (config.EnableCdn || (scriptConfig.EnableCdn.HasValue && scriptConfig.EnableCdn.Value))
- {
- resourceUrl = scriptConfig.Path;
- }
- else
- {
- var page = HttpContext.Current.Handler as Page;
-
- if (page != null)
- {
- resourceUrl = page.ClientScript.GetWebResourceUrl(
- TypeResolutionService.ResolveType("Telerik.Sitefinity.Resources.Reference"),
- scriptConfig.Name);
- }
- }
-
- return resourceUrl;
- }
-
- ///
- /// Registers the resource.
- ///
- /// The context.
- /// The script key.
- /// The script path.
- /// if set to true throws exception.
- ///
- private static MvcHtmlString RegisterResource(HttpContextBase context, string scriptKey, string scriptPath, bool throwException)
- {
- var attributes = new KeyValuePair[2];
- attributes[0] = new KeyValuePair("src", scriptPath);
- attributes[1] = new KeyValuePair("type", "text/javascript");
-
- var register = new ResourceRegister(ResourceHelper.JsRegisterName, context);
-
- return ResourceHelper.RegisterResource(register, scriptKey, throwException, tagName: "script", attribbutes: attributes);
- }
-
- ///
- /// Registers resource reference.
- ///
- /// The register.
- /// The resource key.
- /// if set to true [throw exception].
- /// Name of the tag.
- /// The attribbutes.
- ///
- private static MvcHtmlString RegisterResource(ResourceRegister register, string resourceKey, bool throwException, string tagName, KeyValuePair[] attribbutes)
- {
- string output;
- MvcHtmlString result;
-
- if (throwException)
- {
- register.RegisterResource(resourceKey);
- output = ResourceHelper.GenerateTag(tagName, attribbutes);
- result = new MvcHtmlString(output);
- }
- else if (register.TryRegisterResource(resourceKey))
- {
- output = ResourceHelper.GenerateTag(tagName, attribbutes);
- result = new MvcHtmlString(output);
- }
- else
- {
- result = MvcHtmlString.Empty;
- }
-
- return result;
- }
-
- ///
- /// Creates a string representation of a tag.
- ///
- /// The type of the HTML tag that would be generated for every registered resource.
- /// The attributes associated with the tag.
- /// The string representation of a tag.
- private static string GenerateTag(string tagName, params KeyValuePair[] attribbutes)
- {
- var tag = new TagBuilder(tagName);
-
- if (attribbutes != null)
- {
- foreach (var attr in attribbutes)
- tag.Attributes[attr.Key] = attr.Value;
- }
-
- return tag.ToString();
- }
-
- private const string JsRegisterName = "JsRegister";
- }
+using System;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Web;
+using System.Web.Configuration;
+using System.Web.Mvc;
+using System.Web.UI;
+using System.Collections.Generic;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Abstractions.VirtualPath;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Layouts;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Modules.Pages.Configuration;
+using Telerik.Sitefinity.Mvc.Rendering;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// This class contains helper methods for registering client resources.
+ ///
+ public static class ResourceHelper
+ {
+ ///
+ /// Gets a value indicating whether section name of the scripts should be rendered.
+ /// This value is used client side for placing scripts tag within personalized widgets.
+ ///
+ ///
+ /// true if section name of the scripts should be rendered; otherwise, false.
+ ///
+ internal static bool RenderScriptSection
+ {
+ get
+ {
+ return SystemManager.CurrentHttpContext != null &&
+ SystemManager.CurrentHttpContext.Items.Contains("RenderScriptSection") &&
+ (bool)SystemManager.CurrentHttpContext.Items["RenderScriptSection"];
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the site is in debug mode acording to the web.config
+ ///
+ ///
+ /// true if the site is in debug mode; otherwise, false.
+ ///
+ internal static bool IsDebugMode
+ {
+ get
+ {
+ if (ResourceHelper.isDebugMode.HasValue)
+ return ResourceHelper.isDebugMode.Value;
+
+ CompilationSection compilationSection = (CompilationSection)System.Configuration.ConfigurationManager.GetSection(@"system.web/compilation");
+
+ return compilationSection.Debug;
+ }
+ set
+ {
+ ResourceHelper.isDebugMode = value;
+ }
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// Indicates whether to use script manager(if exists) when register JavaScript reference.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, bool tryUseScriptManager)
+ {
+ return ResourceHelper.Script(helper, scriptPath, null, false, tryUseScriptManager);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath)
+ {
+ return ResourceHelper.Script(helper, scriptPath, null, false, true);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The path to the JavaScript file.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, List> attributes)
+ {
+ return ResourceHelper.Script(helper, scriptPath, null, false, true, attributes);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName)
+ {
+ return ResourceHelper.Script(helper, scriptPath, sectionName, true);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName, bool throwException)
+ {
+ return ResourceHelper.Script(helper, scriptPath, sectionName, throwException, true);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// The path to the JavaScript file.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName, bool throwException, List> attributes)
+ {
+ return ResourceHelper.Script(helper, scriptPath, sectionName, throwException, true, attributes);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// Indicates whether to use script manager(if exists) when register JavaScript reference.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName, bool throwException, bool tryUseScriptManager)
+ {
+ if (tryUseScriptManager && ResourceHelper.TryConfigureScriptManager(scriptPath, helper.ViewContext.HttpContext.CurrentHandler))
+ return MvcHtmlString.Empty;
+
+ return ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, scriptPath, ResourceType.Js, sectionName, throwException, null);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the JavaScript file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// Indicates whether to use script manager(if exists) when register JavaScript reference.
+ /// A list of attribute key value pairs to be added to the script
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ ///
+ /// This method uses directly the resource from the .
+ /// In case you want to use embedded scripts from Sitefinity check .
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName, bool throwException, bool tryUseScriptManager, List> attributes)
+ {
+ if (tryUseScriptManager && ResourceHelper.TryConfigureScriptManager(scriptPath, helper.ViewContext.HttpContext.CurrentHandler))
+ return MvcHtmlString.Empty;
+
+ return ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, scriptPath, ResourceType.Js, sectionName, throwException, attributes);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// Indicates whether to use script manager(if exists) when register JavaScript reference.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, bool tryUseScriptManager)
+ {
+ return ResourceHelper.Script(helper, scriptReference, null, false, tryUseScriptManager);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference)
+ {
+ return ResourceHelper.Script(helper, scriptReference, null, false, true);
+ }
+
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The path to the JavaScript file.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, List> attributes)
+ {
+ return ResourceHelper.Script(helper, scriptReference, null, false, true, attributes);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, string sectionName)
+ {
+ return ResourceHelper.Script(helper, scriptReference, sectionName, true);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// The path to the JavaScript file.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, string sectionName, List> attributes)
+ {
+ return ResourceHelper.Script(helper, scriptReference, sectionName, true, true, attributes);
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// Indicates whether to use script manager (if exists) when register JavaScript reference.
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, string sectionName, bool throwException, bool tryUseScriptManager)
+ {
+ if (tryUseScriptManager && ResourceHelper.TryConfigureScriptManager(scriptReference, helper.ViewContext.HttpContext.CurrentHandler))
+ return System.Web.Mvc.MvcHtmlString.Empty;
+
+ var references = PageManager.GetScriptReferences(scriptReference).Select(r => new MvcScriptReference(r));
+
+ StringBuilder outputMarkup = new StringBuilder();
+
+ foreach (var script in references)
+ {
+ var resourceUrl = script.GetResourceUrl();
+ outputMarkup.Append(ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourceUrl, ResourceType.Js, sectionName, throwException, null));
+ }
+
+ return MvcHtmlString.Create(outputMarkup.ToString());
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// Indicates whether to use script manager (if exists) when register JavaScript reference.
+ /// A list of attribute key value pairs to be added to the script
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, string sectionName, bool throwException, bool tryUseScriptManager, List> attributes)
+ {
+ if (tryUseScriptManager && ResourceHelper.TryConfigureScriptManager(scriptReference, helper.ViewContext.HttpContext.CurrentHandler))
+ return System.Web.Mvc.MvcHtmlString.Empty;
+
+ var references = PageManager.GetScriptReferences(scriptReference).Select(r => new MvcScriptReference(r));
+
+ StringBuilder outputMarkup = new StringBuilder();
+
+ foreach (var script in references)
+ {
+ var resourceUrl = script.GetResourceUrl();
+ outputMarkup.Append(ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourceUrl, ResourceType.Js, sectionName, throwException, attributes));
+ }
+
+ return MvcHtmlString.Create(outputMarkup.ToString());
+ }
+
+ ///
+ /// Registers JavaScript reference and ensures that it loads maximum once for a page.
+ ///
+ ///
+ /// This helper references the same resource existing in Sitefinity.
+ ///
+ /// The helper.
+ /// The script reference.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// A list of attribute key value pairs to be added to the script
+ /// If it is used the script will always be loaded on the top section of the page.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptReference, string sectionName, bool throwException)
+ {
+ return ResourceHelper.Script(helper, scriptReference, sectionName, throwException, true);
+ }
+
+ ///
+ /// Registers style sheet reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the CSS file.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath)
+ {
+ return ResourceHelper.StyleSheet(helper, resourcePath, null, false);
+ }
+
+ ///
+ /// Registers style sheet reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the CSS file.
+ /// A list of attribute key value pairs to be added to the stylesheet
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath, List> attributes)
+ {
+ return ResourceHelper.StyleSheet(helper, resourcePath, null, false, attributes);
+ }
+
+ ///
+ /// Registers style sheet reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the CSS file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath, string sectionName)
+ {
+ return ResourceHelper.StyleSheet(helper, resourcePath, sectionName, true);
+ }
+
+ ///
+ /// Registers style sheet reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the CSS file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath, string sectionName, bool throwException)
+ {
+ return ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourcePath, ResourceType.Css, sectionName, throwException, null);
+ }
+
+ ///
+ /// Registers style sheet reference and ensures that it loads maximum once for a page.
+ ///
+ /// The helper.
+ /// The path to the CSS file.
+ /// The name of the section that will render this script. If null it will render on the same place of the page
+ /// Indicates whether to throw an exception if the specified section does not exist.
+ /// A list of attribute key value pairs to be added to the stylesheet
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath, string sectionName, bool throwException, List> attributes)
+ {
+ return ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourcePath, ResourceType.Css, sectionName, throwException, attributes);
+ }
+
+ ///
+ /// Adds script references required by the Sitefinity's QueryBuilder component.
+ ///
+ /// The helper.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString QueryBuilderScriptReferences(this HtmlHelper helper)
+ {
+ var result = new StringBuilder();
+ var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Web.UI.QueryBuilder", "Telerik.Sitefinity.Web.UI.QueryBuilder.Scripts.QueryData.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Web.UI.QueryBuilder", "Telerik.Sitefinity.Web.UI.QueryBuilder.Scripts.QueryDataItem.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Web.UI.QueryBuilder", "Telerik.Sitefinity.Web.UI.QueryBuilder.Scripts.QueryItem.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Web.UI.QueryBuilder", "Telerik.Sitefinity.Web.UI.QueryBuilder.Scripts.QueryGroup.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Web.UI.QueryBuilder", "Telerik.Sitefinity.Web.UI.QueryBuilder.Scripts.QueryBuilder.js")).ToHtmlString());
+
+ return MvcHtmlString.Create(result.ToString());
+ }
+
+ ///
+ /// Adds script references required by the Sitefinity's CodeMirror component.
+ ///
+ /// The helper.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString CodeMirrorScriptReferences(this HtmlHelper helper)
+ {
+ var result = new StringBuilder();
+ var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);
+ result.Append(ResourceHelper.StyleSheet(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.codemirror.css")).ToHtmlString());
+ result.Append(ResourceHelper.StyleSheet(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.Theme.default.css")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.codemirror.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.Mode.htmlmixed.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.Mode.xml.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.Mode.css.js")).ToHtmlString());
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource(typeof(Telerik.Sitefinity.Resources.Reference).FullName, "Telerik.Sitefinity.Resources.Scripts.CodeMirror.Mode.javascript.js")).ToHtmlString());
+
+ return MvcHtmlString.Create(result.ToString());
+ }
+
+ ///
+ /// Adds script references for Js Beautify library.
+ ///
+ /// The helper.
+ ///
+ /// MvcHtmlString
+ ///
+ public static MvcHtmlString JsBeautifierScriptReference(this HtmlHelper helper)
+ {
+ var result = new StringBuilder();
+ var urlHelper = new UrlHelper(helper.ViewContext.HttpContext.Request.RequestContext);
+ result.Append(ResourceHelper.Script(helper, urlHelper.EmbeddedResource("Telerik.Sitefinity.Frontend.Startup", "Telerik.Sitefinity.Frontend.Mvc.Scripts.JSBeautifier.beautify-html.js")).ToHtmlString());
+ return MvcHtmlString.Create(result.ToString());
+ }
+
+ ///
+ /// Renders the lang attribute.
+ ///
+ /// The helper.
+ ///
+ public static MvcHtmlString RenderLangAttribute(this HtmlHelper helper)
+ {
+ return RenderLangAttribute(helper, Telerik.Sitefinity.Services.SystemManager.CurrentContext.Culture.Name);
+ }
+
+ ///
+ /// Renders the lang attribute.
+ ///
+ /// The helper.
+ /// The culture.
+ ///
+ public static MvcHtmlString RenderLangAttribute(this HtmlHelper helper, string culture)
+ {
+ string attributeString = helper.FormatValue(HttpUtility.HtmlAttributeEncode(culture), "lang=\"{0}\"");
+
+ return new MvcHtmlString(attributeString);
+ }
+
+ ///
+ /// Renders the dir attribute when language is right to left.
+ ///
+ /// The helper.
+ ///
+ public static MvcHtmlString RenderDirAttribute(this HtmlHelper helper)
+ {
+ return RenderDirAttribute(helper, Telerik.Sitefinity.Services.SystemManager.CurrentContext.Culture.Name);
+ }
+
+ ///
+ /// Renders the dir attribute when language is right to left.
+ ///
+ /// The helper.
+ /// The culture.
+ ///
+ public static MvcHtmlString RenderDirAttribute(this HtmlHelper helper, string culture)
+ {
+ var isRtl = (Array.Find(rtlLanguages, s => culture.ToString().StartsWith(s)) != null) || culture.ToString().ToLower().Contains("arab");
+ string attributeString = isRtl && (!SystemManager.IsDesignMode || SystemManager.IsPreviewMode) ? "dir=\"rtl\"" : "";
+
+ return new MvcHtmlString(attributeString);
+ }
+
+ ///
+ /// Gets the web resource URL.
+ ///
+ /// The script reference.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings")]
+ public static string GetWebResourceUrl(ScriptRef scriptReference)
+ {
+ var config = Config.Get().ScriptManager;
+ var scriptConfig = config.ScriptReferences[scriptReference.ToString()];
+ string resourceUrl = string.Empty;
+
+ if (config.EnableCdn || (scriptConfig.EnableCdn.HasValue && scriptConfig.EnableCdn.Value))
+ {
+ resourceUrl = scriptConfig.Path;
+ }
+ else
+ {
+ var page = HttpContext.Current.Handler.GetPageHandler() ?? new PageProxy(null);
+
+ resourceUrl = page.ClientScript.GetWebResourceUrl(
+ TypeResolutionService.ResolveType("Telerik.Sitefinity.Resources.Reference"),
+ scriptConfig.Name);
+ }
+
+ return resourceUrl;
+ }
+
+ ///
+ /// Places the script before the end of the body.
+ ///
+ /// The page.
+ /// The script.
+ public static void PlaceScriptBeforeBodyEnd(this Page page, string script)
+ {
+ /* There is a literal control in the FrontendMVC.aspx which is used as a mark where scripts should be placed.*/
+ int insertAt = page.Controls.Count - 1;
+ MasterPage master = null;
+ bool hasLiteral = false;
+ for (int i = page.Controls.Count - 1; i >= 0; i--)
+ {
+ if (page.Controls[i] is LiteralControl)
+ {
+ insertAt = i;
+ hasLiteral = true;
+ break;
+ }
+ else if (page.Controls[i] is MasterPage)
+ {
+ master = page.Controls[i] as MasterPage;
+ break;
+ }
+ }
+
+ if (master != null)
+ {
+ for (int i = master.Controls.Count - 1; i >= 0; i--)
+ {
+ if (master.Controls[i] is LiteralControl)
+ {
+ insertAt = i;
+ hasLiteral = true;
+ break;
+ }
+ }
+ }
+
+ if (hasLiteral)
+ {
+ if (master != null)
+ {
+ master.Controls.AddAt(insertAt, new LiteralControl(script));
+ }
+ else
+ {
+ if (page.Form != null)
+ {
+ page.Form.Controls.Add(new LiteralControl(script));
+ }
+ else
+ {
+ page.Controls.AddAt(insertAt, new LiteralControl(script));
+ }
+ }
+ }
+ else
+ {
+ if (page.Form != null)
+ {
+ page.Form.Controls.Add(new LiteralControl(script));
+ }
+ else
+ {
+ page.Controls.Add(new LiteralControl(script));
+ }
+ }
+ }
+
+ ///
+ /// Renders all scripts.
+ ///
+ /// The context.
+ /// The section name.
+ ///
+ internal static string RenderAllScripts(HttpContextBase context, string sectionName)
+ {
+ var scriptRegister = new ResourceRegister(ResourceHelper.JsRegisterName, context);
+ var scriptMarkup = ResourceHelper.BuildHtmlResourcesMarkup(scriptRegister, sectionName, ResourceType.Js);
+
+ return scriptMarkup;
+ }
+
+ ///
+ /// Renders all stylesheets.
+ ///
+ /// The context.
+ /// The section name.
+ ///
+ internal static string RenderAllStylesheets(HttpContextBase context, string sectionName)
+ {
+ var stylesheetRegister = new ResourceRegister(ResourceHelper.CssRegisterName, context);
+ var stylesheetMarkup = ResourceHelper.BuildHtmlResourcesMarkup(stylesheetRegister, sectionName, ResourceType.Css);
+
+ return stylesheetMarkup;
+ }
+
+ private static MvcHtmlString RegisterResource(HttpContextBase httpContext, string resourcePath, ResourceType resourceType, string sectionName, bool throwException, List> attributes = null)
+ {
+ throwException = throwException && httpContext.CurrentHandler != null;
+
+ var registerName = string.Empty;
+ if (resourceType == ResourceType.Js)
+ registerName = ResourceHelper.JsRegisterName;
+ else if (resourceType == ResourceType.Css)
+ registerName = ResourceHelper.CssRegisterName;
+
+ var register = new ResourceRegister(registerName, httpContext);
+
+ MvcHtmlString result = MvcHtmlString.Empty;
+
+ if (!string.IsNullOrWhiteSpace(sectionName))
+ {
+ var pageHandler = httpContext.Handler.GetPageHandler();
+ if (pageHandler != null && pageHandler.Master is MvcMasterPage)
+ {
+ if (!throwException && !SectionRenderer.IsAvailable(pageHandler, sectionName))
+ sectionName = null;
+ }
+ else
+ {
+ if (!SectionRenderer.IsAvailable(pageHandler, sectionName))
+ sectionName = null;
+ }
+ }
+ else
+ {
+ sectionName = null;
+ }
+
+ // No section name renders the script inline if it hasn't been rendered
+ if (sectionName == null ||
+ ResourceHelper.RenderScriptSection)
+ {
+ if (!register.IsRegistered(resourcePath))
+ {
+ result = MvcHtmlString.Create(ResourceHelper.BuildSingleResourceMarkup(resourcePath, resourceType, sectionName, attributes));
+ }
+ }
+
+ // Register the resource even if it had to be rendered inline (avoid repetitions).
+ register.Register(resourcePath, sectionName, throwException, attributes);
+
+ return result;
+ }
+
+ ///
+ /// Tries the configure script manager.
+ ///
+ /// The script reference.
+ /// The httpHandler.
+ ///
+ private static bool TryConfigureScriptManager(ScriptRef scriptReference, IHttpHandler httpHandler)
+ {
+ var page = ResourceHelper.GetTopMostPageFromHandler(httpHandler);
+
+ if (page != null)
+ {
+ var scriptManager = PageManager.ConfigureScriptManager(page, scriptReference, false);
+
+ if (scriptManager != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Tries the configure script manager.
+ ///
+ /// The script reference.
+ /// The httpHandler.
+ ///
+ private static bool TryConfigureScriptManager(string scriptReference, IHttpHandler httpHandler)
+ {
+ var page = ResourceHelper.GetTopMostPageFromHandler(httpHandler);
+
+ if (page != null)
+ {
+ var scriptManager = ScriptManager.GetCurrent(page);
+
+ if (scriptManager != null)
+ {
+ var updatedScriptReference = GetResourceOrMinified(scriptReference);
+ scriptManager.Scripts.Add(new ScriptReference(updatedScriptReference));
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static Page GetTopMostPageFromHandler(IHttpHandler httpHandler)
+ {
+ var page = httpHandler.GetPageHandler();
+
+ if (page != null)
+ {
+ var topMostPage = page;
+
+ while (topMostPage.PreviousPage != null)
+ {
+ topMostPage = topMostPage.PreviousPage;
+ }
+
+ return topMostPage;
+ }
+
+ return null;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "sectionName")]
+ private static string BuildHtmlResourcesMarkup(ResourceRegister resourceRegister, string sectionName, ResourceType resourceType)
+ {
+ StringBuilder output = new StringBuilder();
+
+ foreach (var resource in resourceRegister.GetResourceItemsForSection(sectionName))
+ {
+ if (!resourceRegister.IsRendered(resource.ResourceKey))
+ {
+ output.Append(ResourceHelper.BuildSingleResourceMarkup(resource.ResourceKey, resourceType, sectionName, resource.Attributes));
+ resourceRegister.MarkAsRendered(resource.ResourceKey);
+ }
+ }
+
+ return output.ToString();
+ }
+
+ private static string BuildSingleResourceMarkup(string resourceKey, ResourceType resourceType, string sectionName, List> attributes = null)
+ {
+ string result;
+
+ if (resourceType == ResourceType.Js)
+ result = ResourceHelper.BuildScriptMarkup(resourceKey, sectionName, attributes);
+ else if (resourceType == ResourceType.Css)
+ result = ResourceHelper.BuildStyleSheetMarkup(resourceKey, attributes);
+ else
+ result = string.Empty;
+
+ return result;
+ }
+
+ private static string BuildScriptMarkup(string resourceKey, string sectionName, List> attributes)
+ {
+ var tag = new TagBuilder("script");
+
+ resourceKey = GetResourceOrMinified(resourceKey);
+
+ tag.Attributes["src"] = resourceKey;
+ tag.Attributes["type"] = "text/javascript";
+
+ if (attributes != null)
+ {
+ foreach (var attr in attributes)
+ {
+ tag.Attributes[attr.Key] = attr.Value;
+ }
+ }
+
+ if (ResourceHelper.RenderScriptSection && !string.IsNullOrWhiteSpace(sectionName))
+ {
+ tag.Attributes["data-sf-section"] = sectionName;
+ }
+
+ return tag.ToString(TagRenderMode.Normal);
+ }
+
+ private static string GetResourceOrMinified(string resourceKey)
+ {
+ if (!ResourceHelper.IsDebugMode)
+ {
+ var extensionIndex = resourceKey.LastIndexOf(".js");
+ if (extensionIndex > 0 && !resourceKey.Contains(".min.js"))
+ {
+ var minFilePath = resourceKey.Insert(extensionIndex, ".min");
+ var minPathWithoutParams = resourceKey.Substring(0, extensionIndex) + ".min.js";
+
+ if (VirtualPathManager.FileExists(minPathWithoutParams) || VirtualPathManager.FileExists(minFilePath))
+ {
+ resourceKey = minFilePath;
+ }
+ }
+ }
+
+ return resourceKey;
+ }
+
+ private static string BuildStyleSheetMarkup(string resourceKey, List> attributes)
+ {
+ var tag = new TagBuilder("link");
+
+ tag.Attributes["href"] = resourceKey;
+ tag.Attributes["rel"] = "stylesheet";
+ tag.Attributes["type"] = "text/css";
+
+ if (attributes != null)
+ {
+ foreach (var attr in attributes)
+ {
+ tag.Attributes[attr.Key] = attr.Value;
+ }
+ }
+
+ return tag.ToString(TagRenderMode.SelfClosing);
+ }
+
+ internal const string JsRegisterName = "JsRegister";
+ internal const string CssRegisterName = "CssRegister";
+
+ internal class MvcScriptReference : ScriptReference
+ {
+ public MvcScriptReference(ScriptReference reference)
+ {
+ if (reference == null)
+ throw new ArgumentNullException("reference");
+
+ this.Path = reference.Path;
+ this.Assembly = reference.Assembly;
+ this.Name = reference.Name;
+ }
+
+ public string GetResourceUrl()
+ {
+ var scriptManager = new ScriptManager();
+
+ try
+ {
+ return this.GetUrl(scriptManager, zip: false);
+ }
+ catch (NullReferenceException ex)
+ {
+ ////This is because ScriptReference.ClientUrlResolver is null as the class in to initialize on the page.
+ if (this.IsGetUrlFromPathException(ex))
+ {
+ return scriptManager.ResolveClientUrl(this.Path);
+ }
+
+ throw;
+ }
+ }
+
+ private bool IsGetUrlFromPathException(Exception ex)
+ {
+ return ex.TargetSite.Name == "GetUrlFromPath" && ex.TargetSite.DeclaringType.FullName == "System.Web.UI.ScriptReference";
+ }
+ }
+
+ ///
+ /// This enum represents supported resource types.
+ ///
+ internal enum ResourceType
+ {
+ Js,
+ Css
+ }
+
+ private static bool? isDebugMode;
+
+ private static string[] rtlLanguages = { "ar", "he", "fa", "ku", "ur", "dv", "ps", "ha", "ks", "yi" };
+ }
}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SettingsHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SettingsHelpers.cs
new file mode 100644
index 000000000..baa0b4271
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SettingsHelpers.cs
@@ -0,0 +1,67 @@
+using System.Linq;
+using System.Web.Script.Serialization;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Libraries.Model;
+using Telerik.Sitefinity.Modules.Libraries.Configuration;
+using Telerik.Sitefinity.Modules.Libraries.Images;
+using Telerik.Sitefinity.Web.UI.ContentUI.Views.Backend.Detail.Definitions;
+using Telerik.Sitefinity.Web.UI.Fields.Config;
+using Telerik.Sitefinity.Web.UI.Fields.Definitions;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// This class contains helpers for working with widget settings.
+ ///
+ public static class SettingsHelpers
+ {
+ ///
+ /// Gets the media settings for specific media type
+ ///
+ /// Serialized media settings
+ public static string GetMediaSettings(string mediaType)
+ {
+ var libratiesConfig = Config.Get();
+ object settings;
+
+ switch (mediaType)
+ {
+ case "Image":
+ var singleImageUploadDefinition = libratiesConfig.ContentViewControls[ImagesDefinitions.BackendImagesDefinitionName]?.Views[ImagesDefinitions.SingleImageUploadDetailsView] as DetailFormViewDefinition;
+ var altTextRequired = (singleImageUploadDefinition?.Sections.First(s => s.Name == "MainSection")?.Fields.First(f => f.FieldName == nameof(Image.AlternativeText)) as TextFieldDefinition)?.Validation?.Required.GetValueOrDefault();
+
+ settings = new
+ {
+ AllowedExensionsSettings = libratiesConfig.Images.AllowedExensionsSettings,
+ EnableAllLanguagesSearch = libratiesConfig.EnableAllLanguagesSearch,
+ EnableSelectedFolderSearch = libratiesConfig.EnableSelectedFolderSearch,
+ AltTextRequired = altTextRequired
+ };
+ break;
+ case "Video":
+ settings = new
+ {
+ AllowedExensionsSettings = libratiesConfig.Videos.AllowedExensionsSettings,
+ EnableAllLanguagesSearch = libratiesConfig.EnableAllLanguagesSearch,
+ EnableSelectedFolderSearch = libratiesConfig.EnableSelectedFolderSearch
+ };
+ break;
+ case "Document":
+ settings = new
+ {
+ AllowedExensionsSettings = libratiesConfig.Documents.AllowedExensionsSettings,
+ EnableAllLanguagesSearch = libratiesConfig.EnableAllLanguagesSearch,
+ EnableSelectedFolderSearch = libratiesConfig.EnableSelectedFolderSearch,
+ AllowedExensions = libratiesConfig.Documents.AllowedExensions
+ };
+ break;
+ default:
+ settings = null;
+ break;
+ }
+
+ var serialzier = new JavaScriptSerializer();
+ return serialzier.Serialize(settings);
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityContext.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityContext.cs
index bfb793ed7..e1882aa57 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityContext.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityContext.cs
@@ -1,4 +1,7 @@
using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using System.Web;
using System.Web.UI;
using Telerik.Sitefinity.Frontend.Mvc.Helpers.ViewModels;
using Telerik.Sitefinity.Modules.Pages;
@@ -7,6 +10,7 @@
using Telerik.Sitefinity.Security.Model;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.Web.Events;
namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
{
@@ -65,9 +69,40 @@ public static ProfileViewModel Profile
///
public static bool IsBackend
{
- get
- {
- return ControlExtensions.IsBackend();
+ get
+ {
+ return ControlExtensions.IsBackend();
+ }
+ }
+
+ ///
+ /// Determines whether this method is invoked from Sitefinity's preview mode.
+ ///
+ public static bool IsPreviewMode
+ {
+ get
+ {
+ return SystemManager.IsPreviewMode;
+ }
+ }
+
+ ///
+ /// Gets the frontend login URL for the current site as configured in the backend.
+ ///
+ ///
+ /// The frontend login URL.
+ ///
+ [SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")]
+ [SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Login")]
+ public static string FrontendLoginUrl
+ {
+ get
+ {
+ var redirectStrategy = RedirectStrategyType.None;
+ var wrapper = new HttpContextWrapper(HttpContext.Current);
+ var pageUrl = RouteHelper.GetFrontEndLogin(wrapper, out redirectStrategy);
+
+ return pageUrl;
}
}
@@ -126,4 +161,4 @@ private static ProfileViewModel GetCurrentSitefinityUserProfile()
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityHelper.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityHelper.cs
new file mode 100644
index 000000000..bd05c3638
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SitefinityHelper.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Mvc;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// This class contains basic helper method used in Feather.
+ ///
+ public static class SitefinityHelper
+ {
+ ///
+ /// Wrapper helper for Sitefinity.
+ ///
+ /// The helper.
+ ///
+ public static HtmlHelper Sitefinity(this HtmlHelper helper)
+ {
+ return helper;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SocialShareHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SocialShareHelpers.cs
index a5fdac1ef..845919ff0 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/SocialShareHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/SocialShareHelpers.cs
@@ -1,6 +1,6 @@
-using System.Web;
+using System;
+using System.Diagnostics.CodeAnalysis;
using System.Web.Mvc;
-using System.Web.Mvc.Html;
namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
{
@@ -10,22 +10,23 @@ public static class SocialShareHelpers
/// Socials the share options. Redirect to the SocialShare control if exist else render error message
///
/// The HTML helper.
- public static System.Web.Mvc.MvcHtmlString SocialShareOptions(this HtmlHelper helper)
+ [Obsolete("Social sharing module has been removed. This helper will no longer work.")]
+ [SuppressMessage("Microsoft.Design", "CA1801:ReviewUnusedParameters")]
+ public static MvcHtmlString SocialShareOptions(this HtmlHelper helper)
{
- System.Web.Mvc.MvcHtmlString result;
- try
- {
- result = helper.Action(ActionName, ControllerName);
- }
- catch (HttpException)
- {
- result = new System.Web.Mvc.MvcHtmlString("The SocialShare widget could not be found.");
- }
-
- return result;
+ return null;
}
- private const string ActionName = "Index";
- private const string ControllerName = "SocialShare";
+ ///
+ /// Socials the share options. Redirect to the SocialShare control if exist else render error message
+ ///
+ /// The HTML helper.
+ /// The data item which we will be sharing
+ [Obsolete("Social sharing module has been removed. This helper will no longer work.")]
+ [SuppressMessage("Microsoft.Design", "CA1801:ReviewUnusedParameters")]
+ public static MvcHtmlString SocialShareOptions(this HtmlHelper helper, Telerik.Sitefinity.Model.IHasTitle dataItem)
+ {
+ return null;
+ }
}
}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs
index 1c59f45c5..bea9444b9 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs
@@ -1,89 +1,262 @@
-using System;
-using System.Web;
-using System.Web.Hosting;
-using System.Web.Mvc;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
-using Telerik.Sitefinity.Frontend.Resources;
-
-namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
-{
- ///
- /// Extension methods for UrlHelper.
- ///
- public static class UrlHelpers
- {
- ///
- /// Resolves URL based on the current widget.
- ///
- /// The URL helper.
- /// The content path.
- /// Resolved URL.
- /// contentPath
- ///
- /// Could not resolve the given URL because RouteData of the current context is null.
- /// or
- /// Could not resolve the given URL because RouteData does not contain \controller\ key.
- ///
- public static string WidgetContent(this UrlHelper helper, string contentPath)
- {
- if (contentPath.IsNullOrEmpty())
- throw new ArgumentNullException("contentPath");
-
- if (contentPath.StartsWith("~", StringComparison.Ordinal) || contentPath.StartsWith("/", StringComparison.Ordinal) || contentPath.Contains("://"))
- return helper.Content(contentPath);
-
- if (helper.RequestContext.RouteData == null)
- throw new InvalidOperationException("Could not resolve the given URL because RouteData of the current context is null.");
-
- var packagesManager = new PackageManager();
- var packageName = packagesManager.GetCurrentPackage();
-
- var contentResolvedPath = string.Empty;
-
- // "widgetName" is a parameter in the route of the Designer. It allows us to have a special fallback logic
- // where we first check for the requested resource in the widget assembly and then fallback to the current controller assembly.
- object controllerName;
- if (helper.RequestContext.RouteData.Values.TryGetValue("widgetName", out controllerName))
- contentResolvedPath = UrlHelpers.GetResourcePath((string)controllerName, contentPath, PackageManager.PackageUrlParameterName, packageName);
-
- if (string.IsNullOrEmpty(contentResolvedPath))
- {
- if (helper.RequestContext.RouteData.Values.TryGetValue("controller", out controllerName))
- contentResolvedPath = UrlHelpers.GetResourcePath((string)controllerName, contentPath, PackageManager.PackageUrlParameterName, packageName);
- else
- throw new InvalidOperationException("Could not resolve the given URL because RouteData does not contain \"controller\" key.");
- }
-
- if (string.IsNullOrEmpty(contentResolvedPath))
- {
- var url = "~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(UrlHelpers).Assembly) + contentPath;
- contentResolvedPath = UrlTransformations.AppendParam(url, PackageManager.PackageUrlParameterName, packageName);
- }
-
- return helper.Content(contentResolvedPath);
- }
-
- ///
- /// Gets the resource path.
- ///
- /// Name of the controller.
- /// The content path.
- /// Name of the paramater.
- /// Name of the package.
- ///
- private static string GetResourcePath(string controllerName, string contentPath, string paramaterName, string packageName)
- {
- var controllerType = FrontendManager.ControllerFactory.ResolveControllerType(controllerName);
- var alternatePath = FrontendManager.VirtualPathBuilder.GetVirtualPath(controllerType);
- var baseUrl = "~/" + alternatePath + contentPath;
-
- if (HostingEnvironment.VirtualPathProvider == null || HostingEnvironment.VirtualPathProvider.FileExists(baseUrl))
- {
- alternatePath = UrlTransformations.AppendParam(baseUrl, paramaterName, packageName);
- return alternatePath;
- }
-
- return string.Empty;
- }
- }
-}
+using System;
+using System.Linq;
+using System.Text;
+using System.Web.Hosting;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Abstractions.VirtualPath;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Mvc.Rendering;
+using Telerik.Sitefinity.Taxonomies.Model;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
+{
+ ///
+ /// Extension methods for UrlHelper.
+ ///
+ public static class UrlHelpers
+ {
+ ///
+ /// Resolves URL based on the current widget.
+ ///
+ /// The URL helper.
+ /// The content path.
+ /// Resolved URL.
+ /// contentPath
+ ///
+ /// Could not resolve the given URL because RouteData of the current context is null.
+ /// or
+ /// Could not resolve the given URL because RouteData does not contain \controller\ key.
+ ///
+ public static string WidgetContent(this UrlHelper helper, string contentPath)
+ {
+ if (contentPath.IsNullOrEmpty())
+ throw new ArgumentNullException("contentPath");
+
+ var packagesManager = new PackageManager();
+ var packageName = packagesManager.GetCurrentPackage();
+
+ if (contentPath.StartsWith("~", StringComparison.Ordinal) || contentPath.StartsWith("/", StringComparison.Ordinal) || contentPath.Contains("://"))
+ {
+ var url = UrlTransformations.AppendParam(contentPath, PackageManager.PackageUrlParameterName, packageName);
+ url = UrlHelpers.AppendVersion(url);
+ return helper.Content(url);
+ }
+
+ if (helper.RequestContext.RouteData == null)
+ throw new InvalidOperationException("Could not resolve the given URL because RouteData of the current context is null.");
+
+ var contentResolvedPath = string.Empty;
+ object controllerName;
+
+ // "widgetName" is a parameter in the route of the Designer. It allows us to have a special fallback logic
+ // where we first check for the requested resource in the widget assembly and then fallback to the current controller assembly.
+ if (helper.RequestContext.RouteData.Values.TryGetValue("widgetName", out controllerName))
+ contentResolvedPath = UrlHelpers.GetResourcePath((string)controllerName, contentPath, PackageManager.PackageUrlParameterName, packageName);
+
+ if (string.IsNullOrEmpty(contentResolvedPath))
+ {
+ if (helper.RequestContext.RouteData.Values.TryGetValue("controller", out controllerName))
+ contentResolvedPath = UrlHelpers.GetResourcePath((string)controllerName, contentPath, PackageManager.PackageUrlParameterName, packageName);
+ else
+ throw new InvalidOperationException("Could not resolve the given URL because RouteData does not contain \"controller\" key.");
+ }
+
+ if (string.IsNullOrEmpty(contentResolvedPath))
+ {
+ var url = "~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(UrlHelpers).Assembly) + contentPath;
+ contentResolvedPath = UrlTransformations.AppendParam(url, PackageManager.PackageUrlParameterName, packageName);
+ }
+
+ contentResolvedPath = UrlHelpers.AppendVersion(contentResolvedPath);
+
+ return helper.Content(contentResolvedPath);
+ }
+
+ ///
+ /// Resolves URL based on the current widget.
+ ///
+ /// The helper.
+ /// The content path.
+ /// Name of the assembly.
+ ///
+ /// contentPath
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider", MessageId = "System.String.Format(System.String,System.Object,System.Object)")]
+ public static string WidgetContent(this UrlHelper helper, string contentPath, string assemblyName)
+ {
+ if (contentPath.IsNullOrEmpty())
+ throw new ArgumentNullException("contentPath");
+
+ if (string.IsNullOrEmpty(assemblyName))
+ return UrlHelpers.WidgetContent(helper, contentPath);
+
+ var packagesManager = new PackageManager();
+ var packageName = packagesManager.GetCurrentPackage();
+
+ if (contentPath.StartsWith("~", StringComparison.Ordinal) || contentPath.StartsWith("/", StringComparison.Ordinal) || contentPath.Contains("://"))
+ {
+ var url = UrlTransformations.AppendParam(contentPath, PackageManager.PackageUrlParameterName, packageName);
+ url = UrlHelpers.AppendVersion(url);
+ return helper.Content(url);
+ }
+
+ var resourceUrl = string.Format("~/{0}/{1}", FrontendManager.VirtualPathBuilder.GetVirtualPath(assemblyName), contentPath);
+ var contentResolvedPath = UrlTransformations.AppendParam(resourceUrl, PackageManager.PackageUrlParameterName, packageName);
+ contentResolvedPath = UrlHelpers.AppendVersion(contentResolvedPath);
+
+ return helper.Content(contentResolvedPath);
+ }
+
+ ///
+ /// Gets the URL of an embedded resource.
+ ///
+ /// The helper.
+ /// A type from the assembly that embeds the resource.
+ /// The resource path.
+ /// The resource URL.
+ public static string EmbeddedResource(this UrlHelper helper, string type, string path)
+ {
+ var page = helper.RequestContext.HttpContext.Handler.GetPageHandler() ?? new PageProxy(null);
+ return page.ClientScript.GetWebResourceUrl(TypeResolutionService.ResolveType(type), path);
+ }
+
+ ///
+ /// Gets the URL template used for the paging.
+ ///
+ /// The URL template.
+ public static string GetRedirectPagingUrl()
+ {
+ string redirectUrl;
+ if (UrlParamsMapperBase.UseNamedParametersRouting)
+ {
+ redirectUrl = "/" + FeatherActionInvoker.PagingNamedParameter + "/{0}";
+ }
+ else
+ {
+ redirectUrl = "/{0}";
+ }
+
+ return redirectUrl;
+ }
+
+ ///
+ /// Gets the URL template used for the paging when filtered by taxonomy.
+ ///
+ /// The URL template.
+ public static string GetRedirectPagingUrl(ITaxon taxonFilter)
+ {
+ string redirectUrl;
+ if (UrlParamsMapperBase.UseNamedParametersRouting)
+ {
+ redirectUrl = string.Format("/{0}/{1}/{2}", taxonFilter.Taxonomy.Name, taxonFilter.UrlName, FeatherActionInvoker.PagingNamedParameter) + "/{0}";
+ }
+ else
+ {
+ redirectUrl = "/" + taxonFilter.UrlName + "/{0}";
+ }
+
+ return redirectUrl;
+ }
+
+ ///
+ /// Gets the URL template used for the paging when filtered by taxonomy and url evaluation mode.
+ ///
+ /// The URL template.
+ public static string GetRedirectPagingUrl(ITaxon taxonFilter, string[] urlParams, string queryString)
+ {
+ bool addQueryString = !string.IsNullOrEmpty(queryString);
+ string redirectUrl;
+ if (UrlParamsMapperBase.UseNamedParametersRouting)
+ {
+ redirectUrl = string.Format("/{0}/{1}/{2}", taxonFilter.Taxonomy.Name, taxonFilter.UrlName, FeatherActionInvoker.PagingNamedParameter) + "/{0}";
+ }
+ else
+ {
+ // 3 because we have the following structure /-in-tags/tags/tagName
+ if (urlParams != null && urlParams.Length >= 3)
+ {
+ if (taxonFilter is FlatTaxon)
+ {
+ redirectUrl = string.Format("/{0}/{1}/{2}", urlParams[0], urlParams[1], urlParams[2]);
+ }
+ else
+ {
+ if (urlParams[urlParams.Length - 1].Equals(taxonFilter.UrlName))
+ {
+ // url is like /-in-category/categories/cat1/cat2
+ string taxonFilterParams = string.Join("/", urlParams);
+ redirectUrl = "/" + taxonFilterParams;
+ }
+ else
+ {
+ // url is like /-in-category/categories/cat1/cat2/2 where '2' is the page so we want to exclude it from redirect url
+ string taxonFilterParams = string.Join("/", urlParams.Take(urlParams.Length - 1));
+ redirectUrl = "/" + taxonFilterParams;
+ }
+ }
+
+ // add /{0} at the very end for the page number
+ redirectUrl += "/{0}";
+ }
+ else
+ {
+ addQueryString = false;
+ redirectUrl = "/{0}" + queryString;
+ }
+ }
+
+ if (addQueryString)
+ {
+ redirectUrl += queryString;
+ }
+
+ return redirectUrl;
+ }
+
+ ///
+ /// Gets the resource path.
+ ///
+ /// Name of the controller.
+ /// The content path.
+ /// Name of the paramater.
+ /// Name of the package.
+ ///
+ private static string GetResourcePath(string controllerName, string contentPath, string paramaterName, string packageName)
+ {
+ var controllerType = FrontendManager.ControllerFactory.ResolveControllerType(controllerName);
+ if (controllerType != null)
+ {
+ var alternatePath = FrontendManager.VirtualPathBuilder.GetVirtualPath(controllerType);
+ var baseUrl = "~/" + alternatePath + contentPath;
+
+ if (HostingEnvironment.VirtualPathProvider == null || HostingEnvironment.VirtualPathProvider.FileExists(baseUrl))
+ {
+ alternatePath = UrlTransformations.AppendParam(baseUrl, paramaterName, packageName);
+ return alternatePath;
+ }
+ }
+
+ return string.Empty;
+ }
+
+ internal static string AppendVersion(string contentPath)
+ {
+ if (contentPath.EndsWith(".js") || contentPath.Contains(".js?"))
+ {
+ var hash = VirtualPathManager.GetFileHash(contentPath, null);
+ if (hash != null)
+ {
+ var bytes = Encoding.UTF8.GetBytes(hash);
+ var base64version = Convert.ToBase64String(bytes);
+ contentPath = UrlTransformations.AppendParam(contentPath, VersionQueryParam, base64version);
+ }
+ }
+
+ return contentPath;
+ }
+
+ private const string VersionQueryParam = "v";
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/CustomTaxonomyViewModel.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/CustomTaxonomyViewModel.cs
new file mode 100644
index 000000000..55982a9fa
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/CustomTaxonomyViewModel.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Telerik.Sitefinity.Taxonomies.Model;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Helpers.ViewModels
+{
+ ///
+ /// This class represents the view model for the custom taxonomy fields.
+ ///
+ public class CustomTaxonomyViewModel
+ {
+ ///
+ /// Gets or sets the name of the custom field.
+ ///
+ ///
+ /// The name.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Gets or sets the title of the taxonomy.
+ ///
+ ///
+ /// The title.
+ ///
+ public string Title { get; set; }
+
+ ///
+ /// Gets or sets the type of the taxonomy.
+ ///
+ ///
+ /// The type of the taxonomy.
+ ///
+ public TaxonomyType TaxonomyType { get; set; }
+
+ ///
+ /// Gets or sets the identifier of the taxonomy.
+ ///
+ ///
+ /// The identifier.
+ ///
+ public Guid Id { get; set; }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
index 8980512f1..8ee4d93cf 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Telerik.Sitefinity.Web.UI;
namespace Telerik.Sitefinity.Frontend.Mvc.Helpers.ViewModels
{
@@ -17,10 +18,23 @@ public class PagerViewModel
/// The total pages count.
/// The redirect URL template.
public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTemplate)
+ : this(currentPage, totalPagesCount, redirectUrlTemplate, 10)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The current page.
+ /// The total pages count.
+ /// The redirect URL template.
+ /// The amount of page nodes to render
+ public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTemplate, int displayCount)
{
this.CurrentPage = currentPage;
this.TotalPagesCount = totalPagesCount;
this.RedirectUrlTemplate = redirectUrlTemplate;
+ this.DisplayCount = displayCount;
}
///
@@ -39,6 +53,14 @@ public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTe
///
public int TotalPagesCount { get; set; }
+ ///
+ /// Gets or sets the total item count to render.
+ ///
+ ///
+ /// The total numbers that will render.
+ ///
+ public int DisplayCount { get; set; }
+
///
/// Gets or sets the redirect URL template.
///
@@ -46,5 +68,40 @@ public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTe
/// The redirect URL template.
///
public string RedirectUrlTemplate { get; set; }
+
+ ///
+ /// Gets or sets the pager node
+ ///
+ ///
+ /// The amount of nodes to render
+ ///
+ public IList PagerNodes
+ {
+ get
+ {
+ if (this.pagerNodes == null)
+ this.pagerNodes = new List();
+
+ return this.pagerNodes;
+ }
+ }
+
+ ///
+ /// Gets or sets the previous node
+ ///
+ ///
+ /// The previous page
+ ///
+ public Pager.PagerNumericItem PreviousNode { get; set; }
+
+ ///
+ /// Gets or sets the next node
+ ///
+ ///
+ /// The next page
+ ///
+ public Pager.PagerNumericItem NextNode { get; set; }
+
+ private IList pagerNodes;
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
index 8799792e2..3c50bcaab 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
@@ -28,10 +28,64 @@ public static IEnumerable GetViewNames(this HtmlHelper helper, string co
throw new ArgumentException("Controller cannot be resolved.");
}
+ if (controller.RouteData != null && controller.RouteData.Values["controller"] as string != null)
+ {
+ controller.RouteData.Values["controller"] = controllerName;
+ }
var regex = new Regex(templateNamePattern, RegexOptions.IgnoreCase);
var views = controller.GetViews().Where(view => Regex.IsMatch(view, templateNamePattern)).Select(view => regex.Match(view).Groups["viewName"].Value);
return views;
}
+
+ ///
+ /// Gets a collection with the view names which match the given pattern.
+ ///
+ /// The helper.
+ /// The controller.
+ /// The template name pattern.
+ /// Names of the views that match provided pattern.
+ /// Controller cannot be resolved.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper")]
+ public static IEnumerable GetViewNames(this HtmlHelper helper, Controller controller, string templateNamePattern)
+ {
+ return ViewSelectorHelpers.GetViewNames(helper, controller, templateNamePattern, null);
+ }
+
+ ///
+ /// Gets a collection with the view names which match the given pattern.
+ ///
+ /// The helper.
+ /// The controller.
+ /// The template name pattern.
+ /// The name of dynamic module.
+ /// Names of the views that match provided pattern.
+ /// Controller cannot be resolved.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "helper")]
+ public static IEnumerable GetViewNames(this HtmlHelper helper, Controller controller, string templateNamePattern, string moduleName)
+ {
+ if (controller == null)
+ {
+ throw new ArgumentNullException("controller");
+ }
+
+ var regex = new Regex(templateNamePattern, RegexOptions.IgnoreCase);
+ var views = controller.GetViews(null, moduleName).Where(view => Regex.IsMatch(view, templateNamePattern)).Select(view => regex.Match(view).Groups["viewName"].Value);
+
+ return views;
+ }
+
+ ///
+ /// Splits the camel case.
+ ///
+ /// The input.
+ ///
+ public static string SplitCamelCase(this string input)
+ {
+ var inputWithSpaces = Regex.Replace(input, "([A-Z])", " $1", RegexOptions.Compiled).Trim();
+ var capitalizeOnlyFirstLater = inputWithSpaces.First().ToString().ToUpper() + inputWithSpaces.Substring(1).ToLower();
+
+ return capitalizeOnlyFirstLater;
+ }
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/EmailAddressAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/EmailAddressAttribute.cs
new file mode 100644
index 000000000..3979f9a5e
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/EmailAddressAttribute.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Web.Mvc;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Attributes
+{
+ ///
+ /// This class represents and should be used when one needs to validate email property.
+ ///
+ [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
+ public sealed class EmailAddressAttribute : ValidationAttribute, IClientValidatable
+ {
+ public string RegexPattern
+ {
+ get
+ {
+ return this.regexPattern ?? EmailAddressAttribute.DefaultRegexPattern;
+ }
+
+ set
+ {
+ this.regexPattern = value;
+ }
+ }
+
+ ///
+ public IEnumerable GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
+ {
+ yield return new ModelClientValidationRule
+ {
+ ValidationType = "email",
+ ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
+ };
+ }
+
+ ///
+ public override bool IsValid(object value)
+ {
+ if (value != null)
+ {
+ var stringValue = value as string;
+ if (!string.IsNullOrEmpty(stringValue))
+ {
+ var regex = new Regex(this.RegexPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
+ if (regex.Match(stringValue).Success)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ protected override ValidationResult IsValid(object value, ValidationContext validationContext)
+ {
+ return this.IsValid(value) ? ValidationResult.Success : new ValidationResult(this.ErrorMessage);
+ }
+
+ private const string DefaultRegexPattern = @"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$";
+ private string regexPattern;
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/RequiredIfExistAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/RequiredIfExistAttribute.cs
new file mode 100644
index 000000000..9d3f48b4e
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Attributes/RequiredIfExistAttribute.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Attributes
+{
+ ///
+ /// This class represents and should be used when one needs to validate property only if another property has value.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")]
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
+ public class RequiredIfExistAttribute : System.ComponentModel.DataAnnotations.ValidationAttribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public RequiredIfExistAttribute(string dependentPropertyName)
+ : base()
+ {
+ this.innerAttribute = new RequiredAttribute();
+ this.dependentPropertyName = dependentPropertyName;
+ }
+
+ ///
+ protected override ValidationResult IsValid(object value, ValidationContext validationContext)
+ {
+ PropertyInfo dependentProperty = validationContext.ObjectInstance.GetType().GetProperty(this.dependentPropertyName);
+
+ if (dependentProperty != null)
+ {
+ var dependentPropertyValue = dependentProperty.GetValue(validationContext.ObjectInstance, null);
+
+ if (dependentPropertyValue != null)
+ {
+ if (!this.innerAttribute.IsValid(value))
+ return new ValidationResult(this.ErrorMessage);
+ }
+ }
+
+ return ValidationResult.Success;
+ }
+
+ private string dependentPropertyName;
+ private RequiredAttribute innerAttribute;
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorView.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorView.cs
new file mode 100644
index 000000000..b94fcb6e1
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorView.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Web;
+using System.Web.Caching;
+using System.Web.Compilation;
+using System.Web.Hosting;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.DynamicModules.Builder;
+using Telerik.Sitefinity.DynamicModules.Builder.Model;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.HealthMonitoring;
+using Telerik.Sitefinity.Modules.Forms;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Mvc.Store;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Web;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Compilation
+{
+ ///
+ /// This class represents a which allows measurement of its compilation performance.
+ ///
+ ///
+ internal class CompilationPerformanceRazorView : RazorView
+ {
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The controller context.
+ /// The view path.
+ /// The layout or master page.
+ /// A value that indicates whether view start files should be executed before the view.
+ /// The set of extensions that will be used when looking up view start files.
+ public CompilationPerformanceRazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable viewStartFileExtensions) :
+ this(controllerContext, viewPath, layoutPath, runViewStartPages, viewStartFileExtensions, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The controller context.
+ /// The view path.
+ /// The layout or master page.
+ /// A value that indicates whether view start files should be executed before the view.
+ /// The set of extensions that will be used when looking up view start files.
+ /// The view page activator.
+ public CompilationPerformanceRazorView(ControllerContext controllerContext, string viewPath, string layoutPath, bool runViewStartPages, IEnumerable viewStartFileExtensions, IViewPageActivator viewPageActivator)
+ : base(controllerContext, viewPath, layoutPath, runViewStartPages, viewStartFileExtensions, viewPageActivator)
+ {
+ this.controllerContext = controllerContext;
+ this.viewPageActivator = viewPageActivator;
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Renders the specified view context by using the specified the writer object.
+ ///
+ /// Information related to rendering a view, such as view data, temporary data, and form context.
+ /// The writer object.
+ public override void Render(ViewContext viewContext, TextWriter writer)
+ {
+ if (!this.ShouldMeasurePerformance(this.ViewPath) || !this.RenderWithPerformanceMeasurement(viewContext, writer))
+ base.Render(viewContext, writer);
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private bool RenderWithPerformanceMeasurement(ViewContext viewContext, TextWriter writer)
+ {
+ if (viewContext == null)
+ return false;
+
+ var region = this.GetMethodPerformanceRegion(viewContext);
+ if (region == null)
+ return false;
+
+ Type compiledType = null;
+ try
+ {
+ compiledType = System.Web.Compilation.BuildManager.GetCompiledType(this.ViewPath);
+ }
+ finally
+ {
+ region.Dispose();
+ }
+
+ if (compiledType == null)
+ return false;
+
+ var obj = this.viewPageActivator.Create(this.controllerContext, compiledType);
+ if (obj == null)
+ return false;
+
+ this.RenderView(viewContext, writer, obj);
+
+ return true;
+ }
+
+ private bool ShouldMeasurePerformance(string virtualPath)
+ {
+ if (virtualPath == null || HttpContext.Current == null)
+ return false;
+
+ return HostingEnvironment.VirtualPathProvider.FileExists(virtualPath) && BuildManager.GetCachedBuildDependencySet(HttpContext.Current, virtualPath) == null;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "The type of the thrown exception is not of interest in this scenario. We are only interested in marking the start of the performance measurement as failed.")]
+ private IDisposable GetMethodPerformanceRegion(ViewContext viewContext)
+ {
+ if (SystemManager.HttpContextItems == null || !SystemManager.HttpContextItems.Contains(SiteMapBase.CurrentNodeKey) || !(SystemManager.HttpContextItems[SiteMapBase.CurrentNodeKey] is PageSiteNode))
+ return null;
+
+ var pageNode = (PageSiteNode)SystemManager.HttpContextItems[SiteMapBase.CurrentNodeKey];
+
+ try
+ {
+ int slashIndex = this.ViewPath.LastIndexOf('/');
+ var fullViewName = this.ViewPath.Substring(slashIndex + 1);
+
+ int hashIndex = fullViewName.IndexOf('#');
+ var viewName = hashIndex < 0 ? fullViewName : fullViewName.Substring(0, hashIndex);
+
+ var actionName = (string)viewContext.RequestContext.RouteData.Values["action"];
+ var virtualPath = hashIndex < 0 ? this.ViewPath : this.ViewPath.Substring(0, slashIndex + hashIndex + 1);
+ var source = this.GetSource(this.ViewPath);
+ var packageManager = new PackageManager();
+ var resourcePackage = packageManager.GetCurrentPackage();
+ var controllerName = viewContext.Controller.GetType().FullName;
+ var widgetName = this.GetWidgetName(this.controllerContext);
+ if (this.ViewPath.StartsWith(CompilationPerformanceRazorView.FormsResolverPath, StringComparison.OrdinalIgnoreCase) && viewName.EndsWith(CompilationPerformanceRazorView.RazorViewSuffix, StringComparison.OrdinalIgnoreCase))
+ {
+ Guid formId;
+ var formIdString = viewName.Left(viewName.Length - CompilationPerformanceRazorView.RazorViewSuffix.Length);
+ if (Guid.TryParse(formIdString, out formId))
+ {
+ var form = FormsManager.GetManager().GetForms().FirstOrDefault(f => f.Id == formId);
+ if (form != null)
+ {
+ viewName = form.Title + " (" + formIdString + ")";
+ }
+ }
+ }
+
+ var isBackendRequest = bool.Parse(SystemManager.CurrentHttpContext.Items[SystemManager.IsBackendRequestKey].ToString());
+ var rootNodeId = isBackendRequest ? SiteInitializer.BackendRootNodeId : SiteInitializer.CurrentFrontendRootNodeId;
+ var siteId = SystemManager.CurrentContext.CurrentSite.Id;
+ var machineName = Environment.MachineName;
+
+ var key = string.Format("Compile view \"{0}\" of controller \"{1}\"", fullViewName, controllerName);
+ var data = new Dictionary()
+ {
+ { CompilationPerformanceRazorView.ViewNameKey, viewName },
+ { CompilationPerformanceRazorView.PageIdKey, pageNode.PageId },
+ { CompilationPerformanceRazorView.PageTitleKey, pageNode.Title },
+ { CompilationPerformanceRazorView.ResourcePackageKey, resourcePackage ?? string.Empty },
+ { CompilationPerformanceRazorView.ActionNameKey, actionName },
+ { CompilationPerformanceRazorView.ControllerNameKey, controllerName },
+ { CompilationPerformanceRazorView.WidgetNameKey, widgetName ?? string.Empty },
+ { CompilationPerformanceRazorView.VirtualPathKey, this.ViewPath },
+ { CompilationPerformanceRazorView.MachineNameKey, machineName },
+ { CompilationPerformanceRazorView.SiteIdKey, siteId },
+ { CompilationPerformanceRazorView.RootNodeIdKey, rootNodeId },
+ { CompilationPerformanceRazorView.SourceKey, virtualPath },
+ { CompilationPerformanceRazorView.ViewSourceKey, source }
+ };
+
+ return (IDisposable)Activator.CreateInstance(CompilationPerformanceRazorView.methodPerformanceRegionType, new object[] { key, CompilationPerformanceRazorView.ViewCompilationCategory, data });
+ }
+ catch
+ {
+ return null;
+ }
+ }
+
+ private string GetSource(string viewPath)
+ {
+ var cacheDep = HostingEnvironment.VirtualPathProvider.GetCacheDependency(viewPath, new object[0], DateTime.UtcNow);
+
+ // We are looking for both public and non-public method GetFileDependencies because of differences in the .NET Framework versions
+ var getFileDependencies = typeof(CacheDependency).GetMethod("GetFileDependencies", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic);
+ var getDependencyArray = typeof(AggregateCacheDependency).GetMethod("GetDependencyArray", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
+
+ while (cacheDep is AggregateCacheDependency)
+ {
+ var deps = getDependencyArray.Invoke(cacheDep, null) as CacheDependency[];
+ if (deps == null || deps.Length == 0)
+ break;
+
+ cacheDep = deps[deps.Length - 1];
+ }
+
+ if (cacheDep is Abstractions.VirtualPath.ControlPresentationCacheDependency)
+ {
+ var title = this.ExtractTemplateTitle((Abstractions.VirtualPath.ControlPresentationCacheDependency)cacheDep);
+ if (title != null)
+ {
+ return "Widget template: " + title + " (DB)";
+ }
+ }
+
+ var files = getFileDependencies.Invoke(cacheDep, null) as string[];
+ if (files != null && files.Length > 0)
+ return files[files.Length - 1].Replace(HostingEnvironment.ApplicationPhysicalPath, "~\\");
+ else
+ return viewPath;
+ }
+
+ private string ExtractTemplateTitle(Abstractions.VirtualPath.ControlPresentationCacheDependency cacheDep)
+ {
+ var cacheDepId = cacheDep.GetUniqueID().Right(cacheDep.GetUniqueID().Length - cacheDep.UniquePrefix.Length - 1);
+ Guid id;
+ if (!Guid.TryParse(cacheDepId, out id))
+ return null;
+
+ var manager = PageManager.GetManager();
+ string title;
+ using (new Data.ElevatedModeRegion(manager))
+ {
+ title = manager.GetPresentationItem(id).Name;
+ }
+
+ return title;
+ }
+
+ private string GetWidgetName(ControllerContext context)
+ {
+ var controllerType = context.Controller.GetType();
+
+ // Check in controller store
+ var controllerInfo = ControllerStore.Controllers()
+ .FirstOrDefault(c => c.ControllerType == controllerType);
+
+ if (controllerInfo != null && !controllerInfo.DefaultToolboxItemTitle.IsNullOrEmpty())
+ return controllerInfo.DefaultToolboxItemTitle;
+
+ // Check for dynamic type
+ var modelProperty = controllerType.GetProperty("Model");
+ if (modelProperty != null)
+ {
+ var model = modelProperty.GetValue(context.Controller) as ContentModelBase;
+ if (model != null)
+ {
+ var contentType = model.ContentType;
+
+ var dynamicType = ModuleBuilderManager.GetModules().GetTypeByFullName(contentType.FullName);
+ if (dynamicType != null)
+ return dynamicType.DisplayName;
+ }
+ }
+
+ if (this.ViewPath.StartsWith(CompilationPerformanceRazorView.LayoutVirtualPathBeginning, StringComparison.OrdinalIgnoreCase))
+ return CompilationPerformanceRazorView.Layout;
+
+ var controllerName = FrontendManager.ControllerFactory.GetControllerName(controllerType);
+
+ return controllerName;
+ }
+
+ #endregion
+
+ #region Fields and Constants
+
+ private ControllerContext controllerContext;
+ private IViewPageActivator viewPageActivator;
+
+ internal const string ViewCompilationCategory = "ViewCompilation";
+
+ internal const string ViewNameKey = "View";
+ internal const string PageIdKey = "PageId";
+ internal const string PageTitleKey = "PageTitle";
+ internal const string ResourcePackageKey = "ResourcePackage";
+ internal const string ActionNameKey = "Action";
+ internal const string ControllerNameKey = "Controller";
+ internal const string WidgetNameKey = "Widget";
+ internal const string VirtualPathKey = "VirtualPath";
+ internal const string MachineNameKey = "MachineName";
+ internal const string SiteIdKey = "SiteId";
+ internal const string RootNodeIdKey = "RootNodeId";
+ internal const string SourceKey = "Source";
+ internal const string ViewSourceKey = "ViewSource";
+
+ private const string LayoutVirtualPathBeginning = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/Mvc/Views/Layouts";
+ private const string Layout = "Layout";
+ private const string FormsResolverPath = "~/Mvc-Form-View/";
+ private const string RazorViewSuffix = ".cshtml";
+
+ private static Type methodPerformanceRegionType = Assembly.GetAssembly(typeof(SystemManager)).GetType("Telerik.Sitefinity.HealthMonitoring.MethodPerformanceRegion", false);
+
+ #endregion
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorViewEngine.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorViewEngine.cs
new file mode 100644
index 000000000..7a1352bc0
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Compilation/CompilationPerformanceRazorViewEngine.cs
@@ -0,0 +1,76 @@
+using System.Reflection;
+using System.Web.Mvc;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Compilation
+{
+ ///
+ /// This class represents a which creates objects allowing measurement of their compilation performance.
+ ///
+ ///
+ internal class CompilationPerformanceRazorViewEngine : RazorViewEngine
+ {
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CompilationPerformanceRazorViewEngine()
+ : this(null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The page activator.
+ public CompilationPerformanceRazorViewEngine(IViewPageActivator pageActivator)
+ : base(pageActivator)
+ {
+ }
+
+ #endregion
+
+ #region Protected Methods
+
+ ///
+ /// Creates a view by using the specified controller context and the paths of the view and master view.
+ ///
+ /// The controller context.
+ /// The path to the view.
+ /// The path to the master view.
+ ///
+ /// The view.
+ ///
+ protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
+ {
+ var view = new CompilationPerformanceRazorView(controllerContext, viewPath, masterPath, true, this.FileExtensions, this.ViewPageActivator);
+ displayModeProviderProperty.SetValue(view, this.DisplayModeProvider);
+
+ return view;
+ }
+
+ ///
+ /// Creates a partial view using the specified controller context and partial path.
+ ///
+ /// The controller context.
+ /// The path to the partial view.
+ ///
+ /// The partial view.
+ ///
+ protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
+ {
+ var view = new CompilationPerformanceRazorView(controllerContext, partialPath, null, false, this.FileExtensions, this.ViewPageActivator);
+ displayModeProviderProperty.SetValue(view, this.DisplayModeProvider);
+
+ return view;
+ }
+
+ #endregion
+
+ #region Fields
+
+ private static PropertyInfo displayModeProviderProperty = typeof(RazorView).GetProperty("DisplayModeProvider", BindingFlags.Instance | BindingFlags.NonPublic);
+
+ #endregion
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs
index bf29dff94..277634a2f 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs
@@ -3,6 +3,7 @@
using System.Web.Hosting;
using System.Web.Mvc;
using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Web;
namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes
{
@@ -22,19 +23,20 @@ public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
- if (SystemManager.CurrentHttpContext != null && SystemManager.CurrentHttpContext.Response != null)
+ var context = filterContext.HttpContext;
+ if (context != null && context.Response != null && context.Items[PageRouteHandler.AddCacheDependencies] != null)
{
var viewResult = filterContext.Result as ViewResultBase;
- if (viewResult != null)
+ if (viewResult != null && viewResult.View != null)
{
- var builtView = viewResult.View as BuildManagerCompiledView;
- if (builtView != null)
+ var viewPath = FrontendManager.VirtualPathBuilder.GetViewPath(viewResult.View);
+ if (!viewPath.IsNullOrEmpty())
{
- var cacheDependency = this.GetCacheDependency(builtView.ViewPath);
+ var cacheDependency = this.GetCacheDependency(viewPath);
if (cacheDependency != null)
{
- SystemManager.CurrentHttpContext.Response.AddCacheDependency(cacheDependency);
+ context.Response.AddCacheDependency(cacheDependency);
}
}
}
@@ -50,7 +52,8 @@ protected virtual CacheDependency GetCacheDependency(string virtualPath)
{
if (HostingEnvironment.VirtualPathProvider != null)
{
- return HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, null, DateTime.UtcNow);
+ // Sitefinity 8.1 and older throw exception if the second argument of this method is null so we pass an empty array.
+ return HostingEnvironment.VirtualPathProvider.GetCacheDependency(virtualPath, new string[0], DateTime.UtcNow);
}
return null;
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerContainerAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerContainerAttribute.cs
index a3450bf77..892e95b00 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerContainerAttribute.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerContainerAttribute.cs
@@ -1,26 +1,77 @@
-using System;
-
-namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes
-{
- ///
- /// This attribute is used to mark assemblies that may contain types.
- ///
- [AttributeUsage(AttributeTargets.All)]
- public class ControllerContainerAttribute : Attribute
- {
- public ControllerContainerAttribute()
- : this(null, null)
- {
- }
-
- public ControllerContainerAttribute(Type initializationType, string initializationMethodName)
- {
- this.InitializationType = initializationType;
- this.InitializationMethod = initializationMethodName;
- }
-
- public Type InitializationType { get; private set; }
-
- public string InitializationMethod { get; private set; }
- }
-}
+using System;
+using System.Web.Mvc;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes
+{
+ ///
+ /// This attribute is used to mark assemblies that may contain types.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly)]
+ public class ControllerContainerAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ControllerContainerAttribute()
+ : this(null, null, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Type containing the initialization method.
+ /// Name of the initialization method.
+ public ControllerContainerAttribute(Type initializationType, string initializationMethodName)
+ : this(initializationType, initializationMethodName, null, null)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Type containing the initialization method.
+ /// Name of the initialization method.
+ /// Type containing the uninitialization method.
+ /// Name of the uninitialization method.
+ public ControllerContainerAttribute(Type initializationType, string initializationMethodName, Type uninitializationType, string uninitializationMethodName)
+ {
+ this.InitializationType = initializationType;
+ this.InitializationMethod = initializationMethodName;
+ this.UninitializationType = uninitializationType;
+ this.UninitializationMethod = uninitializationMethodName;
+ }
+
+ ///
+ /// Gets the type containing the initialization method.
+ ///
+ ///
+ /// The type containing the initialization method.
+ ///
+ public Type InitializationType { get; private set; }
+
+ ///
+ /// Gets the initialization method name.
+ ///
+ ///
+ /// The name of the initialization method.
+ ///
+ public string InitializationMethod { get; private set; }
+
+ ///
+ /// Gets the type containing the uninitialization method.
+ ///
+ ///
+ /// The type containing the uninitialization method.
+ ///
+ public Type UninitializationType { get; private set; }
+
+ ///
+ /// Gets the uninitialization method name.
+ ///
+ ///
+ /// The name of the uninitialization method.
+ ///
+ public string UninitializationMethod { get; private set; }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerMetadataAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerMetadataAttribute.cs
new file mode 100644
index 000000000..59a1d0526
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ControllerMetadataAttribute.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes
+{
+ ///
+ /// Controller attribute for determining whether the controller is templatable control
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes"), AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
+ public class ControllerMetadataAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ControllerMetadataAttribute()
+ {
+ this.IsTemplatableControl = true;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the controller is templatable control
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Templatable")]
+ public bool IsTemplatableControl { get; set; }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ResourcePackageAttribute.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ResourcePackageAttribute.cs
new file mode 100644
index 000000000..fb283d9c6
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/ResourcePackageAttribute.cs
@@ -0,0 +1,36 @@
+using System;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes
+{
+ ///
+ /// This attribute is used to mark assemblies that contain a precompiled resource package.
+ ///
+ [AttributeUsage(AttributeTargets.Assembly)]
+ public sealed class ResourcePackageAttribute : Attribute
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ResourcePackageAttribute()
+ : this(string.Empty)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The name of the package.
+ public ResourcePackageAttribute(string name)
+ {
+ this.Name = name;
+ }
+
+ ///
+ /// Gets the name of the package.
+ ///
+ ///
+ /// The name.
+ ///
+ public string Name { get; private set; }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ContentBaseController.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ContentBaseController.cs
new file mode 100644
index 000000000..7220e04a4
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ContentBaseController.cs
@@ -0,0 +1,252 @@
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Web.Mvc;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.DynamicModules;
+using Telerik.Sitefinity.Model;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Mvc;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Services.Configuration;
+using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.ContentLocations;
+using Telerik.Sitefinity.Lifecycle;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
+{
+ ///
+ /// This class represents the base controller of content widgets.
+ ///
+ public abstract class ContentBaseController : Controller
+ {
+ #region Properties
+
+ ///
+ /// Gets the metadata container.Title for search engines
+ ///
+ ///
+ ///
+ /// The metadata container.
+ ///
+ [TypeConverter(typeof(ExpandableObjectConverter))]
+ public virtual MetadataModel MetadataFields
+ {
+ get
+ {
+ if (this.metadata == null)
+ {
+ this.metadata = new MetadataModel();
+ this.metadata.OpenGraphType = PageHelper.OpenGraphTypes.Website;
+ this.metadata.PageTitleMode = Telerik.Sitefinity.Mvc.ControllerActionInvoker.PageTitleModes.Replace;
+ }
+
+ return this.metadata;
+ }
+ set
+ {
+ metadata = value;
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Populates the viewbag with values for metadata properties.
+ ///
+ /// The item.
+ protected void InitializeMetadataDetailsViewBag(IDataItem item)
+ {
+ if (this.IsDesignMode && !this.IsPreviewMode)
+ {
+ return;
+ }
+
+ string parentMainValue = string.Empty;
+ var seoAndOpenGraphConfig = this.GetSeoAndOpenGraphConfig();
+ MetadataModel metadataProperties = new MetadataModel();
+
+ metadataProperties.SEOEnabled = seoAndOpenGraphConfig.EnabledSEO;
+ metadataProperties.OpenGraphEnabled = seoAndOpenGraphConfig.EnabledOpenGraph;
+ metadataProperties.SEOEnabledPerWidget = this.MetadataFields.SEOEnabled;
+ metadataProperties.OpenGraphEnabledPerWidget = this.MetadataFields.OpenGraphEnabled;
+ bool isSeoEnabled = metadataProperties.SEOEnabled && metadataProperties.SEOEnabledPerWidget;
+ bool isOpenGraphEnabled = metadataProperties.OpenGraphEnabled && metadataProperties.OpenGraphEnabledPerWidget;
+
+ if (isSeoEnabled)
+ {
+ metadataProperties.MetaTitle = this.GetTitleProperty(item, new[] { this.MetadataFields.MetaTitle, PageHelper.MetaDataProperties.MetaTitle });
+ metadataProperties.MetaDescription = this.GetDescriptionProperty(item, new[] { this.MetadataFields.MetaDescription, PageHelper.MetaDataProperties.MetaDescription });
+ }
+
+ if (isOpenGraphEnabled)
+ {
+ metadataProperties.OpenGraphTitle = this.GetTitleProperty(item, new[] { this.MetadataFields.OpenGraphTitle, PageHelper.MetaDataProperties.OpenGraphTitle, this.MetadataFields.MetaTitle, PageHelper.MetaDataProperties.MetaTitle });
+ metadataProperties.OpenGraphDescription = this.GetDescriptionProperty(item, new[] { this.MetadataFields.OpenGraphDescription, PageHelper.MetaDataProperties.OpenGraphDescription, this.MetadataFields.MetaDescription, PageHelper.MetaDataProperties.MetaDescription });
+ metadataProperties.Url = this.GetDefaultCanonicalUrl(item);
+ metadataProperties.OpenGraphType = this.MetadataFields.OpenGraphType;
+ metadataProperties.OpenGraphImage = PageHelper.GetFieldValue(item, new[] { this.MetadataFields.OpenGraphImage, PageHelper.MetaDataProperties.OpenGraphImage });
+ metadataProperties.OpenGraphVideo = PageHelper.GetFieldValue(item, new[] { this.MetadataFields.OpenGraphVideo, PageHelper.MetaDataProperties.OpenGraphVideo });
+ metadataProperties.SiteName = SystemManager.CurrentContext.CurrentSite.Name;
+ }
+
+ if (isSeoEnabled || isOpenGraphEnabled)
+ {
+ metadataProperties.PageTitleMode = this.MetadataFields.PageTitleMode;
+
+ if (this.MetadataFields.PageTitleMode == Telerik.Sitefinity.Mvc.ControllerActionInvoker.PageTitleModes.Hierarchy && typeof(IDynamicContentWidget).IsAssignableFrom(this.GetType()))
+ {
+ parentMainValue = this.GetParentMainFiledValue(item);
+ if (!string.IsNullOrEmpty(parentMainValue))
+ {
+ this.ViewBag.ParentMainValue = parentMainValue;
+ }
+ }
+ }
+
+ this.ViewBag.Metadata = metadataProperties;
+ }
+
+ ///
+ /// Determines whether the page is in design mode.
+ ///
+ protected virtual bool IsDesignMode
+ {
+ get
+ {
+ return SystemManager.IsDesignMode;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the current request is for page in preview mode.
+ ///
+ protected virtual bool IsPreviewMode
+ {
+ get
+ {
+ return SystemManager.IsPreviewMode;
+ }
+ }
+
+ ///
+ /// Gets the item default location.
+ ///
+ /// The item.
+ ///
+ /// The item default location.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1055:UriReturnValuesShouldNotBeStrings", Justification = "The url is needed here as a string.")]
+ internal virtual string GetDefaultCanonicalUrl(IDataItem item)
+ {
+ IManager manager = null;
+ if (!ManagerBase.TryGetMappedManager(item.GetType(), item.GetProviderName(), out manager))
+ return null;
+
+ var locationsService = SystemManager.GetContentLocationService();
+ if (item is ILifecycleDataItemGeneric lifecycleDataItem &&
+ lifecycleDataItem.Status != GenericContent.Model.ContentLifecycleStatus.Master &&
+ manager is ILifecycleManager lifecycleManager)
+ {
+ item = lifecycleManager.Lifecycle.GetMaster(lifecycleDataItem);
+ }
+
+ var location = locationsService.GetItemDefaultLocation(item);
+ if (location != null)
+ {
+ return location.ItemAbsoluteUrl;
+ }
+
+ var page = this.HttpContext.CurrentHandler.GetPageHandler();
+ var pageNode = SiteMapBase.GetActualCurrentNode();
+ var canonicalUrl = page.GetCanonicalUrlForPage(pageNode);
+
+ return canonicalUrl;
+ }
+
+ ///
+ /// Adds the canonical tag in the page headers HTML tag, like .
+ ///
+ /// The page.
+ /// The item.
+ protected void AddCanonicalUrlTagIfEnabled(System.Web.UI.Page page, IDataItem item)
+ {
+ var contentView = this as IContentLocatableView;
+ if (contentView != null)
+ {
+ var defaultCanonicalUrl = this.GetDefaultCanonicalUrl(item);
+ contentView.AddCanonicalUrlTagIfEnabled(page, defaultCanonicalUrl);
+ }
+ }
+
+ internal virtual SeoAndOpenGraphElement GetSeoAndOpenGraphConfig()
+ {
+ return Config.Get().SeoAndOpenGraphConfig;
+ }
+
+ private string GetParentMainFiledValue(IDataItem item)
+ {
+ string parentMainValue = string.Empty;
+ var property = TypeDescriptor.GetProperties(item)["ParentItem"];
+ if (property != null)
+ {
+ var parent = property.GetValue(item);
+ parentMainValue = this.GetParentMainValueFromProperty(parent);
+
+ var parentProperty = TypeDescriptor.GetProperties(parent)["ParentItem"];
+
+ if (parentProperty != null)
+ {
+ var grandParent = property.GetValue(parent);
+ parentMainValue = parentMainValue + " - " + this.GetParentMainValueFromProperty(grandParent);
+ }
+ }
+
+ return parentMainValue;
+ }
+
+ private string GetParentMainValueFromProperty(object itemValue)
+ {
+ var parentMainField = this.GetDynamicContentType().ParentModuleType.MainShortTextFieldName;
+ string[] field = { parentMainField };
+
+ return PageHelper.GetFieldValue(itemValue, field);
+ }
+
+ private string GetTitleProperty(object detailItem, string[] propertyNames)
+ {
+ var fields = new List(propertyNames);
+
+ if (typeof(IDynamicContentWidget).IsAssignableFrom(this.GetType()))
+ {
+ var dynamicType = this.GetDynamicContentType();
+ var title = dynamicType == null ? PageHelper.MetaDataProperties.Title : dynamicType.MainShortTextFieldName;
+ fields.Add(title);
+ }
+ else
+ {
+ fields.Add(PageHelper.MetaDataProperties.Title);
+ }
+
+ return PageHelper.GetFieldValue(detailItem, fields.ToArray());
+ }
+
+ private string GetDescriptionProperty(object detailItem, string[] propertyNames)
+ {
+ var fields = new List(propertyNames);
+ fields.Add(PageHelper.MetaDataProperties.Description);
+
+ return PageHelper.GetFieldValue(detailItem, fields.ToArray());
+ }
+
+ #endregion
+
+ #region Fields and constants
+
+ private MetadataModel metadata;
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs
index 2a2139d41..f7b33cb2d 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs
@@ -1,295 +1,660 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Reflection;
-using System.Web.Hosting;
-using System.Web.Mvc;
-using System.Web.Routing;
-using Telerik.Microsoft.Practices.Unity;
-using Telerik.Sitefinity.Abstractions;
-using Telerik.Sitefinity.Abstractions.VirtualPath;
-using Telerik.Sitefinity.Configuration;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
-using Telerik.Sitefinity.Frontend.Resources;
-using Telerik.Sitefinity.Frontend.Resources.Resolvers;
-using Telerik.Sitefinity.Localization;
-using Telerik.Sitefinity.Mvc;
-using Telerik.Sitefinity.Mvc.Store;
-using Telerik.Sitefinity.Services;
-
-namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
-{
- ///
- /// This class contains logic for locating and initializing controllers.
- ///
- public class ControllerContainerInitializer
- {
- #region Public members
-
- ///
- /// Initializes the controllers that are available to the web application.
- ///
- public virtual void Initialize()
- {
- GlobalFilters.Filters.Add(new CacheDependentAttribute());
-
- var assemblies = this.RetrieveAssemblies();
-
- this.RegisterVirtualPaths(assemblies);
-
- var controllerTypes = this.GetControllers(assemblies);
- this.InitializeControllers(controllerTypes);
-
- this.InitializeCustomRouting();
- }
-
- #endregion
-
- #region Protected members
-
- ///
- /// Gets the assemblies that are marked as controller containers with the attribute.
- ///
- protected virtual IEnumerable RetrieveAssemblies()
- {
- var assemblyFileNames = this.RetrieveAssembliesFileNames().ToArray();
- var result = new List();
-
- foreach (var assemblyFileName in assemblyFileNames)
- {
- if (this.IsControllerContainer(assemblyFileName))
- {
- var assembly = this.LoadAssembly(assemblyFileName);
- this.InitializeControllerContainer(assembly);
- result.Add(assembly);
- }
- }
-
- return result;
- }
-
- ///
- /// Executes the initialization method specified in the attribute.
- ///
- ///
- protected virtual void InitializeControllerContainer(Assembly container)
- {
- if (container == null)
- throw new ArgumentNullException("container");
-
- var containerAttribute = container.GetCustomAttributes(false).Single(attr => attr.GetType().AssemblyQualifiedName == typeof(ControllerContainerAttribute).AssemblyQualifiedName) as ControllerContainerAttribute;
-
- if (containerAttribute.InitializationType == null || containerAttribute.InitializationMethod.IsNullOrWhitespace())
- return;
-
- var initializationMethod = containerAttribute.InitializationType.GetMethod(containerAttribute.InitializationMethod);
- initializationMethod.Invoke(null, null);
- }
-
- ///
- /// Loads the assembly file into the current application domain.
- ///
- /// Name of the assembly file.
- /// The loaded assembly
- protected virtual Assembly LoadAssembly(string assemblyFileName)
- {
- return Assembly.LoadFrom(assemblyFileName);
- }
-
- ///
- /// Registers virtual paths for the given .
- ///
- /// The assemblies.
- protected virtual void RegisterVirtualPaths(IEnumerable assemblies)
- {
- foreach (var assembly in assemblies)
- {
- this.RegisterAssemblyPaths(assembly);
- }
- }
-
- ///
- /// Gets the controllers from the given .
- ///
- /// The assemblies.
- ///
- protected virtual IEnumerable GetControllers(IEnumerable assemblies)
- {
- return assemblies
- .SelectMany(asm => asm.GetExportedTypes().Where(FrontendManager.ControllerFactory.IsController))
- .ToArray();
- }
-
- ///
- /// Initializes the specified by ensuring they have their proper registrations in the toolbox and that the controller factory will be able to resolve them.
- ///
- /// The controllers.
- protected virtual void InitializeControllers(IEnumerable controllers)
- {
- this.RegisterControllerFactory();
- this.RemoveSitefinityViewEngine();
- this.ReplaceControllerFactory();
-
- foreach (var controller in controllers)
- {
- this.RegisterController(controller);
- }
- }
-
- ///
- /// Registers
- ///
- protected virtual void RegisterControllerFactory()
- {
- ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
- }
-
- ///
- /// Registers the controller so its views and resources can be resolved.
- ///
- /// The controller.
- protected virtual void RegisterController(Type controller)
- {
- var controllerStore = new ControllerStore();
- var configManager = ConfigManager.GetManager();
-
- using (var modeRegion = new ElevatedConfigModeRegion())
- {
- this.RegisterStringResources(controller);
- controllerStore.AddController(controller, configManager);
- }
- }
-
- ///
- /// Gets the assemblies file names that will be inspected for controllers.
- ///
- protected virtual IEnumerable RetrieveAssembliesFileNames()
- {
- var controllerAssemblyPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "bin");
- return Directory.EnumerateFiles(controllerAssemblyPath, "*.dll", SearchOption.TopDirectoryOnly);
- }
-
- ///
- /// Determines whether the given is an assembly file that is marked as .
- ///
- /// Filename of the assembly.
- /// True if the given file name is of an assembly file that is marked as , false otherwise.
- protected virtual bool IsControllerContainer(string assemblyFileName)
- {
- if (assemblyFileName == null)
- return false;
-
- bool result;
- AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += this.CurrentDomain_ReflectionOnlyAssemblyResolve;
- try
- {
- try
- {
- var reflOnlyAssembly = Assembly.ReflectionOnlyLoadFrom(assemblyFileName);
-
- result = reflOnlyAssembly != null &&
- reflOnlyAssembly.GetCustomAttributesData()
- .Any(d => d.Constructor.DeclaringType.AssemblyQualifiedName == typeof(ControllerContainerAttribute).AssemblyQualifiedName);
- }
- catch (IOException)
- {
- // We might not be able to load some .DLL files as .NET assemblies. Those files cannot contain controllers.
- result = false;
- }
- catch (BadImageFormatException)
- {
- result = false;
- }
- }
- finally
- {
- AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= this.CurrentDomain_ReflectionOnlyAssemblyResolve;
- }
-
- return result;
- }
-
- ///
- /// Replaces the current controller factory used by the MVC framework with the factory that is registered by Sitefinity.
- ///
- protected virtual void ReplaceControllerFactory()
- {
- var factory = ObjectFactory.Resolve();
- ControllerBuilder.Current.SetControllerFactory(factory);
- }
-
- ///
- /// Registers types of the custom routing.
- ///
- protected virtual void InitializeCustomRouting()
- {
- ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
- ObjectFactory.Container.RegisterType("category");
- ObjectFactory.Container.RegisterType("tag");
- }
-
- #endregion
-
- #region Private members
-
- private Assembly CurrentDomain_ReflectionOnlyAssemblyResolve(object sender, ResolveEventArgs args)
- {
- var assWithPolicy = AppDomain.CurrentDomain.ApplyPolicy(args.Name);
-
- return Assembly.ReflectionOnlyLoad(assWithPolicy);
- }
-
- private void RegisterAssemblyPaths(Assembly assembly)
- {
- var assemblyName = assembly.GetName();
- var virtualPath = FrontendManager.VirtualPathBuilder.GetVirtualPath(assembly);
-
- VirtualPathManager.AddVirtualFileResolver(
- "~/" + virtualPath + "*",
- assemblyName.Name,
- assemblyName.CodeBase);
-
- SystemManager.RegisterRoute(
- assemblyName.Name,
- new Route(
- virtualPath + "{*Params}",
- new RouteHandler()),
- assemblyName.Name,
- requireBasicAuthentication: false);
- }
-
- private void RemoveSitefinityViewEngine()
- {
- var sitefinityViewEngine = ViewEngines.Engines.FirstOrDefault(v => v is SitefinityViewEngine);
-
- if (sitefinityViewEngine != null)
- {
- ViewEngines.Engines.Remove(sitefinityViewEngine);
- }
- }
-
- ///
- /// Registers the controller string resources.
- ///
- /// Type of the controller.
- private void RegisterStringResources(Type controller)
- {
- var localizationAttributes = controller.GetCustomAttributes(typeof(LocalizationAttribute), true);
- foreach (var attribute in localizationAttributes)
- {
- var localAttr = (LocalizationAttribute)attribute;
- var resourceClass = localAttr.ResourceClass;
- var resourceClassId = Res.GetResourceClassId(resourceClass);
-
- if (!ObjectFactory.Container.IsRegistered(resourceClass, resourceClassId))
- {
- Res.RegisterResource(resourceClass);
- }
- }
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Threading.Tasks;
+using System.Web.Hosting;
+using System.Web.Mvc;
+using System.Web.Routing;
+using System.Web.Script.Serialization;
+using Telerik.Microsoft.Practices.Unity;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Abstractions.VirtualPath;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.Frontend.Mvc.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Frontend.Resources.Resolvers;
+using Telerik.Sitefinity.Localization;
+using Telerik.Sitefinity.Modules.ControlTemplates;
+using Telerik.Sitefinity.Mvc;
+using Telerik.Sitefinity.Mvc.Proxy.TypeDescription;
+using Telerik.Sitefinity.Mvc.Store;
+using Telerik.Sitefinity.Pages;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Taxonomies;
+using Telerik.Sitefinity.Taxonomies.Model;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
+{
+ ///
+ /// This class contains logic for locating and initializing controllers.
+ ///
+ internal class ControllerContainerInitializer : IInitializer
+ {
+ #region Public members
+
+ ///
+ /// Gets the controller container assemblies.
+ ///
+ ///
+ /// The controller container assemblies.
+ ///
+ public IEnumerable ControllerContainerAssemblies
+ {
+ get
+ {
+ if (ControllerContainerInitializer.controllerContainerAssemblies == null)
+ {
+ lock (ControllerContainerInitializer.ControllerContainerAssembliesLock)
+ {
+ if (ControllerContainerInitializer.controllerContainerAssemblies == null)
+ {
+ ControllerContainerInitializer.controllerContainerAssemblies = this.RetrieveAssemblies();
+ }
+ }
+ }
+
+ return ControllerContainerInitializer.controllerContainerAssemblies;
+ }
+
+ private set
+ {
+ lock (ControllerContainerInitializer.ControllerContainerAssembliesLock)
+ {
+ ControllerContainerInitializer.controllerContainerAssemblies = value;
+ }
+ }
+ }
+
+ ///
+ /// Registers the string resources.
+ ///
+ public void RegisterStringResources()
+ {
+ this.ControllerContainerAssemblies
+ .SelectMany(asm => asm.GetExportedTypes().Where(FrontendManager.ControllerFactory.IsController))
+ .ToList()
+ .ForEach(ControllerContainerInitializer.RegisterStringResources);
+ }
+
+ ///
+ /// Initializes the controllers that are available to the web application.
+ ///
+ public virtual void Initialize()
+ {
+ GlobalFilters.Filters.Add(new CacheDependentAttribute());
+
+ this.RegisterVirtualPaths(this.ControllerContainerAssemblies);
+
+ var controllerTypes = this.GetControllers();
+ this.InitializeControllers(controllerTypes);
+
+ this.InitializeCustomRouting();
+
+ this.RegisterPrecompiledViewEngines(this.ControllerContainerAssemblies);
+ }
+
+ ///
+ /// Uninitializes the controllers that are available to the web application.
+ ///
+ public virtual void Uninitialize()
+ {
+ this.RegisterStringResources();
+
+ this.UninitializeGlobalFilters();
+
+ foreach (var assembly in this.ControllerContainerAssemblies)
+ {
+ this.UninitializeControllerContainer(assembly);
+ }
+
+ this.ControllerContainerAssemblies = null;
+
+ // Clears all controllers
+ // ControllerStore.ClearControllers();
+
+ var sitefinityViewEngines = ViewEngines.Engines.Where(v => v != null && v.GetType() == typeof(CompositePrecompiledMvcEngineWrapper)).ToList();
+ foreach (var sitefinityViewEngine in sitefinityViewEngines)
+ {
+ ViewEngines.Engines.Remove(sitefinityViewEngine);
+ }
+
+ var sitefinityViewEngineExists = ViewEngines.Engines.Any(v => v.GetType() == typeof(SitefinityViewEngine));
+ if (!sitefinityViewEngineExists)
+ {
+ // add Sitefinity view engine
+ ViewEngines.Engines.Add(new SitefinityViewEngine());
+ }
+ }
+
+ ///
+ /// Gets the assemblies that are marked as controller containers with the attribute.
+ ///
+ public virtual IEnumerable RetrieveAssemblies()
+ {
+ using (new HealthMonitoring.MethodPerformanceRegion("ControllerContainerInitializer.RetrieveAssemblies"))
+ {
+ IEnumerable assemblyFileNames = this.RetrieveControllerAssembliesFileNames().Distinct().ToArray();
+ IList> retrieveAssemblyTasks = new List>();
+
+ foreach (string assemblyFileName in assemblyFileNames)
+ {
+ retrieveAssemblyTasks.Add(this.LoadControllerAssemblyAsync(assemblyFileName));
+ }
+
+ Task.WaitAll(retrieveAssemblyTasks.ToArray());
+
+ IEnumerable result = retrieveAssemblyTasks
+ .Select(v => v.Result)
+ .Where(v => v != null);
+
+ return result;
+ }
+ }
+
+ #endregion
+
+
+ #region Internal
+ internal IEnumerable GetControllers()
+ {
+ using (new HealthMonitoring.MethodPerformanceRegion("ControllerContainerInitializer.GetControllers"))
+ return this.GetControllers(this.ControllerContainerAssemblies);
+ }
+ #endregion
+
+ #region Protected members
+
+ ///
+ /// Executes the initialization method specified in the attribute.
+ ///
+ ///
+ protected virtual void InitializeControllerContainer(Assembly container)
+ {
+ if (container == null)
+ throw new ArgumentNullException("container");
+
+ var containerAttribute = container.GetCustomAttributes(false).FirstOrDefault(attr => attr.GetType().AssemblyQualifiedName == typeof(ControllerContainerAttribute).AssemblyQualifiedName) as ControllerContainerAttribute;
+
+ if (containerAttribute == null || containerAttribute.InitializationType == null || containerAttribute.InitializationMethod.IsNullOrWhitespace())
+ return;
+
+ var initializationMethod = containerAttribute.InitializationType.GetMethod(containerAttribute.InitializationMethod);
+ initializationMethod.Invoke(null, null);
+ }
+
+ ///
+ /// Executes the uninitialization method specified in the attribute.
+ ///
+ ///
+ protected virtual void UninitializeControllerContainer(Assembly container)
+ {
+ if (container == null)
+ throw new ArgumentNullException("container");
+
+ var containerAttribute = container.GetCustomAttributes(false).SingleOrDefault(attr => attr.GetType().AssemblyQualifiedName == typeof(ControllerContainerAttribute).AssemblyQualifiedName) as ControllerContainerAttribute;
+
+ if (containerAttribute == null || containerAttribute.UninitializationType == null || containerAttribute.UninitializationMethod.IsNullOrWhitespace())
+ return;
+
+ var uninitializationMethod = containerAttribute.UninitializationType.GetMethod(containerAttribute.UninitializationMethod);
+ uninitializationMethod.Invoke(null, null);
+ }
+
+ ///
+ /// Loads the assembly file into the current application domain.
+ ///
+ /// Name of the assembly file.
+ /// The loaded assembly
+ protected virtual Assembly LoadAssembly(string assemblyFileName)
+ {
+ return Assembly.LoadFrom(assemblyFileName);
+ }
+
+ ///
+ /// Registers virtual paths for the given .
+ ///
+ /// The assemblies.
+ protected virtual void RegisterVirtualPaths(IEnumerable assemblies)
+ {
+ foreach (var assembly in assemblies)
+ {
+ this.RegisterAssemblyPaths(assembly);
+ }
+ }
+
+ ///
+ /// Gets the controllers from the given .
+ ///
+ /// The assemblies.
+ ///
+ protected virtual IEnumerable GetControllers(IEnumerable assemblies)
+ {
+ return assemblies
+ .SelectMany(asm => asm.GetExportedTypes().Where(FrontendManager.ControllerFactory.IsController))
+ .ToArray();
+ }
+
+ ///
+ /// Initializes the specified by ensuring they have their proper registrations in the toolbox and that the controller factory will be able to resolve them.
+ ///
+ /// The controllers.
+ protected virtual void InitializeControllers(IEnumerable controllers)
+ {
+ this.RegisterTemplateableControls(controllers);
+ this.RegisterControllerFactory();
+ this.RemoveSitefinityViewEngine();
+ this.ReplaceControllerFactory();
+ this.RegisterControllers(controllers);
+ }
+
+ protected virtual void UninitializeGlobalFilters()
+ {
+ var cacheDependentAttribute = GlobalFilters.Filters.FirstOrDefault(f => f.Instance.GetType().FullName == typeof(CacheDependentAttribute).FullName);
+ if (cacheDependentAttribute != null)
+ GlobalFilters.Filters.Remove(cacheDependentAttribute.Instance);
+ }
+
+ ///
+ /// Registers
+ ///
+ protected virtual void RegisterControllerFactory()
+ {
+ ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
+ }
+
+ ///
+ /// Registers the controller so its views and resources can be resolved.
+ ///
+ /// The controller.
+ protected virtual void RegisterController(Type controller)
+ {
+ var controllerStore = new ControllerStore();
+ var configManager = ConfigManager.GetManager();
+
+ using (var modeRegion = new ElevatedConfigModeRegion())
+ {
+ controllerStore.AddController(controller, configManager);
+ }
+ }
+
+ ///
+ /// Gets the assemblies file names from the controller container assembly cache.
+ ///
+ protected virtual IEnumerable RetrieveControllerAssembliesFileNames()
+ {
+ var isFeatherenabled = SystemManager.GetApplicationModule(FrontendModule.ModuleName) != null;
+ bool useCachedControllerContainerAssemblies = false;
+ if (isFeatherenabled)
+ {
+ useCachedControllerContainerAssemblies = Config.Get().UseCachedControllerContainerAssemblies;
+ }
+
+ var controllerAssemblyPath = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "bin");
+
+ if (useCachedControllerContainerAssemblies)
+ {
+ var pathToCacheFile = Path.Combine(HostingEnvironment.ApplicationPhysicalPath, "bin", "ControllerContainerAsembliesLocation.json");
+ if (File.Exists(pathToCacheFile))
+ {
+ try
+ {
+ var file = File.ReadAllText(pathToCacheFile);
+ var filesFromcache = new JavaScriptSerializer().Deserialize(file);
+
+ // append path to the bin in order to be able to load the assemblies
+ return filesFromcache.Select(x => Path.Combine(controllerAssemblyPath, x));
+ }
+ catch (Exception ex)
+ {
+ Log.Write(string.Format("Attempt to read from ControllerContainerAsembliesLocation.json was unsuccessful: {0}", ex.Message), ConfigurationPolicy.ErrorLog);
+ }
+ }
+ }
+
+ var allAssemblyNames = Directory.EnumerateFiles(controllerAssemblyPath, "*.dll", SearchOption.TopDirectoryOnly);
+ IList> retrieveAssemblyTasks = new List>();
+
+ foreach (string assemblyFileName in allAssemblyNames)
+ {
+ retrieveAssemblyTasks.Add(this.RetrieveControllerAssemblyAsync(assemblyFileName));
+ }
+
+ Task.WaitAll(retrieveAssemblyTasks.ToArray());
+ var controllerAssembliesNames = retrieveAssemblyTasks
+ .Select(x => x.Result)
+ .Where(x => !string.IsNullOrEmpty(x));
+
+ return controllerAssembliesNames;
+ }
+
+ ///
+ /// Determines whether the given is an assembly file that is marked as .
+ ///
+ /// Filename of the assembly.
+ /// True if the given file name is of an assembly file that is marked as , false otherwise.
+ protected virtual bool IsControllerContainer(string assemblyFileName)
+ {
+ return this.IsMarkedAssembly(assemblyFileName);
+ }
+
+ ///
+ /// Replaces the current controller factory used by the MVC framework with the factory that is registered by Sitefinity.
+ ///
+ protected virtual void ReplaceControllerFactory()
+ {
+ var factory = ObjectFactory.Resolve();
+ ControllerBuilder.Current.SetControllerFactory(factory);
+ }
+
+ ///
+ /// Registers types of the custom routing.
+ ///
+ protected virtual void InitializeCustomRouting()
+ {
+ ObjectFactory.Container.RegisterType();
+ ObjectFactory.Container.RegisterType("int");
+
+ Task.Run(() =>
+ {
+ this.RegisterTaxonomyRoutes();
+ });
+
+ CacheDependency.Subscribe(typeof(Taxonomy), this.OnTaxonomiesUpdated);
+
+ string mvcControllerProxySettingsPropertyDescriptorName = string.Format("{0}.{1}", typeof(MvcWidgetProxy).FullName, "Settings");
+ ObjectFactory.Container.RegisterType(mvcControllerProxySettingsPropertyDescriptorName);
+
+ FrontendManager.AttributeRouting.MapMvcAttributeRoutes();
+ }
+
+ ///
+ /// Register controllers into the store
+ ///
+ /// The controllers to be registered.
+ protected virtual void RegisterControllers(IEnumerable controllers)
+ {
+ controllers.ToList().ForEach(c => ControllerContainerInitializer.RegisterStringResources(c));
+ }
+
+ #endregion
+
+ #region Private members
+
+ private void RegisterTaxonomyRoutes()
+ {
+ var taxonomies = TaxonomyManager.GetTaxonomiesCache();
+
+ if (taxonomies.Count() > 0)
+ {
+ foreach (var taxonomy in taxonomies)
+ {
+ var taxonomyName = this.GetTaxonomyName(taxonomy.Id, taxonomy.Name);
+ if (!ObjectFactory.IsTypeRegistered(taxonomyName))
+ ObjectFactory.Container.RegisterType(taxonomyName, new InjectionConstructor(taxonomyName));
+ }
+ }
+ }
+
+ private string GetTaxonomyName(Guid id, string name)
+ {
+ var taxonomyName = string.Empty;
+
+ if (id == TaxonomyManager.TagsTaxonomyId)
+ {
+ taxonomyName = "tag";
+ }
+ else if (id == TaxonomyManager.CategoriesTaxonomyId)
+ {
+ taxonomyName = "category";
+ }
+ else
+ {
+ taxonomyName = name.ToLowerInvariant();
+ }
+
+ return taxonomyName;
+ }
+
+ private Task