-
-
- error
-
-
- success
-
-
- Number of Violations:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/StyleCop/StyleCop.targets b/StyleCop/StyleCop.targets
deleted file mode 100644
index f839b9c56..000000000
--- a/StyleCop/StyleCop.targets
+++ /dev/null
@@ -1,121 +0,0 @@
-
-
-
-
-
- $(BuildDependsOn);StyleCop
- StyleCopForceFullAnalysis;$(RebuildDependsOn)
-
-
-
-
- $(SourceAnalysisForceFullAnalysis)
-
-
- false
-
-
-
-
- $(SourceAnalysisCacheResults)
-
-
- true
-
-
-
-
- false
-
-
- false
-
-
-
-
- $(SourceAnalysisEnabled)
-
-
- true
-
-
-
-
- $(SourceAnalysisOverrideSettingsFile)
-
-
-
-
-
-
-
- $(SourceAnalysisOutputFile)
-
-
- $(IntermediateOutputPath)StyleCopViolations.xml
-
-
-
-
-
- 0
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileData.cs b/Telerik.Sitefinity.Frontend.Data/FileData.cs
similarity index 98%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileData.cs
rename to Telerik.Sitefinity.Frontend.Data/FileData.cs
index b1e6fc784..e768d93e6 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileData.cs
+++ b/Telerik.Sitefinity.Frontend.Data/FileData.cs
@@ -8,7 +8,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
///
[Persistent]
[ManagerType(typeof(FileMonitorDataManager))]
- internal class FileData
+ public class FileData
{
#region Contructors
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataManager.cs b/Telerik.Sitefinity.Frontend.Data/FileMonitorDataManager.cs
similarity index 98%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataManager.cs
rename to Telerik.Sitefinity.Frontend.Data/FileMonitorDataManager.cs
index 44fd8c84e..620a406ce 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataManager.cs
+++ b/Telerik.Sitefinity.Frontend.Data/FileMonitorDataManager.cs
@@ -10,7 +10,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
///
/// Represents the data manager for the file monitoring functionality.
///
- internal class FileMonitorDataManager : ManagerBase
+ public class FileMonitorDataManager : ManagerBase
{
#region Constructors
///
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataProvider.cs b/Telerik.Sitefinity.Frontend.Data/FileMonitorDataProvider.cs
similarity index 98%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataProvider.cs
rename to Telerik.Sitefinity.Frontend.Data/FileMonitorDataProvider.cs
index 5fcfe83c9..2256d2795 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorDataProvider.cs
+++ b/Telerik.Sitefinity.Frontend.Data/FileMonitorDataProvider.cs
@@ -8,7 +8,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
/// Represents the data provider for the file monitoring functionality.
///
[Telerik.Microsoft.Practices.Unity.InterceptionExtension.ApplyNoPolicies]
- internal abstract class FileMonitorDataProvider : DataProviderBase
+ public abstract class FileMonitorDataProvider : DataProviderBase
{
#region Public methods
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMapping.cs b/Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMapping.cs
similarity index 87%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMapping.cs
rename to Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMapping.cs
index 010eb5eb6..18c0c1d5a 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMapping.cs
+++ b/Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMapping.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Telerik.OpenAccess;
using Telerik.OpenAccess.Metadata.Fluent;
+using Telerik.OpenAccess.Metadata.Fluent.Advanced;
using Telerik.Sitefinity.Model;
namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
@@ -8,7 +9,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
///
/// A concrete implementation of the for the file monitoring.
///
- internal class FileMonitorFluentMapping : OpenAccessFluentMappingBase
+ public class FileMonitorFluentMapping : OpenAccessFluentMappingBase
{
#region Contructors
@@ -49,6 +50,8 @@ private void MapFileData(List mappings)
fileDataMapping.HasProperty(p => p.FilePath).ToColumn("file_path");
fileDataMapping.HasProperty(p => p.PackageName).ToColumn("package_name");
+ fileDataMapping.HasIndex(p => new { p.FilePath }).IsUnique().WithName("idx_sf_fls_mntr_dta_path");
+
mappings.Add(fileDataMapping);
}
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMetadataSource.cs b/Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMetadataSource.cs
similarity index 94%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMetadataSource.cs
rename to Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMetadataSource.cs
index 4cfdc4abc..1da7c8866 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/FileMonitorFluentMetadataSource.cs
+++ b/Telerik.Sitefinity.Frontend.Data/FileMonitorFluentMetadataSource.cs
@@ -7,7 +7,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
///
/// A concrete implementation of the for the file monitoring.
///
- internal class FileMonitorFluentMetadataSource : SitefinityMetadataSourceBase
+ public class FileMonitorFluentMetadataSource : SitefinityMetadataSourceBase
{
#region Contructors
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/OpenAccessFileMonitorDataProvider.cs b/Telerik.Sitefinity.Frontend.Data/OpenAccessFileMonitorDataProvider.cs
similarity index 94%
rename from Telerik.Sitefinity.Frontend/FilesMonitoring/Data/OpenAccessFileMonitorDataProvider.cs
rename to Telerik.Sitefinity.Frontend.Data/OpenAccessFileMonitorDataProvider.cs
index 43f00c80e..745659edc 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/Data/OpenAccessFileMonitorDataProvider.cs
+++ b/Telerik.Sitefinity.Frontend.Data/OpenAccessFileMonitorDataProvider.cs
@@ -8,7 +8,7 @@ namespace Telerik.Sitefinity.Frontend.FilesMonitoring.Data
///
/// OpenAccess implementation of the .
///
- internal class OpenAccessFileMonitorDataProvider : FileMonitorDataProvider, IOpenAccessDataProvider, IOpenAccessUpgradableProvider
+ public class OpenAccessFileMonitorDataProvider : FileMonitorDataProvider, IOpenAccessDataProvider, IOpenAccessUpgradableProvider
{
#region Upgrade
diff --git a/Tests/Telerik.Sitefinity.Frontend.ClientTest/Properties/AssemblyInfo.cs b/Telerik.Sitefinity.Frontend.Data/Properties/AssemblyInfo.cs
similarity index 70%
rename from Tests/Telerik.Sitefinity.Frontend.ClientTest/Properties/AssemblyInfo.cs
rename to Telerik.Sitefinity.Frontend.Data/Properties/AssemblyInfo.cs
index 42868e033..de8521556 100644
--- a/Tests/Telerik.Sitefinity.Frontend.ClientTest/Properties/AssemblyInfo.cs
+++ b/Telerik.Sitefinity.Frontend.Data/Properties/AssemblyInfo.cs
@@ -1,10 +1,10 @@
using System.Reflection;
using System.Runtime.InteropServices;
-// General Information about an assembly is controlled through the following
+// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
-[assembly: AssemblyTitle("Telerik.Sitefinity.Frontend.ClientTest")]
+[assembly: AssemblyTitle("Telerik.Sitefinity.Frontend.Data")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
-[assembly: Guid("01b70fb0-dd5b-44d4-a09b-e6f85c8ae946")]
+[assembly: Guid("14d8a400-6989-4870-ada1-766622caeb86")]
diff --git a/Telerik.Sitefinity.Frontend.Data/Telerik.Sitefinity.Frontend.Data.csproj b/Telerik.Sitefinity.Frontend.Data/Telerik.Sitefinity.Frontend.Data.csproj
new file mode 100644
index 000000000..971cd5129
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend.Data/Telerik.Sitefinity.Frontend.Data.csproj
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(PkgTelerik_DataAccess_Fluent)\tools\enhancer\enhancer.exe
+
+
+
+
+
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/ControllerProvider.cs b/Telerik.Sitefinity.Frontend/ControllerProvider.cs
new file mode 100644
index 000000000..91757aeec
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/ControllerProvider.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Mvc.Store;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ internal class ControllerProvider : IControllerProvider
+ {
+ public IEnumerable GetControllers()
+ {
+ var controllerContainerInitializer = new ControllerContainerInitializer();
+ return controllerContainerInitializer.GetControllers();
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Designers/DesignerInitializer.cs b/Telerik.Sitefinity.Frontend/Designers/DesignerInitializer.cs
index 73b4e79bf..e4012c865 100644
--- a/Telerik.Sitefinity.Frontend/Designers/DesignerInitializer.cs
+++ b/Telerik.Sitefinity.Frontend/Designers/DesignerInitializer.cs
@@ -1,10 +1,13 @@
using System;
+using System.Collections.Generic;
using System.Text;
+using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Web.UI;
using Telerik.Microsoft.Practices.Unity;
using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Frontend.Mvc.Helpers;
using Telerik.Sitefinity.Frontend.Resources;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Web.UI;
@@ -14,21 +17,21 @@ namespace Telerik.Sitefinity.Frontend.Designers
///
/// This class contains logic for initializing the MVC designer.
///
- internal class DesignerInitializer
+ internal class DesignerInitializer : IInitializer
{
///
/// Initializes the MVC designer.
///
public void Initialize()
{
- if (RouteTable.Routes["MvcDesigner"] == null)
+ if (RouteTable.Routes[DesignerInitializer.MvcDesignerRouteName] == null)
{
- RouteTable.Routes.MapRoute("MvcDesigner", "Telerik.Sitefinity.Frontend/{controller}/Master/{widgetName}", new { controller = "DesignerController", action = "Master" });
+ RouteTable.Routes.MapRoute(DesignerInitializer.MvcDesignerRouteName, "Telerik.Sitefinity.Frontend/{controller}/Master/{widgetName}", new { controller = "DesignerController", action = "Master" });
}
- if (RouteTable.Routes["MvcDesignerView"] == null)
+ if (RouteTable.Routes[DesignerInitializer.MvcDesignerViewRouteName] == null)
{
- RouteTable.Routes.MapRoute("MvcDesignerView", "Telerik.Sitefinity.Frontend/{controller}/View/{widgetName}/{viewType}", new { controller = "DesignerController", action = "View", viewType = "PropertyGrid" });
+ RouteTable.Routes.MapRoute(DesignerInitializer.MvcDesignerViewRouteName, "Telerik.Sitefinity.Frontend/{controller}/View/{widgetName}/{viewType}", new { controller = "DesignerController", action = "View", viewType = "PropertyGrid" });
}
ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
@@ -37,6 +40,15 @@ public void Initialize()
EventHub.Subscribe(this.RegisteringScriptsHandler);
}
+ ///
+ /// Uninitializes the MVC designer.
+ ///
+ public void Uninitialize()
+ {
+ RouteTable.Routes.Remove(RouteTable.Routes[DesignerInitializer.MvcDesignerRouteName]);
+ RouteTable.Routes.Remove(RouteTable.Routes[DesignerInitializer.MvcDesignerViewRouteName]);
+ }
+
///
/// Registering the scripts for ZoneEditor.
///
@@ -45,11 +57,12 @@ private void RegisteringScriptsHandler(IScriptsRegisteringEvent @event)
{
if (@event.Sender.GetType() == typeof(ZoneEditor))
{
- var scriptRootPath = "~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(this.GetType().Assembly);
+ var scriptRootPath = VirtualPathUtility.ToAbsolute("~/" + FrontendManager.VirtualPathBuilder.GetVirtualPath(this.GetType().Assembly));
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Mvc/Scripts/Angular/angular.min.js"));
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Mvc/Scripts/Angular/angular-route.min.js"));
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Mvc/Scripts/Bootstrap/js/ui-bootstrap-tpls.min.js"));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Mvc/Scripts/Angular/angular.min.js")));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Mvc/Scripts/Angular/angular-route.min.js")));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Mvc/Scripts/Angular/angular-sanitize.min.js")));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Mvc/Scripts/Bootstrap/js/ui-bootstrap-tpls.min.js")));
////var references = PageManager.GetScriptReferences(ScriptRef.KendoAll);
////foreach (var scriptRef in references)
@@ -57,11 +70,10 @@ private void RegisteringScriptsHandler(IScriptsRegisteringEvent @event)
//// @event.Scripts.Add(scriptRef);
////}
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Mvc/Scripts/Kendo/kendo.all.min.js"));
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Designers/Scripts/page-editor-services.js"));
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Designers/Scripts/page-editor.js"));
-
- @event.Scripts.Add(new ScriptReference(scriptRootPath + "Mvc/Scripts/LABjs/LAB.min.js"));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Designers/Scripts/page-editor-services.js")));
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Designers/Scripts/page-editor.js")));
+
+ @event.Scripts.Add(new ScriptReference(UrlHelpers.AppendVersion(scriptRootPath + "Mvc/Scripts/LABjs/LAB.min.js")));
var currentPackage = new PackageManager().GetCurrentPackage();
if (!currentPackage.IsNullOrEmpty())
@@ -85,5 +97,8 @@ private void RegisteringScriptsHandler(IScriptsRegisteringEvent @event)
}
}
}
+
+ private const string MvcDesignerRouteName = "MvcDesigner";
+ private const string MvcDesignerViewRouteName = "MvcDesignerView";
}
}
diff --git a/Telerik.Sitefinity.Frontend/Designers/DesignerResolver.cs b/Telerik.Sitefinity.Frontend/Designers/DesignerResolver.cs
index 1d422a908..ad4a32feb 100644
--- a/Telerik.Sitefinity.Frontend/Designers/DesignerResolver.cs
+++ b/Telerik.Sitefinity.Frontend/Designers/DesignerResolver.cs
@@ -1,8 +1,14 @@
using System;
+using System.Collections.Generic;
using System.Linq;
+using System.Text.RegularExpressions;
+using System.Web.Mvc;
using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Frontend.Mvc.Helpers;
using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Mvc;
+using Telerik.Sitefinity.Mvc.Proxy;
using Telerik.Sitefinity.Web.UI.ControlDesign;
namespace Telerik.Sitefinity.Frontend.Designers
@@ -12,6 +18,15 @@ namespace Telerik.Sitefinity.Frontend.Designers
///
internal class DesignerResolver : IDesignerResolver
{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// An instance of a package manager.
+ public DesignerResolver(PackageManager packageManager = null)
+ {
+ this.packageManager = packageManager ?? new PackageManager();
+ }
+
#region Public members
///
@@ -35,7 +50,45 @@ public string GetUrl(Type widgetType)
if (!this.TryResolveUrlFromAttribute(widgetType, out designerUrl))
designerUrl = this.GetDefaultUrl(widgetType);
- return new PackageManager().EnhanceUrl(designerUrl);
+ return this.packageManager.EnhanceUrl(designerUrl);
+ }
+
+ ///
+ /// Checks if there are separate custom desiger views for the particular control type.
+ ///
+ /// The type of the control.
+ ///
+ public bool HasCustomDesigners(string controlType)
+ {
+ var mvcProxy = new MvcControllerProxy();
+ mvcProxy.ControllerName = controlType;
+ var controller = mvcProxy.GetController() as Controller;
+
+ if (controller == null)
+ return true;
+
+ var designerNamePattern = "DesignerView.*";
+ var views = controller.GetViews(null).Where(view => Regex.IsMatch(view, designerNamePattern));
+
+ return views.Count() > 0;
+ }
+
+ ///
+ /// Gets all view names that match a pattern.
+ ///
+ /// The control type.
+ /// The view name pattern
+ ///
+ public IEnumerable GetViewNames(string controlType, string viewNamePattern)
+ {
+ var mvcProxy = new MvcControllerProxy();
+ mvcProxy.ControllerName = controlType;
+ var controller = mvcProxy.GetController() as Controller;
+
+ if (controller == null)
+ return new List();
+
+ return ViewSelectorHelpers.GetViewNames(null, controller, viewNamePattern);
}
#endregion
@@ -105,6 +158,9 @@ private string GetDefaultUrl(Type widgetType)
private const string DefaultActionUrlTemplate = "~/Telerik.Sitefinity.Frontend/Designer/Master/{0}";
private const string DefaultGridActionUrlTemplate = "~/Telerik.Sitefinity.Frontend/GridDesigner/Master/GridDesigner";
+ private readonly PackageManager packageManager;
+
+ public object ViewSelectorHelper { get; private set; }
#endregion
}
diff --git a/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor-services.js b/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor-services.js
index c65172129..a605cac44 100644
--- a/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor-services.js
+++ b/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor-services.js
@@ -72,7 +72,7 @@
*/
var getUrl = function () {
if (!httpGetUrl) {
- httpGetUrl = widgetContext.webServiceUrl + widgetContext.widgetId + '/?pageId=' + widgetContext.pageId;
+ httpGetUrl = widgetContext.webServiceUrl + widgetContext.widgetId + '/?pageId=' + widgetContext.pageId + "&mediaType=" + widgetContext.mediaType;
}
return httpGetUrl;
};
@@ -95,6 +95,25 @@
return widget;
};
+ var toHierarchyArray = function (propertyBag) {
+ var widget = {};
+ for (var i = 0; i < propertyBag.length; i++) {
+ var prop = propertyBag[i];
+ if (!prop.NeedsEditor) {
+ var pathArray = prop.PropertyPath.split('/').splice(2);
+
+ var temp = widget;
+ for (var j = 0; j < pathArray.length - 1; j++) {
+ temp[pathArray[j]] = temp[pathArray[j]] || {};
+ temp = temp[pathArray[j]];
+ }
+ temp[prop.PropertyName] = prop;
+ }
+ }
+
+ return widget;
+ };
+
//get the property data from the service
var load = function () {
if (loadingPromise)
@@ -103,16 +122,15 @@
var deferred = $q.defer();
$http.get(getUrl(), requestOptions())
- .success(function (data) {
- properties = data;
+ .then(function (response) {
+ properties = response.data;
//clone the result in initialData
- initialData = $.extend(true, {}, data);
+ initialData = $.extend(true, {}, response.data);
loadingPromise = null;
- deferred.resolve(data);
- })
- .error(function (data) {
+ deferred.resolve(response.data);
+ }, function (err) {
loadingPromise = null;
- deferred.reject(data);
+ deferred.reject(err);
});
loadingPromise = deferred.promise;
@@ -159,17 +177,24 @@
save: function (saveMode, modifiedData) {
if (!modifiedData || modifiedData.length === 0) {
modifiedData = this.getDirty();
+
+ // If any of the property values is not string - convert it to json
+ for (var i = 0; i < modifiedData.length; i++) {
+ var value = modifiedData[i].PropertyValue;
+ if (value !== null && typeof value !== 'string') {
+ modifiedData[i].PropertyValue = JSON.stringify(value);
+ }
+ }
}
var deferred = $q.defer();
$http.put(updateUrl(saveMode), modifiedData, requestOptions())
- .success(function (data) {
+ .then(function (response) {
initialData = $.extend(true, {}, properties);
- deferred.resolve(data);
- })
- .error(function (data) {
- deferred.reject(data);
+ deferred.resolve(response.data);
+ }, function (err) {
+ deferred.reject(err);
});
return deferred.promise;
@@ -204,6 +229,10 @@
*/
toAssociativeArray: function (propertyBag) {
return toAssociativeArray(propertyBag);
+ },
+
+ toHierarchyArray: function (propertyBag) {
+ return toHierarchyArray(propertyBag);
}
};
@@ -238,7 +267,7 @@
var getInnerElements = function (layoutRoot) {
var gridElements = [];
- var elements = $(layoutRoot).find('[data-sf-element]');
+ var elements = $(layoutRoot).findExcludeNested('[data-sf-element]');
//Sort out the inner columns
for (var i = 0; i < elements.length; i++) {
@@ -260,7 +289,7 @@
var tempLayout = $(gridContext.layoutRoot).clone();
for (var i = 0; i < elements.length; i++) {
- var innerDiv = elements[i].id ? $(tempLayout).find('#' + elements[i].id) : $(tempLayout).find('[data-sf-element=' + elements[i].name + ']');
+ var innerDiv = elements[i].id ? $(tempLayout).findExcludeNested('#' + elements[i].id) : $(tempLayout).findExcludeNested('[data-sf-element="' + elements[i].name + '"]');
if (innerDiv) {
var label = elements[i].label;
@@ -288,14 +317,22 @@
var refreshContainer = function (elements) {
for (var i = 0; i < elements.length; i++) {
- var innerDiv = elements[i].id ? $(gridContext.layoutContainer).find('#' + elements[i].id) : $(gridContext.layoutContainer).find('[data-sf-element=' + elements[i].name + ']');
+ var innerDiv = elements[i].id ? $(gridContext.layoutContainer).findExcludeNested('#' + elements[i].id) : $(gridContext.layoutContainer).findExcludeNested('[data-sf-element="' + elements[i].name + '"]');
if (innerDiv) {
var css = elements[i].isPlaceholder ? 'sf_colsIn ' + elements[i].css : elements[i].css;
$(innerDiv).attr('class', css.trim());
var label = elements[i].label;
- $(innerDiv).find('.zeDockZoneLabel b').html(label);
+ var labelElement = $(innerDiv).find('.zeDockZoneLabel b');
+
+ if (labelElement && labelElement.length > 0) {
+ labelElement.html(label);
+ }
+ else if (label) {
+ $(innerDiv).find('.RadDockZone').addClass('zeDockZoneHasLabel');
+ $(innerDiv).find('.RadDockZone .emptyZoneDraggingText').after('
' + label + '
');
+ }
if (label) {
$(innerDiv).attr('data-placeholder-label', label);
@@ -307,6 +344,27 @@
}
};
+ // find all child elements until you find child grid widget inside the current grid
+ $.fn.findExcludeNested = function (selector, result) {
+
+ // Default result to an empty jQuery object if not provided
+ result = typeof result !== 'undefined' ? result : new jQuery();
+
+ // Iterate through all children, except those with the RadDockZone class
+ this.children().each(function () {
+
+ var thisObject = jQuery(this);
+ if (thisObject.is(selector))
+ result.push(this);
+
+ // Recursively seek children without RadDockZone class
+ if (!thisObject.hasClass('RadDockZone'))
+ thisObject.findExcludeNested(selector, result);
+ });
+
+ return result;
+ };
+
var invokePut = function (url, data, deferred, success) {
var wRequest = new Sys.Net.WebRequest();
wRequest.set_url(url);
diff --git a/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor.js b/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor.js
index ac3fc854d..c664c7aec 100644
--- a/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor.js
+++ b/Telerik.Sitefinity.Frontend/Designers/Scripts/page-editor.js
@@ -3,182 +3,228 @@
var sitefinity = sitefinity || {};
(function ($) {
-
- var loader,
+ $(function () {
+ var loader,
loaderMarkup = '
',
loaderTemplate = kendo.template(loaderMarkup),
- dialog;
+ dialog,
+ pageScripts = $('script').map(function () {
+ return $(this).attr('src');
+ }).toArray();
- function isScriptTag(tag) {
- return tag.tagName == 'SCRIPT' && (!tag.type || tag.type.toLowerCase() == 'text/javascript');
- }
+ function isScriptTag(tag) {
+ return tag.tagName == 'SCRIPT' && (!tag.type || tag.type.toLowerCase() == 'text/javascript');
+ }
- function extractScripts(markup) {
- var div = document.createElement('div');
- div.innerHTML = markup;
- var scripts = div.getElementsByTagName('script');
+ function extractScripts(markup) {
+ var div = document.createElement('div');
+ div.innerHTML = markup;
+ var scripts = div.getElementsByTagName('script');
- var result = [];
- for (var i = 0; i < scripts.length; i++) {
- if (isScriptTag(scripts[i])) {
- result.push(scripts[i]);
+ var result = [];
+ for (var i = 0; i < scripts.length; i++) {
+ if (isScriptTag(scripts[i])) {
+ result.push(scripts[i]);
+ }
}
- }
- return result;
- }
+ return result;
+ }
- function stripScripts(markup) {
- var div = document.createElement('div');
- div.innerHTML = markup;
- var scripts = div.getElementsByTagName('script');
+ function stripScripts(markup) {
+ var div = document.createElement('div');
+ div.innerHTML = markup;
+ var scripts = div.getElementsByTagName('script');
- var i = scripts.length;
- while (i--) {
- if (isScriptTag(scripts[i])) {
- scripts[i].parentNode.removeChild(scripts[i]);
+ var i = scripts.length;
+ while (i--) {
+ if (isScriptTag(scripts[i])) {
+ scripts[i].parentNode.removeChild(scripts[i]);
+ }
}
- }
- return div.innerHTML;
- }
-
- function loadScripts(container, scriptTags, loadHandler) {
- var lab = $LAB.setOptions({
- AlwaysPreserveOrder: true,
- AllowDuplicates: true
- });
+ return div.innerHTML;
+ }
- for (var i = 0; i < scriptTags.length; i++) {
- if (scriptTags[i].src) {
- lab = lab.script(scriptTags[i].src);
- }
- else if (scriptTags[i].text) {
- var text = scriptTags[i].text;
- lab = lab.wait(function () { eval(text); }); // jshint ignore:line
+ function loadScripts(container, scriptTags, loadHandler) {
+ var lab = $LAB.setOptions({
+ AlwaysPreserveOrder: true,
+ AllowDuplicates: true,
+ });
+
+ var labNoDuplicates = $LAB.setOptions({
+ AlwaysPreserveOrder: true,
+ AllowDuplicates: false
+ });
+
+ for (var i = 0; i < scriptTags.length; i++) {
+ if (scriptTags[i].src) {
+ // Hack to not load bootstrap multiple times, should be removed with the next version of bootstrap or with feather designer script refactoring
+ if (scriptTags[i].src.indexOf('Mvc/Scripts/Bootstrap/js/bootstrap.min.js') >= 0) {
+ var src = $(scriptTags[i]).attr('src');
+
+ if (pageScripts.indexOf(src) < 0) {
+ labNoDuplicates.script(src);
+ }
+ }
+ else {
+ lab = lab.script(scriptTags[i].src);
+ }
+ }
+ else if (scriptTags[i].text) {
+ var text = scriptTags[i].text;
+ lab = lab.wait(function () { eval(text); }); // jshint ignore:line
+ }
}
+
+ labNoDuplicates.wait(function () {
+ lab.wait(loadHandler);
+ });
}
- lab.wait(loadHandler);
- }
-
- /**
- * Represents the Sitefinity page editor.
- */
- sitefinity.pageEditor = {
- /**
- * Shows the loading animation in the page editor.
- *
- * @param {String} appPath The relative path of the application used to resolve the loading image.
- */
- showLoader: function (appPath) {
- if (loader) {
- loader.show();
- } else {
- loader = $(loaderTemplate({ appPath: appPath }));
- $('body').append(loader);
- }
- },
-
- /**
- * Hides the loading animation in the page editor.
- */
- hideLoader: function () {
- $(loader).hide();
- },
-
- /**
- * Renders the dialog within page editor with the provided markup.
- *
- * @param {String} markup The HTML markup to be rendered within the dialog.
- */
- renderDialog: function (markup) {
- dialog = $('');
- $('body').append(dialog);
-
- var scriptTags = extractScripts(markup);
- markup = stripScripts(markup);
-
- dialog.append(markup);
- dialog.on('hidden.bs.modal', this.destroyDialog);
-
- var that = this;
- loadScripts(dialog[0], scriptTags, function () {
- that.hideLoader();
-
- if (typeof ($telerik) != 'undefined') {
- $telerik.$(document).trigger('dialogRendered');
- }
- });
- },
-
- /**
- * Event handler that handles the needDialog event from the Sitefinity
- * page editor.
- */
- openDialog: function (ev, args) {
- this.showLoader(args.AppPath);
- this.widgetContext = args;
-
- var separator;
- if (this.widgetContext.url.indexOf('?') > -1)
- separator = '&';
- else
- separator = '?';
-
- var url = this.widgetContext.url + separator + 'controlId=' + this.widgetContext.Id;
- $.get(url)
- .done($.proxy(this.renderDialog, this))
- .fail(function (data) {
- alert('There is a problem with loading the widget designer: ' + data);
- });
- },
-
- openGridDialog: function (ev, args) {
- this.showLoader(args.AppPath);
- this.gridContext = args;
-
- var separator;
- if (this.gridContext.url.indexOf('?') > -1)
- separator = '&';
- else
- separator = '?';
-
- var url = this.gridContext.url + separator + 'controlId=' + this.gridContext.Id;
- url += '&gridTitle=' + this.gridContext.GridTitle;
- $.get(url)
- .done($.proxy(this.renderDialog, this))
- .fail(function (data) {
- alert('There is a problem with loading the grid designer: ' + data);
- });
- },
-
- /**
- * Destroys the currently opened dialog.
- */
- destroyDialog: function () {
- if (dialog) {
- dialog.remove();
- this.widgetContext = null;
- this.gridContext = null;
- }
- },
-
- /**
- * Provides the context for the currently active widget.
- */
- widgetContext: null,
- gridContext: null
-
- };
-
- /**
- * Register the global Sitefinity events with the pageEditor component.
- */
- if (typeof ($telerik) != 'undefined') {
- $telerik.$(document).on('needsModalDialog', $.proxy(sitefinity.pageEditor.openDialog, sitefinity.pageEditor));
- $telerik.$(document).on('needsGridModalDialog', $.proxy(sitefinity.pageEditor.openGridDialog, sitefinity.pageEditor));
- $telerik.$(document).on('modalDialogClosed', $.proxy(sitefinity.pageEditor.destroyDialog, sitefinity.pageEditor));
- $telerik.$(document).on('gridModalDialogClosed', $.proxy(sitefinity.pageEditor.destroyDialog, sitefinity.pageEditor));
- }
+ /**
+ * Represents the Sitefinity page editor.
+ */
+ sitefinity.pageEditor = {
+ /**
+ * Shows the loading animation in the page editor.
+ *
+ * @param {String} appPath The relative path of the application used to resolve the loading image.
+ */
+ showLoader: function (appPath) {
+ if (loader) {
+ loader.show();
+ } else {
+ loader = $(loaderTemplate({ appPath: appPath }));
+ $('body').append(loader);
+ }
+ },
+
+ /**
+ * Hides the loading animation in the page editor.
+ */
+ hideLoader: function () {
+ $(loader).hide();
+ },
+
+ /**
+ * Renders the dialog within page editor with the provided markup.
+ *
+ * @param {String} markup The HTML markup to be rendered within the dialog.
+ */
+ renderDialog: function (markup) {
+ dialog = $('');
+ $('body').append(dialog);
+
+ var scriptTags = extractScripts(markup);
+ markup = stripScripts(markup);
+
+ dialog.append(markup);
+ dialog.on('hidden.bs.modal', this.destroyDialog);
+
+ var that = this;
+ loadScripts(dialog[0], scriptTags, function () {
+ that.hideLoader();
+
+ if (typeof ($telerik) != 'undefined') {
+ $telerik.$(document).trigger('dialogRendered');
+ }
+ });
+ },
+
+ /**
+ * Event handler that handles the needDialog event from the Sitefinity
+ * page editor.
+ */
+ openDialog: function (args) {
+ if (args.detail.openNewEditor)
+ return true;
+
+ this.showLoader(args.detail.AppPath);
+ this.widgetContext = args.detail;
+
+ var separator;
+ if (this.widgetContext.url.indexOf('?') > -1)
+ separator = '&';
+ else
+ separator = '?';
+
+ var url = this.widgetContext.url + separator + 'controlId=' + this.widgetContext.Id;
+ if (this.widgetContext.ModuleName) {
+ url += '&moduleName=' + this.widgetContext.ModuleName;
+ }
+ if (this.widgetContext.MediaType) {
+ url += '&mediaType=' + this.widgetContext.MediaType;
+ }
+
+ $.get(url)
+ .done($.proxy(this.renderDialog, this))
+ .fail(function (data) {
+ alert('There is a problem with loading the widget designer: ' + data);
+ });
+ },
+
+ openGridDialog: function (ev, args) {
+ this.showLoader(args.AppPath);
+ this.gridContext = args;
+
+ var separator;
+ if (this.gridContext.url.indexOf('?') > -1)
+ separator = '&';
+ else
+ separator = '?';
+
+ var url = this.gridContext.url + separator + 'controlId=' + this.gridContext.Id;
+ url += '&gridTitle=' + this.gridContext.GridTitle;
+ $.get(url)
+ .done($.proxy(this.renderDialog, this))
+ .fail(function (data) {
+ alert('There is a problem with loading the grid designer: ' + data);
+ });
+ },
+
+ openPersonalizationDialog: function (ev, args) {
+ this.showLoader(args.AppPath);
+ this.widgetContext = args;
+
+ var url = this.widgetContext.AppPath + "Telerik.Sitefinity.Frontend/PersonalizationDesigner/Master/PersonalizationDesigner/";
+ $.get(url)
+ .done($.proxy(this.renderDialog, this))
+ .fail(function (data) {
+ alert('There is a problem with loading the widget designer: ' + data);
+ });
+ },
+
+ /**
+ * Destroys the currently opened dialog.
+ */
+ destroyDialog: function () {
+ if (dialog) {
+ dialog.remove();
+ this.widgetContext = null;
+ this.gridContext = null;
+ }
+ },
+
+ /**
+ * Provides the context for the currently active widget.
+ */
+ widgetContext: null,
+ gridContext: null
+
+ };
+
+ /**
+ * Register the global Sitefinity events with the pageEditor component.
+ */
+ if (typeof ($telerik) != 'undefined') {
+ $telerik.$(document).on('needsModalDialog', $.proxy(sitefinity.pageEditor.openDialog, sitefinity.pageEditor));
+
+ $telerik.$(document).on('needsPersonalizationModalDialog', $.proxy(sitefinity.pageEditor.openPersonalizationDialog, sitefinity.pageEditor));
+
+ $telerik.$(document).on('needsGridModalDialog', $.proxy(sitefinity.pageEditor.openGridDialog, sitefinity.pageEditor));
+ $telerik.$(document).on('modalDialogClosed', $.proxy(sitefinity.pageEditor.destroyDialog, sitefinity.pageEditor));
+ $telerik.$(document).on('gridModalDialogClosed', $.proxy(sitefinity.pageEditor.destroyDialog, sitefinity.pageEditor));
+ }
+ });
})(jQuery);
diff --git a/Telerik.Sitefinity.Frontend/FeatherConfig.cs b/Telerik.Sitefinity.Frontend/FeatherConfig.cs
new file mode 100644
index 000000000..232ea2dfd
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/FeatherConfig.cs
@@ -0,0 +1,142 @@
+using System.Configuration;
+using System.Diagnostics.CodeAnalysis;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Localization;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Configuration for the Feather module
+ ///
+ [ObjectInfo(typeof(InfrastructureResources), Title = "FeatherConfigCaption", Description = "FeatherConfigDescription")]
+ public class FeatherConfig : ConfigSection
+ {
+ ///
+ /// Gets or sets a value indicating whether to disable precompilation.
+ ///
+ ///
+ /// true if precompilation is disabled; otherwise, false.
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Precompilation")]
+ [ConfigurationProperty("disablePrecompilation")]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "DisablePrecompilationCaption", Description = "DisablePrecompilationDescription")]
+ public bool DisablePrecompilation
+ {
+ get
+ {
+ return (bool)this["disablePrecompilation"];
+ }
+
+ set
+ {
+ this["disablePrecompilation"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to always use the precompiled version of a resource regardless of changes to physical files.
+ ///
+ ///
+ /// true if precompiled version should be used regardless of changes to physical files; otherwise, false.
+ ///
+ [ConfigurationProperty("alwaysUsePrecompiledVersion")]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "AlwaysUsePrecompiledVersionCaption", Description = "AlwaysUsePrecompiledVersionDescription")]
+ public bool AlwaysUsePrecompiledVersion
+ {
+ get
+ {
+ return (bool)this["alwaysUsePrecompiledVersion"];
+ }
+
+ set
+ {
+ this["alwaysUsePrecompiledVersion"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to log when precompiled views are used.
+ ///
+ ///
+ /// true if usage of precompiled views should be logged; otherwise, false.
+ ///
+ [ConfigurationProperty("logPrecompiledViewUsage")]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "LogPrecompiledViewUsageCaption", Description = "LogPrecompiledViewUsageDescription")]
+ public bool LogPrecompiledViewUsage
+ {
+ get
+ {
+ return (bool)this["logPrecompiledViewUsage"];
+ }
+
+ set
+ {
+ this["logPrecompiledViewUsage"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether to require parameter naming in the widget routings.
+ ///
+ ///
+ /// true if the routes will work only with named params (e.g /tag/london/page/2); otherwise, false when the route will be /london/2.
+ ///
+ [ConfigurationProperty("useNamedParametersRouting")]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "UseNamedParametersRoutingCaption", Description = "UseNamedParametersRoutingDescription")]
+ public bool UseNamedParametersRouting
+ {
+ get
+ {
+ return (bool)this["useNamedParametersRouting"];
+ }
+
+ set
+ {
+ this["useNamedParametersRouting"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether cached controller assemblies to be used
+ ///
+ ///
+ /// true if the cached controller assemblies are used; otherwise, false
+ ///
+ [ConfigurationProperty("useCachedControllerContainerAssemblies", DefaultValue = true)]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "UseCachedControllerContainerAssembliesTitle", Description = "UseCachedControllerContainerAssembliesDescription")]
+ public bool UseCachedControllerContainerAssemblies
+ {
+ get
+ {
+ return (bool)this["useCachedControllerContainerAssemblies"];
+ }
+
+ set
+ {
+ this["useCachedControllerContainerAssemblies"] = value;
+ }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the Ninject kernel should automatically load extensions at startup
+ ///
+ ///
+ /// true if the Ninject kernel should automatically load extensions at startup; otherwise, false
+ ///
+ [ConfigurationProperty("ninjectLoadExtensions", DefaultValue = true)]
+ [ObjectInfo(typeof(InfrastructureResources), Title = "NinjectLoadExtensionsTitle", Description = "NinjectLoadExtensionsDescription")]
+ public bool NinjectLoadExtensions
+ {
+ get
+ {
+ return (bool)this["ninjectLoadExtensions"];
+ }
+
+ set
+ {
+ this["ninjectLoadExtensions"] = value;
+ }
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitor.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitor.cs
index b7905c681..4f4b0435a 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitor.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitor.cs
@@ -1,570 +1,650 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Web;
-using System.Web.Hosting;
-using Telerik.Sitefinity.Abstractions;
-using Telerik.Sitefinity.Frontend.FilesMonitoring.Data;
-using Telerik.Sitefinity.Frontend.Resources;
-using Telerik.Sitefinity.Services;
-
-namespace Telerik.Sitefinity.Frontend.FilesMonitoring
-{
- ///
- /// This class manages the file monitoring and is responsible to fire events on changes in the observed file structure.
- ///
- internal class FileMonitor : IFileMonitor
- {
- #region IFileMonitor methods
-
- ///
- /// Observes the resources locations, watch for changes
- /// and take certain actions depending on the change
- ///
- /// The monitored directories.
- public void Start(IList directoriesInfo)
- {
- if (this.rootWatcher == null)
- {
- this.AddRootWatcher();
- }
-
- foreach (var directory in directoriesInfo)
- {
- var direcotryPath = this.MapPath(directory.Path);
-
- if (this.WatchedFoldersAndPackages.Contains(directory))
- continue;
-
- DirectoryInfo dir = new DirectoryInfo(direcotryPath);
-
- if (!dir.Exists)
- {
- this.QueuedFoldersAndPackages.Add(directory);
- continue;
- }
- else
- {
- this.WatchedFoldersAndPackages.Add(directory);
- this.ProcessDirecotryFiles(dir);
- this.AddFileWatcher(directory);
- }
- }
- }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- public void Dispose()
- {
- if (this.fileWatchers != null)
- {
- foreach (var watcher in this.fileWatchers)
- {
- if (watcher.Value != null)
- {
- watcher.Value.Dispose();
- }
- }
- }
-
- if (this.rootWatcher != null)
- this.rootWatcher.Dispose();
-
- GC.SuppressFinalize(this);
- }
-
- #endregion
-
- #region Protected methods
-
- ///
- /// Takes appropriate actions based on the resource file type
- ///
- /// The file path.
- /// Type of the change.
- /// The old file path.
- protected virtual void FileChanged(string filePath, FileChangeType changeType, string oldFilePath = "")
- {
- var virtualFilePath = this.ConvertToVirtualPath(filePath);
- var oldFileVirtualPath = this.ConvertToVirtualPath(oldFilePath);
-
- var watchedDirInfo = this.WatchedFoldersAndPackages.FirstOrDefault(dirInfo => virtualFilePath.StartsWith(dirInfo.Path, StringComparison.Ordinal));
-
- if (watchedDirInfo == null)
- return;
-
- string packageName = string.Empty;
-
- var resourceDirecotryPath = VirtualPathUtility.GetDirectory(virtualFilePath);
- var resourceDirectoryTree = resourceDirecotryPath.Split('/');
-
- if (resourceDirectoryTree.Length < 3)
- return;
-
- if (watchedDirInfo.IsPackage)
- packageName = resourceDirectoryTree[2];
-
- string resourceFolder = resourceDirectoryTree[resourceDirectoryTree.Length - 2];
-
- var fileName = virtualFilePath.Replace(resourceDirecotryPath, string.Empty);
-
- SystemManager.RunWithElevatedPrivilegeDelegate elevDelegate = this.GetFileChangedDelegate(new FileChangedDelegateArguments()
- {
- FilePath = virtualFilePath,
- ChangeType = changeType,
- OldFilePath = oldFileVirtualPath,
- PackageName = packageName,
- ResourceFolder = resourceFolder,
- FileName = fileName
- });
- SystemManager.RunWithElevatedPrivilege(elevDelegate);
- }
-
- ///
- /// Removes the non existing files data.
- ///
- protected virtual void RemoveNonExistingData()
- {
- SystemManager.RunWithElevatedPrivilegeDelegate elevDelegate = (p) =>
- {
- var fileMonitorDataManager = FileMonitorDataManager.GetManager();
-
- // finds all records containing file paths which no longer exists
- var nonExistingFilesData = fileMonitorDataManager.GetFilesData().Select(fd => fd.FilePath).ToArray()
- .Where(f => !HostingEnvironment.VirtualPathProvider.FileExists(f));
-
- if (nonExistingFilesData != null && nonExistingFilesData.Any())
- {
- // remove all records in the lists
- foreach (var filePath in nonExistingFilesData)
- {
- var resourceDirectoryTree = VirtualPathUtility.GetDirectory(filePath).Split('/');
-
- if (resourceDirectoryTree.Length >= 2)
- {
- var packageFolderIdx = Array.IndexOf(resourceDirectoryTree, PackageManager.PackagesFolder);
- string packageName = packageFolderIdx > 0 && packageFolderIdx + 1 < resourceDirectoryTree.Length ? resourceDirectoryTree[packageFolderIdx + 1] : string.Empty;
- string resourceFolder = resourceDirectoryTree[resourceDirectoryTree.Length - 2];
- IFileManager resourceFilesManager = this.GetResourceFileManager(resourceFolder, filePath);
- resourceFilesManager.FileDeleted(filePath, packageName);
- }
- }
- }
- };
-
- SystemManager.RunWithElevatedPrivilege(elevDelegate);
- }
-
- ///
- /// Gets the resource file manager.
- ///
- /// Type of the resource.
- /// FileManager
- protected virtual IFileManager GetResourceFileManager(string resourceFolder, string resourcePath)
- {
- IFileManager resourceFilesManager = null;
- var resourceType = this.GetResourceType(resourceFolder, resourcePath);
-
- // the resource folder must follow the convention and the folder name must corresponds to a resource type
- if (resourceType != null)
- {
- if (ObjectFactory.IsTypeRegistered(resourceType.ToString()))
- resourceFilesManager = ObjectFactory.Resolve(resourceType.ToString());
- }
-
- return resourceFilesManager;
- }
-
- ///
- /// Gets the type of the resource.
- ///
- /// The resource folder.
- ///
- protected virtual ResourceType? GetResourceType(string resourceFolder, string resourcePath)
- {
- if (resourcePath.Contains("/GridSystem/Templates/"))
- return ResourceType.Grid;
-
- ResourceType resourceType;
- if (Enum.TryParse(resourceFolder, true, out resourceType))
- return resourceType;
- else
- return null;
- }
-
- ///
- /// Gets the physical path of the application.
- ///
- ///
- protected virtual string GetApplicationPhysicalPath()
- {
- return HostingEnvironment.ApplicationPhysicalPath;
- }
-
- ///
- /// Maps the virtual path to physical path.
- ///
- /// The virtual Path.
- /// The physical path on the server specified by virtualPath.
- protected virtual string MapPath(string virtualPath)
- {
- return FrontendManager.VirtualPathBuilder.MapPath(virtualPath);
- }
-
- #endregion
-
- #region Private methods
-
- ///
- /// Adds the root watcher.
- ///
- private void AddRootWatcher()
- {
- this.rootWatcher = new FileSystemWatcher();
-
- this.rootWatcher.Created += this.OnRootCreated;
- this.rootWatcher.Deleted += this.OnRootDeleted;
- this.rootWatcher.Renamed += this.OnRootRenamed;
-
- this.rootWatcher.IncludeSubdirectories = true;
- this.rootWatcher.Path = this.GetApplicationPhysicalPath();
- this.rootWatcher.NotifyFilter = NotifyFilters.DirectoryName;
- this.rootWatcher.EnableRaisingEvents = true;
-
- this.RemoveNonExistingData();
- }
-
- ///
- /// Adds the file watcher.
- ///
- /// The directory.
- private void AddFileWatcher(MonitoredDirectory directory)
- {
- var fileWatcher = new FileSystemWatcher();
-
- fileWatcher.IncludeSubdirectories = directory.IsPackage;
- fileWatcher.Path = this.MapPath(directory.Path);
- fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName;
-
- fileWatcher.Created += this.OnFileCreated;
- fileWatcher.Deleted += this.OnFileDeleted;
- fileWatcher.Renamed += this.OnFileRenamed;
-
- fileWatcher.EnableRaisingEvents = true;
-
- this.FileWatchers.Add(directory.Path, fileWatcher);
- }
-
- ///
- /// Removes the file watcher.
- ///
- /// The queued dir information key.
- private void RemoveFileWatcher(string queuedDirInfoKey)
- {
- if (this.FileWatchers.ContainsKey(queuedDirInfoKey))
- {
- var watcher = this.FileWatchers.Where(f => f.Key.Equals(queuedDirInfoKey)).First();
-
- watcher.Value.EnableRaisingEvents = false;
-
- this.FileWatchers.Remove(queuedDirInfoKey);
-
- watcher.Value.Created -= new FileSystemEventHandler(this.OnFileCreated);
- watcher.Value.Deleted -= new FileSystemEventHandler(this.OnFileDeleted);
- watcher.Value.Renamed -= new RenamedEventHandler(this.OnFileRenamed);
-
- watcher.Value.Dispose();
- }
- }
-
- ///
- /// Converts to virtual path.
- ///
- /// The path.
- ///
- private string ConvertToVirtualPath(string path)
- {
- var appPhysicalPath = this.GetApplicationPhysicalPath();
-
- // converting the file path to a virtual file path
- if (!appPhysicalPath.IsNullOrEmpty() && !path.IsNullOrEmpty())
- path = path.Substring(appPhysicalPath.Length - 1);
-
- var virtualFilePath = path.Replace('\\', '/').Insert(0, "~");
-
- return virtualFilePath;
- }
-
- ///
- /// Processes all files under a directory, including subfolders.
- ///
- /// The directory information.
- private void ProcessDirecotryFiles(DirectoryInfo dirInfo)
- {
- var files = dirInfo.GetFiles("*", SearchOption.AllDirectories);
-
- foreach (var file in files)
- {
- try
- {
- var filePath = file.FullName;
- this.FileChanged(filePath, FileChangeType.Created);
- }
- catch (IOException ex)
- {
- Log.Write(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Exception occurred while processing the file named {0}, details: {1}", file.Name, ex));
- }
- }
- }
-
- ///
- /// Called when root directory is renamed.
- ///
- /// The sender.
- /// The instance containing the event data.
- private void OnRootRenamed(object sender, RenamedEventArgs e)
- {
- string directoryPath = e.FullPath;
- string oldDirecotoryPath = e.OldFullPath;
-
- this.QueueWatch(oldDirecotoryPath);
-
- this.StartWatch(directoryPath);
- }
-
- ///
- /// Called when root directory is created.
- ///
- /// The sender.
- /// The instance containing the event data.
- private void OnRootCreated(object sender, FileSystemEventArgs e)
- {
- this.StartWatch(e.FullPath);
- }
-
- ///
- /// Called when root directory is deleted.
- ///
- /// The sender.
- /// The instance containing the event data.
- private void OnRootDeleted(object sender, FileSystemEventArgs e)
- {
- this.QueueWatch(e.FullPath);
- }
-
- ///
- /// Called when a file is renamed.
- ///
- /// The source.
- /// The instance containing the event data.
- private void OnFileRenamed(object source, RenamedEventArgs e)
- {
- if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
- {
- this.RemoveNonExistingData();
-
- DirectoryInfo dirInfo = new DirectoryInfo(e.FullPath);
-
- if (dirInfo.Exists)
- this.ProcessDirecotryFiles(dirInfo);
- }
- else
- {
- this.FileChanged(e.FullPath, FileChangeType.Renamed, e.OldFullPath);
- }
- }
-
- ///
- /// Called when a file is created.
- ///
- /// The source.
- /// The instance containing the event data.
- private void OnFileCreated(object source, FileSystemEventArgs e)
- {
- if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
- return;
-
- this.FileChanged(e.FullPath, FileChangeType.Created);
- }
-
- ///
- /// Called when a file is deleted.
- ///
- /// The source.
- /// The instance containing the event data.
- private void OnFileDeleted(object source, FileSystemEventArgs e)
- {
- if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
- this.RemoveNonExistingData();
- else
- this.FileChanged(e.FullPath, FileChangeType.Deleted);
- }
-
- ///
- /// Queues the watch action for certain directory.
- ///
- /// The directory path.
- private void QueueWatch(string directoryPath)
- {
- var virtualFilePath = this.ConvertToVirtualPath(directoryPath);
-
- var queuedDirInfo = this.WatchedFoldersAndPackages.FirstOrDefault(dirInfo => dirInfo.Path.StartsWith(virtualFilePath, StringComparison.OrdinalIgnoreCase));
-
- if (queuedDirInfo == null)
- return;
-
- this.QueuedFoldersAndPackages.Add(queuedDirInfo);
-
- this.WatchedFoldersAndPackages.Remove(queuedDirInfo);
-
- this.RemoveFileWatcher(queuedDirInfo.Path);
-
- var directories = new List();
- this.Start(directories);
- }
-
- ///
- /// Starts the watch action for certain directory.
- ///
- /// The directory path.
- private void StartWatch(string directoryPath)
- {
- var virtualFilePath = this.ConvertToVirtualPath(directoryPath);
-
- var queuedDirInfo = this.QueuedFoldersAndPackages.FirstOrDefault(dirInfo => dirInfo.Path.StartsWith(virtualFilePath, StringComparison.OrdinalIgnoreCase));
-
- if (queuedDirInfo == null)
- return;
-
- this.QueuedFoldersAndPackages.Remove(queuedDirInfo);
-
- var directories = new List();
- directories.Add(queuedDirInfo);
- this.Start(directories);
- }
-
- ///
- /// Gets the file changed delegate. It will call the appropriate methods of when the file structure has changed.
- ///
- /// The arguments.
- /// RunWithElevatedPrivilegeDelegate
- private SystemManager.RunWithElevatedPrivilegeDelegate GetFileChangedDelegate(FileChangedDelegateArguments args)
- {
- return (p) =>
- {
- // get the resource file manager depending on its type
- IFileManager resourceFilesManager = this.GetResourceFileManager(args.ResourceFolder, args.FilePath);
-
- if (resourceFilesManager != null)
- {
- switch (args.ChangeType)
- {
- case FileChangeType.Created:
- {
- resourceFilesManager.FileAdded(args.FileName, args.FilePath, args.PackageName);
- break;
- }
-
- case FileChangeType.Deleted:
- {
- resourceFilesManager.FileDeleted(args.FilePath, args.PackageName);
- break;
- }
-
- case FileChangeType.Renamed:
- {
- var fileNameStartIndex = args.OldFilePath.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1;
- var oldFileName = args.OldFilePath.Substring(fileNameStartIndex);
- resourceFilesManager.FileRenamed(args.FileName, oldFileName, args.FilePath, args.OldFilePath, args.PackageName);
- break;
- }
- }
- }
- };
- }
-
- #endregion
-
- #region Private fields
-
- private Dictionary fileWatchers;
- private FileSystemWatcher rootWatcher = null;
- private IList queuedFoldersAndPackages;
- private IList watchedFoldersAndPackages;
-
- #endregion
-
- #region Properties
-
- ///
- /// Gets the watched folders and packages.
- ///
- internal IList WatchedFoldersAndPackages
- {
- get
- {
- if (this.watchedFoldersAndPackages == null)
- this.watchedFoldersAndPackages = new List();
-
- return this.watchedFoldersAndPackages;
- }
- }
-
- ///
- /// Gets the queued folders and packages.
- ///
- internal IList QueuedFoldersAndPackages
- {
- get
- {
- if (this.queuedFoldersAndPackages == null)
- this.queuedFoldersAndPackages = new List();
-
- return this.queuedFoldersAndPackages;
- }
- }
-
- ///
- /// Gets the file watchers.
- ///
- ///
- /// The file watchers.
- ///
- internal IDictionary FileWatchers
- {
- get
- {
- if (this.fileWatchers == null)
- this.fileWatchers = new Dictionary();
-
- return this.fileWatchers;
- }
- }
-
- #endregion
-
- #region Private classes
-
- ///
- /// This class represents the arguments for invoking the GetFileChangedDelegate method.
- ///
- private class FileChangedDelegateArguments
- {
- public string FilePath { get; set; }
-
- public FileChangeType ChangeType { get; set; }
-
- public string OldFilePath { get; set; }
-
- public string PackageName { get; set; }
-
- public string ResourceFolder { get; set; }
-
- public string FileName { get; set; }
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Web;
+using System.Web.Hosting;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Frontend.FilesMonitoring.Data;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend.FilesMonitoring
+{
+ ///
+ /// This class manages the file monitoring and is responsible to fire events on changes in the observed file structure.
+ ///
+ internal class FileMonitor : IFileMonitor
+ {
+ #region IFileMonitor methods
+
+ ///
+ /// Observes the resources locations, watch for changes
+ /// and take certain actions depending on the change
+ ///
+ /// The monitored directories.
+ public void Start(IList directoriesInfo)
+ {
+ lock (FileMonitor.FileMonitorStartLockObj)
+ {
+ if (this.rootWatcher == null)
+ {
+ this.AddRootWatcher();
+ }
+
+ foreach (var directory in directoriesInfo)
+ {
+ var direcotryPath = this.MapPath(directory.Path);
+
+ if (this.WatchedFoldersAndPackages.Contains(directory))
+ continue;
+
+ DirectoryInfo dir = new DirectoryInfo(direcotryPath);
+
+ if (!dir.Exists)
+ {
+ this.QueuedFoldersAndPackages.Add(directory);
+ continue;
+ }
+ else
+ {
+ this.WatchedFoldersAndPackages.Add(directory);
+ this.ProcessDirecotryFiles(dir);
+ this.AddFileWatcher(directory);
+ }
+ }
+ }
+ }
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ if (this.fileWatchers != null)
+ {
+ foreach (var watcher in this.fileWatchers)
+ {
+ if (watcher.Value != null)
+ {
+ watcher.Value.Dispose();
+ }
+ }
+ }
+
+ if (this.rootWatcher != null)
+ this.rootWatcher.Dispose();
+
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region Protected methods
+
+ ///
+ /// Takes appropriate actions based on the resource file type
+ ///
+ /// The file path.
+ /// Type of the change.
+ /// The file data.
+ /// The old file path.
+ protected virtual void FileChanged(string filePath, FileChangeType changeType, string oldFilePath = "", FileData fileData = null)
+ {
+ var virtualFilePath = this.ConvertToVirtualPath(filePath);
+ var oldFileVirtualPath = this.ConvertToVirtualPath(oldFilePath);
+
+ var watchedDirInfo = this.WatchedFoldersAndPackages.FirstOrDefault(dirInfo => virtualFilePath.StartsWith(dirInfo.Path, StringComparison.Ordinal));
+
+ if (watchedDirInfo == null)
+ return;
+
+ string packageName = string.Empty;
+
+ var resourceDirecotryPath = VirtualPathUtility.GetDirectory(virtualFilePath);
+ var resourceDirectoryTree = resourceDirecotryPath.Split('/');
+
+ if (resourceDirectoryTree.Length < 3)
+ return;
+
+ if (watchedDirInfo.IsPackage)
+ packageName = resourceDirectoryTree[2];
+
+ string resourceFolder = resourceDirectoryTree[resourceDirectoryTree.Length - 2];
+
+ var fileName = virtualFilePath.Replace(resourceDirecotryPath, string.Empty);
+
+ SystemManager.RunWithElevatedPrivilegeDelegate elevDelegate = this.GetFileChangedDelegate(new FileChangedDelegateArguments()
+ {
+ FilePath = virtualFilePath,
+ ChangeType = changeType,
+ OldFilePath = oldFileVirtualPath,
+ PackageName = packageName,
+ ResourceFolder = resourceFolder,
+ FileName = fileName,
+ FileData = fileData
+ });
+
+ SystemManager.RunWithElevatedPrivilege(elevDelegate);
+ }
+
+ ///
+ /// Removes the non existing files data.
+ ///
+ protected virtual void RemoveNonExistingData()
+ {
+ SystemManager.RunWithElevatedPrivilegeDelegate elevDelegate = (p) =>
+ {
+ var fileMonitorDataManager = FileMonitorDataManager.GetManager();
+
+ // finds all records containing file paths which no longer exists
+ var nonExistingFilesData = fileMonitorDataManager.GetFilesData().Select(fd => fd.FilePath).ToArray()
+ .Where(f => !HostingEnvironment.VirtualPathProvider.FileExists(f));
+
+ if (nonExistingFilesData != null && nonExistingFilesData.Any())
+ {
+ // remove all records in the lists
+ foreach (var filePath in nonExistingFilesData)
+ {
+ var resourceDirectoryTree = VirtualPathUtility.GetDirectory(filePath).Split('/');
+
+ if (resourceDirectoryTree.Length >= 2)
+ {
+ var packageFolderIdx = Array.IndexOf(resourceDirectoryTree, PackageManager.PackagesFolder);
+ string packageName = packageFolderIdx > 0 && packageFolderIdx + 1 < resourceDirectoryTree.Length ? resourceDirectoryTree[packageFolderIdx + 1] : string.Empty;
+ string resourceFolder = resourceDirectoryTree[resourceDirectoryTree.Length - 2];
+ IFileManager resourceFilesManager = this.GetResourceFileManager(resourceFolder, filePath);
+ resourceFilesManager.FileDeleted(filePath, packageName);
+ }
+ }
+ }
+ };
+
+ SystemManager.RunWithElevatedPrivilege(elevDelegate);
+ }
+
+ ///
+ /// Gets the resource file manager.
+ ///
+ /// The resource folder.
+ /// The resource path.
+ /// FileManager
+ protected virtual IFileManager GetResourceFileManager(string resourceFolder, string resourcePath)
+ {
+ IFileManager resourceFilesManager = null;
+ var resourceType = this.GetResourceType(resourceFolder, resourcePath);
+
+ // the resource folder must follow the convention and the folder name must corresponds to a resource type
+ if (resourceType != null)
+ {
+ if (ObjectFactory.IsTypeRegistered(resourceType.ToString()))
+ resourceFilesManager = ObjectFactory.Resolve(resourceType.ToString());
+ }
+
+ return resourceFilesManager;
+ }
+
+ ///
+ /// Gets the type of the resource.
+ ///
+ /// The resource folder.
+ /// The resource path.
+ /// Resource type
+ protected virtual ResourceType? GetResourceType(string resourceFolder, string resourcePath)
+ {
+ if (resourcePath.Contains("/GridSystem/Templates/"))
+ return ResourceType.Grid;
+
+ ResourceType resourceType;
+ if (Enum.TryParse(resourceFolder, true, out resourceType))
+ return resourceType;
+ else
+ return null;
+ }
+
+ ///
+ /// Gets the physical path of the application.
+ ///
+ ///
+ protected virtual string GetApplicationPhysicalPath()
+ {
+ return HostingEnvironment.ApplicationPhysicalPath;
+ }
+
+ ///
+ /// Maps the virtual path to physical path.
+ ///
+ /// The virtual Path.
+ /// The physical path on the server specified by virtualPath.
+ protected virtual string MapPath(string virtualPath)
+ {
+ return FrontendManager.VirtualPathBuilder.MapPath(virtualPath);
+ }
+
+ #endregion
+
+ #region Private methods
+
+ private static void CreateFiles(FileMonitor monitor)
+ {
+ if (FileMonitor.filesNeedCreation)
+ {
+ lock (FileMonitor.FilesNeedCreationLockObj)
+ {
+ if (FileMonitor.filesNeedCreation)
+ {
+ FileMonitor.filesNeedCreation = false;
+
+ SystemManager.BackgroundTasksService.EnqueueTask(() =>
+ {
+ Thread.Sleep(1000);
+ string file;
+ lock (FileMonitor.FileMonitorStartLockObj)
+ {
+ while (FileMonitor.FilesToCreate.TryDequeue(out file))
+ {
+ monitor.FileChanged(file, FileChangeType.Created);
+ }
+ }
+
+ Thread.Sleep(1000);
+ FileMonitor.filesNeedCreation = true;
+ if (!FileMonitor.FilesToCreate.IsEmpty)
+ {
+ CreateFiles(monitor);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ ///
+ /// Adds the root watcher.
+ ///
+ private void AddRootWatcher()
+ {
+ this.rootWatcher = new FileSystemWatcher();
+
+ this.rootWatcher.Created += this.OnRootCreated;
+ this.rootWatcher.Deleted += this.OnRootDeleted;
+ this.rootWatcher.Renamed += this.OnRootRenamed;
+
+ this.rootWatcher.IncludeSubdirectories = true;
+ this.rootWatcher.Path = this.GetApplicationPhysicalPath();
+ this.rootWatcher.NotifyFilter = NotifyFilters.DirectoryName;
+ this.rootWatcher.EnableRaisingEvents = true;
+
+ this.RemoveNonExistingData();
+ }
+
+ ///
+ /// Adds the file watcher.
+ ///
+ /// The directory.
+ private void AddFileWatcher(MonitoredDirectory directory)
+ {
+ var fileWatcher = new FileSystemWatcher();
+
+ fileWatcher.IncludeSubdirectories = directory.IsPackage;
+ fileWatcher.Path = this.MapPath(directory.Path);
+ fileWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName;
+
+ fileWatcher.Created += this.OnFileCreated;
+ fileWatcher.Deleted += this.OnFileDeleted;
+ fileWatcher.Renamed += this.OnFileRenamed;
+
+ fileWatcher.EnableRaisingEvents = true;
+
+ this.FileWatchers.Add(directory.Path, fileWatcher);
+ }
+
+ ///
+ /// Removes the file watcher.
+ ///
+ /// The queued dir information key.
+ private void RemoveFileWatcher(string queuedDirInfoKey)
+ {
+ if (this.FileWatchers.ContainsKey(queuedDirInfoKey))
+ {
+ var watcher = this.FileWatchers.Where(f => f.Key.Equals(queuedDirInfoKey)).First();
+
+ watcher.Value.EnableRaisingEvents = false;
+
+ this.FileWatchers.Remove(queuedDirInfoKey);
+
+ watcher.Value.Created -= new FileSystemEventHandler(this.OnFileCreated);
+ watcher.Value.Deleted -= new FileSystemEventHandler(this.OnFileDeleted);
+ watcher.Value.Renamed -= new RenamedEventHandler(this.OnFileRenamed);
+
+ watcher.Value.Dispose();
+ }
+ }
+
+ ///
+ /// Converts to virtual path.
+ ///
+ /// The path.
+ ///
+ private string ConvertToVirtualPath(string path)
+ {
+ var appPhysicalPath = this.GetApplicationPhysicalPath();
+
+ // converting the file path to a virtual file path
+ if (!appPhysicalPath.IsNullOrEmpty() && !path.IsNullOrEmpty())
+ {
+ if (path.StartsWith(appPhysicalPath))
+ {
+ // path within site root
+ path = path.Substring(appPhysicalPath.Length - 1);
+ }
+ else
+ {
+ // path within virtual directory
+ foreach (var monitoredDirectory in this.WatchedFoldersAndPackages)
+ {
+ var physicalPath = this.MapPath(monitoredDirectory.Path);
+ if (path.StartsWith(physicalPath))
+ {
+ // path within site root
+ path = path.Substring(physicalPath.Length).Replace('\\', '/');
+ path = string.Concat(monitoredDirectory.Path, path);
+ return path;
+ }
+ }
+ }
+ }
+
+ var virtualFilePath = path.Replace('\\', '/').Insert(0, "~");
+
+ return virtualFilePath;
+ }
+
+ ///
+ /// Processes all files under a directory, including subfolders.
+ ///
+ /// The directory information.
+ private void ProcessDirecotryFiles(DirectoryInfo dirInfo)
+ {
+ var files = dirInfo.GetFiles("*", SearchOption.AllDirectories);
+
+ var fileManager = FileMonitorDataManager.GetManager();
+ var fileDatasList = fileManager.GetFilesData().ToList();
+
+ foreach (var file in files)
+ {
+ try
+ {
+ var virtualFilePath = this.ConvertToVirtualPath(file.FullName);
+
+ // Get the file monitoring data for the files that are already monitored. If the file data exist we don't have to create new monitoring for this file.
+ var fileData = fileDatasList.Where(x => x.FilePath.Equals(virtualFilePath, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
+
+ this.FileChanged(file.FullName, FileChangeType.Created, fileData: fileData);
+ }
+ catch (IOException ex)
+ {
+ Log.Write(string.Format(System.Globalization.CultureInfo.InvariantCulture, "Exception occurred while processing the file named {0}, details: {1}", file.Name, ex));
+ }
+ }
+ }
+
+ ///
+ /// Called when root directory is renamed.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnRootRenamed(object sender, RenamedEventArgs e)
+ {
+ string directoryPath = e.FullPath;
+ string oldDirecotoryPath = e.OldFullPath;
+
+ this.QueueWatch(oldDirecotoryPath);
+
+ this.StartWatch(directoryPath);
+ }
+
+ ///
+ /// Called when root directory is created.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnRootCreated(object sender, FileSystemEventArgs e)
+ {
+ this.StartWatch(e.FullPath);
+ }
+
+ ///
+ /// Called when root directory is deleted.
+ ///
+ /// The sender.
+ /// The instance containing the event data.
+ private void OnRootDeleted(object sender, FileSystemEventArgs e)
+ {
+ this.QueueWatch(e.FullPath);
+ }
+
+ ///
+ /// Called when a file is renamed.
+ ///
+ /// The source.
+ /// The instance containing the event data.
+ private void OnFileRenamed(object source, RenamedEventArgs e)
+ {
+ if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
+ {
+ this.RemoveNonExistingData();
+
+ DirectoryInfo dirInfo = new DirectoryInfo(e.FullPath);
+
+ if (dirInfo.Exists)
+ this.ProcessDirecotryFiles(dirInfo);
+ }
+ else
+ {
+ this.FileChanged(e.FullPath, FileChangeType.Renamed, e.OldFullPath);
+ }
+ }
+
+ ///
+ /// Called when a file is created.
+ ///
+ /// The source.
+ /// The instance containing the event data.
+ private void OnFileCreated(object source, FileSystemEventArgs e)
+ {
+ if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
+ return;
+
+ FilesToCreate.Enqueue(e.FullPath);
+ CreateFiles(this);
+ }
+
+ ///
+ /// Called when a file is deleted.
+ ///
+ /// The source.
+ /// The instance containing the event data.
+ private void OnFileDeleted(object source, FileSystemEventArgs e)
+ {
+ if (string.IsNullOrEmpty(Path.GetExtension(e.FullPath)))
+ this.RemoveNonExistingData();
+ else
+ this.FileChanged(e.FullPath, FileChangeType.Deleted);
+ }
+
+ ///
+ /// Queues the watch action for certain directory.
+ ///
+ /// The directory path.
+ private void QueueWatch(string directoryPath)
+ {
+ var virtualFilePath = this.ConvertToVirtualPath(directoryPath);
+
+ var queuedDirInfo = this.WatchedFoldersAndPackages.FirstOrDefault(dirInfo => dirInfo.Path.StartsWith(virtualFilePath, StringComparison.OrdinalIgnoreCase));
+
+ if (queuedDirInfo == null)
+ return;
+
+ this.QueuedFoldersAndPackages.Add(queuedDirInfo);
+
+ this.WatchedFoldersAndPackages.Remove(queuedDirInfo);
+
+ this.RemoveFileWatcher(queuedDirInfo.Path);
+
+ var directories = new List();
+ this.Start(directories);
+ }
+
+ ///
+ /// Starts the watch action for certain directory.
+ ///
+ /// The directory path.
+ private void StartWatch(string directoryPath)
+ {
+ var virtualFilePath = this.ConvertToVirtualPath(directoryPath);
+
+ var queuedDirInfo = this.QueuedFoldersAndPackages.FirstOrDefault(dirInfo => dirInfo.Path.StartsWith(virtualFilePath, StringComparison.OrdinalIgnoreCase));
+
+ if (queuedDirInfo == null)
+ return;
+
+ this.QueuedFoldersAndPackages.Remove(queuedDirInfo);
+
+ var directories = new List();
+ directories.Add(queuedDirInfo);
+ this.Start(directories);
+ }
+
+ ///
+ /// Gets the file changed delegate. It will call the appropriate methods of when the file structure has changed.
+ ///
+ /// The arguments.
+ /// RunWithElevatedPrivilegeDelegate
+ private SystemManager.RunWithElevatedPrivilegeDelegate GetFileChangedDelegate(FileChangedDelegateArguments args)
+ {
+ return (p) =>
+ {
+ // get the resource file manager depending on its type
+ IFileManager resourceFilesManager = this.GetResourceFileManager(args.ResourceFolder, args.FilePath);
+
+ if (resourceFilesManager != null)
+ {
+ switch (args.ChangeType)
+ {
+ case FileChangeType.Created:
+ {
+ resourceFilesManager.FileAdded(args.FileName, args.FilePath, args.FileData, args.PackageName);
+ break;
+ }
+
+ case FileChangeType.Deleted:
+ {
+ resourceFilesManager.FileDeleted(args.FilePath, args.PackageName);
+ break;
+ }
+
+ case FileChangeType.Renamed:
+ {
+ var fileNameStartIndex = args.OldFilePath.LastIndexOf("/", StringComparison.OrdinalIgnoreCase) + 1;
+ var oldFileName = args.OldFilePath.Substring(fileNameStartIndex);
+ resourceFilesManager.FileRenamed(args.FileName, oldFileName, args.FilePath, args.OldFilePath, args.PackageName);
+ break;
+ }
+ }
+ }
+ };
+ }
+
+ #endregion
+
+ #region Private fields
+
+ private Dictionary fileWatchers;
+ private FileSystemWatcher rootWatcher = null;
+ private IList queuedFoldersAndPackages;
+ private IList watchedFoldersAndPackages;
+
+ private static readonly ConcurrentQueue FilesToCreate = new ConcurrentQueue();
+ private static bool filesNeedCreation = true;
+ private static readonly object FilesNeedCreationLockObj = new object();
+ private static readonly object FileMonitorStartLockObj = new object();
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets the watched folders and packages.
+ ///
+ internal IList WatchedFoldersAndPackages
+ {
+ get
+ {
+ if (this.watchedFoldersAndPackages == null)
+ this.watchedFoldersAndPackages = new List();
+
+ return this.watchedFoldersAndPackages;
+ }
+ }
+
+ ///
+ /// Gets the queued folders and packages.
+ ///
+ internal IList QueuedFoldersAndPackages
+ {
+ get
+ {
+ if (this.queuedFoldersAndPackages == null)
+ this.queuedFoldersAndPackages = new List();
+
+ return this.queuedFoldersAndPackages;
+ }
+ }
+
+ ///
+ /// Gets the file watchers.
+ ///
+ ///
+ /// The file watchers.
+ ///
+ internal IDictionary FileWatchers
+ {
+ get
+ {
+ if (this.fileWatchers == null)
+ this.fileWatchers = new Dictionary();
+
+ return this.fileWatchers;
+ }
+ }
+
+ #endregion
+
+ #region Private classes
+
+ ///
+ /// This class represents the arguments for invoking the GetFileChangedDelegate method.
+ ///
+ private class FileChangedDelegateArguments
+ {
+ public string FilePath { get; set; }
+
+ public FileChangeType ChangeType { get; set; }
+
+ public string OldFilePath { get; set; }
+
+ public string PackageName { get; set; }
+
+ public string ResourceFolder { get; set; }
+
+ public string FileName { get; set; }
+
+ public FileData FileData { get; set; }
+ }
+
+ #endregion
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitoringInitializer.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitoringInitializer.cs
index d233cdd02..c1111b12d 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitoringInitializer.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/FileMonitoringInitializer.cs
@@ -1,31 +1,35 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
using Telerik.Microsoft.Practices.Unity;
using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Utilities.TypeConverters;
namespace Telerik.Sitefinity.Frontend.FilesMonitoring
{
///
/// This class contains logic for configuring the file monitoring functionality.
///
- internal class FileMonitoringInitializer
+ internal class FileMonitoringInitializer : IInitializer
{
///
/// Initializes and configure file monitoring functionality.
///
public void Initialize()
{
- if (!ObjectFactory.Container.IsRegistered(typeof(IFileMonitor)))
+ if (!ObjectFactory.IsTypeRegistered(typeof(IFileMonitor)))
{
ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
}
- if (!ObjectFactory.Container.IsRegistered(typeof(IFileManager), ResourceType.Layouts.ToString()))
+ if (!ObjectFactory.IsTypeRegistered(ResourceType.Layouts.ToString()))
{
ObjectFactory.Container.RegisterType(ResourceType.Layouts.ToString(), new ContainerControlledLifetimeManager());
}
- if (!ObjectFactory.Container.IsRegistered(typeof(IFileManager), ResourceType.Grid.ToString()))
+ if (!ObjectFactory.IsTypeRegistered(ResourceType.Grid.ToString()))
{
ObjectFactory.Container.RegisterType(ResourceType.Grid.ToString(), new ContainerControlledLifetimeManager());
}
@@ -33,18 +37,90 @@ public void Initialize()
this.RegisterFileObservers();
}
+ ///
+ /// Uninitializes the file monitoring functionality.
+ ///
+ public void Uninitialize()
+ {
+ if (this.fileMonitor != null)
+ {
+ this.fileMonitor.Dispose();
+ }
+ }
+
///
/// Registers the file observers.
///
private void RegisterFileObservers()
{
- var fileObserver = ObjectFactory.Resolve();
+ if (this.IsFileMonitoringEnabled())
+ {
+ this.fileMonitor = ObjectFactory.Resolve();
+
+ var monitoredDirectories = new List();
+
+ monitoredDirectories.Add(new MonitoredDirectory("~/" + PackageManager.PackagesFolder, true));
+ monitoredDirectories.Add(new MonitoredDirectory("~/Mvc/Views/Layouts", false));
+ monitoredDirectories.Add(new MonitoredDirectory("~/GridSystem/Templates", false));
+
+ this.fileMonitor.Start(monitoredDirectories);
+ }
+ }
+
+ private bool IsFileMonitoringEnabled()
+ {
+ // temporary solution to avoid any public APIs
+ var enableWatcherSetting = ConfigurationManager.AppSettings["sf:featherFileSystemWatcherBehaviour"];
+ if (!string.IsNullOrEmpty(enableWatcherSetting))
+ {
+ if (string.Equals(enableWatcherSetting, "true", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ else if (string.Equals(enableWatcherSetting, "false", StringComparison.OrdinalIgnoreCase))
+ {
+ return false;
+ }
+ }
+
+ ////var sectionType = TypeResolutionService.ResolveType("Telerik.Sitefinity.SiteSync.Configuration.SiteSyncConfig", false);
+ ////if (sectionType != null)
+ ////{
+ //// try
+ //// {
+ //// var siteSyncConfig = Config.Get(sectionType);
+ //// var isTarget = (bool)siteSyncConfig["enabledAsTarget"];
+ //// if (isTarget)
+ //// return false;
+ //// }
+ //// catch (Exception)
+ //// {
+ //// // the config is not available
+ //// }
+ ////}
- var monitoredDirectories = new List();
- monitoredDirectories.Add(new MonitoredDirectory("~/" + PackageManager.PackagesFolder, true));
- monitoredDirectories.Add(new MonitoredDirectory("~/Mvc/Views/Layouts", false));
+ ////var sectionType = TypeResolutionService.ResolveType("Telerik.Sitefinity.Packaging.Configuration.PackagingConfig", false);
+ ////if (sectionType != null)
+ ////{
+ //// try
+ //// {
+ //// var packagingConfig = Config.Get(sectionType);
+ //// var packagingMode = packagingConfig["packagingMode"];
- fileObserver.Start(monitoredDirectories);
+ //// var enumType = TypeResolutionService.ResolveType("Telerik.Sitefinity.Packaging.Restriction.PackagingMode", true);
+ //// var enumName = Enum.GetName(enumType, packagingMode);
+ //// if (enumName == "Target")
+ //// return false;
+ //// }
+ //// catch (Exception)
+ //// {
+ //// // the config is not available
+ //// }
+ ////}
+
+ return true;
}
+
+ private IFileMonitor fileMonitor;
}
}
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/GridFileManager.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/GridFileManager.cs
index d710343e7..7b885edb5 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/GridFileManager.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/GridFileManager.cs
@@ -2,20 +2,10 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Web;
using System.Web.Hosting;
-using System.Web.Mvc;
-using Telerik.Sitefinity.Abstractions;
-using Telerik.Sitefinity.Configuration;
-using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.Frontend.FilesMonitoring.Data;
using Telerik.Sitefinity.Frontend.GridSystem;
-using Telerik.Sitefinity.Modules.Pages;
-using Telerik.Sitefinity.Modules.Pages.Configuration;
-using Telerik.Sitefinity.Multisite;
-using Telerik.Sitefinity.Project.Configuration;
-using Telerik.Sitefinity.Services;
-using Telerik.Sitefinity.Web.UI;
+using Telerik.Sitefinity.Frontend.Resources;
namespace Telerik.Sitefinity.Frontend.FilesMonitoring
{
@@ -64,16 +54,18 @@ protected virtual GridWidgetRegistrator WidgetRegistrator
///
/// Process the file if such is added to the existing folder.
///
- ///
- ///
+ /// Name of the file.
+ /// The file path.
+ /// The file data.
/// Name of the package.
- public void FileAdded(string fileName, string filePath, string packageName = "")
+ public void FileAdded(string fileName, string filePath, FileData fileData, string packageName = "")
{
var fileMonitorDataManager = FileMonitorDataManager.GetManager();
- var fileData = fileMonitorDataManager.GetFilesData().Where(file => file.FilePath.Equals(filePath, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
- this.AddToToolboxAndFileData(fileMonitorDataManager, fileName, filePath, packageName, fileData);
- fileMonitorDataManager.SaveChanges();
+ if (this.AddToToolboxAndFileData(fileMonitorDataManager, fileName, filePath, packageName, fileData))
+ {
+ fileMonitorDataManager.SaveChanges();
+ }
}
///
@@ -92,7 +84,7 @@ public void FileDeleted(string filePath, string packageName)
fileMonitorDataManager.SaveChanges();
}
- this.WidgetRegistrator.UnregisterToolboxItem(this.GetFileName(filePath), packageName);
+ this.WidgetRegistrator.UnregisterToolboxItem(this.GetFileName(filePath));
}
///
@@ -108,25 +100,38 @@ public void FileRenamed(string newFileName, string oldFileName, string newFilePa
var fileMonitorDataManager = FileMonitorDataManager.GetManager();
var fileData = fileMonitorDataManager.GetFilesData().Where(file => file.FilePath.Equals(oldFilePath, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
- this.AddToToolboxAndFileData(fileMonitorDataManager, newFileName, newFilePath, packageName, fileData, oldFileName);
-
- this.WidgetRegistrator.UpdateControlData(newFileName, oldFileName);
-
- fileMonitorDataManager.SaveChanges();
+ if (fileData != null)
+ {
+ fileData.FilePath = newFilePath;
+ if (this.AddToToolboxAndFileData(fileMonitorDataManager, newFileName, newFilePath, packageName, fileData, oldFileName))
+ {
+ this.WidgetRegistrator.UpdateControlData(newFileName, oldFileName);
+ fileMonitorDataManager.SaveChanges();
+ }
+ else
+ {
+ fileMonitorDataManager.Delete(fileData);
+ fileMonitorDataManager.SaveChanges();
+ }
+ }
+ else
+ {
+ this.FileAdded(newFileName, newFilePath, null, packageName);
+ }
}
#endregion
#region Private methods
- private void AddToToolboxAndFileData(FileMonitorDataManager fileMonitorDataManager, string newFileName, string newFilePath, string packageName, FileData fileData, string oldFileName = "")
+ private bool AddToToolboxAndFileData(FileMonitorDataManager fileMonitorDataManager, string newFileName, string newFilePath, string packageName, FileData fileData, string oldFileName = "")
{
if (!this.IsFileValid(newFileName, newFilePath, packageName))
- return;
+ return false;
- this.WidgetRegistrator.RegisterToolboxItem(newFileName, packageName, oldFileName);
+ this.WidgetRegistrator.RegisterToolboxItem(newFileName, oldFileName);
- this.CreateOrUpdateFileData(fileMonitorDataManager, newFileName, newFilePath, packageName, fileData);
+ return this.CreateOrUpdateFileData(fileMonitorDataManager, newFileName, newFilePath, packageName, fileData);
}
///
@@ -154,7 +159,13 @@ private bool IsFileInValidFolder(string filePath, string packageName = "")
if (!string.IsNullOrEmpty(packageName))
expectedGridFolderStructure = expectedGridFolderStructure.Insert(0, packageName + Path.DirectorySeparatorChar);
- if (directory.FullName.EndsWith(expectedGridFolderStructure, StringComparison.OrdinalIgnoreCase) && directory.FullName.StartsWith(HostingEnvironment.ApplicationPhysicalPath, StringComparison.OrdinalIgnoreCase))
+ var resourcePackagesPath = FrontendManager.VirtualPathBuilder.MapPath(string.Concat("~/", PackageManager.PackagesFolder));
+ if (directory.FullName.EndsWith(expectedGridFolderStructure, StringComparison.OrdinalIgnoreCase) &&
+ (
+ directory.FullName.StartsWith(HostingEnvironment.ApplicationPhysicalPath, StringComparison.OrdinalIgnoreCase) ||
+ directory.FullName.StartsWith(resourcePackagesPath, StringComparison.OrdinalIgnoreCase)
+ )
+ )
isFileInValidFolder = true;
return isFileInValidFolder;
@@ -173,19 +184,34 @@ private bool IsFileValid(string fileName, string filePath, string packageName)
var absolutePath = FrontendManager.VirtualPathBuilder.MapPath(filePath);
var isSupproted = extension == GridFileManager.GridTemplateExtension;
- var isValid = isSupproted && !packageName.IsNullOrEmpty() && this.IsFileInValidFolder(absolutePath, packageName);
+ var isValid = isSupproted && this.IsFileInValidFolder(absolutePath, packageName);
return isValid;
}
- private void CreateOrUpdateFileData(FileMonitorDataManager dataManager, string fileName, string filePath, string packageName, FileData fileData)
+ private bool CreateOrUpdateFileData(FileMonitorDataManager dataManager, string fileName, string filePath, string packageName, FileData fileData)
{
+ var changed = false;
if (fileData == null)
+ {
fileData = dataManager.CreateFileData();
+ fileData.FilePath = filePath;
+ changed = true;
+ }
+
+ if (fileData.FileName != fileName)
+ {
+ fileData.FileName = fileName;
+ changed = true;
+ }
+
+ if (fileData.PackageName != packageName)
+ {
+ fileData.PackageName = packageName;
+ changed = true;
+ }
- fileData.FilePath = filePath;
- fileData.FileName = fileName;
- fileData.PackageName = packageName;
+ return changed;
}
///
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/IFileManager.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/IFileManager.cs
index 8127af86b..8097346ae 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/IFileManager.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/IFileManager.cs
@@ -1,4 +1,6 @@
-namespace Telerik.Sitefinity.Frontend.FilesMonitoring
+using Telerik.Sitefinity.Frontend.FilesMonitoring.Data;
+
+namespace Telerik.Sitefinity.Frontend.FilesMonitoring
{
///
/// Classes that implement this interface should define the Sitefinity's behavior when a file is moved over the application folder structure.
@@ -10,8 +12,9 @@ internal interface IFileManager
///
/// Name of the file.
/// The file path.
+ /// The file data.
/// Name of the package.
- void FileAdded(string fileName, string filePath, string packageName = "");
+ void FileAdded(string fileName, string filePath, FileData fileData, string packageName = "");
///
/// Reacts on file deletion.
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/LayoutFileManager.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/LayoutFileManager.cs
index 18c16ddb0..2f2bfc6dc 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/LayoutFileManager.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/LayoutFileManager.cs
@@ -2,17 +2,23 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
+using System.Security.Cryptography;
+using System.Text;
using System.Web.Hosting;
using System.Web.Mvc;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.Frontend.FilesMonitoring.Data;
using Telerik.Sitefinity.Frontend.GridSystem;
+using Telerik.Sitefinity.Frontend.Resources;
using Telerik.Sitefinity.Libraries.Model;
using Telerik.Sitefinity.Modules.Libraries;
+using Telerik.Sitefinity.Modules.Libraries.ImageProcessing;
+using Telerik.Sitefinity.Modules.Libraries.Images;
using Telerik.Sitefinity.Modules.Pages;
using Telerik.Sitefinity.Multisite;
using Telerik.Sitefinity.Pages.Model;
@@ -20,7 +26,6 @@
using Telerik.Sitefinity.Services.RelatedData.Messages;
using Telerik.Sitefinity.Taxonomies;
using Telerik.Sitefinity.Taxonomies.Model;
-using Telerik.Sitefinity.Web;
using Telerik.Sitefinity.Web.UI;
namespace Telerik.Sitefinity.Frontend.FilesMonitoring
@@ -62,25 +67,12 @@ public virtual ICollection FolderPathStructure
/// The id of the category.
public virtual Guid GetOrCreateTemplateCategoryId(string templateCategoryName, bool createIfNotExist = true)
{
- var taxonomyManager = TaxonomyManager.GetManager();
+ if (string.IsNullOrWhiteSpace(templateCategoryName))
+ return SiteInitializer.CustomTemplatesCategoryId;
- var pageTemplatesTaxonomy = taxonomyManager.GetTaxonomy(SiteInitializer.PageTemplatesTaxonomyId);
- var templateCategory = pageTemplatesTaxonomy.Taxa.SingleOrDefault(t => t.Name.Equals(templateCategoryName, StringComparison.OrdinalIgnoreCase));
+ var createdIds = this.CreateTemplateCategories(new HashSet() { templateCategoryName }, createIfNotExist);
- if (templateCategory == null && createIfNotExist)
- {
- templateCategory = taxonomyManager.CreateTaxon();
- templateCategory.Name = templateCategoryName;
- templateCategory.UrlName = templateCategoryName;
- templateCategory.RenderAsLink = false;
- templateCategory.Title = templateCategoryName;
- templateCategory.Description = string.Format("Represents category for {0} page templates.", templateCategoryName);
-
- pageTemplatesTaxonomy.Taxa.Add(templateCategory);
- taxonomyManager.SaveChanges();
- }
-
- return templateCategory.Id;
+ return createdIds[0];
}
///
@@ -108,14 +100,16 @@ public virtual void AttachImageToTemplate(PageTemplate template, PageManager pag
///
/// The virtual file name.
/// The virtual file path.
+ /// The file data
/// Name of the package.
- public void FileAdded(string fileName, string filePath, string packageName = "")
+ public void FileAdded(string fileName, string filePath, FileData fileData, string packageName = "")
{
var fileMonitorDataManager = FileMonitorDataManager.GetManager();
- var fileData = fileMonitorDataManager.GetFilesData().Where(file => file.FilePath.Equals(filePath, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
-
- this.CreateTemplateAndFileData(fileName, filePath, packageName, fileMonitorDataManager, fileData);
+ if (fileData == null && this.CreateTemplateAndFileData(fileName, filePath, packageName, fileMonitorDataManager, ref fileData))
+ {
+ fileMonitorDataManager.SaveChanges();
+ }
}
///
@@ -151,207 +145,177 @@ public void FileRenamed(string newFileName, string oldFileName, string newFilePa
var fileData = fileMonitorDataManager.GetFilesData().Where(file => file.FilePath.Equals(oldFilePath, StringComparison.InvariantCultureIgnoreCase)).FirstOrDefault();
if (fileData != null)
- this.CreateTemplateAndFileData(newFileName, newFilePath, packageName, fileMonitorDataManager, fileData);
+ {
+ fileData.FilePath = newFilePath;
+ if (this.CreateTemplateAndFileData(newFileName, newFilePath, packageName, fileMonitorDataManager, ref fileData))
+ {
+ fileMonitorDataManager.SaveChanges();
+ }
+ else
+ {
+ fileMonitorDataManager.Delete(fileData);
+ fileMonitorDataManager.SaveChanges();
+ }
+ }
else
- this.FileAdded(newFileName, newFilePath, packageName);
+ {
+ this.FileAdded(newFileName, newFilePath, null, packageName);
+ }
}
- internal void CreateDefaultSemanticUiTemplates()
- {
- var header = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionHeader,
- Description = LayoutFileManager.LayoutDescriptionHeader,
- Path = LayoutFileManager.GridTemplatePathSemanticUiContainer
- };
+ #endregion
- var content = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionContent,
- Description = LayoutFileManager.LayoutDescriptionContent,
- Path = LayoutFileManager.GridTemplatePathSemanticUiContainer
- };
+ #region Internal methods
- var footer = new LayoutControlDescription()
+ ///
+ /// The same logic can be found in PageTemplateHelper -> UpdateDefaultTemplateImages(). In need of a change do it in both places
+ ///
+ internal static void UpdateDefaultTemplateImages(LibrariesManager librariesManager)
+ {
+ var imagesToUpgrade = new List
{
- Caption = LayoutFileManager.LayoutCaptionFooter,
- Description = LayoutFileManager.LayoutDescriptionFooter,
- Path = LayoutFileManager.GridTemplatePathSemanticUiContainer
+ LayoutFileManager.TemplateImage1Column,
+ LayoutFileManager.TemplateImage2Columns,
+ LayoutFileManager.TemplateImage3Columns,
+ LayoutFileManager.TemplateImage4Columns,
+ LayoutFileManager.TemplateImageLeftSidebar,
+ LayoutFileManager.TemplateImageRightSidebar,
+ "default"
};
- var twoColumns = new LayoutControlDescription()
+ var templateThumbsImageLibrary = librariesManager.GetAlbums().FirstOrDefault(lib => lib.Id == LibrariesModule.DefaultTemplateThumbnailsLibraryId);
+ if (templateThumbsImageLibrary != null)
{
- Caption = LayoutFileManager.LayoutCaption2Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathSemanticUi2Columns
- };
+ var images = templateThumbsImageLibrary.Images().ToList();
+ foreach (var imageToUpgrade in imagesToUpgrade)
+ {
+ var image = images.FirstOrDefault(i => i.Title == "MVC_" + imageToUpgrade && i.Status == GenericContent.Model.ContentLifecycleStatus.Master);
- var threeColumns = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaption3Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathSemanticUi3Columns
- };
+ if (image != null)
+ {
+ var iconResource = string.Format(CultureInfo.InvariantCulture, LayoutFileManager.PageTemplateIconPathFormat, imageToUpgrade);
+ if (Assembly.GetExecutingAssembly().GetManifestResourceNames().Any(mrn => mrn.Equals(iconResource, StringComparison.OrdinalIgnoreCase)))
+ {
+ using (var imageStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(iconResource))
+ {
+ using (var resourceImage = ImagesHelper.CurrentImageProcessor.GetImageFromStream(imageStream))
+ {
+ var resourceImageStream = new MemoryStream();
+ resourceImage.Save(resourceImageStream, System.Drawing.Imaging.ImageFormat.Png);
+
+ librariesManager.Upload(image, resourceImageStream, ".png", true);
+ librariesManager.Lifecycle.Publish(image);
+ librariesManager.SaveChanges();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
- var fourColumns = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaption4Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathSemanticUi4Columns
- };
+ internal IList CreateTemplateCategories(ISet categoryNames, bool createIfNotExist)
+ {
+ var categoryIds = new List();
+ var taxonomyManager = TaxonomyManager.GetManager();
- var leftSidebar = new LayoutControlDescription()
+ foreach (var templateCategoryName in categoryNames)
{
- Caption = LayoutFileManager.LayoutCaptionContentSidebar,
- Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathSemanticUiLeftSidebar
- };
+ var pageTemplatesTaxonomy = taxonomyManager.GetTaxonomy(SiteInitializer.PageTemplatesTaxonomyId);
+ var templateCategory = pageTemplatesTaxonomy.Taxa.SingleOrDefault(t => t.Name != null && t.Name.Equals(templateCategoryName, StringComparison.OrdinalIgnoreCase));
- var rightSidebar = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionContentSidebar,
- Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathSemanticUiRightSidebar
- };
+ if (templateCategory == null && createIfNotExist)
+ {
+ var guid = this.GuidFromString(templateCategoryName);
+ templateCategory = taxonomyManager.CreateTaxon(guid);
+ templateCategory.Name = templateCategoryName;
+ templateCategory.UrlName = templateCategoryName;
+ templateCategory.RenderAsLink = false;
+ templateCategory.Title = templateCategoryName;
+ templateCategory.Description = string.Format(CultureInfo.InvariantCulture, "Represents category for {0} page templates.", templateCategoryName);
+
+ pageTemplatesTaxonomy.Taxa.Add(templateCategory);
+ categoryIds.Add(guid);
+ }
+ else
+ {
+ categoryIds.Add(templateCategory.Id);
+ }
+ }
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaption1Column, LayoutFileManager.TemplateImage1Column, new LayoutControlDescription[] { header, content, footer });
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaption2Columns, LayoutFileManager.TemplateImage2Columns, new LayoutControlDescription[] { header, twoColumns, footer });
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaption3Columns, LayoutFileManager.TemplateImage3Columns, new LayoutControlDescription[] { header, threeColumns, footer });
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaption4Columns, LayoutFileManager.TemplateImage4Columns, new LayoutControlDescription[] { header, fourColumns, footer });
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaptionLeftSidebar, LayoutFileManager.TemplateImageLeftSidebar, new LayoutControlDescription[] { header, leftSidebar, footer });
- this.CreateSemanticUiTemplate(LayoutFileManager.TemplateCaptionRightSidebar, LayoutFileManager.TemplateImageRightSidebar, new LayoutControlDescription[] { header, rightSidebar, footer });
+ taxonomyManager.SaveChanges();
+
+ return categoryIds;
}
- internal void CreateDefaultFoundationTemplates()
+ internal void CreateDefaultTemplates(string packageName, string layoutFile)
{
var header = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaptionHeader,
Description = LayoutFileManager.LayoutDescriptionHeader,
- Path = LayoutFileManager.GridTemplatePathFoundationContainer
+ Path = LayoutFileManager.GridTemplatePathContainer
};
var content = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaptionContent,
Description = LayoutFileManager.LayoutDescriptionContent,
- Path = LayoutFileManager.GridTemplatePathFoundationContainer
+ Path = LayoutFileManager.GridTemplatePathContainer
};
var footer = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaptionFooter,
Description = LayoutFileManager.LayoutDescriptionFooter,
- Path = LayoutFileManager.GridTemplatePathFoundationContainer
+ Path = LayoutFileManager.GridTemplatePathContainer
};
var twoColumns = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaption2Columns,
Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathFoundation2Columns
+ Path = LayoutFileManager.GridTemplatePath2Columns
};
var threeColumns = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaption3Columns,
Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathFoundation3Columns
+ Path = LayoutFileManager.GridTemplatePath3Columns
};
var fourColumns = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaption4Columns,
Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathFoundation4Columns
+ Path = LayoutFileManager.GridTemplatePath4Columns
};
var leftSidebar = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaptionContentSidebar,
Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathFoundationLeftSidebar
+ Path = LayoutFileManager.GridTemplatePathLeftSidebar
};
var rightSidebar = new LayoutControlDescription()
{
Caption = LayoutFileManager.LayoutCaptionContentSidebar,
Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathFoundationRightSidebar
+ Path = LayoutFileManager.GridTemplatePathRightSidebar
};
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaption1Column, LayoutFileManager.TemplateImage1Column, new LayoutControlDescription[] { header, content, footer });
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaption2Columns, LayoutFileManager.TemplateImage2Columns, new LayoutControlDescription[] { header, twoColumns, footer });
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaption3Columns, LayoutFileManager.TemplateImage3Columns, new LayoutControlDescription[] { header, threeColumns, footer });
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaption4Columns, LayoutFileManager.TemplateImage4Columns, new LayoutControlDescription[] { header, fourColumns, footer });
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaptionLeftSidebar, LayoutFileManager.TemplateImageLeftSidebar, new LayoutControlDescription[] { header, leftSidebar, footer });
- this.CreateFoundationTemplate(LayoutFileManager.TemplateCaptionRightSidebar, LayoutFileManager.TemplateImageRightSidebar, new LayoutControlDescription[] { header, rightSidebar, footer });
- }
-
- internal void CreateDefaultBootstrapTemplates()
- {
- var header = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionHeader,
- Description = LayoutFileManager.LayoutDescriptionHeader,
- Path = LayoutFileManager.GridTemplatePathBootstrapContainer
- };
-
- var content = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionContent,
- Description = LayoutFileManager.LayoutDescriptionContent,
- Path = LayoutFileManager.GridTemplatePathBootstrapContainer
- };
-
- var footer = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionFooter,
- Description = LayoutFileManager.LayoutDescriptionFooter,
- Path = LayoutFileManager.GridTemplatePathBootstrapContainer
- };
-
- var twoColumns = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaption2Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathBootstrap2Columns
- };
-
- var threeColumns = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaption3Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathBootstrap3Columns
- };
-
- var fourColumns = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaption4Columns,
- Description = LayoutFileManager.LayoutDescriptionContentColumns,
- Path = LayoutFileManager.GridTemplatePathBootstrap4Columns
- };
-
- var leftSidebar = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionContentSidebar,
- Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathBootstrapLeftSidebar
- };
+ if (!packageName.StartsWith("Bootstrap4") && !packageName.StartsWith("Bootstrap5"))
+ {
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaption2Columns, LayoutFileManager.TemplateImage2Columns, new LayoutControlDescription[] { header, twoColumns, footer });
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaption3Columns, LayoutFileManager.TemplateImage3Columns, new LayoutControlDescription[] { header, threeColumns, footer });
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaption4Columns, LayoutFileManager.TemplateImage4Columns, new LayoutControlDescription[] { header, fourColumns, footer });
+ }
- var rightSidebar = new LayoutControlDescription()
- {
- Caption = LayoutFileManager.LayoutCaptionContentSidebar,
- Description = LayoutFileManager.LayoutDescriptionContentSidebar,
- Path = LayoutFileManager.GridTemplatePathBootstrapRightSidebar
- };
-
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaption1Column, LayoutFileManager.TemplateImage1Column, new LayoutControlDescription[] { header, content, footer });
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaption2Columns, LayoutFileManager.TemplateImage2Columns, new LayoutControlDescription[] { header, twoColumns, footer });
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaption3Columns, LayoutFileManager.TemplateImage3Columns, new LayoutControlDescription[] { header, threeColumns, footer });
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaption4Columns, LayoutFileManager.TemplateImage4Columns, new LayoutControlDescription[] { header, fourColumns, footer });
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaptionLeftSidebar, LayoutFileManager.TemplateImageLeftSidebar, new LayoutControlDescription[] { header, leftSidebar, footer });
- this.CreateBootstrapTemplate(LayoutFileManager.TemplateCaptionRightSidebar, LayoutFileManager.TemplateImageRightSidebar, new LayoutControlDescription[] { header, rightSidebar, footer });
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaption1Column, LayoutFileManager.TemplateImage1Column, new LayoutControlDescription[] { header, content, footer });
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaptionLeftSidebar, LayoutFileManager.TemplateImageLeftSidebar, new LayoutControlDescription[] { header, leftSidebar, footer });
+ this.CreateTemplate(packageName, layoutFile, LayoutFileManager.TemplateCaptionRightSidebar, LayoutFileManager.TemplateImageRightSidebar, new LayoutControlDescription[] { header, rightSidebar, footer });
}
#endregion
@@ -384,7 +348,13 @@ private bool IsFileInValidFolder(string filePath, string packageName = "")
if (!string.IsNullOrEmpty(packageName))
expectedLayoutFolderStructure = packageName + Path.DirectorySeparatorChar + expectedLayoutFolderStructure;
- if (directory.FullName.EndsWith(expectedLayoutFolderStructure, StringComparison.OrdinalIgnoreCase) && directory.FullName.StartsWith(HostingEnvironment.ApplicationPhysicalPath, StringComparison.OrdinalIgnoreCase))
+ var resourcePackagesPath = FrontendManager.VirtualPathBuilder.MapPath(string.Concat("~/", PackageManager.PackagesFolder));
+ if (directory.FullName.EndsWith(expectedLayoutFolderStructure, StringComparison.OrdinalIgnoreCase) &&
+ (
+ directory.FullName.StartsWith(HostingEnvironment.ApplicationPhysicalPath, StringComparison.OrdinalIgnoreCase) ||
+ directory.FullName.StartsWith(resourcePackagesPath, StringComparison.OrdinalIgnoreCase)
+ )
+ )
isFileInValidFolder = true;
return isFileInValidFolder;
@@ -398,31 +368,59 @@ private bool IsFileInValidFolder(string filePath, string packageName = "")
/// Name of the package.
/// The file monitor data manager.
/// The file data.
- private void CreateTemplateAndFileData(string fileName, string filePath, string packageName, FileMonitorDataManager fileMonitorDataManager, FileData fileData)
+ ///
+ private bool CreateTemplateAndFileData(string fileName, string filePath, string packageName, FileMonitorDataManager fileMonitorDataManager, ref FileData fileData)
{
var absolutePath = FrontendManager.VirtualPathBuilder.MapPath(filePath);
- if (!this.IsFileInValidFolder(absolutePath, packageName))
- return;
-
- var extension = fileName.Split('.').LastOrDefault();
- var fileNameWithoutExtension = fileName.Substring(0, fileName.Length - (extension.Length + 1));
+ if (this.IsFileInValidFolder(absolutePath, packageName))
+ {
+ var extension = fileName.Split('.').LastOrDefault();
+ var fileNameWithoutExtension = fileName.Substring(0, fileName.Length - (extension.Length + 1));
- var viewFileExtensions = this.GetViewExtensions();
+ var viewFileExtensions = this.GetViewExtensions();
- if (viewFileExtensions.Contains(extension, StringComparer.Ordinal))
- {
- if (fileData == null)
- fileData = fileMonitorDataManager.CreateFileData();
+ if (viewFileExtensions.Contains(extension, StringComparer.Ordinal))
+ {
+ var changed = false;
+ if (fileData == null)
+ {
+ fileData = fileMonitorDataManager.CreateFileData();
+ fileData.FilePath = filePath;
+ fileData.FileName = fileName;
+ fileData.PackageName = packageName;
+ changed = true;
+ }
+ else
+ {
+ if (fileData.FilePath != filePath)
+ {
+ fileData.FilePath = filePath;
+ changed = true;
+ }
- fileData.FilePath = filePath;
- fileData.FileName = fileName;
- fileData.PackageName = packageName;
+ if (fileData.FileName != fileName)
+ {
+ fileData.FileName = fileName;
+ changed = true;
+ }
- fileMonitorDataManager.SaveChanges();
+ if (fileData.PackageName != packageName)
+ {
+ fileData.PackageName = packageName;
+ changed = true;
+ }
+ }
- this.CreateTemplate(packageName, fileNameWithoutExtension);
+ if (changed)
+ {
+ this.CreateTemplate(packageName, fileNameWithoutExtension);
+ return true;
+ }
+ }
}
+
+ return false;
}
///
@@ -440,21 +438,6 @@ private void CreateTemplate(string packageName, string fileNameWithoutExtension)
this.CreateTemplate(packageName, fileNameWithoutExtension, fileNameWithoutExtension, null, null);
}
- private void CreateBootstrapTemplate(string title, string image, LayoutControlDescription[] layoutControls)
- {
- this.CreateTemplate("Bootstrap", "default", title, image, layoutControls);
- }
-
- private void CreateFoundationTemplate(string title, string image, LayoutControlDescription[] layoutControls)
- {
- this.CreateTemplate("Foundation", "default", title, image, layoutControls);
- }
-
- private void CreateSemanticUiTemplate(string title, string image, LayoutControlDescription[] layoutControls)
- {
- this.CreateTemplate("SemanticUI", "default", title, image, layoutControls);
- }
-
private void CreateTemplate(string packageName, string fileNameWithoutExtension, string title, string image, LayoutControlDescription[] layoutControls)
{
var multisiteContext = SystemManager.CurrentContext as MultisiteContext;
@@ -474,35 +457,42 @@ private void CreateTemplate(string packageName, string fileNameWithoutExtension,
if (!pageManager.GetTemplates().Any(pt => (string.Compare(pt.Name, fullTemplateName, true) == 0 && string.Compare(pt.Title, title, true) == 0) || string.Compare(pt.Title, fullTemplateName, true) == 0))
{
- var template = pageManager.CreateTemplate();
+ var templateGuid = this.GuidFromString(string.Concat(fullTemplateName, title));
+ var template = pageManager.CreateTemplate(templateGuid);
template.Category = this.GetOrCreateTemplateCategoryId(packageName);
template.Name = fullTemplateName;
template.Title = title;
template.Framework = Pages.Model.PageTemplateFramework.Mvc;
template.Theme = ThemeController.NoThemeName;
- var languageData = pageManager.CreatePublishedInvarianLanguageData();
+ var languageData = pageManager.CreatePublishedLanguageData();
template.LanguageData.Add(languageData);
this.AttachImageToTemplate(template, pageManager, image ?? "default");
this.AddLayoutControls(pageManager, template, layoutControls);
- pageManager.SaveChanges();
-
var master = pageManager.TemplatesLifecycle.Edit(template);
pageManager.TemplatesLifecycle.Publish(master);
pageManager.SaveChanges();
+ if (string.Equals(LayoutFileManager.Bootstrap5DefaultTemplateName, fullTemplateName, StringComparison.OrdinalIgnoreCase))
+ this.CreateDefaultTemplates("Bootstrap5", "default");
+ if (string.Equals(LayoutFileManager.Bootstrap4DefaultTemplateName, fullTemplateName, StringComparison.OrdinalIgnoreCase))
+ this.CreateDefaultTemplates("Bootstrap4", "default");
if (string.Equals(LayoutFileManager.BootstrapDefaultTemplateName, fullTemplateName, StringComparison.OrdinalIgnoreCase))
- this.CreateDefaultBootstrapTemplates();
+ this.CreateDefaultTemplates("Bootstrap", "default");
else if (string.Equals(LayoutFileManager.FoundationDefaultTemplateName, fullTemplateName, StringComparison.OrdinalIgnoreCase))
- this.CreateDefaultFoundationTemplates();
+ this.CreateDefaultTemplates("Foundation", "default");
else if (string.Equals(LayoutFileManager.SemanticUIDefaultTemplateName, fullTemplateName, StringComparison.OrdinalIgnoreCase))
- this.CreateDefaultSemanticUiTemplates();
+ this.CreateDefaultTemplates("SemanticUI", "default");
}
}
}
+ catch (Exception)
+ {
+ Log.Write("Automatic template generation failed. To disable the automatic generation, set 'sf:featherFileSystemWatcherBehaviour' to false in the AppSettings in web.config.", ConfigurationPolicy.ErrorLog);
+ }
finally
{
if (multisiteContext != null)
@@ -553,7 +543,7 @@ private Image GetTemplateImage(PageTemplate template, string imageName)
if (templateThumbsImageLibrary != null)
{
// Try get image from library
- image = templateThumbsImageLibrary.Images().FirstOrDefault(i => i.Title.Equals("MVC_" + imageName, StringComparison.OrdinalIgnoreCase));
+ image = templateThumbsImageLibrary.Images().FirstOrDefault(i => i.Title == "MVC_" + imageName && i.Status == GenericContent.Model.ContentLifecycleStatus.Master);
if (image == null)
{
// Check if image is in the resources and upload it
@@ -590,7 +580,7 @@ private Image UploadTemplateImage(LibrariesManager libraryManager, Album templat
using (var imageStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(iconResource))
{
- using (var resourceImage = System.Drawing.Image.FromStream(imageStream))
+ using (var resourceImage = ImagesHelper.CurrentImageProcessor.GetImageFromStream(imageStream))
{
var resourceImageStream = new MemoryStream();
resourceImage.Save(resourceImageStream, System.Drawing.Imaging.ImageFormat.Png);
@@ -621,8 +611,8 @@ private void AddTemplatePresentation(PageTemplate template, PageManager pageMana
private void AddTemplateRelatedData(PageTemplate template, Image image, PageManager pageManager)
{
- var changedRelations = new ContentLinkChange[]
- {
+ var changedRelations = new ContentLinkChange[]
+ {
new ContentLinkChange()
{
ChildItemId = image.Id,
@@ -639,6 +629,15 @@ private void AddTemplateRelatedData(PageTemplate template, Image image, PageMana
method.Invoke(null, new object[] { pageManager, template, changedRelations, false });
}
+ private Guid GuidFromString(string key)
+ {
+ using (var md5 = MD5.Create())
+ {
+ byte[] hash = md5.ComputeHash(Encoding.Default.GetBytes(key));
+ return new Guid(hash);
+ }
+ }
+
#endregion
#region Private fileds
@@ -670,15 +669,19 @@ private class LayoutControlDescription
///
public const string PageTemplateIconPathFormat = "Telerik.Sitefinity.Frontend.Resources.PageTemplateImages.{0}.gif";
+ public const string Bootstrap5DefaultTemplateName = "Bootstrap5.default";
+ public const string Bootstrap4DefaultTemplateName = "Bootstrap4.default";
public const string BootstrapDefaultTemplateName = "Bootstrap.default";
public const string SemanticUIDefaultTemplateName = "SemanticUI.default";
public const string FoundationDefaultTemplateName = "Foundation.default";
- public static readonly string[] DefaultTemplateNames = new string[]
- {
- LayoutFileManager.BootstrapDefaultTemplateName,
- LayoutFileManager.SemanticUIDefaultTemplateName,
- LayoutFileManager.FoundationDefaultTemplateName
+ public static readonly string[] DefaultTemplateNames = new string[]
+ {
+ LayoutFileManager.Bootstrap5DefaultTemplateName,
+ LayoutFileManager.Bootstrap4DefaultTemplateName,
+ LayoutFileManager.BootstrapDefaultTemplateName,
+ LayoutFileManager.SemanticUIDefaultTemplateName,
+ LayoutFileManager.FoundationDefaultTemplateName
};
private const string TemplateCaption1Column = "1 Column, Header, Footer";
@@ -709,26 +712,12 @@ private class LayoutControlDescription
private const string LayoutDescriptionContentSidebar = "Represents the sidebar and content areas of the template.";
private const string LayoutDescriptionContentColumns = "Represents the content areas of the template.";
- private const string GridTemplatePathBootstrapContainer = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/container.html";
- private const string GridTemplatePathBootstrap2Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html";
- private const string GridTemplatePathBootstrap3Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html";
- private const string GridTemplatePathBootstrap4Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html";
- private const string GridTemplatePathBootstrapLeftSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html";
- private const string GridTemplatePathBootstrapRightSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html";
-
- private const string GridTemplatePathFoundationContainer = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12.html";
- private const string GridTemplatePathFoundation2Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html";
- private const string GridTemplatePathFoundation3Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html";
- private const string GridTemplatePathFoundation4Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html";
- private const string GridTemplatePathFoundationLeftSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html";
- private const string GridTemplatePathFoundationRightSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html";
-
- private const string GridTemplatePathSemanticUiContainer = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-one-col.html";
- private const string GridTemplatePathSemanticUi2Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-two-cols.html";
- private const string GridTemplatePathSemanticUi3Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-three-cols.html";
- private const string GridTemplatePathSemanticUi4Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-four-cols.html";
- private const string GridTemplatePathSemanticUiLeftSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+12.html";
- private const string GridTemplatePathSemanticUiRightSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12+4.html";
+ private const string GridTemplatePathContainer = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/container.html";
+ private const string GridTemplatePath2Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html";
+ private const string GridTemplatePath3Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html";
+ private const string GridTemplatePath4Columns = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html";
+ private const string GridTemplatePathLeftSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html";
+ private const string GridTemplatePathRightSidebar = "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html";
#endregion
}
diff --git a/Telerik.Sitefinity.Frontend/FilesMonitoring/MonitoredDirectory.cs b/Telerik.Sitefinity.Frontend/FilesMonitoring/MonitoredDirectory.cs
index 0d4223e6a..57415c2f1 100644
--- a/Telerik.Sitefinity.Frontend/FilesMonitoring/MonitoredDirectory.cs
+++ b/Telerik.Sitefinity.Frontend/FilesMonitoring/MonitoredDirectory.cs
@@ -36,7 +36,7 @@ public MonitoredDirectory(string path, bool isPackage)
public bool IsPackage { get; set; }
///
- /// Determines whether the specified , is equal to this instance.
///
/// The to compare with this instance.
///
diff --git a/Telerik.Sitefinity.Frontend/FrontendModule.cs b/Telerik.Sitefinity.Frontend/FrontendModule.cs
index ef4601046..4ba38c3ed 100644
--- a/Telerik.Sitefinity.Frontend/FrontendModule.cs
+++ b/Telerik.Sitefinity.Frontend/FrontendModule.cs
@@ -3,24 +3,22 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
+using System.Threading.Tasks;
+using System.Web.Mvc;
using Ninject;
-using Telerik.Microsoft.Practices.Unity;
+using Ninject.Modules;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Configuration;
using Telerik.Sitefinity.Data;
-using Telerik.Sitefinity.Frontend.Designers;
-using Telerik.Sitefinity.Frontend.FilesMonitoring;
+using Telerik.Sitefinity.Frontend;
using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Layouts;
-using Telerik.Sitefinity.Frontend.Resources;
-using Telerik.Sitefinity.Frontend.Services.FilesService;
-using Telerik.Sitefinity.Frontend.Services.ListsService;
-using Telerik.Sitefinity.Frontend.Services.ReviewsService;
-using Telerik.Sitefinity.Modules.Pages;
-using Telerik.Sitefinity.Modules.Pages.Configuration;
-using Telerik.Sitefinity.Pages.Model;
using Telerik.Sitefinity.Services;
-using Telerik.Sitefinity.Services.Comments.Notifications;
+
+[assembly: SitefinityModule(FrontendModule.ModuleName,
+ typeof(FrontendModule),
+ "Feather",
+ "Modern, intuitive, convention based, mobile-first UI for Progress Sitefinity CMS.",
+ StartupType.OnApplicationStart)]
namespace Telerik.Sitefinity.Frontend
{
@@ -40,7 +38,7 @@ public static FrontendModule Current
{
get
{
- return (FrontendModule)SystemManager.GetModule("Feather");
+ return (FrontendModule)SystemManager.GetModule(FrontendModule.ModuleName);
}
}
@@ -59,7 +57,7 @@ public override Guid LandingPageId
/// An array of objects.
public override Type[] Managers
{
- get { return new Type[0]; }
+ get { return ManagerTypes; }
}
///
@@ -72,7 +70,7 @@ public IKernel DependencyResolver
{
get
{
- return this.ninjectDependencyResolver;
+ return ninjectDependencyResolver;
}
}
@@ -82,31 +80,38 @@ public IKernel DependencyResolver
/// The initializer.
public override void Install(SiteInitializer initializer)
{
- if (this.FrontendServiceExists())
- {
- this.InitialUpgrade(initializer);
- }
+ FrontendModuleInstaller.Install(initializer);
}
///
- /// Initializes the service with specified settings.
+ /// Initializes the module with specified settings.
///
/// The settings.
public override void Initialize(ModuleSettings settings)
{
base.Initialize(settings);
- Bootstrapper.Initialized -= this.Bootstrapper_Initialized;
- Bootstrapper.Initialized += this.Bootstrapper_Initialized;
+ App.WorkWith()
+ .Module(settings.Name)
+ .Initialize()
+ .Configuration();
+
+ this.PreloadControllerTypeCacheAsync();
+ }
+
+ ///
+ /// Integrate the module into the system.
+ ///
+ public override void Load()
+ {
+ base.Load();
- SystemManager.RegisterServiceStackPlugin(new ListsServiceStackPlugin());
- SystemManager.RegisterServiceStackPlugin(new FilesServiceStackPlugin());
- SystemManager.RegisterServiceStackPlugin(new ReviewsServiceStackPlugin());
+ this.InitializeDependencyResolver();
- this.controllerAssemblies = new ControllerContainerInitializer().RetrieveAssemblies();
+ FrontendModuleInstaller.Initialize();
- this.ninjectDependencyResolver = new StandardKernel();
- this.ninjectDependencyResolver.Load(this.controllerAssemblies);
+ Bootstrapper.Initialized -= this.Bootstrapper_Initialized;
+ Bootstrapper.Initialized += this.Bootstrapper_Initialized;
}
///
@@ -115,14 +120,22 @@ public override void Initialize(ModuleSettings settings)
///
public override void Unload()
{
- if (this.ninjectDependencyResolver != null && !this.ninjectDependencyResolver.IsDisposed)
- this.ninjectDependencyResolver.Dispose();
-
- Bootstrapper.Initialized -= this.Bootstrapper_Initialized;
-
+ this.Uninitialize();
+ FrontendModuleUninstaller.Unload(this.initializers.Value);
base.Unload();
}
+ ///
+ /// Uninstall the module from Sitefinity system. Deletes the module artifacts added with Install method.
+ ///
+ /// The site initializer instance.
+ public override void Uninstall(SiteInitializer initializer)
+ {
+ this.Uninitialize();
+ FrontendModuleUninstaller.Uninstall(this.initializers.Value);
+ base.Uninstall(initializer);
+ }
+
///
/// Upgrades this module from the specified version.
///
@@ -131,33 +144,7 @@ public override void Unload()
public override void Upgrade(SiteInitializer initializer, Version upgradeFrom)
{
base.Upgrade(initializer, upgradeFrom);
-
- if (upgradeFrom < new Version(1, 2, 140, 0))
- {
- this.DeleteOldGridSection();
- this.UpdateContentBlockTitle();
- }
-
- if (upgradeFrom <= new Version(1, 2, 180, 1))
- {
- this.RemoveMvcWidgetToolboxItems();
- this.RenameDynamicContentMvcToolboxItems();
- }
-
- if (upgradeFrom <= new Version(1, 2, 260, 1))
- {
- this.RecategorizePageTemplates();
- }
-
- if (upgradeFrom <= new Version(1, 2, 270, 1))
- {
- this.UpdatePageTemplates();
- }
-
- if (upgradeFrom <= new Version(1, 2, 280, 2))
- {
- this.CreateDefaultTemplates();
- }
+ FrontendModuleUpgrader.Upgrade(upgradeFrom, initializer);
}
///
@@ -165,283 +152,177 @@ public override void Upgrade(SiteInitializer initializer, Version upgradeFrom)
///
protected override ConfigSection GetModuleConfig()
{
- return null;
+ return Config.Get();
}
///
- /// Handles the Initialized event of the Bootstrapper.
+ /// Gets the meta data aggregation mode of the module persistence.
///
- /// The source of the event.
- /// The instance containing the event data.
- protected virtual void Bootstrapper_Initialized(object sender, ExecutedEventArgs e)
+ protected override ManagersInitializationMode ManagersInitializationMode
{
- if (e.CommandName == "Bootstrapped")
+ get
{
- var resourcesInitializer = new ResourcesInitializer();
- resourcesInitializer.Initialize();
-
- var fileMonitoringInitilizer = new FileMonitoringInitializer();
- fileMonitoringInitilizer.Initialize();
-
- var controllerContainerInitializer = new ControllerContainerInitializer();
- controllerContainerInitializer.Initialize(this.controllerAssemblies);
- this.controllerAssemblies = null; // We won't be needing those anymore. Set them free.
-
- var layoutsInitializer = new LayoutInitializer();
- layoutsInitializer.Initialize();
-
- var designerInitializer = new DesignerInitializer();
- designerInitializer.Initialize();
-
- ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
+ return ManagersInitializationMode.OnStartup;
}
}
- private void InitialUpgrade(SiteInitializer initializer)
- {
- this.RemoveFrontendService();
- this.RenameControllers(initializer);
- this.ClearToolboxItems();
- }
-
- private void ClearToolboxItems()
+ ///
+ /// Creates Ninject kernel.
+ ///
+ ///
+ protected virtual IKernel CreateKernel()
{
- var configManager = ConfigManager.GetManager();
- var toolboxesConfig = configManager.GetSection();
-
- var pagesToolbox = toolboxesConfig.Toolboxes["PageControls"];
- if (pagesToolbox == null)
- return;
-
- var mvcWidgetsSection = pagesToolbox.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
- if (mvcWidgetsSection != null)
+ var bootstrapper = new Ninject.Web.Common.Bootstrapper();
+ IKernel kernel;
+ if (bootstrapper.Kernel != null)
+ kernel = bootstrapper.Kernel;
+ else
{
- this.RemoveToolboxItemIfExists(mvcWidgetsSection, "ContentBlock");
- this.RemoveToolboxItemIfExists(mvcWidgetsSection, "Navigation");
- this.RemoveToolboxItemIfExists(mvcWidgetsSection, "News");
-
- configManager.SaveSection(toolboxesConfig);
+ var ninjectSettings = new NinjectSettings();
+ ninjectSettings.LoadExtensions = Config.Get().NinjectLoadExtensions;
+ kernel = new SitefinityKernel(ninjectSettings);
}
- }
- private void RemoveToolboxItemIfExists(ToolboxSection mvcWidgetsSection, string toolName)
- {
- var tool = mvcWidgetsSection.Tools.FirstOrDefault(t => t.Name == toolName);
- if (tool != null)
- mvcWidgetsSection.Tools.Remove(tool);
+ return kernel;
}
- private void RenameControllers(SiteInitializer initializer)
+ ///
+ /// Handles the Initialized event of the Bootstrapper.
+ ///
+ /// The source of the event.
+ /// The instance containing the event data.
+ protected virtual void Bootstrapper_Initialized(object sender, ExecutedEventArgs e)
{
- var manager = initializer.PageManager;
- var controllerNameProperties = manager.GetControls()
- .Where(c => c.ObjectType == "Telerik.Sitefinity.Mvc.Proxy.MvcControllerProxy" || c.ObjectType == "Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.MvcWidgetProxy, Telerik.Sitefinity.Frontend")
- .SelectMany(c => c.Properties.Where(p => p.Name == "ControllerName"));
-
- foreach (var property in controllerNameProperties)
+ if (e.CommandName == "Bootstrapped")
{
- if (property.Value == "News.Mvc.Controllers.NewsController")
- {
- property.Value = "Telerik.Sitefinity.Frontend.News.Mvc.Controllers.NewsController";
- }
- else if (property.Value == "ContentBlock.Mvc.Controllers.ContentBlockController")
- {
- property.Value = "Telerik.Sitefinity.Frontend.ContentBlock.Mvc.Controllers.ContentBlockController";
- }
- else if (property.Value == "Navigation.Mvc.Controllers.NavigationController")
+ using (new HealthMonitoring.MethodPerformanceRegion("Feather"))
{
- property.Value = "Telerik.Sitefinity.Frontend.Navigation.Mvc.Controllers.NavigationController";
- }
- else if (property.Value == "SocialShare.Mvc.Controllers.SocialShareController")
- {
- property.Value = "Telerik.Sitefinity.Frontend.SocialShare.Mvc.Controllers.SocialShareController";
- }
- else if (property.Value == "DynamicContent.Mvc.Controllers.DynamicContentController")
- {
- property.Value = "Telerik.Sitefinity.Frontend.DynamicContent.Mvc.Controllers.DynamicContentController";
+ FrontendModuleInstaller.Bootstrapper_Initialized(this.initializers.Value);
}
}
}
- private void RemoveMvcWidgetToolboxItems()
+ // Called both by Unload and Uninstall
+ private void Uninitialize()
{
- var configManager = ConfigManager.GetManager();
- using (new ElevatedConfigModeRegion())
- {
- var toolboxConfig = configManager.GetSection();
- var pageControls = toolboxConfig.Toolboxes["PageControls"];
+ this.UninitializeDependencyResolver();
- foreach (var section in pageControls.Sections)
- {
- var widgets = section.Tools.Where(t => t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.", StringComparison.Ordinal) && !t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.DynamicContent", StringComparison.Ordinal)).ToArray();
- foreach (ToolboxItem tool in widgets)
- {
- section.Tools.Remove(tool);
- }
- }
-
- var mvcWidgetsSection = pageControls.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
- if (mvcWidgetsSection != null)
- {
- pageControls.Sections.Remove(mvcWidgetsSection);
- }
-
- configManager.SaveSection(toolboxConfig);
- }
+ Bootstrapper.Initialized -= this.Bootstrapper_Initialized;
}
- private void RenameDynamicContentMvcToolboxItems()
+ ///
+ /// Triggers the initialization of the controller type cache for the default controller factory asynchroniously.
+ ///
+ private void PreloadControllerTypeCacheAsync()
{
- var configManager = ConfigManager.GetManager();
- using (new ElevatedConfigModeRegion())
- {
- var toolboxConfig = configManager.GetSection();
- var pageControls = toolboxConfig.Toolboxes["PageControls"];
-
- foreach (var section in pageControls.Sections)
- {
- var widgets = section.Tools.Where(t => t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.DynamicContent", StringComparison.Ordinal)).ToArray();
- foreach (ToolboxItem tool in widgets)
- {
- tool.CssClass = "sfNewsViewIcn sfMvcIcn";
- var indexOfMvcSuffix = tool.Title.LastIndexOf(" MVC", StringComparison.Ordinal);
- tool.Title = tool.Title.Substring(0, indexOfMvcSuffix);
- }
- }
-
- configManager.SaveSection(toolboxConfig);
- }
+ Task.Run(() => this.InitializeControllerTypeCache());
}
- private void RemoveFrontendService()
+ ///
+ /// Initializes the controller type cache of the default controller factory.
+ ///
+ private void InitializeControllerTypeCache()
{
- var configManager = ConfigManager.GetManager();
- var systemConfig = configManager.GetSection();
- systemConfig.SystemServices.Remove(FrontendModule.FrontendServiceName);
- configManager.SaveSection(systemConfig);
- }
+ DefaultControllerFactory defaultControllerFactory =
+ System.Web.Mvc.DependencyResolver.Current.GetService() as DefaultControllerFactory ??
+ (ControllerBuilder.Current.GetControllerFactory() as DefaultControllerFactory ?? new DefaultControllerFactory());
- private bool FrontendServiceExists()
- {
- var systemConfig = Config.Get();
- return systemConfig.SystemServices.ContainsKey(FrontendModule.FrontendServiceName);
+ MethodInfo getControllerTypesMethod = defaultControllerFactory.GetType().GetMethod("GetControllerTypes", BindingFlags.Instance | BindingFlags.NonPublic);
+ getControllerTypesMethod.Invoke(defaultControllerFactory, null);
}
- private void DeleteOldGridSection()
+ private void InitializeDependencyResolver()
{
- var configManager = ConfigManager.GetManager();
- using (new ElevatedConfigModeRegion())
- {
- var toolboxConfig = configManager.GetSection();
- var layoutsToolbox = toolboxConfig.Toolboxes["PageLayouts"];
- if (layoutsToolbox != null)
- {
- var htmlLayoutsSection = layoutsToolbox.Sections.FirstOrDefault(s => s.Name == "HtmlLayouts");
- if (htmlLayoutsSection != null)
- {
- layoutsToolbox.Sections.Remove(htmlLayoutsSection);
- configManager.SaveSection(toolboxConfig);
- }
- }
- }
- }
+ if (ninjectDependencyResolver != null)
+ return;
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Telerik.Sitefinity.Pages.Model.ControlData.set_Caption(System.String)")]
- private void UpdateContentBlockTitle()
- {
- var configManager = ConfigManager.GetManager();
- using (new ElevatedConfigModeRegion())
+ ninjectDependencyResolver = this.CreateKernel();
+ var assemblies = new ControllerContainerInitializer().ControllerContainerAssemblies;
+ var loadedModules = ninjectDependencyResolver.GetModules();
+
+ foreach (var assembly in assemblies)
{
- var toolboxConfig = configManager.GetSection();
- var controlsToolbox = toolboxConfig.Toolboxes["PageControls"];
- if (controlsToolbox != null)
+ var assemblyModules = this.GetNinjectModules(assembly);
+
+ // check assembly for already registered ninject modules
+ var registeredAssemblyModules = assemblyModules.Where(module => loadedModules.Where(loadedModule => loadedModule.Name.Equals(module.Name, StringComparison.OrdinalIgnoreCase)).Any());
+ if (registeredAssemblyModules.Any())
{
- var mvcWidgetsSection = controlsToolbox.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
- if (mvcWidgetsSection != null)
+ foreach (var module in assemblyModules)
{
- var contentBlockTool = mvcWidgetsSection.Tools.FirstOrDefault(t => t.Name == "ContentBlock");
- if (contentBlockTool != null)
+ if (!registeredAssemblyModules.Any(registeredModule => registeredModule.Name.Equals(module.Name, StringComparison.OrdinalIgnoreCase)))
{
- contentBlockTool.Title = "Content Block";
- configManager.SaveSection(toolboxConfig);
+ ninjectDependencyResolver.Load(module);
}
}
}
- }
-
- var pageManager = PageManager.GetManager();
- using (new ElevatedModeRegion(pageManager))
- {
- var contentBlocks = pageManager.GetControls().Where(c => c.ObjectType == "Telerik.Sitefinity.Mvc.Proxy.MvcControllerProxy" && c.Caption == "ContentBlock").ToArray();
- foreach (var contentBlock in contentBlocks)
+ else
{
- contentBlock.Caption = "Content Block";
+ ninjectDependencyResolver.Load(assembly);
}
-
- pageManager.SaveChanges();
}
}
- private void RecategorizePageTemplates()
+ private void UninitializeDependencyResolver()
{
- var pageManager = PageManager.GetManager();
- var layoutManager = new LayoutFileManager();
-
- var customPageTemplates = pageManager.GetTemplates().Where(pt => pt.Category == SiteInitializer.CustomTemplatesCategoryId).ToArray();
- foreach (var customPageTemplate in customPageTemplates)
+ if (ninjectDependencyResolver != null && !ninjectDependencyResolver.IsDisposed && ninjectDependencyResolver is SitefinityKernel)
{
- var titleTokens = customPageTemplate.Title.ToString().Split('.');
- if (titleTokens.Length > 1 && (new PackageManager()).PackageExists(titleTokens[0]))
- {
- customPageTemplate.Category = layoutManager.GetOrCreateTemplateCategoryId(titleTokens[0]);
- }
+ ninjectDependencyResolver.Dispose();
+ ninjectDependencyResolver = null;
}
-
- pageManager.SaveChanges();
}
- private void UpdatePageTemplates()
+ private Lazy> initializers = new Lazy>(() =>
{
- var pageManager = PageManager.GetManager();
- var layoutFileManager = new LayoutFileManager();
-
- var defaultPageTemplates = pageManager.GetTemplates().Where(pt => LayoutFileManager.DefaultTemplateNames.Contains(pt.Name)).ToArray();
- foreach (var defaultPageTemplate in defaultPageTemplates)
+ try
{
- // Renaming template title
- var titleParts = defaultPageTemplate.Title.ToString().Split('.');
- if (titleParts.Length > 1)
- {
- defaultPageTemplate.Title = titleParts[1];
- }
-
- // Adding icon to title
- layoutFileManager.AttachImageToTemplate(defaultPageTemplate, pageManager, "default");
+ return typeof(FrontendModule).Assembly.GetTypes().Where(t => typeof(IInitializer).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract).Select(t => Activator.CreateInstance(t) as IInitializer).ToList();
+ }
+ catch (System.Reflection.ReflectionTypeLoadException ex)
+ {
+ string message = string.Join(" ", ex.LoaderExceptions.Select(e => e.Message));
+ throw new InvalidOperationException(message, ex);
+ }
+ catch (Exception)
+ {
+ throw;
}
+ });
- pageManager.SaveChanges();
+ private IEnumerable GetNinjectModules(Assembly assembly)
+ {
+ return assembly.GetExportedTypes().Where(new Func(this.IsLoadableModule)).Select((Type type) => Activator.CreateInstance(type) as INinjectModule);
}
- private void CreateDefaultTemplates()
- {
- var layoutManager = new LayoutFileManager();
+ private bool IsLoadableModule(Type type)
+ {
+ if (!typeof(INinjectModule).IsAssignableFrom(type) || type.IsAbstract || type.IsInterface)
+ {
+ return false;
+ }
+
+ return type.GetConstructor(Type.EmptyTypes) != null;
+ }
- var pageManager = PageManager.GetManager();
- var defaultPageTemplates = pageManager.GetTemplates().Where(pt => LayoutFileManager.DefaultTemplateNames.Contains(pt.Name));
- foreach (var template in defaultPageTemplates)
+ ///
+ /// The name.
+ ///
+ public const string ModuleName = "Feather";
+
+ private static IKernel ninjectDependencyResolver;
+ private static readonly Type[] ManagerTypes = new Type[] { typeof(FilesMonitoring.Data.FileMonitorDataManager) };
+
+ private class SitefinityKernel : StandardKernel
+ {
+ public SitefinityKernel()
+ : base()
{
- if (string.Equals(LayoutFileManager.BootstrapDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
- layoutManager.CreateDefaultBootstrapTemplates();
- else if (string.Equals(LayoutFileManager.FoundationDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
- layoutManager.CreateDefaultFoundationTemplates();
- else if (string.Equals(LayoutFileManager.SemanticUIDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
- layoutManager.CreateDefaultSemanticUiTemplates();
}
- }
-
- private IKernel ninjectDependencyResolver;
- private IEnumerable controllerAssemblies;
- private const string FrontendServiceName = "Telerik.Sitefinity.Frontend";
+ public SitefinityKernel(INinjectSettings settings)
+ : base(settings)
+ {
+ }
+ }
}
}
diff --git a/Telerik.Sitefinity.Frontend/FrontendModuleFilter.cs b/Telerik.Sitefinity.Frontend/FrontendModuleFilter.cs
new file mode 100644
index 000000000..093a9cf36
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/FrontendModuleFilter.cs
@@ -0,0 +1,23 @@
+using System.Web.Mvc;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Action filter that terminates the request to a controller if the Frontend (Feather) Module is disabled or uninstalled.
+ ///
+ internal sealed class FrontendModuleFilter : ActionFilterAttribute, IActionFilter
+ {
+ ///
+ /// Called by the ASP.NET MVC framework before the action method executes.
+ ///
+ /// The filter context.
+ public override void OnActionExecuting(ActionExecutingContext filterContext)
+ {
+ if (SystemManager.GetModule("Feather") == null && filterContext != null && filterContext.Controller != null && filterContext.Controller.GetType().FullName.StartsWith("Telerik.Sitefinity.Frontend"))
+ {
+ filterContext.Result = new EmptyResult();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/FrontendModuleInstaller.cs b/Telerik.Sitefinity.Frontend/FrontendModuleInstaller.cs
new file mode 100644
index 000000000..3fb0520a4
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/FrontendModuleInstaller.cs
@@ -0,0 +1,141 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Ninject;
+using Telerik.Microsoft.Practices.Unity;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Frontend.Modules.Comments;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Frontend.Services.FilesService;
+using Telerik.Sitefinity.Frontend.Services.ListsService;
+using Telerik.Sitefinity.Frontend.Services.ReviewsService;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Modules.Pages.Configuration;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Services.Comments.Notifications;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Handles the logic for Feather install/initialize
+ ///
+ internal static class FrontendModuleInstaller
+ {
+ ///
+ /// Installs the specified initializer.
+ ///
+ /// The initializer.
+ public static void Install(SiteInitializer initializer)
+ {
+ if (FrontendModuleInstaller.FrontendServiceExists())
+ FrontendModuleInstaller.InitialUpgrade(initializer);
+ }
+
+ ///
+ /// Initializes the specified settings.
+ ///
+ public static void Initialize()
+ {
+ SystemManager.RegisterServiceStackPlugin(new ListsServiceStackPlugin());
+ SystemManager.RegisterServiceStackPlugin(new FilesServiceStackPlugin());
+ IModule module = SystemManager.GetApplicationModule("Comments");
+ if (module != null && !(module is InactiveModule))
+ {
+ SystemManager.RegisterServiceStackPlugin(new ReviewsServiceStackPlugin());
+ }
+ }
+
+ ///
+ /// Bootstrapper_s the initialized.
+ ///
+ /// The initializers.
+ public static void Bootstrapper_Initialized(IEnumerable initializers)
+ {
+ foreach (var initializer in initializers)
+ {
+ using (new HealthMonitoring.MethodPerformanceRegion("Initializing '{0}'.".Arrange(initializer.GetType().Name)))
+ {
+ initializer.Initialize();
+ }
+ }
+
+ ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
+ }
+
+ #region Install
+
+ private static bool FrontendServiceExists()
+ {
+ var systemConfig = Config.Get();
+ return systemConfig.SystemServices.ContainsKey(FrontendModuleInstaller.FrontendServiceName);
+ }
+
+ private static void InitialUpgrade(SiteInitializer initializer)
+ {
+ FrontendModuleInstaller.RemoveFrontendService();
+ FrontendModuleInstaller.RenameControllers(initializer);
+ FrontendModuleInstaller.ClearToolboxItems();
+ }
+
+ private static void RemoveFrontendService()
+ {
+ var configManager = ConfigManager.GetManager();
+ var systemConfig = configManager.GetSection();
+ systemConfig.SystemServices.Remove(FrontendModuleInstaller.FrontendServiceName);
+ configManager.SaveSection(systemConfig);
+ }
+
+ private static void RenameControllers(SiteInitializer initializer)
+ {
+ var manager = initializer.PageManager;
+ var controllerNameProperties = manager.GetControls()
+ .Where(c => c.ObjectType == "Telerik.Sitefinity.Mvc.Proxy.MvcControllerProxy" || c.ObjectType == "Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.MvcWidgetProxy, Telerik.Sitefinity.Frontend")
+ .SelectMany(c => c.Properties.Where(p => p.Name == "ControllerName"));
+
+ foreach (var property in controllerNameProperties)
+ {
+ if (property.Value == "News.Mvc.Controllers.NewsController")
+ property.Value = "Telerik.Sitefinity.Frontend.News.Mvc.Controllers.NewsController";
+ else if (property.Value == "ContentBlock.Mvc.Controllers.ContentBlockController")
+ property.Value = "Telerik.Sitefinity.Frontend.ContentBlock.Mvc.Controllers.ContentBlockController";
+ else if (property.Value == "Navigation.Mvc.Controllers.NavigationController")
+ property.Value = "Telerik.Sitefinity.Frontend.Navigation.Mvc.Controllers.NavigationController";
+ else if (property.Value == "DynamicContent.Mvc.Controllers.DynamicContentController")
+ property.Value = "Telerik.Sitefinity.Frontend.DynamicContent.Mvc.Controllers.DynamicContentController";
+ }
+ }
+
+ private static void ClearToolboxItems()
+ {
+ var configManager = ConfigManager.GetManager();
+ var toolboxesConfig = configManager.GetSection();
+
+ var pagesToolbox = toolboxesConfig.Toolboxes["PageControls"];
+ if (pagesToolbox == null)
+ return;
+
+ var mvcWidgetsSection = pagesToolbox.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
+ if (mvcWidgetsSection != null)
+ {
+ FrontendModuleInstaller.RemoveToolboxItemIfExists(mvcWidgetsSection, "ContentBlock");
+ FrontendModuleInstaller.RemoveToolboxItemIfExists(mvcWidgetsSection, "Navigation");
+ FrontendModuleInstaller.RemoveToolboxItemIfExists(mvcWidgetsSection, "News");
+
+ configManager.SaveSection(toolboxesConfig);
+ }
+ }
+
+ private static void RemoveToolboxItemIfExists(ToolboxSection mvcWidgetsSection, string toolName)
+ {
+ var tool = mvcWidgetsSection.Tools.FirstOrDefault(t => t.Name == toolName);
+ if (tool != null && tool.Source == ConfigSource.Database)
+ mvcWidgetsSection.Tools.Remove(tool);
+ }
+
+ private const string FrontendServiceName = "Telerik.Sitefinity.Frontend";
+
+ #endregion
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/FrontendModuleUninstaller.cs b/Telerik.Sitefinity.Frontend/FrontendModuleUninstaller.cs
new file mode 100644
index 000000000..033701f64
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/FrontendModuleUninstaller.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Modules.Pages.Configuration;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Handles the logic for Feather unload/uninstall
+ ///
+ internal static class FrontendModuleUninstaller
+ {
+ ///
+ /// Unloads the specified initializers.
+ ///
+ /// The initializers.
+ public static void Unload(IEnumerable initializers)
+ {
+ FrontendModuleUninstaller.Uninitialize(initializers);
+ }
+
+ ///
+ /// Uninstalls the specified initializers.
+ ///
+ /// The initializers.
+ public static void Uninstall(IEnumerable initializers)
+ {
+ FrontendModuleUninstaller.Uninitialize(initializers);
+ }
+
+ // Called both by Unload and Uninstall
+ private static void Uninitialize(IEnumerable initializers)
+ {
+ foreach (var initializer in initializers)
+ initializer.Uninitialize();
+
+ // Force mvc initialization to run again after feather uninstalls
+ typeof(SystemManager).GetField("mvcEnabled", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, false);
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/FrontendModuleUpgrader.cs b/Telerik.Sitefinity.Frontend/FrontendModuleUpgrader.cs
new file mode 100644
index 000000000..307418959
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/FrontendModuleUpgrader.cs
@@ -0,0 +1,405 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.Frontend.FilesMonitoring;
+using Telerik.Sitefinity.Frontend.GridSystem;
+using Telerik.Sitefinity.Frontend.Resources;
+using Telerik.Sitefinity.Modules.Libraries;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Modules.Pages.Configuration;
+using Telerik.Sitefinity.Pages.Model;
+
+namespace Telerik.Sitefinity.Frontend
+{
+ ///
+ /// Handles upgrades for Feather module
+ ///
+ internal static class FrontendModuleUpgrader
+ {
+ ///
+ /// Upgrades the specified upgrade from.
+ ///
+ /// The upgrade from.
+ /// The site initializer.
+ public static void Upgrade(Version upgradeFrom, SiteInitializer initializer)
+ {
+ if (upgradeFrom < new Version(1, 2, 140, 0))
+ {
+ FrontendModuleUpgrader.DeleteOldGridSection();
+ FrontendModuleUpgrader.UpdateContentBlockTitle();
+ }
+
+ if (upgradeFrom <= new Version(1, 2, 180, 1))
+ {
+ FrontendModuleUpgrader.RemoveMvcWidgetToolboxItems();
+ FrontendModuleUpgrader.RenameDynamicContentMvcToolboxItems();
+ }
+
+ if (upgradeFrom <= new Version(1, 2, 260, 1))
+ {
+ FrontendModuleUpgrader.RecategorizePageTemplates();
+ }
+
+ if (upgradeFrom <= new Version(1, 2, 270, 1))
+ {
+ FrontendModuleUpgrader.UpdatePageTemplates();
+ }
+
+ if (upgradeFrom <= new Version(1, 2, 280, 2))
+ {
+ FrontendModuleUpgrader.CreateDefaultTemplates();
+ }
+
+ if (upgradeFrom <= new Version(1, 3, 320, 0))
+ {
+ FrontendModuleUpgrader.UpdateGridWidgetsToolbox();
+ FrontendModuleUpgrader.UpdateGridWidgetPaths();
+ }
+
+ if (upgradeFrom <= new Version(1, 7, 600, 0))
+ {
+ FrontendModuleUpgrader.UpgradeLimitCountProperty(initializer);
+ }
+
+ if (upgradeFrom < new Version(12, 0))
+ {
+ FrontendModuleUpgrader.UpdateDefaultTemplateImages();
+ }
+
+ if (upgradeFrom < new Version(14, 1))
+ {
+ FrontendModuleUpgrader.CreateBootstrap5Templates();
+ }
+ }
+
+ // 1, 2, 140, 0
+ private static void DeleteOldGridSection()
+ {
+ var configManager = ConfigManager.GetManager();
+ using (new ElevatedConfigModeRegion())
+ {
+ var toolboxConfig = configManager.GetSection();
+ var layoutsToolbox = toolboxConfig.Toolboxes["PageLayouts"];
+ if (layoutsToolbox != null)
+ {
+ var htmlLayoutsSection = layoutsToolbox.Sections.FirstOrDefault(s => s.Name == "HtmlLayouts");
+ if (htmlLayoutsSection != null && htmlLayoutsSection.Source == ConfigSource.Database)
+ {
+ layoutsToolbox.Sections.Remove(htmlLayoutsSection);
+ configManager.SaveSection(toolboxConfig);
+ }
+ }
+ }
+ }
+
+ // 1, 2, 140, 0
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "Telerik.Sitefinity.Pages.Model.ControlData.set_Caption(System.String)")]
+ private static void UpdateContentBlockTitle()
+ {
+ var configManager = ConfigManager.GetManager();
+ using (new ElevatedConfigModeRegion())
+ {
+ var toolboxConfig = configManager.GetSection();
+ var controlsToolbox = toolboxConfig.Toolboxes["PageControls"];
+ if (controlsToolbox != null)
+ {
+ var mvcWidgetsSection = controlsToolbox.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
+ if (mvcWidgetsSection != null)
+ {
+ var contentBlockTool = mvcWidgetsSection.Tools.FirstOrDefault(t => t.Name == "ContentBlock");
+ if (contentBlockTool != null)
+ {
+ contentBlockTool.Title = "Content Block";
+ configManager.SaveSection(toolboxConfig);
+ }
+ }
+ }
+ }
+
+ var pageManager = PageManager.GetManager();
+ using (new ElevatedModeRegion(pageManager))
+ {
+ var contentBlocks = pageManager.GetControls().Where(c => c.ObjectType == "Telerik.Sitefinity.Mvc.Proxy.MvcControllerProxy" && c.Caption == "ContentBlock").ToArray();
+ foreach (var contentBlock in contentBlocks)
+ {
+ contentBlock.Caption = "Content Block";
+ }
+
+ pageManager.SaveChanges();
+ }
+ }
+
+ // 1, 2, 180, 1
+ private static void RemoveMvcWidgetToolboxItems()
+ {
+ var configManager = ConfigManager.GetManager();
+ using (new ElevatedConfigModeRegion())
+ {
+ var toolboxConfig = configManager.GetSection();
+ var pageControls = toolboxConfig.Toolboxes["PageControls"];
+
+ foreach (var section in pageControls.Sections)
+ {
+ var widgets = section.Tools.Where(t => t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.", StringComparison.Ordinal) && !t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.DynamicContent", StringComparison.Ordinal)).ToArray();
+ foreach (ToolboxItem tool in widgets)
+ {
+ if (tool.Source == ConfigSource.Database)
+ section.Tools.Remove(tool);
+ }
+ }
+
+ var mvcWidgetsSection = pageControls.Sections.FirstOrDefault(s => s.Name == "MvcWidgets");
+ if (mvcWidgetsSection != null && mvcWidgetsSection.Source == ConfigSource.Database)
+ {
+ pageControls.Sections.Remove(mvcWidgetsSection);
+ }
+
+ configManager.SaveSection(toolboxConfig);
+ }
+ }
+
+ // 1, 2, 180, 1
+ private static void RenameDynamicContentMvcToolboxItems()
+ {
+ var configManager = ConfigManager.GetManager();
+ using (new ElevatedConfigModeRegion())
+ {
+ var toolboxConfig = configManager.GetSection();
+ var pageControls = toolboxConfig.Toolboxes["PageControls"];
+
+ foreach (var section in pageControls.Sections)
+ {
+ var widgets = section.Tools.Where(t => t.ControllerType.StartsWith("Telerik.Sitefinity.Frontend.DynamicContent", StringComparison.Ordinal)).ToArray();
+ foreach (ToolboxItem tool in widgets)
+ {
+ tool.CssClass = "sfNewsViewIcn sfMvcIcn";
+ var indexOfMvcSuffix = tool.Title.LastIndexOf(" MVC", StringComparison.Ordinal);
+ tool.Title = tool.Title.Substring(0, indexOfMvcSuffix);
+ }
+ }
+
+ configManager.SaveSection(toolboxConfig);
+ }
+ }
+
+ // 1, 2, 260, 1
+ private static void RecategorizePageTemplates()
+ {
+ var pageManager = PageManager.GetManager();
+ var layoutManager = new LayoutFileManager();
+
+ var customPageTemplates = pageManager.GetTemplates().Where(pt => pt.Category == SiteInitializer.CustomTemplatesCategoryId).ToArray();
+ foreach (var customPageTemplate in customPageTemplates)
+ {
+ var titleTokens = customPageTemplate.Title.ToString().Split('.');
+ if (titleTokens.Length > 1 && (new PackageManager()).PackageExists(titleTokens[0]))
+ {
+ customPageTemplate.Category = layoutManager.GetOrCreateTemplateCategoryId(titleTokens[0]);
+ }
+ }
+
+ pageManager.SaveChanges();
+ }
+
+ // 1, 2, 270, 1
+ private static void UpdatePageTemplates()
+ {
+ var pageManager = PageManager.GetManager();
+ var layoutFileManager = new LayoutFileManager();
+
+ var defaultPageTemplates = pageManager.GetTemplates().Where(pt => LayoutFileManager.DefaultTemplateNames.Contains(pt.Name)).ToArray();
+ foreach (var defaultPageTemplate in defaultPageTemplates)
+ {
+ // Renaming template title
+ var titleParts = defaultPageTemplate.Title.ToString().Split('.');
+ if (titleParts.Length > 1)
+ {
+ defaultPageTemplate.Title = titleParts[1];
+ }
+
+ // Adding icon to title
+ layoutFileManager.AttachImageToTemplate(defaultPageTemplate, pageManager, "default");
+ }
+
+ pageManager.SaveChanges();
+ }
+
+ // 1, 2, 280, 2
+ private static void CreateDefaultTemplates()
+ {
+ var layoutManager = new LayoutFileManager();
+
+ var pageManager = PageManager.GetManager();
+ var defaultPageTemplates = pageManager.GetTemplates().Where(pt => LayoutFileManager.DefaultTemplateNames.Contains(pt.Name));
+ foreach (var template in defaultPageTemplates)
+ {
+ if (string.Equals(LayoutFileManager.BootstrapDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
+ layoutManager.CreateDefaultTemplates("Bootstrap", "default");
+ else if (string.Equals(LayoutFileManager.FoundationDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
+ layoutManager.CreateDefaultTemplates("Foundation", "default");
+ else if (string.Equals(LayoutFileManager.SemanticUIDefaultTemplateName, template.Name, StringComparison.OrdinalIgnoreCase))
+ layoutManager.CreateDefaultTemplates("SemanticUI", "default");
+ }
+ }
+
+ private static void UpdateDefaultTemplateImages()
+ {
+ var librariesManager = LibrariesManager.GetManager("SystemLibrariesProvider");
+ if (librariesManager != null)
+ {
+ var suppressSecurityChecks = librariesManager.Provider.SuppressSecurityChecks;
+ try
+ {
+ librariesManager.Provider.SuppressSecurityChecks = true;
+ LayoutFileManager.UpdateDefaultTemplateImages(librariesManager);
+ }
+ finally
+ {
+ librariesManager.Provider.SuppressSecurityChecks = suppressSecurityChecks;
+ }
+ }
+ }
+
+ // 1, 3, 320, 0
+ private static void UpdateGridWidgetsToolbox()
+ {
+ FrontendModuleUpgrader.TransferGridWidgetSectionToDefault("BootstrapGrids");
+ FrontendModuleUpgrader.TransferGridWidgetSectionToDefault("FoundationGrids");
+ FrontendModuleUpgrader.TransferGridWidgetSectionToDefault("SemanticUIGrids");
+ }
+
+ private static void TransferGridWidgetSectionToDefault(string sectionName)
+ {
+ var layoutConfig = Config.Get().Toolboxes["PageLayouts"];
+ var section = layoutConfig.Sections.FirstOrDefault(e => e.Name == sectionName);
+ if (section != null)
+ {
+ var registrator = new GridWidgetRegistrator();
+ foreach (var tool in section.Tools)
+ {
+ if (tool.LayoutTemplate.IsNullOrEmpty())
+ continue;
+
+ registrator.RegisterToolboxItem(System.Web.VirtualPathUtility.GetFileName(tool.LayoutTemplate));
+ }
+
+ var configurationManager = ConfigManager.GetManager();
+ using (new ElevatedConfigModeRegion())
+ {
+ var toolboxesConfig = configurationManager.GetSection();
+ var pageControls = toolboxesConfig.Toolboxes["PageLayouts"];
+
+ var sectionToDelete = pageControls.Sections.FirstOrDefault(e => e.Name == sectionName);
+ if (sectionToDelete != null && sectionToDelete.Source == ConfigSource.Database)
+ pageControls.Sections.Remove(sectionToDelete);
+
+ configurationManager.SaveSection(toolboxesConfig);
+ }
+ }
+ }
+
+ // 1, 3, 320, 0
+ private static void UpdateGridWidgetPaths()
+ {
+ const int BATCH = 200;
+
+ var pathPairs = new Dictionary()
+ {
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-11+5.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12+4.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-5+11.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+12.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-five-cols.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-four-cols.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-one-col.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-three-cols.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html" },
+ { "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-two-cols.html", "~/Frontend-Assembly/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html" }
+ };
+
+ var pageManager = PageManager.GetManager();
+ var oldPaths = pathPairs.Keys.ToArray();
+
+ var layoutControlIds = pageManager.GetControls().Where(c => c.IsLayoutControl).Select(c => c.Id).ToArray();
+ var currentControl = 0;
+ while (currentControl < layoutControlIds.Length)
+ {
+ var batchArray = layoutControlIds.Skip(currentControl).Take(BATCH).ToArray();
+
+ var propertiesToUpdate = pageManager.GetProperties()
+ .Where(p => batchArray.Contains(p.Control.Id))
+ .Where(p => oldPaths.Contains(p.Value))
+ .ToArray();
+
+ foreach (var property in propertiesToUpdate)
+ property.Value = pathPairs[property.Value];
+
+ if (propertiesToUpdate.Length > 0)
+ pageManager.SaveChanges();
+
+ currentControl += BATCH;
+ }
+ }
+
+ // 1, 7, 600, 0
+ private static void UpgradeLimitCountProperty(SiteInitializer initializer)
+ {
+ var pageMan = initializer.PageManager;
+ string[] controllersList =
+ {
+ "Telerik.Sitefinity.Frontend.Blogs.Mvc.Controllers.BlogController",
+ "Telerik.Sitefinity.Frontend.Blogs.Mvc.Controllers.BlogPostController",
+ "Telerik.Sitefinity.Frontend.News.Mvc.Controllers.NewsController",
+ "Telerik.Sitefinity.Frontend.Events.Mvc.Controllers.EventController",
+ "Telerik.Sitefinity.Frontend.Media.Mvc.Controllers.ImageGalleryController",
+ "Telerik.Sitefinity.Frontend.Media.Mvc.Controllers.VideoGalleryController",
+ "Telerik.Sitefinity.Frontend.Media.Mvc.Controllers.DocumentsListController",
+ "Telerik.Sitefinity.Frontend.DynamicContent.Mvc.Controllers.DynamicContentController",
+ "Telerik.Sitefinity.Frontend.Identity.Mvc.Controllers.UsersListController",
+ "Telerik.Sitefinity.Frontend.Search.Mvc.Controllers.SearchResultsController"
+ };
+
+ foreach (var controller in controllersList)
+ {
+ var controlIds = pageMan.GetProperties()
+ .Where(x => x.Name == "ControllerName" && x.Value == controller)
+ .Select(x => x.Control.Id).ToList();
+
+ foreach (var controlId in controlIds)
+ {
+ var settingids = pageMan.GetProperties().Where(x => x.Name == "Settings" && x.Control.Id == controlId).Select(x => x.Id).ToList();
+ foreach (var settingId in settingids)
+ {
+ var models = pageMan.GetProperties().Where(x => x.Name == "Model" && x.ParentProperty.Id == settingId).ToList();
+ foreach (var model in models)
+ {
+ var itemsPerPage = pageMan.GetProperties().Where(x => x.Name == "ItemsPerPage" && x.ParentProperty.Id == model.Id).FirstOrDefault();
+ if (itemsPerPage != null)
+ {
+ var limitCount = pageMan.GetProperties().Where(x => x.Name == "LimitCount" && x.ParentProperty.Id == model.Id).FirstOrDefault();
+ if (limitCount == null)
+ {
+ limitCount = pageMan.CreateProperty();
+ pageMan.CopyProperty(itemsPerPage, limitCount);
+ limitCount.Name = "LimitCount";
+ limitCount.Language = itemsPerPage.Language;
+ limitCount.ParentProperty = model;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // 14.1
+ private static void CreateBootstrap5Templates()
+ {
+ var layoutManager = new LayoutFileManager();
+
+ layoutManager.CreateDefaultTemplates("Bootstrap5", "default");
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/GlobalSuppressions.cs b/Telerik.Sitefinity.Frontend/GlobalSuppressions.cs
index f39cd2f55..eec0e2562 100644
Binary files a/Telerik.Sitefinity.Frontend/GlobalSuppressions.cs and b/Telerik.Sitefinity.Frontend/GlobalSuppressions.cs differ
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/GridControl.cs b/Telerik.Sitefinity.Frontend/GridSystem/GridControl.cs
index dd4872547..03cb18e0b 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/GridControl.cs
+++ b/Telerik.Sitefinity.Frontend/GridSystem/GridControl.cs
@@ -1,16 +1,21 @@
-using System;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Web.Hosting;
-using System.Web.UI;
-using Telerik.Sitefinity.Configuration;
-using Telerik.Sitefinity.Localization;
-using Telerik.Sitefinity.Modules.Newsletters;
-using Telerik.Sitefinity.Utilities.HtmlParsing;
-using Telerik.Sitefinity.Utilities.TypeConverters;
-using Telerik.Sitefinity.Web;
-using Telerik.Sitefinity.Web.Configuration;
+using System;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Web.Hosting;
+using System.Web.UI;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
+using Telerik.Sitefinity.Frontend.Mvc.StringResources;
+using Telerik.Sitefinity.Localization;
+using Telerik.Sitefinity.Modules.Newsletters;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Utilities.HtmlParsing;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.Web.Configuration;
using Telerik.Sitefinity.Web.UI;
namespace Telerik.Sitefinity.Frontend.GridSystem
@@ -18,6 +23,7 @@ namespace Telerik.Sitefinity.Frontend.GridSystem
///
/// The LayoutControl comprises the basic building block of Sitefinity layouts. GridControl adds the ability to use pure HTML templates.
///
+ [RequiresEmbeddedWebResource("Telerik.Sitefinity.Resources.Themes.LayoutsBasics.css", "Telerik.Sitefinity.Resources.Reference")]
public class GridControl : LayoutControl
{
///
@@ -25,6 +31,12 @@ public class GridControl : LayoutControl
///
protected override ITemplate GetTemplate()
{
+ if (SystemManager.GetModule("Feather") == null)
+ {
+ var markup = this.ProcessLayoutString(Res.Get().GridDoesNotWork, ensureSfColsWrapper: true);
+ return ControlUtilities.GetTemplate(null, markup.GetHashCode().ToString(CultureInfo.InvariantCulture), null, markup);
+ }
+
var layout = this.Layout;
bool isVirtualPath = layout.StartsWith("~/", StringComparison.Ordinal);
bool isHtmlTemplate = layout.EndsWith(".html", StringComparison.OrdinalIgnoreCase) || layout.EndsWith(".htm", StringComparison.Ordinal);
@@ -45,7 +57,6 @@ protected virtual string ProcessLayoutString(string targetTemplate, bool ensureS
parser.AutoExtractBetweenTagsOnly = false;
parser.CompressWhiteSpaceBeforeTag = false;
parser.KeepRawHTML = true;
- bool hasSfCols = false;
var output = new StringBuilder();
HtmlChunk chunk;
while ((chunk = parser.ParseNext()) != null)
@@ -57,9 +68,7 @@ protected virtual string ProcessLayoutString(string targetTemplate, bool ensureS
if (cssClass != null)
{
var classes = cssClass.Split(new char[] { ' ' });
- var chunkHasSfCols = classes.Contains("sf_cols", StringComparer.Ordinal);
- hasSfCols = hasSfCols || chunkHasSfCols;
- if (chunkHasSfCols ||
+ if (classes.Contains("sf_cols", StringComparer.Ordinal) ||
classes.Contains("sf_colsIn", StringComparer.Ordinal) ||
classes.Contains("sf_colsOut", StringComparer.Ordinal))
{
@@ -72,7 +81,7 @@ protected virtual string ProcessLayoutString(string targetTemplate, bool ensureS
output.Append(modified ? chunk.GenerateHtml() : chunk.Html);
}
- if (!hasSfCols && ensureSfColsWrapper)
+ if (ensureSfColsWrapper)
{
return "
" + output.ToString() + "
";
}
@@ -93,8 +102,8 @@ protected virtual ITemplate GetTemplate(bool isVirtualPath, bool isHtmlTemplate,
if (isVirtualPath && isHtmlTemplate)
{
if (!HostingEnvironment.VirtualPathProvider.FileExists(layout))
- {
- throw new ArgumentException(Res.Get("CannotFindTemplate", layout));
+ {
+ throw new ArgumentException(Res.Get("CannotFindTemplateMvcForm", layout));
}
using (var reader = new StreamReader(HostingEnvironment.VirtualPathProvider.GetFile(layout).Open()))
@@ -119,10 +128,10 @@ protected virtual ITemplate GetTemplate(bool isVirtualPath, bool isHtmlTemplate,
}
// Add sf_cols wrapper for back end pages and email campaigns.
- var currentNode = SiteMapBase.GetActualCurrentNode();
- var rootNode = currentNode != null ? currentNode.RootNode as PageSiteNode : null;
- var ensureSfColsWrapper = this.IsBackend() || rootNode == null || rootNode.Id == NewslettersModule.standardCampaignRootNodeId ||
- System.Web.HttpContext.Current.Items[SiteMapBase.CurrentNodeKey] == null;
+ var ensureSfColsWrapper = (this.IsBackend() && !SystemManager.IsPreviewMode)
+ || SiteMapBase.GetActualCurrentNode()?.RootNodeId == NewslettersModule.standardCampaignRootNodeId
+ || System.Web.HttpContext.Current.Items[SiteMapBase.CurrentNodeKey] == null;
+
layout = this.ProcessLayoutString(layout, ensureSfColsWrapper);
return ControlUtilities.GetTemplate(null, layout.GetHashCode().ToString(System.Globalization.CultureInfo.InvariantCulture), null, layout);
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/GridControlToolboxFilter.cs b/Telerik.Sitefinity.Frontend/GridSystem/GridControlToolboxFilter.cs
new file mode 100644
index 000000000..ce019848b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/GridControlToolboxFilter.cs
@@ -0,0 +1,115 @@
+using System;
+using Telerik.Sitefinity.Configuration;
+using Telerik.Sitefinity.DesignerToolbox;
+using Telerik.Sitefinity.Frontend.Mvc.Helpers;
+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.Pages.Model;
+using Telerik.Sitefinity.Services;
+using Telerik.Sitefinity.Utilities.TypeConverters;
+using Telerik.Sitefinity.Web.UI;
+
+namespace Telerik.Sitefinity.Frontend.GridSystem
+{
+ ///
+ /// Determines which layout controls should be visible in the toolbox of the .
+ ///
+ internal class GridControlToolboxFilter : IToolboxFilter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Function that returns the Framework for the current context.
+ public GridControlToolboxFilter(Func frameworkExtractor)
+ {
+ this.frameworkExtractor = frameworkExtractor;
+ }
+
+ ///
+ /// Determines whether a toolbox section should be visible in the .
+ ///
+ /// The section in question.
+ /// The filter context passed externally.
+ /// true if it should be visible.
+ public bool IsSectionVisible(IToolboxSection section, IToolboxFilterContext context)
+ {
+ if (section != null && context != null && section.Name == GridWidgetRegistrator.GridSectionName && section.Title == GridWidgetRegistrator.GridSectionTitle)
+ return SystemManager.GetModule("Feather") != null && context.ContainerId == "LayoutToolboxContainer";
+
+ return true;
+ }
+
+ ///
+ /// Determines whether a tool (a.k.a toolbox item) should be visible in the .
+ ///
+ /// The tool in question.
+ /// true if it should be visible.
+ public bool IsToolVisible(IToolboxItem tool)
+ {
+ if (tool == null)
+ return true;
+
+ var currentFramework = this.frameworkExtractor();
+
+ // Pages: Everything works for Hybrid mode. No need to reflect on the specific item.
+ // Newsletters: Always run in Hybrid mode. Message body type needs check.
+ if (currentFramework == PageTemplateFramework.Hybrid)
+ {
+ var currentHttpContext = SystemManager.CurrentHttpContext;
+
+ if (NewslettersHelper.IsMvcNewsletter())
+ {
+ currentFramework = PageTemplateFramework.Mvc;
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ if (!tool.ControlType.IsNullOrEmpty())
+ {
+ var controlType = TypeResolutionService.ResolveType(tool.ControlType, throwOnError: false);
+ if (controlType == null)
+ return true;
+
+ var isLayoutControl = typeof(LayoutControl).IsAssignableFrom(controlType);
+ var isGridControl = typeof(GridControl).IsAssignableFrom(controlType);
+
+ if (currentFramework == PageTemplateFramework.WebForms && isGridControl)
+ return false;
+
+ if (currentFramework == PageTemplateFramework.Mvc && isLayoutControl && !isGridControl)
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool IsNewslettersModuleEnabled
+ {
+ get
+ {
+ if (this.isNewslettersModuleEnabled == null)
+ {
+ try
+ {
+ Config.Get();
+ this.isNewslettersModuleEnabled = true;
+ }
+ catch (Exception)
+ {
+ this.isNewslettersModuleEnabled = false;
+ }
+ }
+
+ return this.isNewslettersModuleEnabled.Value;
+ }
+ }
+
+ private bool? isNewslettersModuleEnabled;
+ private readonly Func frameworkExtractor;
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/GridWidgetRegistrator.cs b/Telerik.Sitefinity.Frontend/GridSystem/GridWidgetRegistrator.cs
index 310caf38f..c39967fff 100644
--- a/Telerik.Sitefinity.Frontend/GridSystem/GridWidgetRegistrator.cs
+++ b/Telerik.Sitefinity.Frontend/GridSystem/GridWidgetRegistrator.cs
@@ -20,35 +20,68 @@ internal class GridWidgetRegistrator
/// Registers the grid widget in toolbox.
///
/// Name of the file.
- /// Name of the package.
/// Old name of the file.
- public void RegisterToolboxItem(string fileName, string packageName, string oldFileName = "")
+ public void RegisterToolboxItem(string fileName, string oldFileName = "")
{
- var configManager = ConfigManager.GetManager();
- using (new ElevatedConfigModeRegion())
+ ConfigManager configManager = null;
+ var toolboxConfig = Config.Get();
+ var layoutsToolbox = toolboxConfig.Toolboxes[ToolboxesConfig.LayoutsToolboxName];
+ var htmlLayoutsSection = layoutsToolbox.Sections.FirstOrDefault(s => s.Name == GridWidgetRegistrator.GridSectionName);
+ if (htmlLayoutsSection == null)
+ {
+ configManager = ConfigManager.GetManager();
+ toolboxConfig = configManager.GetSection();
+ layoutsToolbox = toolboxConfig.Toolboxes[ToolboxesConfig.LayoutsToolboxName];
+ htmlLayoutsSection = this.CreateToolBoxSection(layoutsToolbox, GridWidgetRegistrator.GridSectionName, GridWidgetRegistrator.GridSectionTitle);
+ }
+
+ var layoutControl = this.CreateGridControlsData(fileName);
+
+ string toolboxItemName;
+ if (oldFileName.IsNullOrEmpty())
+ {
+ toolboxItemName = layoutControl.Name;
+ }
+ else
{
- var toolboxConfig = configManager.GetSection();
- var sectionName = packageName + GridWidgetRegistrator.GridSectionNameSuffix;
- var sectionTitle = packageName + GridWidgetRegistrator.GridSectionTitleSuffix;
- var htmlLayoutsSection = this.GetOrCreateToolBoxSection(toolboxConfig, sectionName, sectionTitle);
+ var oldFileNameWithoutExtension = this.GetFileNameWithoutExtension(oldFileName);
+ toolboxItemName = oldFileNameWithoutExtension;
+ }
- var layoutControl = this.CreateGridControlsData(fileName);
- this.AddOrRenameGridControl(htmlLayoutsSection.Tools, layoutControl, oldFileName);
+ ToolboxItem toolboxItem = null;
+ if (configManager == null)
+ toolboxItem = htmlLayoutsSection.Tools.FirstOrDefault(t => t.Name == toolboxItemName);
- configManager.SaveSection(toolboxConfig);
+ if (toolboxItem == null || (toolboxItem.Name != layoutControl.Name || toolboxItem.Title != layoutControl.Title || toolboxItem.LayoutTemplate != layoutControl.LayoutTemplatePath))
+ {
+ if (configManager == null)
+ {
+ configManager = ConfigManager.GetManager();
+ toolboxConfig = configManager.GetSection();
+ layoutsToolbox = toolboxConfig.Toolboxes[ToolboxesConfig.LayoutsToolboxName];
+ htmlLayoutsSection = layoutsToolbox.Sections.FirstOrDefault(s => s.Name == GridWidgetRegistrator.GridSectionName);
+ }
+
+ this.AddOrRenameGridControl(htmlLayoutsSection, layoutControl, toolboxItemName);
+ }
+
+ if (configManager != null)
+ {
+ using (new ElevatedConfigModeRegion())
+ configManager.SaveSection(toolboxConfig);
}
}
///
/// Unregisters the toolbox item.
///
- /// Name of the content type.
- public virtual void UnregisterToolboxItem(string fileName, string packageName)
+ /// Name of the content type.
+ public virtual void UnregisterToolboxItem(string fileName)
{
var configurationManager = ConfigManager.GetManager();
var toolboxesConfig = configurationManager.GetSection();
var pageControls = toolboxesConfig.Toolboxes["PageLayouts"];
- var sectionName = packageName + GridWidgetRegistrator.GridSectionNameSuffix;
+ var sectionName = GridWidgetRegistrator.GridSectionName;
var section = pageControls.Sections.Where(e => e.Name == sectionName).FirstOrDefault();
if (section != null)
@@ -56,12 +89,12 @@ public virtual void UnregisterToolboxItem(string fileName, string packageName)
var fileNameWithoutExtension = this.GetFileNameWithoutExtension(fileName);
var itemToDelete = section.Tools.FirstOrDefault(e => e.Name == fileNameWithoutExtension);
- if (itemToDelete != null)
+ if (itemToDelete != null && itemToDelete.Source == ConfigSource.Database)
{
section.Tools.Remove(itemToDelete);
}
- if (!section.Tools.Any())
+ if (!section.Tools.Any() && section.Source == ConfigSource.Database)
{
pageControls.Sections.Remove(section);
}
@@ -82,7 +115,7 @@ public void UpdateControlData(string newFileName, string oldFileName)
var baseTemplatePath = string.Format(
CultureInfo.InvariantCulture,
GridWidgetRegistrator.GridFolderPathStringTemplate,
- FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(FrontendService).Assembly));
+ FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(FrontendModule).Assembly));
var properties = pageManger.GetProperties().Where(prop => prop.Name == "Layout" && prop.Value == baseTemplatePath + oldFileName).ToList();
var newCaption = this.GetFileNameWithoutExtension(newFileName);
@@ -102,25 +135,19 @@ public void UpdateControlData(string newFileName, string oldFileName)
///
/// Gets existing or create a tool box section for the grid controls.
///
- /// The toolbox configuration.
+ /// The toolbox configuration.
/// Name of the section.
/// The section title.
- ///
- protected virtual ToolboxSection GetOrCreateToolBoxSection(ToolboxesConfig toolboxConfig, string sectionName, string sectionTitle)
+ /// The section.
+ protected virtual ToolboxSection CreateToolBoxSection(Toolbox toolbox, string sectionName, string sectionTitle)
{
- var layoutsToolbox = toolboxConfig.Toolboxes["PageLayouts"];
+ var htmlLayoutsSection = new ToolboxSection(toolbox.Sections);
+ htmlLayoutsSection.Name = sectionName;
- var htmlLayoutsSection = layoutsToolbox.Sections.FirstOrDefault(s => s.Name == sectionName);
- if (htmlLayoutsSection == null)
- {
- htmlLayoutsSection = new ToolboxSection(layoutsToolbox.Sections);
- htmlLayoutsSection.Name = sectionName;
+ /// TODO: Set resource class id and use resource keys for Title and Description.
+ htmlLayoutsSection.Title = sectionTitle;
- /// TODO: Set resource class id and use resource keys for Title and Description.
- htmlLayoutsSection.Title = sectionTitle;
-
- layoutsToolbox.Sections.Add(htmlLayoutsSection);
- }
+ toolbox.Sections.Add(htmlLayoutsSection);
return htmlLayoutsSection;
}
@@ -128,34 +155,25 @@ protected virtual ToolboxSection GetOrCreateToolBoxSection(ToolboxesConfig toolb
///
/// Adds or renames the grid control.
///
- /// The parent.
+ /// The parent toolbox section.
/// The data.
- /// Old name of the file.
+ /// Old name of the file.
/// data
- protected virtual void AddOrRenameGridControl(ConfigElementList parent, GridControlData data, string oldFileName = "")
+ protected virtual void AddOrRenameGridControl(ToolboxSection section, GridControlData data, string toolboxItemName = default(string))
{
if (data == null)
throw new ArgumentNullException("data");
- string toolboxItemName;
- if (oldFileName.IsNullOrEmpty())
- {
- toolboxItemName = data.Name;
- }
- else
- {
- var oldFileNameWithoutExtension = this.GetFileNameWithoutExtension(oldFileName);
- toolboxItemName = oldFileNameWithoutExtension;
- }
+ var tools = section.Tools;
- var control = parent.FirstOrDefault(t => t.Name == toolboxItemName);
+ var control = tools.FirstOrDefault(t => t.Name == toolboxItemName);
if (control == null)
{
- control = new ToolboxItem(parent);
+ control = new ToolboxItem(tools);
control.ControlType = typeof(GridControl).AssemblyQualifiedName;
control.CssClass = data.CssClass;
- parent.Add(control);
+ tools.Add(control);
}
control.Name = data.Name;
@@ -173,7 +191,7 @@ protected GridControlData CreateGridControlsData(string fileName)
var baseTemplatePath = string.Format(
System.Globalization.CultureInfo.InvariantCulture,
GridWidgetRegistrator.GridFolderPathStringTemplate,
- FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(FrontendService).Assembly));
+ FrontendManager.VirtualPathBuilder.GetVirtualPath(typeof(FrontendModule).Assembly));
var fileNameWithoutExtension = this.GetFileNameWithoutExtension(fileName);
var cssClass = this.GetInferredCssClass(fileNameWithoutExtension);
@@ -243,14 +261,14 @@ private string GetInferredCssClass(string fileNameWithoutExtension)
public const string GridFolderPathStringTemplate = "~/{0}GridSystem/Templates/";
///
- /// The grid section name suffix
+ /// The grid section name.
///
- public const string GridSectionNameSuffix = "Grids";
+ public const string GridSectionName = "Grids";
///
- /// The grid section title suffix
+ /// The grid section title.
///
- public const string GridSectionTitleSuffix = " grid widgets";
+ public const string GridSectionTitle = "Grid widgets";
#endregion
}
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/container.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/container.html
new file mode 100644
index 000000000..5f1bc9d4d
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/container.html
@@ -0,0 +1,2 @@
+
+
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12.html
new file mode 100644
index 000000000..daae99815
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-12.html
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 000000000..db3f65f78
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-2+3+2+3+2.html
@@ -0,0 +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
new file mode 100644
index 000000000..97e08843b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+3+3+3.html
@@ -0,0 +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
new file mode 100644
index 000000000..a0d9f0538
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+6+3.html
@@ -0,0 +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
new file mode 100644
index 000000000..7e9733a7b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-3+9.html
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html
new file mode 100644
index 000000000..262d0bcce
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+4+4.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 000000000..f4b2485f3
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-4+8.html
@@ -0,0 +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
new file mode 100644
index 000000000..d392b690b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-6+6.html
@@ -0,0 +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
new file mode 100644
index 000000000..5fa5e715b
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-8+4.html
@@ -0,0 +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
new file mode 100644
index 000000000..c43e44cc8
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/GridSystem/Templates/grid-9+3.html
@@ -0,0 +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/InterfaceMappings.cs b/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
index baccfba32..147979f5c 100644
--- a/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
+++ b/Telerik.Sitefinity.Frontend/InterfaceMappings.cs
@@ -12,6 +12,7 @@ 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
index ef5f4139e..8c537930a 100644
--- a/Telerik.Sitefinity.Frontend/Modules/Comments/ReviewNotificationStrategy.cs
+++ b/Telerik.Sitefinity.Frontend/Modules/Comments/ReviewNotificationStrategy.cs
@@ -4,6 +4,7 @@
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
{
@@ -16,42 +17,33 @@ internal class ReviewNotificationStrategy : CommentNotificationsStrategy, IComme
/// Gets the message template unique identifier for the specified event.
///
/// The event.
- protected override Guid GetMessageTemplateId(ICommentEvent @event)
+ protected override IMessageTemplateRequest GetMessageTemplate(ICommentEvent @event)
{
- IComment comment = @event.Item;
+ if (@event == null || @event.Item == null)
+ return null;
+ IMessageTemplateRequest messageTemplate;
+ IComment comment = @event.Item;
ICommentService cs = SystemManager.GetCommentsService();
IThread thread = cs.GetThread(comment.ThreadKey);
- var ns = SystemManager.GetNotificationService();
- Guid messageTemplateId;
-
if (this.IsReviewThread(thread))
{
- messageTemplateId = ns.GetMessageTemplates(this.ServiceContext, null)
- .Where(mt => mt.Subject == "A new review was posted")
- .Select(m => m.Id).FirstOrDefault();
+ messageTemplate = this.GetNewReviewMessageTemplate();
}
else
{
- messageTemplateId = ns.GetMessageTemplates(this.ServiceContext, null)
- .Where(mt => mt.Subject == "A new comment was posted")
- .Select(m => m.Id).FirstOrDefault();
- }
-
- if (messageTemplateId == Guid.Empty)
- {
- messageTemplateId = base.GetMessageTemplateId(@event);
+ messageTemplate = this.GetNewCommentMessageTemplate();
}
- return messageTemplateId;
+ return messageTemplate;
}
private bool IsReviewThread(IThread thread)
{
var isReview = false;
- if (thread.Behavior == null)
+ if (thread.Behavior.IsNullOrEmpty())
{
isReview = thread.Key.EndsWith("_review", StringComparison.Ordinal);
}
diff --git a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js
index d711734e7..a167d157b 100644
--- a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js
+++ b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/Scripts/MvcControlTemplateEditor.js
@@ -13,11 +13,12 @@ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEd
initialize: function () {
Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "initialize");
- this._containerToHide = jQuery(this._otherPropertiesContainer).parent();
+ this._containerToHide = this.getInsertSectionContainer();
+ this._containerToHide.hide();
},
dispose: function () {
- Telerik.Sitefinity.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "dispose");
+ Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.UI.MvcControlTemplateEditor.callBaseMethod(this, "dispose");
},
_valueChangedHandler: function (sender, args) {
diff --git a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs
index 96bb11b41..ab0b5c5b3 100644
--- a/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs
+++ b/Telerik.Sitefinity.Frontend/Modules/ControlTemplates/Web/UI/MvcControlTemplateEditor.cs
@@ -84,6 +84,6 @@ private List GetRestoreButtonsClientIds()
return returnResult;
}
- private const string MvcControlTemplateEditorScript = "Telerik.Sitefinity.Frontend.Modules.ControlTemplates.Web.Scripts.MvcControlTemplateEditor.js";
+ 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 6d7c62be1..c3941a4e4 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/ContentPagerController.cs
@@ -1,10 +1,11 @@
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
@@ -12,7 +13,8 @@ namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
///
/// This class represents controller for pager widget.
///
- [ControllerMetadataAttribute(IsTemplatableControl = false)]
+ [Localization(typeof(ContentPagerResources))]
+ [ControllerMetadata(IsTemplatableControl = false)]
public class ContentPagerController : Controller
{
///
@@ -21,11 +23,12 @@ 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.
///
[OutputCache(Duration = 1)]
- public PartialViewResult Index(int currentPage, int totalPagesCount, string redirectUrlTemplate)
+ 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;
@@ -66,7 +69,66 @@ public PartialViewResult Index(int currentPage, int totalPagesCount, string redi
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 e5237c41e..67be1f5a7 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Controllers/DesignerController.cs
@@ -8,6 +8,8 @@
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;
@@ -21,23 +23,26 @@ namespace Telerik.Sitefinity.Frontend.Mvc.Controllers
[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 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, Guid.Parse(controlId));
+ var model = this.GetModel(widgetName, Guid.Parse(controlId), moduleName, (DesignMediaType)Enum.Parse(typeof(DesignMediaType), mediaType));
return this.View(DesignerController.DefaultView, model);
}
@@ -54,15 +59,17 @@ public virtual ActionResult Master(string widgetName)
{
this.GetHttpContext().Items[SystemManager.IsBackendRequestKey] = true;
- var viewName = DesignerController.DesignerViewTemplate.Arrange(viewType);
+ 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();
+ var model = this.GetViewModel(designMediaType);
// Passing the DesignerModel to the view model
var controlIdParam = this.GetControlIdParam();
if (controlIdParam.HasValue)
{
- ViewBag.DesignerModel = this.GetModel(widgetName, controlIdParam.Value);
+ ViewBag.DesignerModel = this.GetModel(widgetName, controlIdParam.Value, designMediaType: designMediaType);
}
return this.PartialView(viewName, model);
@@ -97,21 +104,25 @@ protected virtual HttpContextBase GetHttpContext()
///
/// Gets the model of the designer.
///
- private IDesignerModel GetModel(string widgetName, Guid controlId)
- {
+ 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 },
- { "controlId", controlId },
- { "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()
+ private Control GetViewModel(DesignMediaType designMediaType)
{
var controlIdParam = this.GetControlIdParam();
@@ -119,7 +130,7 @@ private Control GetViewModel()
return null;
var controlId = controlIdParam.Value;
- var manager = PageManager.GetManager();
+ var manager = this.GetControlManager(designMediaType);
var viewModel = manager.LoadControl(manager.GetControl(controlId));
var widgetProxy = viewModel as MvcWidgetProxy;
@@ -134,6 +145,18 @@ private Control GetViewModel()
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/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
index 059ef207b..f3eacb5a7 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/CommentsHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/CommentsHelpers.cs
@@ -34,6 +34,12 @@ public static MvcHtmlString CommentsCount(this HtmlHelper helper, string navigat
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;
@@ -151,13 +157,10 @@ private static string GetDataSourceName(IDataItem item)
if (item != null && item is DynamicContent)
{
- var moduleProvider = ModuleBuilderManager.GetManager().Provider;
- var itemType = item.GetType().FullName;
+ var itemType = item.GetType();
- var dynamicContentType = moduleProvider.GetDynamicModules()
- .Where(m => m.Status == DynamicModuleStatus.Active)
- .Join(moduleProvider.GetDynamicModuleTypes().Where(t => string.Concat(t.TypeNamespace, ".", t.TypeName) == itemType), m => m.Id, t => t.ParentModuleId, (m, t) => t)
- .FirstOrDefault();
+ var dynamicContentType = ModuleBuilderManager.GetActiveTypes()
+ .FirstOrDefault(t => t.TypeNamespace == itemType.Namespace && t.TypeName == itemType.Name);
if (dynamicContentType != null)
{
@@ -205,6 +208,11 @@ private static string GetTitle(IDataItem item)
}
}
+ if (NewslettersHelper.IsNewsletter())
+ {
+ allowComments = false;
+ }
+
return allowComments;
}
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/HtmlHelperExtensions.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
index 028f2d2aa..6bcf78465 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HtmlHelperExtensions.cs
@@ -1,142 +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 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));
- }
- }
- }
+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
index bba497f32..f715d3919 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/HyperLinkHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/HyperLinkHelpers.cs
@@ -1,16 +1,21 @@
using System;
-using System.Linq;
-using System.Threading;
using System.Web;
-using System.Web.Mvc;
+using System.Linq;
using Telerik.Sitefinity.Abstractions;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers;
+using Telerik.Sitefinity.Data.Linq.Dynamic;
using Telerik.Sitefinity.Frontend.Mvc.Models;
-using Telerik.Sitefinity.Localization.UrlLocalizationStrategies;
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
{
@@ -82,6 +87,60 @@ public static string GetDetailPageUrl(ItemViewModel item, Guid detailsPageId, bo
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.
///
@@ -109,9 +168,102 @@ public static string GetFullPageUrl(Guid pageId)
{
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 2a7998c39..73aace273 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/InlineEditingHtmlHelpers.cs
@@ -2,8 +2,10 @@
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
@@ -47,10 +49,10 @@ public static IHtmlString TextField(this HtmlHelper helper, string propName, str
{
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.
///
@@ -62,14 +64,14 @@ public static IHtmlString TextField(this HtmlHelper helper, string propName, 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);
}
@@ -85,6 +87,11 @@ public static IHtmlString InlineEditingAttributes(this HtmlHelper htmlHelper, st
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);
@@ -122,7 +129,7 @@ public static IHtmlString InlineEditingFieldAttributes(this HtmlHelper htmlHelpe
}
///
- /// Returns if the inline editin section should be rendered.
+ /// Returns if the inline editing section should be rendered.
///
/// The HTML helper.
///
@@ -150,5 +157,31 @@ public static bool ShouldRenderInlineEditing(this HtmlHelper htmlHelper)
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/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 03ff4c720..8008231e8 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ResourceHelper.cs
@@ -3,8 +3,12 @@
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;
@@ -12,6 +16,7 @@
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
@@ -21,11 +26,71 @@ namespace Telerik.Sitefinity.Frontend.Mvc.Helpers
///
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
///
@@ -35,7 +100,26 @@ public static class ResourceHelper
///
public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath)
{
- return ResourceHelper.Script(helper, scriptPath, null, false);
+ 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);
}
///
@@ -44,6 +128,7 @@ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath)
/// 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
///
@@ -63,6 +148,7 @@ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, st
/// 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
///
@@ -72,10 +158,95 @@ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, st
///
public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, string sectionName, bool throwException)
{
- if (ResourceHelper.TryConfigureScriptManager(scriptPath, helper.ViewContext.HttpContext.CurrentHandler))
+ 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);
+ 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);
}
///
@@ -86,12 +257,32 @@ public static MvcHtmlString Script(this HtmlHelper helper, string scriptPath, st
///
/// 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);
+ 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);
}
///
@@ -103,6 +294,7 @@ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptRefer
/// 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
///
@@ -111,6 +303,25 @@ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptRefer
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.
///
@@ -121,12 +332,48 @@ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptRefer
/// 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)
+ 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 (ResourceHelper.TryConfigureScriptManager(scriptReference, helper.ViewContext.HttpContext.CurrentHandler))
+ 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));
@@ -136,12 +383,32 @@ public static MvcHtmlString Script(this HtmlHelper helper, ScriptRef scriptRefer
foreach (var script in references)
{
var resourceUrl = script.GetResourceUrl();
- outputMarkup.Append(ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourceUrl, ResourceType.Js, sectionName, throwException));
+ 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.
///
@@ -155,6 +422,20 @@ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePa
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.
///
@@ -181,7 +462,23 @@ public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePa
///
public static MvcHtmlString StyleSheet(this HtmlHelper helper, string resourcePath, string sectionName, bool throwException)
{
- return ResourceHelper.RegisterResource(helper.ViewContext.HttpContext, resourcePath, ResourceType.Css, sectionName, 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);
}
///
@@ -226,6 +523,21 @@ public static MvcHtmlString CodeMirrorScriptReferences(this HtmlHelper helper)
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.
///
@@ -233,7 +545,7 @@ public static MvcHtmlString CodeMirrorScriptReferences(this HtmlHelper helper)
///
public static MvcHtmlString RenderLangAttribute(this HtmlHelper helper)
{
- return RenderLangAttribute(helper, CultureInfo.CurrentUICulture.Name);
+ return RenderLangAttribute(helper, Telerik.Sitefinity.Services.SystemManager.CurrentContext.Culture.Name);
}
///
@@ -244,7 +556,31 @@ public static MvcHtmlString RenderLangAttribute(this HtmlHelper helper)
///
public static MvcHtmlString RenderLangAttribute(this HtmlHelper helper, string culture)
{
- string attributeString = helper.FormatValue(culture, "lang=\"{0}\"");
+ 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);
}
@@ -277,6 +613,76 @@ public static string GetWebResourceUrl(ScriptRef scriptReference)
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.
///
@@ -305,7 +711,7 @@ internal static string RenderAllStylesheets(HttpContextBase context, string sect
return stylesheetMarkup;
}
- private static MvcHtmlString RegisterResource(HttpContextBase httpContext, string resourcePath, ResourceType resourceType, string sectionName, bool throwException)
+ private static MvcHtmlString RegisterResource(HttpContextBase httpContext, string resourcePath, ResourceType resourceType, string sectionName, bool throwException, List> attributes = null)
{
throwException = throwException && httpContext.CurrentHandler != null;
@@ -319,17 +725,37 @@ private static MvcHtmlString RegisterResource(HttpContextBase httpContext, strin
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 (string.IsNullOrEmpty(sectionName) || !SectionRenderer.IsAvailable(httpContext.Handler.GetPageHandler(), sectionName))
+ if (sectionName == null ||
+ ResourceHelper.RenderScriptSection)
{
- if (!register.IsRegistered(resourcePath, sectionName: null))
+ if (!register.IsRegistered(resourcePath))
{
- result = MvcHtmlString.Create(ResourceHelper.BuildSingleResourceMarkup(resourcePath, resourceType));
+ 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);
+ register.Register(resourcePath, sectionName, throwException, attributes);
return result;
}
@@ -373,7 +799,8 @@ private static bool TryConfigureScriptManager(string scriptReference, IHttpHandl
if (scriptManager != null)
{
- scriptManager.Scripts.Add(new ScriptReference(scriptReference));
+ var updatedScriptReference = GetResourceOrMinified(scriptReference);
+ scriptManager.Scripts.Add(new ScriptReference(updatedScriptReference));
return true;
}
}
@@ -405,43 +832,78 @@ private static string BuildHtmlResourcesMarkup(ResourceRegister resourceRegister
{
StringBuilder output = new StringBuilder();
- foreach (var resource in resourceRegister.GetResourcesForSection(sectionName))
+ foreach (var resource in resourceRegister.GetResourceItemsForSection(sectionName))
{
- if (!resourceRegister.IsRendered(resource))
+ if (!resourceRegister.IsRendered(resource.ResourceKey))
{
- output.Append(ResourceHelper.BuildSingleResourceMarkup(resource, resourceType));
- resourceRegister.MarkAsRendered(resource);
+ 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)
+ private static string BuildSingleResourceMarkup(string resourceKey, ResourceType resourceType, string sectionName, List> attributes = null)
{
string result;
if (resourceType == ResourceType.Js)
- result = ResourceHelper.BuildScriptMarkup(resourceKey);
+ result = ResourceHelper.BuildScriptMarkup(resourceKey, sectionName, attributes);
else if (resourceType == ResourceType.Css)
- result = ResourceHelper.BuildStyleSheetMarkup(resourceKey);
+ result = ResourceHelper.BuildStyleSheetMarkup(resourceKey, attributes);
else
result = string.Empty;
return result;
}
- private static string BuildScriptMarkup(string resourceKey)
+ 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 BuildStyleSheetMarkup(string resourceKey)
+ 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");
@@ -449,13 +911,21 @@ private static string BuildStyleSheetMarkup(string 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);
}
- private const string JsRegisterName = "JsRegister";
- private const string CssRegisterName = "CssRegister";
+ internal const string JsRegisterName = "JsRegister";
+ internal const string CssRegisterName = "CssRegister";
- private class MvcScriptReference : ScriptReference
+ internal class MvcScriptReference : ScriptReference
{
public MvcScriptReference(ScriptReference reference)
{
@@ -496,10 +966,14 @@ private bool IsGetUrlFromPathException(Exception ex)
///
/// This enum represents supported resource types.
///
- private enum ResourceType
+ 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/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 fce4ef374..bea9444b9 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/UrlHelpers.cs
@@ -1,145 +1,262 @@
-using System;
-using System.Globalization;
-using System.Web;
-using System.Web.Hosting;
-using System.Web.Mvc;
-using System.Web.UI;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure;
-using Telerik.Sitefinity.Frontend.Resources;
-using Telerik.Sitefinity.Modules.Pages;
-using Telerik.Sitefinity.Mvc.Rendering;
-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);
- 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);
- }
-
- 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);
- return helper.Content(url);
- }
-
- var resourceUrl = string.Format("~/{0}/{1}", FrontendManager.VirtualPathBuilder.GetVirtualPath(assemblyName), contentPath);
- var contentResolvedPath = UrlTransformations.AppendParam(resourceUrl, PackageManager.PackageUrlParameterName, packageName);
-
- 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)
+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)
{
- var page = helper.RequestContext.HttpContext.Handler.GetPageHandler() ?? new PageProxy(null);
- return page.ClientScript.GetWebResourceUrl(TypeResolutionService.ResolveType(type), path);
- }
-
- ///
- /// 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;
- }
- }
-}
+ 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/PagerViewModel.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
index 65b8c0cc9..8ee4d93cf 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewModels/PagerViewModel.cs
@@ -28,7 +28,7 @@ public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTe
/// The current page.
/// The total pages count.
/// The redirect URL template.
- /// The amount of page nodes to render
+ /// The amount of page nodes to render
public PagerViewModel(int currentPage, int totalPagesCount, string redirectUrlTemplate, int displayCount)
{
this.CurrentPage = currentPage;
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
index 2793965bc..3c50bcaab 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Helpers/ViewSelectorHelpers.cs
@@ -28,6 +28,10 @@ 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);
@@ -44,6 +48,21 @@ public static IEnumerable GetViewNames(this HtmlHelper helper, string co
/// 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)
{
@@ -51,9 +70,22 @@ public static IEnumerable GetViewNames(this HtmlHelper helper, Controlle
}
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);
+ 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/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 4c02f84c6..277634a2f 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/Attributes/CacheDependentAttribute.cs
@@ -28,12 +28,12 @@ public override void OnResultExecuted(ResultExecutedContext filterContext)
{
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)
{
context.Response.AddCacheDependency(cacheDependency);
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/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 262c53228..f7b33cb2d 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerContainerInitializer.cs
@@ -4,341 +4,657 @@
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
- {
- #region Public members
-
- ///
- /// Initializes the controllers that are available to the web application.
- ///
- public virtual void Initialize(IEnumerable controllerAssemblies)
- {
- GlobalFilters.Filters.Add(new CacheDependentAttribute());
-
- var assemblies = controllerAssemblies ?? this.RetrieveAssemblies();
- this.RegisterVirtualPaths(assemblies);
-
- var controllerTypes = this.GetControllers(assemblies);
- this.InitializeControllers(controllerTypes);
-
- this.InitializeCustomRouting();
- }
-
- ///
- /// Gets the assemblies that are marked as controller containers with the attribute.
- ///
- public 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;
- }
-
- #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).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.RegisterTemplateableControls(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();
- ObjectFactory.Container.RegisterType("int");
- ObjectFactory.Container.RegisterType("category");
- ObjectFactory.Container.RegisterType("tag");
+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();
- }
-
- #endregion
-
- #region Private members
-
- ///
- /// Determines whether the specified controller is allowed template registration
- ///
- /// Type of the controller.
- ///
- /// true if specified controller is allowed template registration; otherwise, false.
- ///
- private bool IsTemplatableControl(Type controllerType)
- {
- ControllerMetadataAttribute attr;
- var attributes = controllerType.GetCustomAttributes(typeof(ControllerMetadataAttribute), false);
-
- if (attributes != null && attributes.Length > 0)
- {
- attr = (ControllerMetadataAttribute)attributes[0];
- return attr.IsTemplatableControl;
- }
-
- // if there is no ControllerMetaDataAttribute, by default allow template registration
- return true;
- }
-
- 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(
- string.Format(CultureInfo.InvariantCulture, "~/{0}*", virtualPath),
- assemblyName.Name,
- assemblyName.CodeBase);
-
- SystemManager.RegisterRoute(
- assemblyName.Name,
- new Route(
- virtualPath + "{*Params}",
- new GenericRouteHandler(() => new ResourceHttpHandler(virtualPath))),
- assemblyName.Name,
- requireBasicAuthentication: false);
- }
-
- ///
- /// Registers MVC widgets as templatable controls
- ///
- /// The controller types.
- private void RegisterTemplateableControls(IEnumerable controllerTypes)
- {
- controllerTypes = controllerTypes.Where(x => this.IsTemplatableControl(x));
-
- foreach (Type controllerType in controllerTypes)
- {
- var widgetName = controllerType.Name.Replace("Controller", string.Empty);
- var mvcWidgetName = string.Format(CultureInfo.InvariantCulture, "{0} (MVC)", widgetName);
-
- Telerik.Sitefinity.Modules.ControlTemplates.ControlTemplates.RegisterTemplatableControl(controllerType, controllerType, string.Empty, widgetName, mvcWidgetName);
- }
- }
-
- 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
- }
-}
+ 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 RetrieveControllerAssemblyAsync(string assemblyFileName)
+ {
+ return Task.Run(() => this.RetrieveAssembly(assemblyFileName));
+ }
+
+ private Task LoadControllerAssemblyAsync(string assemblyFileName)
+ {
+ return Task.Run(() => this.LoadControllerAssembly(assemblyFileName));
+ }
+
+ private Assembly LoadControllerAssembly(string assemblyFileNameWithPath)
+ {
+ try
+ {
+ Assembly assembly = this.LoadAssembly(assemblyFileNameWithPath);
+ this.InitializeControllerContainer(assembly);
+ return assembly;
+ }
+ catch (Exception)
+ {
+ var lastIndexOfPathSeperator = assemblyFileNameWithPath.LastIndexOf("\\");
+ var fileNameFailedToLoad = assemblyFileNameWithPath.Substring(lastIndexOfPathSeperator >= 0 ? lastIndexOfPathSeperator : 0);
+ Log.Write(string.Format("Attempt to load {0} failed. Check if the assembly is present in the bin", fileNameFailedToLoad), ConfigurationPolicy.ErrorLog);
+ }
+
+ return null;
+ }
+
+ private string RetrieveAssembly(string assemblyFileName)
+ {
+ if (this.IsControllerContainer(assemblyFileName) || this.IsMarkedAssembly(assemblyFileName))
+ {
+ return assemblyFileName;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Registers the controller string resources.
+ ///
+ /// Type of the controller.
+ private static 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.IsTypeRegistered(resourceClass, null, resourceClassId, false))
+ {
+ Res.RegisterResource(resourceClass);
+ }
+ }
+ }
+
+ ///
+ /// Determines whether the specified controller is allowed template registration
+ ///
+ /// Type of the controller.
+ ///
+ /// true if specified controller is allowed template registration; otherwise, false.
+ ///
+ private bool IsTemplatableControl(Type controllerType)
+ {
+ ControllerMetadataAttribute attr;
+ var attributes = controllerType.GetCustomAttributes(typeof(ControllerMetadataAttribute), false);
+
+ if (attributes != null && attributes.Length > 0)
+ {
+ attr = (ControllerMetadataAttribute)attributes[0];
+ return attr.IsTemplatableControl;
+ }
+
+ // if there is no ControllerMetaDataAttribute, by default allow template registration
+ return true;
+ }
+
+ ///
+ /// Determines whether the specified assembly file name is marked with a given attribute.
+ ///
+ /// The type of the attribute.
+ /// File name of the assembly.
+ /// True if the assembly has the given attribute.
+ private bool IsMarkedAssembly(string assemblyFileName)
+ where TAttribute : Attribute
+ {
+ 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(TAttribute).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;
+ }
+
+ 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(
+ string.Format(CultureInfo.InvariantCulture, "~/{0}*", virtualPath),
+ assemblyName.Name,
+ assemblyName.CodeBase);
+
+ SystemManager.RegisterRoute(
+ assemblyName.Name,
+ new Route(
+ virtualPath + "{*Params}",
+ new GenericRouteHandler(() => new ResourceHttpHandler(virtualPath))),
+ assemblyName.Name,
+ requireBasicAuthentication: false);
+ }
+
+ ///
+ /// Registers MVC widgets as templatable controls
+ ///
+ /// The controller types.
+ private void RegisterTemplateableControls(IEnumerable controllerTypes)
+ {
+ controllerTypes = controllerTypes.Where(x => this.IsTemplatableControl(x));
+
+ foreach (Type controllerType in controllerTypes)
+ {
+ var widgetName = controllerType.Name.Replace("Controller", string.Empty);
+ var mvcWidgetName = string.Format(CultureInfo.InvariantCulture, "{0} (MVC)", widgetName);
+
+ ControlTemplates.RegisterTemplatableControl(controllerType, controllerType, string.Empty, widgetName, mvcWidgetName);
+ }
+ }
+
+ private void RemoveSitefinityViewEngine()
+ {
+ var sitefinityViewEngines = ViewEngines.Engines.Where(v => v != null && v.GetType() == typeof(SitefinityViewEngine)).ToList();
+ foreach (var sitefinityViewEngine in sitefinityViewEngines)
+ {
+ ViewEngines.Engines.Remove(sitefinityViewEngine);
+ }
+ }
+
+ private static IEnumerable controllerContainerAssemblies;
+ private static readonly object ControllerContainerAssembliesLock = new object();
+
+ private Dictionary> PrecompiledResourcePackages(IEnumerable assemblies)
+ {
+ var precompiledViewAssemblies = new Dictionary>();
+ foreach (var assembly in assemblies)
+ {
+ var package = this.AssemblyPackage(assembly);
+ if (package == null)
+ continue;
+
+ if (!precompiledViewAssemblies.ContainsKey(package))
+ precompiledViewAssemblies[package] = new List();
+
+ precompiledViewAssemblies[package].Add(new PrecompiledViewAssemblyWrapper(assembly, package));
+ }
+
+ return precompiledViewAssemblies;
+ }
+
+ private void RegisterPrecompiledViewEngines(IEnumerable assemblies)
+ {
+ var precompiledAssemblies = assemblies.Where(a => a.GetCustomAttribute() != null).Select(a => new PrecompiledViewAssemblyWrapper(a, null)).ToArray();
+ if (precompiledAssemblies.Length > 0)
+ {
+ ViewEngines.Engines.Insert(0, new CompositePrecompiledMvcEngineWrapper(precompiledAssemblies));
+ }
+
+ var precompiledResourcePackages = this.PrecompiledResourcePackages(assemblies);
+ foreach (var package in precompiledResourcePackages.Keys)
+ {
+ if (package == null)
+ continue;
+
+ var packageAssemblies = precompiledResourcePackages[package];
+ if (packageAssemblies.Count > 0)
+ ViewEngines.Engines.Insert(0, new CompositePrecompiledMvcEngineWrapper(packageAssemblies, null, package));
+ }
+ }
+
+ private string AssemblyPackage(Assembly assembly)
+ {
+ var attribute = assembly.GetCustomAttribute();
+ if (attribute == null)
+ return null;
+ else
+ return attribute.Name;
+ }
+
+ private void OnTaxonomiesUpdated(ICacheDependencyHandler caller, Type itemType, string itemKey)
+ {
+ // needs to run on a separate thread otherwise messes up the open access cache for som reason
+ // no time to investigate and I see this method has already been called in the same fashion in earlier checkins
+ Task.Run(() =>
+ {
+ RegisterTaxonomyRoutes();
+ });
+ }
+
+ #endregion
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerExtensions.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerExtensions.cs
index 2b9f4e7af..b36a8430f 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerExtensions.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerExtensions.cs
@@ -5,16 +5,22 @@
using System.Reflection;
using System.Web;
using System.Web.Mvc;
+using ServiceStack.Text;
using Telerik.Sitefinity.Abstractions;
using Telerik.Sitefinity.Abstractions.VirtualPath;
using Telerik.Sitefinity.Data;
using Telerik.Sitefinity.DynamicModules.Builder;
using Telerik.Sitefinity.DynamicModules.Builder.Model;
+using Telerik.Sitefinity.Frontend.Mvc.Controllers;
+using Telerik.Sitefinity.Frontend.Mvc.Models;
using Telerik.Sitefinity.Frontend.Resources;
using Telerik.Sitefinity.Frontend.Resources.Resolvers;
+using Telerik.Sitefinity.Security.Sanitizers;
using Telerik.Sitefinity.Services;
using Telerik.Sitefinity.Web;
+using Telerik.Sitefinity.Web.OutputCache;
using Telerik.Sitefinity.Web.UI;
+using Telerik.Sitefinity.Web.UI.ContentUI.Enums;
namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
{
@@ -25,12 +31,32 @@ public static class ControllerExtensions
{
#region Public methods
+ ///
+ /// Determines whether the controller should return the details view.
+ ///
+ /// The controller.
+ /// Display mode of the view.
+ /// A view model containing list of selected items.
+ /// A value indicating whether the controller should return the details view.
+ public static bool ShouldReturnDetails(this Controller controller, ContentViewDisplayMode contentViewDisplayMode, ContentListViewModel viewModel)
+ {
+ if (controller == null)
+ throw new ArgumentNullException("controller");
+
+ if (contentViewDisplayMode == ContentViewDisplayMode.Detail && viewModel != null)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
///
/// Updates the view engines collection of the given by making the engines aware of the controller's container virtual path.
///
/// The controller.
- /// controller
/// Transformations func that have to be applied to each view engine search path.
+ /// controller
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")]
public static void UpdateViewEnginesCollection(this Controller controller, Func>> pathTransformationsFunc)
{
@@ -65,7 +91,9 @@ public static void UpdateViewEnginesCollection(this Controller controller, IList
/// Gets the partial views that are available to the controller.
///
/// The controller.
- public static IEnumerable GetPartialViews(this Controller controller)
+ /// The full view paths.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fullViewPaths")]
+ public static IEnumerable GetPartialViews(this Controller controller, IList fullViewPaths = null)
{
var viewLocations = ControllerExtensions.GetPartialViewLocations(controller);
return ControllerExtensions.GetViews(controller, viewLocations);
@@ -75,10 +103,24 @@ public static IEnumerable GetPartialViews(this Controller controller)
/// Gets the views that are available to the controller.
///
/// The controller.
- public static IEnumerable GetViews(this Controller controller)
+ /// The full view paths.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fullViewPaths")]
+ public static IEnumerable GetViews(this Controller controller, IList fullViewPaths = null)
+ {
+ return ControllerExtensions.GetViews(controller, fullViewPaths, null);
+ }
+
+ ///
+ /// Gets the views that are available to the controller.
+ ///
+ /// The controller.
+ /// The full view paths.
+ /// The name of dynamic module (if any). Default is null.
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fullViewPaths")]
+ public static IEnumerable GetViews(this Controller controller, IList fullViewPaths, string moduleName)
{
var viewLocations = ControllerExtensions.GetViewLocations(controller);
- return ControllerExtensions.GetViews(controller, viewLocations);
+ return ControllerExtensions.GetViews(controller, viewLocations, moduleName);
}
///
@@ -94,25 +136,33 @@ public static void AddCacheDependencies(this Controller controller, IEnumerable<
if (keys == null)
throw new ArgumentNullException("keys");
- if (SystemManager.CurrentHttpContext == null)
+ var httpContext = SystemManager.CurrentHttpContext;
+ if (httpContext == null)
throw new InvalidOperationException("Current HttpContext is null. Cannot add cache dependencies.");
- IList dependencies = null;
- if (SystemManager.CurrentHttpContext.Items.Contains(PageCacheDependencyKeys.PageData))
- {
- dependencies = SystemManager.CurrentHttpContext.Items[PageCacheDependencyKeys.PageData] as IList;
- }
+ string widgetId = controller.ViewData[Telerik.Sitefinity.Mvc.ControllerActionInvoker.ControlDataIdKey] as string;
- if (dependencies == null)
- {
- dependencies = new List();
- SystemManager.CurrentHttpContext.Items.Add(PageCacheDependencyKeys.PageData, dependencies);
- }
+ httpContext.AddCacheDependencies(keys, widgetId);
+ }
- foreach (var key in keys)
- dependencies.Add(key);
+ ///
+ /// Adds cache dependencies for the current response.
+ ///
+ /// The controller.
+ /// The type of the content.
+ /// The name of the provider.
+ public static void AddCacheVariations(this Controller controller, Type contentType, string providerName = null)
+ {
+ if (controller == null)
+ throw new ArgumentNullException("controller");
+
+ if (contentType == null)
+ throw new ArgumentNullException("contentType");
+
+ PageRouteHandler.RegisterContentListCacheVariation(contentType, providerName);
}
+
///
/// Gets the partial view paths of the given controller.
///
@@ -148,7 +198,7 @@ public static string GetCurrentPageUrl(this IController controller)
///
/// The controller.
/// The dynamic module type.
- public static DynamicModuleType GetDynamicContentType(this ControllerBase controller)
+ public static IDynamicModuleType GetDynamicContentType(this ControllerBase controller)
{
if (controller == null)
throw new ArgumentNullException("controller");
@@ -168,7 +218,7 @@ public static DynamicModuleType GetDynamicContentType(this ControllerBase contro
///
/// The dynamic module type.
///
- public static DynamicModuleType GetDynamicContentType(this ControllerBase controller, string controllerName)
+ public static IDynamicModuleType GetDynamicContentType(this ControllerBase controller, string controllerName)
{
if (controller == null)
throw new ArgumentNullException("controller");
@@ -176,7 +226,9 @@ public static DynamicModuleType GetDynamicContentType(this ControllerBase contro
if (controllerName == null)
throw new ArgumentNullException("controllerName");
- return ControllerExtensions.GetDynamicContentType(controllerName);
+ var moduleName = controller.ViewBag.ModuleName as string;
+
+ return ControllerExtensions.GetDynamicContentType(controllerName, moduleName);
}
///
@@ -186,21 +238,22 @@ public static DynamicModuleType GetDynamicContentType(this ControllerBase contro
///
/// The dynamic module type.
///
- public static DynamicModuleType GetDynamicContentType(string controllerName)
+ public static IDynamicModuleType GetDynamicContentType(string controllerName)
{
- if (controllerName == null)
- throw new ArgumentNullException("controllerName");
-
- if (SystemManager.GetModule("ModuleBuilder") == null)
- return null;
-
- var moduleProvider = ModuleBuilderManager.GetManager().Provider;
- var dynamicContentType = moduleProvider.GetDynamicModules()
- .Where(m => m.Status == DynamicModuleStatus.Active)
- .Join(moduleProvider.GetDynamicModuleTypes().Where(t => t.TypeName == controllerName), m => m.Id, t => t.ParentModuleId, (m, t) => t)
- .FirstOrDefault();
+ return ControllerExtensions.GetDynamicContentType(controllerName, null);
+ }
- return dynamicContentType;
+ ///
+ /// Gets the type of the dynamic content that is inferred for the given controller name.
+ ///
+ /// Name of the controller.
+ /// The name of the module. If empty or null will search all dynamic modules. Default value is null.
+ ///
+ /// The dynamic module type.
+ ///
+ public static IDynamicModuleType GetDynamicContentType(string controllerName, string moduleName)
+ {
+ return ControllerExtensions.FindDynamicContentTypes(controllerName, moduleName).FirstOrDefault();
}
///
@@ -208,13 +261,124 @@ public static DynamicModuleType GetDynamicContentType(string controllerName)
///
public static IndexRenderModes GetIndexRenderMode(this IController controller)
{
- if (controller == null)
+ if (controller == null)
throw new ArgumentNullException("controller");
+ var searchableControl = controller as ISearchIndexBehavior;
+ if (searchableControl != null)
+ {
+ var exclude = searchableControl.ExcludeFromSearchIndex;
+ if (exclude)
+ return IndexRenderModes.NoOutput;
+ else
+ return IndexRenderModes.Normal;
+ }
+
var attribute = controller.GetType().GetCustomAttributes(true).OfType().LastOrDefault();
return attribute == null ? IndexRenderModes.Normal : attribute.Mode;
}
+ ///
+ /// Returns the invalid response for the details view.
+ ///
+ /// The
+ public static ActionResult HandleInvalidDetailsAction(this IController controller, string message)
+ {
+ if (SystemManager.IsDesignMode || SystemManager.IsPreviewMode)
+ {
+ var sanitizer = ObjectFactory.Resolve();
+ message = sanitizer.Sanitize(message);
+
+ var response = new ContentResult();
+ response.Content = message;
+
+ return response;
+ }
+
+ return new EmptyResult();
+ }
+
+ #endregion
+
+ #region Internal methods
+
+ ///
+ /// Gets the type of the dynamic content that is inferred for the given controller name.
+ ///
+ /// Name of the controller.
+ /// The name of the module. If empty or null will search all dynamic modules.
+ ///
+ /// The dynamic module types.
+ ///
+ internal static IEnumerable FindDynamicContentTypes(string controllerName, string moduleName)
+ {
+ if (controllerName == null)
+ throw new ArgumentNullException("controllerName");
+
+ if (SystemManager.GetModule("ModuleBuilder") == null)
+ return Enumerable.Empty().AsQueryable();
+
+ var dynamicModuleTypes = ModuleBuilderManager.GetActiveTypes()
+ .Where(t => string.Equals(t.TypeName, controllerName, StringComparison.OrdinalIgnoreCase));
+
+ if (!moduleName.IsNullOrWhitespace())
+ {
+ dynamicModuleTypes = dynamicModuleTypes.Where(t => t.ModuleName == moduleName);
+ }
+
+ return dynamicModuleTypes;
+ }
+
+ ///
+ /// Gets the partial views that are available to the controller.
+ ///
+ /// The controller.
+ /// The view files mappings.
+ /// The name of dynamic module name.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "fullViewPaths")]
+ internal static IEnumerable GetPartialViews(this Controller controller, ref Dictionary viewFilesMappings, string moduleName)
+ {
+ var viewLocations = ControllerExtensions.GetPartialViewLocations(controller);
+ return ControllerExtensions.GetViews(controller, viewLocations, ref viewFilesMappings, moduleName);
+ }
+
+ ///
+ /// Resolves the widget name of the dynamic widget controller.
+ ///
+ /// The controller.
+ ///
+ /// The widget name.
+ ///
+ internal static string ResolveDynamicControllerWidgetName(this Controller controller)
+ {
+ if (controller == null ||
+ controller.Request == null ||
+ controller.Request.QueryString == null ||
+ controller.RouteData == null ||
+ !controller.RouteData.Values.ContainsKey("widgetName") ||
+ (string)controller.RouteData.Values["widgetName"] != "DynamicContent")
+ {
+ return null;
+ }
+
+ var controlId = controller.Request.QueryStringGet("controlId") as string;
+ Guid controlIdGuid;
+ if (string.IsNullOrEmpty(controlId) || !Guid.TryParse(controlId, out controlIdGuid))
+ return null;
+
+ var pageManager = Telerik.Sitefinity.Modules.Pages.PageManager.GetManager();
+ var controlObjectData = pageManager.GetControl(controlIdGuid);
+ if (controlObjectData == null || controlObjectData.Properties == null)
+ return null;
+
+ var controllerWidgetProperty = controlObjectData.Properties.FirstOrDefault(x => x.Name == "WidgetName");
+ if (controllerWidgetProperty == null)
+ return null;
+
+ return controllerWidgetProperty.Value;
+ }
+
#endregion
#region Private methods
@@ -257,6 +421,10 @@ private static string GetKey(Controller controller)
{
var widgetName = (string)controller.RouteData.Values[WidgetNameKey];
key = string.Format("{0}-{1}", key, widgetName);
+
+ var dynamicControllerWidgetName = controller.ResolveDynamicControllerWidgetName();
+ if (!string.IsNullOrEmpty(dynamicControllerWidgetName))
+ key = string.Format("{0}-{1}", key, dynamicControllerWidgetName);
}
var currentPackage = new PackageManager().GetCurrentPackage();
@@ -280,7 +448,10 @@ private static ViewEngineCollection BuildViewEngineCollection(IList> pathTransformations)
{
- var newEngine = (VirtualPathProviderViewEngine)Activator.CreateInstance(viewEngine.GetType());
+ VirtualPathProviderViewEngine newEngine;
+ var precompiledEngine = viewEngine as CompositePrecompiledMvcEngineWrapper;
+ if (precompiledEngine != null)
+ {
+ if (!precompiledEngine.PackageName.IsNullOrEmpty() &&
+ !string.Equals(precompiledEngine.PackageName, new PackageManager().GetCurrentPackage(), StringComparison.OrdinalIgnoreCase))
+ {
+ return null;
+ }
+
+ newEngine = precompiledEngine.Clone();
+ }
+ else
+ {
+ var viewEngineType = viewEngine.GetType();
+ var defaultCtor = viewEngineType.GetConstructor(Type.EmptyTypes);
+ if (defaultCtor != null)
+ newEngine = (VirtualPathProviderViewEngine)Activator.CreateInstance(viewEngineType);
+ else
+ return null;
+ }
+
+ newEngine.ViewLocationCache = DefaultViewLocationCache.Null;
+
newEngine.AreaViewLocationFormats = ControllerExtensions.AppendControllerVirtualPath(viewEngine.AreaViewLocationFormats, pathTransformations);
newEngine.AreaMasterLocationFormats = ControllerExtensions.AppendControllerVirtualPath(viewEngine.AreaPartialViewLocationFormats, pathTransformations);
newEngine.AreaPartialViewLocationFormats = ControllerExtensions.AppendControllerVirtualPath(viewEngine.AreaPartialViewLocationFormats, pathTransformations);
@@ -302,7 +496,15 @@ private static IViewEngine GetViewEngine(VirtualPathProviderViewEngine viewEngin
newEngine.MasterLocationFormats = ControllerExtensions.AppendControllerVirtualPath(viewEngine.MasterLocationFormats, pathTransformations);
newEngine.PartialViewLocationFormats = ControllerExtensions.AppendControllerVirtualPath(viewEngine.PartialViewLocationFormats, pathTransformations);
- newEngine.ViewLocationCache = DefaultViewLocationCache.Null;
+ if (precompiledEngine != null)
+ {
+ newEngine.AreaViewLocationFormats = newEngine.AreaViewLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ newEngine.AreaMasterLocationFormats = newEngine.AreaMasterLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ newEngine.AreaPartialViewLocationFormats = newEngine.AreaPartialViewLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ newEngine.ViewLocationFormats = newEngine.ViewLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ newEngine.MasterLocationFormats = newEngine.MasterLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ newEngine.PartialViewLocationFormats = newEngine.PartialViewLocationFormats.Select(p => FrontendManager.VirtualPathBuilder.RemoveParams(p)).ToArray();
+ }
return newEngine;
}
@@ -354,30 +556,75 @@ private static IEnumerable GetViewFileExtensions(Controller controller)
.Distinct();
}
- private static IEnumerable GetViews(Controller controller, IEnumerable viewLocations)
+ private static IEnumerable GetViews(Controller controller, IEnumerable viewLocations, string moduleName = null)
{
var viewExtensions = ControllerExtensions.GetViewFileExtensions(controller);
var widgetName = controller.RouteData != null ? controller.RouteData.Values["widgetName"] as string : null;
- var baseFiles = ControllerExtensions.GetViewsForAssembly(controller.GetType().Assembly, viewLocations, viewExtensions);
+ var assembly = controller.GetType().Assembly;
+ var baseFiles = ControllerExtensions.GetViewsForAssembly(assembly, viewLocations, viewExtensions, moduleName).ToList();
+
+ foreach (var viewEngine in controller.ViewEngineCollection)
+ {
+ var compositeViewEngine = viewEngine as CompositePrecompiledMvcEngineWrapper;
+ if (compositeViewEngine != null)
+ {
+ var files = compositeViewEngine.GetViews(viewLocations);
+ baseFiles.AddRange(files);
+ }
+ }
+ var filesForBasePath = baseFiles.Distinct();
+
if (!widgetName.IsNullOrEmpty())
{
var widgetAssembly = FrontendManager.ControllerFactory.ResolveControllerType(widgetName).Assembly;
var widgetFiles = ControllerExtensions.GetViewsForAssembly(widgetAssembly, viewLocations, viewExtensions);
+ return filesForBasePath.Union(widgetFiles);
+ }
+
+ return filesForBasePath;
+ }
+
+ private static IEnumerable GetViews(Controller controller, IEnumerable viewLocations, ref Dictionary viewFilesMappings, string moduleName = null)
+ {
+ var viewExtensions = ControllerExtensions.GetViewFileExtensions(controller);
+ var widgetName = controller.RouteData != null ? controller.RouteData.Values["widgetName"] as string : null;
+
+ var baseFiles = ControllerExtensions.GetViewsForAssembly(controller.GetType().Assembly, viewLocations, viewExtensions,
+ ref viewFilesMappings, moduleName);
+ if (!widgetName.IsNullOrEmpty())
+ {
+ var widgetAssembly = FrontendManager.ControllerFactory.ResolveControllerType(widgetName).Assembly;
+ var widgetFiles = ControllerExtensions.GetViewsForAssembly(widgetAssembly, viewLocations, viewExtensions,
+ ref viewFilesMappings, moduleName);
return baseFiles.Union(widgetFiles);
}
return baseFiles;
}
- private static IEnumerable GetViewsForAssembly(Assembly assembly, IEnumerable viewLocations, IEnumerable viewExtensions)
+ private static IEnumerable GetViewsForAssembly(Assembly assembly, IEnumerable viewLocations, IEnumerable viewExtensions, string moduleName = null)
{
- var pathDef = FrontendManager.VirtualPathBuilder.GetPathDefinition(assembly);
+ var pathDef = FrontendManager.VirtualPathBuilder.GetPathDefinition(assembly, moduleName);
return viewLocations
.SelectMany(l => ControllerExtensions.GetViewsForPath(pathDef, l, viewExtensions))
.Distinct();
}
+ private static IEnumerable GetViewsForAssembly(Assembly assembly, IEnumerable viewLocations, IEnumerable viewExtensions,
+ ref Dictionary viewFilesMappings, string moduleName = null)
+ {
+ var pathDef = FrontendManager.VirtualPathBuilder.GetPathDefinition(assembly, moduleName);
+ var views = new List();
+
+ foreach (var viewLocation in viewLocations)
+ {
+ views.AddRange(ControllerExtensions.GetViewsForPath(pathDef, viewLocation, viewExtensions, ref viewFilesMappings));
+ }
+
+ return views.Distinct();
+ }
+
private static IEnumerable GetViewsForPath(PathDefinition definition, string path, IEnumerable viewExtensions)
{
var files = ObjectFactory.Resolve().GetFiles(definition, path);
@@ -396,6 +643,31 @@ private static IEnumerable GetViewsForPath(PathDefinition definition, st
return new string[] { };
}
+ private static IEnumerable GetViewsForPath(PathDefinition definition, string path, IEnumerable viewExtensions, ref Dictionary viewFilesMappings)
+ {
+ var views = new List();
+ var files = ObjectFactory.Resolve().GetFiles(definition, path);
+
+ if (files != null)
+ {
+ foreach (var file in files)
+ {
+ if (viewExtensions.Any(e => file.EndsWith(e, StringComparison.Ordinal)))
+ {
+ var fileName = VirtualPathUtility.GetFileName(file);
+ var fileNameWithoutExtension = System.IO.Path.GetFileNameWithoutExtension(fileName);
+ if (!viewFilesMappings.ContainsKey(fileNameWithoutExtension))
+ {
+ viewFilesMappings.Add(fileNameWithoutExtension, file);
+ views.Add(fileNameWithoutExtension);
+ }
+ }
+ }
+ }
+
+ return views;
+ }
+
#endregion
#region Private Fields
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerModelFactory.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerModelFactory.cs
index 6062d186a..299f4a68b 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerModelFactory.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ControllerModelFactory.cs
@@ -62,7 +62,11 @@ private static IEnumerable GetTypeHierarchyAssemblies(Type type)
while (currentType != null && currentType != typeof(Controller) && currentType != typeof(object))
{
- result.Add(currentType.Assembly);
+ if (!result.Contains(currentType.Assembly))
+ {
+ result.Add(currentType.Assembly);
+ }
+
currentType = currentType.BaseType;
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FeatherEnabledToolboxFilter.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FeatherEnabledToolboxFilter.cs
new file mode 100644
index 000000000..a0c4923ec
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FeatherEnabledToolboxFilter.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Telerik.Sitefinity.DesignerToolbox;
+using Telerik.Sitefinity.Services;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
+{
+ ///
+ /// Determines which controls should be visible in the toolbox of the when Feather is deactivated.
+ ///
+ internal class FeatherEnabledToolboxFilter : IToolboxFilter
+ {
+ ///
+ public virtual bool IsSectionVisible(IToolboxSection section, IToolboxFilterContext context)
+ {
+ return true;
+ }
+
+ ///
+ public virtual bool IsToolVisible(IToolboxItem tool)
+ {
+ var isFeatherDeactivated = SystemManager.GetModule("Feather") == null;
+ if (isFeatherDeactivated)
+ {
+ var isFeatherWidget = tool.ControllerType.StartsWith("Telerik.Sitefinity.Frontend", StringComparison.Ordinal);
+ if (isFeatherWidget)
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FrontendControllerFactory.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FrontendControllerFactory.cs
index 2b6c17505..11fd93e9a 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FrontendControllerFactory.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/FrontendControllerFactory.cs
@@ -17,20 +17,8 @@ namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
///
/// This class extends the by adding additional virtual paths for controller view engines.
///
- public class FrontendControllerFactory : SitefinityControllerFactory, IDisposable
+ public class FrontendControllerFactory : SitefinityControllerFactory
{
- #region Constructors
-
- ///
- /// Initializes a new instance of the class.
- ///
- public FrontendControllerFactory()
- {
- this.ninjectKernel = new StandardKernel();
- }
-
- #endregion
-
#region Public members
///
@@ -53,29 +41,23 @@ public override IController CreateController(RequestContext requestContext, stri
var controller = baseController as Controller;
if (controller != null)
{
- this.EnhanceViewEngines(controller);
+ FrontendControllerFactory.EnhanceViewEngines(controller);
}
return baseController;
}
///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ /// Enhances the view engines.
///
- public void Dispose()
+ /// The controller.
+ internal static void EnhanceViewEngines(Controller controller)
{
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- ///
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- ///
- /// If false cleans up native resources, otherwise cleans up both managed and native resources.
- protected virtual void Dispose(bool cleanManagedResources)
- {
- if (cleanManagedResources)
- this.ninjectKernel.Dispose();
+ var enhanceAttr = FrontendControllerFactory.GetEnhanceAttribute(controller.GetType());
+ if (!enhanceAttr.Disabled)
+ {
+ controller.UpdateViewEnginesCollection(() => FrontendControllerFactory.GetControllerPathTransformations(controller, enhanceAttr.VirtualPath));
+ }
}
#endregion
@@ -83,9 +65,12 @@ protected virtual void Dispose(bool cleanManagedResources)
#region Protected members
///
- protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
+ protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
- return (IController)this.ninjectKernel.Get(controllerType);
+ object controllerObject = FrontendModule.Current.DependencyResolver.Get(controllerType);
+ IController controller = (IController)controllerObject;
+
+ return controller;
}
#endregion
@@ -122,29 +107,11 @@ private static IList> GetControllerPathTransformations(Cont
private static void AddDynamicControllerPathTransformations(Controller controller, string virtualPath, string currentPackage, List> pathTransformations)
{
- if (controller != null && controller.Request != null && controller.Request.QueryString != null && controller.RouteData != null && controller.RouteData.Values.ContainsKey("widgetName") && (string)controller.RouteData.Values["widgetName"] == "DynamicContent")
- {
- var controlId = controller.Request.QueryString["controlId"] as string;
- Guid controlIdGuid;
+ var dynamicControllerWidgetName = controller.ResolveDynamicControllerWidgetName();
+ if (string.IsNullOrEmpty(dynamicControllerWidgetName))
+ return;
- if (!string.IsNullOrEmpty(controlId) && Guid.TryParse(controlId, out controlIdGuid))
- {
- var controlObjectData = PageManager.GetManager().GetControl(controlIdGuid);
-
- if (controlObjectData != null && controlObjectData.Properties != null)
- {
- var controllerWidgetProperty = controlObjectData.Properties.FirstOrDefault(x => x.Name == "WidgetName");
- if (controllerWidgetProperty != null)
- {
- var dynamicControllerWidgetName = controllerWidgetProperty.Value;
- if (!string.IsNullOrEmpty(dynamicControllerWidgetName))
- {
- pathTransformations.Add(FrontendControllerFactory.GetPathTransformation(virtualPath, currentPackage, dynamicControllerWidgetName));
- }
- }
- }
- }
- }
+ pathTransformations.Add(FrontendControllerFactory.GetPathTransformation(virtualPath, currentPackage, dynamicControllerWidgetName));
}
private static Func GetPathTransformation(string controllerVirtualPath, string currentPackage, string widgetName = null)
@@ -171,16 +138,7 @@ private static string AppendDefaultPath(string virtualPath)
return VirtualPathUtility.AppendTrailingSlash(virtualPath) + "Mvc/";
}
- private void EnhanceViewEngines(Controller controller)
- {
- var enhanceAttr = this.GetEnhanceAttribute(controller.GetType());
- if (!enhanceAttr.Disabled)
- {
- controller.UpdateViewEnginesCollection(() => FrontendControllerFactory.GetControllerPathTransformations(controller, enhanceAttr.VirtualPath));
- }
- }
-
- private EnhanceViewEnginesAttribute GetEnhanceAttribute(Type controllerType)
+ private static EnhanceViewEnginesAttribute GetEnhanceAttribute(Type controllerType)
{
var enhanceAttr = controllerType.GetCustomAttributes(typeof(EnhanceViewEnginesAttribute), true).FirstOrDefault() as EnhanceViewEnginesAttribute;
if (enhanceAttr != null)
@@ -198,7 +156,7 @@ private EnhanceViewEnginesAttribute GetEnhanceAttribute(Type controllerType)
{
var newEnhanceAttr = new EnhanceViewEnginesAttribute
{
- Disabled = !this.IsInDefaultMvcNamespace(controllerType),
+ Disabled = !FrontendControllerFactory.IsInDefaultMvcNamespace(controllerType),
VirtualPath = AppendDefaultPath(FrontendManager.VirtualPathBuilder.GetVirtualPath(controllerType.Assembly))
};
@@ -212,7 +170,7 @@ private EnhanceViewEnginesAttribute GetEnhanceAttribute(Type controllerType)
return enhanceAttr;
}
- private bool IsInDefaultMvcNamespace(Type controller)
+ private static bool IsInDefaultMvcNamespace(Type controller)
{
var expectedTypeName = controller.Assembly.GetName().Name + ".Mvc.Controllers." + controller.Name;
return string.Equals(expectedTypeName, controller.FullName, StringComparison.OrdinalIgnoreCase);
@@ -222,10 +180,8 @@ private bool IsInDefaultMvcNamespace(Type controller)
#region Fields
- private IKernel ninjectKernel;
-
private static readonly Dictionary EnhanceAttributes = new Dictionary();
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ICanFilterByParent.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ICanFilterByParent.cs
new file mode 100644
index 000000000..be2317a25
--- /dev/null
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/ICanFilterByParent.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers
+{
+ ///
+ /// Classes that implement this interface can filter items by parent item resolved from URL
+ ///
+ public interface ICanFilterByParent
+ {
+ ///
+ /// Gets the parent types.
+ ///
+ /// Collection of parent types to filter by.
+ IEnumerable GetParentTypes();
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/MvcWidgetProxy.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/MvcWidgetProxy.cs
index fbc2831cc..95dcf3821 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/MvcWidgetProxy.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Controllers/MvcWidgetProxy.cs
@@ -25,27 +25,51 @@ public string WidgetName
set
{
this.widgetName = value;
- if (this.Controller != null && this.Controller.ViewBag != null)
- this.Controller.ViewBag.WidgetName = this.widgetName;
+ UpdateControllerViewBag(this.Controller);
}
}
///
- /// Gets or sets the instance of that
- /// is being proxied.
+ /// Gets or sets the name of the module.
///
- public override Controller Controller
+ /// The name of the module.
+ [Browsable(true)]
+ public string ModuleName
{
get
{
- var controller = base.Controller;
- if (controller != null && controller.ViewBag != null && !this.WidgetName.IsNullOrEmpty())
- controller.ViewBag.WidgetName = this.WidgetName;
+ return this.moduleName;
+ }
- return controller;
+ set
+ {
+ this.moduleName = value;
+ UpdateControllerViewBag(this.Controller);
}
}
+ ///
+ protected override void OnControllerInitialized(Controller controller)
+ {
+ UpdateControllerViewBag(controller);
+ }
+
private string widgetName;
+ private string moduleName;
+
+ private void UpdateControllerViewBag(Controller controller)
+ {
+ if (controller == null || controller.ViewBag == null) return;
+
+ if (!this.WidgetName.IsNullOrEmpty())
+ {
+ controller.ViewBag.WidgetName = this.WidgetName;
+ }
+
+ if (!this.ModuleName.IsNullOrEmpty())
+ {
+ controller.ViewBag.ModuleName = this.ModuleName;
+ }
+ }
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/InfrastructureResources.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/InfrastructureResources.cs
index 6a72c5b9d..f1cab8b98 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/InfrastructureResources.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/InfrastructureResources.cs
@@ -1,4 +1,5 @@
-using Telerik.Sitefinity.Localization;
+using System.Diagnostics.CodeAnalysis;
+using Telerik.Sitefinity.Localization;
namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure
{
@@ -52,5 +53,232 @@ public string ErrorExecutingController
return this["ErrorExecutingController"];
}
}
+
+ ///
+ /// Caption of the Feather config section.
+ ///
+ [ResourceEntry("FeatherConfigCaption",
+ Value = "Feather",
+ Description = "Caption of the Feather config section.",
+ LastModified = "2015/11/23")]
+ public string FeatherConfigCaption
+ {
+ get
+ {
+ return this["FeatherConfigCaption"];
+ }
+ }
+
+ ///
+ /// Description of the Feather config section.
+ ///
+ [ResourceEntry("FeatherConfigDescription",
+ Value = "Configuration for the Feather module.",
+ Description = "Description of the Feather config section.",
+ LastModified = "2015/11/23")]
+ public string FeatherConfigDescription
+ {
+ get
+ {
+ return this["FeatherConfigDescription"];
+ }
+ }
+
+ ///
+ /// Caption of the 'Disable precompiled views' checkbox.
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Precompilation")]
+ [ResourceEntry("DisablePrecompilationCaption",
+ Value = "Disable precompiled views",
+ Description = "Caption of the 'Disable precompiled views' checkbox.",
+ LastModified = "2015/11/23")]
+ public string DisablePrecompilationCaption
+ {
+ get
+ {
+ return this["DisablePrecompilationCaption"];
+ }
+ }
+
+ ///
+ /// Description of the 'Disable precompiled views' checkbox.
+ ///
+ [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Precompilation")]
+ [ResourceEntry("DisablePrecompilationDescription",
+ Value = "Feather will use precompiled view templated unless this checkbox is checked.",
+ Description = "Description of the 'Disable precompiled views' checkbox.",
+ LastModified = "2015/11/23")]
+ public string DisablePrecompilationDescription
+ {
+ get
+ {
+ return this["DisablePrecompilationDescription"];
+ }
+ }
+
+ ///
+ /// Caption of the 'Always use precompiled version' checkbox.
+ ///
+ [ResourceEntry("AlwaysUsePrecompiledVersionCaption",
+ Value = "Always use precompiled version",
+ Description = "Caption of the 'Always use precompiled version' checkbox.",
+ LastModified = "2015/11/23")]
+ public string AlwaysUsePrecompiledVersionCaption
+ {
+ get
+ {
+ return this["AlwaysUsePrecompiledVersionCaption"];
+ }
+ }
+
+ ///
+ /// Description of the 'Always use precompiled version' checkbox.
+ ///
+ [ResourceEntry("AlwaysUsePrecompiledVersionDescription",
+ Value = "When checked Feather will always use the precompiled version of a resource regardless of changes to physical files.",
+ Description = "Description of the 'Always use precompiled version' checkbox.",
+ LastModified = "2015/11/23")]
+ public string AlwaysUsePrecompiledVersionDescription
+ {
+ get
+ {
+ return this["AlwaysUsePrecompiledVersionDescription"];
+ }
+ }
+
+ ///
+ /// Caption of the 'Log usage of precompiled views' checkbox.
+ ///
+ [ResourceEntry("LogPrecompiledViewUsageCaption",
+ Value = "Log usage of precompiled views",
+ Description = "Caption of the 'Log usage of precompiled views' checkbox.",
+ LastModified = "2015/11/24")]
+ public string LogPrecompiledViewUsageCaption
+ {
+ get
+ {
+ return this["LogPrecompiledViewUsageCaption"];
+ }
+ }
+
+ ///
+ /// Description of the 'Log usage of precompiled views' checkbox.
+ ///
+ [ResourceEntry("LogPrecompiledViewUsageDescription",
+ Value = "Feather will log when it uses precompiled views in the Trace log. Use for debugging.",
+ Description = "Description of the 'Log usage of precompiled views' checkbox.",
+ LastModified = "2015/11/24")]
+ public string LogPrecompiledViewUsageDescription
+ {
+ get
+ {
+ return this["LogPrecompiledViewUsageDescription"];
+ }
+ }
+
+ ///
+ /// Caption of the 'Log usage of precompiled views' checkbox.
+ ///
+ [ResourceEntry("UseNamedParametersRoutingCaption",
+ Value = "Use named route parameters",
+ Description = "Caption of the 'Use named route parameters' checkbox.",
+ LastModified = "2019/6/20")]
+ public string UseNamedParametersRoutingCaption
+ {
+ get
+ {
+ return this["UseNamedParametersRoutingCaption"];
+ }
+ }
+
+ ///
+ /// Description of the 'Log usage of precompiled views' checkbox.
+ ///
+ [ResourceEntry("UseNamedParametersRoutingDescription",
+ Value = "If enabled, the frontend routes will work only with named params (e.g /tag/london/page/2); otherwise, the route will be /london/2.",
+ Description = "Description of the 'Use named route parameters' checkbox.",
+ LastModified = "2019/6/20")]
+ public string UseNamedParametersRoutingDescription
+ {
+ get
+ {
+ return this["UseNamedParametersRoutingDescription"];
+ }
+ }
+
+ ///
+ /// Detailed error message when cannot find template in MVC form.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", MessageId = "Telerik.Sitefinity.Localization.Resource.get_Item(System.String)"), ResourceEntry("CannotFindTemplateMvcForm",
+ Value = "Cannot find template \"{0}\". Most probably it is specific for a particular resource package which is not applied in the current context. Try duplicating the template in SitefinityWebApp root folder while keeping the same folder hierarchy.",
+ Description = "Detailed error message when cannot find template in MVC form.",
+ LastModified = "2016/11/28")]
+ public string CannotFindTemplateMvcForm
+ {
+ get
+ {
+ return base["CannotFindTemplateMvcForm"];
+ }
+ }
+
+ ///
+ /// Title of the "Use cached controller container assemblies" setting
+ ///
+ [ResourceEntry("UseCachedControllerContainerAssembliesTitle",
+ Value = "Use cached controller container assemblies",
+ Description = "Title of the setting",
+ LastModified = "2019/9/04")]
+ public string UseCachedControllerContainerAssembliesTitle
+ {
+ get
+ {
+ return this["UseCachedControllerContainerAssembliesTitle"];
+ }
+ }
+
+ ///
+ /// Description of the "Use cached controller container assemblies" setting
+ ///
+ [ResourceEntry("UseCachedControllerContainerAssembliesDescription",
+ Value = "Use a JSON with the location to the assemblies that contain controllers.",
+ Description = "Description of the setting",
+ LastModified = "2019/9/04")]
+ public string UseCachedControllerContainerAssembliesDescription
+ {
+ get
+ {
+ return this["UseCachedControllerContainerAssembliesDescription"];
+ }
+ }
+
+ ///
+ /// Caption of the NinjectLoadExtensions config property
+ ///
+ [ResourceEntry("NinjectLoadExtensionsTitle",
+ Value = "Automatically load Ninject extensions",
+ Description = "Caption of the NinjectLoadExtensions config property",
+ LastModified = "2019/09/18")]
+ public string NinjectLoadExtensionsTitle
+ {
+ get
+ {
+ return this["NinjectLoadExtensionsTitle"];
+ }
+ }
+
+ ///
+ /// Description of the NinjectLoadExtensions config property
+ ///
+ [ResourceEntry("NinjectLoadExtensionsDescription",
+ Value = "Indicates whether the Ninject kernel should automatically load extensions at startup",
+ Description = "Description of the NinjectLoadExtensions config property",
+ LastModified = "2019/09/18")]
+ public string NinjectLoadExtensionsDescription
+ {
+ get
+ {
+ return this["NinjectLoadExtensionsDescription"];
+ }
+ }
}
}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutInitializer.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutInitializer.cs
index ea2756b8c..f9264fd4b 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutInitializer.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutInitializer.cs
@@ -1,30 +1,117 @@
-using Telerik.Microsoft.Practices.Unity;
-using Telerik.Sitefinity.Abstractions;
-using Telerik.Sitefinity.Abstractions.VirtualPath;
-using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
-using Telerik.Sitefinity.Modules.Pages;
-using Telerik.Sitefinity.Web;
-
-namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Layouts
-{
- ///
- /// This class contains logic for registration and initialization of the layouts.
- ///
- internal class LayoutInitializer
- {
- ///
- /// Registers the types and resolvers related to the layouts functionality.
- ///
- public virtual void Initialize()
- {
- ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
- ObjectFactory.Container.RegisterType("PureMvcPageResolver", new ContainerControlledLifetimeManager(), new InjectionConstructor());
-
- VirtualPathManager.AddVirtualFileResolver(string.Format(System.Globalization.CultureInfo.InvariantCulture, "~/{0}*", LayoutVirtualFileResolver.ResolverPath), typeof(LayoutVirtualFileResolver).FullName);
- ObjectFactory.Container.RegisterType();
- ObjectFactory.Container.RegisterType();
- ObjectFactory.Container.RegisterType();
- System.Web.Routing.RouteTable.Routes.Insert(1, new System.Web.Routing.Route("Sitefinity/Versioning/{itemId}/{VersionNumber}", ObjectFactory.Resolve()));
- }
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Telerik.Microsoft.Practices.Unity;
+using Telerik.Sitefinity.Abstractions;
+using Telerik.Sitefinity.Abstractions.VirtualPath;
+using Telerik.Sitefinity.Data;
+using Telerik.Sitefinity.Frontend.FilesMonitoring;
+using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Routing;
+using Telerik.Sitefinity.Modules.Pages;
+using Telerik.Sitefinity.Pages.Model;
+using Telerik.Sitefinity.Security;
+using Telerik.Sitefinity.Web;
+
+namespace Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Layouts
+{
+ ///
+ /// This class contains logic for registration and initialization of the layouts.
+ ///
+ internal class LayoutInitializer : IInitializer
+ {
+ ///
+ /// Registers the types and resolvers related to the layouts functionality.
+ ///
+ public virtual void Initialize()
+ {
+ ObjectFactory.Container.RegisterType(new ContainerControlledLifetimeManager());
+ ObjectFactory.Container.RegisterType("PureMvcPageResolver", new ContainerControlledLifetimeManager(), new InjectionConstructor());
+
+ VirtualPathManager.AddVirtualFileResolver(string.Format(System.Globalization.CultureInfo.InvariantCulture, "~/{0}*", LayoutVirtualFileResolver.ResolverPath), typeof(LayoutVirtualFileResolver).FullName);
+ ObjectFactory.Container.RegisterType();
+ ObjectFactory.Container.RegisterType();
+ ObjectFactory.Container.RegisterType();
+
+ this.mvcVersioningRoute = new System.Web.Routing.Route("Sitefinity/Versioning/{itemId}/{VersionNumber}", ObjectFactory.Resolve());
+ System.Web.Routing.RouteTable.Routes.Insert(1, this.mvcVersioningRoute);
+
+ PageManager.Executing -= LayoutInitializer.Provider_Executing;
+ PageManager.Executing += LayoutInitializer.Provider_Executing;
+
+ PageManager.Executed -= LayoutInitializer.Provider_Executed;
+ PageManager.Executed += LayoutInitializer.Provider_Executed;
+ }
+
+ ///
+ /// Uninitializes the functionality related to the layouts.
+ ///
+ public virtual void Uninitialize()
+ {
+ System.Web.Routing.RouteTable.Routes.Remove(this.mvcVersioningRoute);
+
+ PageManager.Executed -= LayoutInitializer.Provider_Executed;
+ PageManager.Executing -= LayoutInitializer.Provider_Executing;
+ }
+
+ private System.Web.Routing.Route mvcVersioningRoute;
+ private const string CreatedPageTemplatesCategoryIds = "created-page-templates";
+
+ private static void Provider_Executing(object sender, ExecutingEventArgs args)
+ {
+ if (!(args.CommandName == "CommitTransaction" || args.CommandName == "FlushTransaction"))
+ return;
+
+ var provider = sender as PageDataProvider;
+
+ var dirtyItems = provider.GetDirtyItems();
+ if (dirtyItems.Count == 0)
+ return;
+
+ var createdPageTemplates = provider.GetExecutionStateData(CreatedPageTemplatesCategoryIds) as HashSet;
+ if (createdPageTemplates == null)
+ createdPageTemplates = new HashSet();
+
+ foreach (var item in dirtyItems)
+ {
+ SecurityConstants.TransactionActionType itemStatus = provider.GetDirtyItemStatus(item);
+ var pageTemplate = item as PageTemplate;
+ if (pageTemplate != null)
+ {
+ if (itemStatus == SecurityConstants.TransactionActionType.New && pageTemplate.Framework == PageTemplateFramework.Mvc)
+ {
+ // the template name is formulated as Bootstrap.default
+ var nameParts = pageTemplate.Name.Split(new char[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
+ if (nameParts.Length > 1)
+ {
+ var package = nameParts[0];
+
+ // always create the template, desipite if the package is not there
+ // in case the client first performs a sync and then deploys the files by mistake
+ createdPageTemplates.Add(package);
+ }
+ }
+ }
+ }
+
+ if (createdPageTemplates.Any())
+ {
+ provider.SetExecutionStateData(CreatedPageTemplatesCategoryIds, createdPageTemplates);
+ }
+ }
+
+ private static void Provider_Executed(object sender, ExecutedEventArgs args)
+ {
+ if (args.CommandName != "CommitTransaction")
+ return;
+
+ var provider = sender as PageDataProvider;
+ var packageNames = provider.GetExecutionStateData(CreatedPageTemplatesCategoryIds) as HashSet;
+ if (packageNames != null)
+ {
+ provider.SetExecutionStateData(CreatedPageTemplatesCategoryIds, null);
+ var layoutFileManager = ObjectFactory.Resolve(Enum.GetName(typeof(ResourceType), ResourceType.Layouts)) as LayoutFileManager;
+ layoutFileManager.CreateTemplateCategories(packageNames, true);
+ }
+ }
+ }
+}
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutMvcPageResolver.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutMvcPageResolver.cs
index 3b03aea7b..83e4bc860 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutMvcPageResolver.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutMvcPageResolver.cs
@@ -52,9 +52,11 @@ protected override void BuildWithMasterPage(string virtualPath, RequestContext c
{
if (LayoutMvcPageResolver.IsLayoutPath(virtualPath))
{
- var httpContext = new HttpContextWrapper(new HttpContext(HttpContext.Current.Request, HttpContext.Current.Response));
- httpContext.Items[PackageManager.CurrentPackageKey] = context.HttpContext.Items[PackageManager.CurrentPackageKey];
- SystemManager.RunWithHttpContext(httpContext, () => base.BuildWithMasterPage(virtualPath, context, output, placeHolders, directives));
+ SystemManager.RunWithElevatedPrivilege((args) =>
+ {
+ HttpContext.Current.Items[PackageManager.CurrentPackageKey] = context.HttpContext.Items[PackageManager.CurrentPackageKey];
+ base.BuildWithMasterPage(virtualPath, context, output, placeHolders, directives);
+ });
}
else
{
diff --git a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutRenderer.cs b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutRenderer.cs
index 2842be38b..c8e6d7801 100644
--- a/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutRenderer.cs
+++ b/Telerik.Sitefinity.Frontend/Mvc/Infrastructure/Layouts/LayoutRenderer.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Hosting;
@@ -24,10 +25,9 @@ internal class LayoutRenderer
///
/// Creates a controller instance and sets its ControllerContext depending on the current Http context.
///
- ///
/// The route data.
- /// The context.
///
+ /// Can not create ControllerContext if no active HttpContext instance is available.
/// Can't create Controller Context if no active HttpContext instance is available.
public virtual Controller CreateController(RouteData routeData = null)
{
@@ -74,7 +74,7 @@ public virtual string RenderViewToString(ControllerContext context, string viewP
if (viewPath == null)
throw new ArgumentNullException("viewPath");
-
+
string result = null;
IView view = null;
@@ -89,12 +89,12 @@ public virtual string RenderViewToString(ControllerContext context, string viewP
// assigning the model so it can be available to the view
context.Controller.ViewData.Model = null;
- var builtView = view as BuildManagerCompiledView;
+ var viewVirtualPath = FrontendManager.VirtualPathBuilder.GetViewPath(view);
using (var writer = new StringWriter(System.Globalization.CultureInfo.InvariantCulture))
{
- if (placeholdersOnly && builtView != null)
+ if (placeholdersOnly && !viewVirtualPath.IsNullOrEmpty())
{
- this.RenderContentPlaceHolders(builtView.ViewPath, writer);
+ this.RenderContentPlaceHolders(viewVirtualPath, writer);
}
else
{
@@ -109,10 +109,10 @@ public virtual string RenderViewToString(ControllerContext context, string viewP
// Add cache dependency on the virtual file that is used for rendering the view.
var httpContext = SystemManager.CurrentHttpContext;
- if (httpContext != null && builtView != null)
+ if (httpContext != null && !viewVirtualPath.IsNullOrEmpty())
{
var virtualPathDependency = HostingEnvironment.VirtualPathProvider != null ?
- HostingEnvironment.VirtualPathProvider.GetCacheDependency(builtView.ViewPath, null, DateTime.UtcNow) : null;
+ HostingEnvironment.VirtualPathProvider.GetCacheDependency(viewVirtualPath, null, DateTime.UtcNow) : null;
if (virtualPathDependency != null)
{
httpContext.Response.AddCacheDependency(virtualPathDependency);
@@ -204,12 +204,7 @@ public string LayoutViewPath(string templateName)
if (viewEngineResult != null && viewEngineResult.View != null)
{
- var builtView = viewEngineResult.View as BuildManagerCompiledView;
- if (builtView != null)
- {
- result = builtView.ViewPath;
- }
-
+ result = FrontendManager.VirtualPathBuilder.GetViewPath(viewEngineResult.View);
viewEngineResult.ViewEngine.ReleaseView(genericController.ControllerContext, viewEngineResult.View);
}
@@ -258,6 +253,8 @@ private void RenderContentPlaceHolders(string layoutPath, StringWriter writer)
layoutText = layoutFile.ReadToEnd();
}
+ layoutText = this.RenderPartialViews(layoutPath, layoutText, new HashSet());
+
var matches = new Regex(@"@Html\.SfPlaceHolder(?:\(\s*\""(?\w*)\""\s*\)|(?\(\)))").Matches(layoutText);
foreach (Match match in matches)
{
@@ -275,6 +272,46 @@ private void RenderContentPlaceHolders(string layoutPath, StringWriter writer)
writer.Write("