- @*OrderBy() takes the property to sort by and optionally order desc/asc *@
-
+ @* OrderBy() takes the property to sort by and optionally order desc/asc *@
@foreach (var page in Model.Children.Where("Visible").OrderBy("CreateDate desc"))
{
- @*OrderBy() takes the property to sort by*@
+ @* OrderBy() takes the property to sort by *@
@foreach (var page in Model.Children.Where("Visible").OrderBy("Name"))
{
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml
index 8383d9077e..46846b2d3f 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesOrderedByProperty.cshtml
@@ -5,7 +5,6 @@
Show:True Alias:propertyAlias Name:Property Alias Type:Textstring
*@
-
@{
@* Get the property alias we want to filter on from the macro parameter *@
@@ -13,7 +12,6 @@
var selection = Model.Children.Where("Visible").OrderBy(propertyAlias);
}
-
@foreach (var page in selection)
{
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml
index 7bf279768b..d82a0823e0 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListChildPagesWithDoctype.cshtml
@@ -7,9 +7,8 @@
Settings section).
*@
-
@{
- @*Build a query and return the visible items *@
+ @* Build a query and return the visible items *@
var selection= Model.Textpages.Where("Visible");
@*
@@ -18,8 +17,7 @@
*@
}
-
-@*Determine if there are any nodes in the selection, then render list *@
+@* Determine if there are any nodes in the selection, then render list *@
@if(selection.Any()){
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml
index 7f63b3a98a..b27e6d5636 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/ListImagesFromMediaFolder.cshtml
@@ -1,5 +1,3 @@
-
-
@inherits umbraco.MacroEngines.DynamicNodeContext
@*
@@ -7,7 +5,6 @@ Macro Parameters To Create, for this macro to work:
Show:True Alias:mediaId Name:Media Folder ID Type:MediaCurrent
*@
-
@if (Parameter.mediaId != null)
{
@* Get the media folder as a dynamic node *@
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml
index 7e2d252473..3c1d149fbb 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/MultinodeTree-picker.cshtml
@@ -12,7 +12,6 @@
var selection = Model.PropertyWithPicker;
}
-
@* Lists each selected value from the picker as a link *@
@foreach(var id in selection){
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml
index dd8be57d9b..0c10425cd9 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/Navigation.cshtml
@@ -6,9 +6,8 @@
the css class "current".
*@
-
@{
- @*Get the root of the website *@
+ @* Get the root of the website *@
var root = Model.AncestorOrSelf(1);
}
diff --git a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml
index d9d2ec35df..987011c08c 100644
--- a/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml
+++ b/src/Umbraco.Web.UI/umbraco/scripting/templates/cshtml/SiteMap.cshtml
@@ -1,38 +1,34 @@
@inherits umbraco.MacroEngines.DynamicNodeContext
-@{
- @* Walk up the tree from the current page to get the root node *@
- var rootNode = Model.AncestorOrself(1);
-}
-
-@*Render the sitemap by passing the root node to the traverse helper*@
-
+@* Render the sitemap by passing the root node to the traverse helper *@
+
@traverse(@Model.AncestorOrSelf())
+@* Helper method to travers through all descendants *@
+@helper traverse(dynamic node)
+{
-
-@*Helper method to travers through all descendants*@
-@helper traverse(dynamic node){
-
-@*If a MaxLevelForSitemap parameter is passed to the macro, otherwise default to 4 levels*@
-var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap);
+ @* If a MaxLevelForSitemap parameter is passed to the macro, otherwise default to 4 levels *@
+ var maxLevelForSitemap = String.IsNullOrEmpty(Parameter.MaxLevelForSitemap) ? 4 : int.Parse(Parameter.MaxLevelForSitemap);
-@*Select visible children *@
-var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
+ @* Select visible children *@
+ var items = node.Children.Where("Visible").Where("Level <= " + maxLevelForSitemap);
-@*If any items are returned, render a list *@
-if (items.Any()) {
-
- @foreach (var item in items) {
-
- @item.Name
+ @* If any items are returned, render a list *@
+ if (items.Any())
+ {
+
+ @foreach (var item in items)
+ {
+
+ @item.Name
- @*Run the traverse helper again *@
- @traverse(item)
+ @*Run the traverse helper again *@
+ @traverse(item)
diff --git a/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js b/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js
index 3761c316a9..ddd51059a7 100644
--- a/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js
+++ b/src/Umbraco.Web.UI/umbraco_client/Editors/EditScript.js
@@ -44,10 +44,10 @@
submitSucces: function(t) {
if (t != 'true') {
- top.UmbSpeechBubble.ShowMessage('error', this._opts.text.fileErrorHeader, this._opts.text.fileErrorText);
+ top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.fileErrorHeader), unescape(this._opts.text.fileErrorText));
}
else {
- top.UmbSpeechBubble.ShowMessage('save', this._opts.text.fileSavedHeader, this._opts.text.fileSavedText);
+ top.UmbSpeechBubble.ShowMessage('save', unescape(this._opts.text.fileSavedHeader), unescape(this._opts.text.fileSavedText));
}
@@ -60,7 +60,7 @@
},
submitFailure: function(t) {
- top.UmbSpeechBubble.ShowMessage('error', this._opts.text.fileErrorHeader, this._opts.text.fileErrorText);
+ top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.fileErrorHeader), unescape(this._opts.text.fileErrorText));
}
});
})(jQuery);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js b/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js
index b68cc7e2ac..3fce5e3987 100644
--- a/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js
+++ b/src/Umbraco.Web.UI/umbraco_client/Editors/EditStyleSheet.js
@@ -44,10 +44,10 @@
submitSucces: function(t) {
if (t != 'true') {
- top.UmbSpeechBubble.ShowMessage('error', this._opts.text.cssErrorHeader, this._opts.text.cssErrorText);
+ top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.cssErrorHeader), unescape(this._opts.text.cssErrorText));
}
else {
- top.UmbSpeechBubble.ShowMessage('save', this._opts.text.cssSavedHeader, this._opts.text.cssSavedText);
+ top.UmbSpeechBubble.ShowMessage('save', unescape(this._opts.text.cssSavedHeader), unescape(this._opts.text.cssSavedText));
}
UmbClientMgr.mainTree().setActiveTreeType('stylesheets');
@@ -58,7 +58,7 @@
},
submitFailure: function(t) {
- top.UmbSpeechBubble.ShowMessage('error', this._opts.text.cssErrorHeader, this._opts.text.cssErrorText);
+ top.UmbSpeechBubble.ShowMessage('error', unescape(this._opts.text.cssErrorHeader), unescape(this._opts.text.cssErrorText));
}
});
})(jQuery);
\ No newline at end of file
diff --git a/src/Umbraco.Web.UI/umbraco_client/ui/default.css b/src/Umbraco.Web.UI/umbraco_client/ui/default.css
index ecc1163290..576bd0d0ce 100644
--- a/src/Umbraco.Web.UI/umbraco_client/ui/default.css
+++ b/src/Umbraco.Web.UI/umbraco_client/ui/default.css
@@ -743,7 +743,12 @@ table.tabs-table tr.propertyContent td
}
table.tabs-table input[type=text] { width: 200px; }
table.tabs-table input[type=submit] { text-align: right; }
-table.tabs-table tr.propertyContent input.sort-order {display: none;}
+
+table.tabs-table tr.propertyContent input.sort-order {
+ width: 20px;
+ background-color: lightgray;
+ padding: 0px 5px 0px 5px;
+}
li.no-properties-on-tab {background: none; background-color: #fff; cursor: default; }
diff --git a/src/Umbraco.Web.UI/web.Template.Debug.config b/src/Umbraco.Web.UI/web.Template.Debug.config
index 1ba153e45e..5c5a33545d 100644
--- a/src/Umbraco.Web.UI/web.Template.Debug.config
+++ b/src/Umbraco.Web.UI/web.Template.Debug.config
@@ -25,7 +25,16 @@
-
+
+
+
+
+
+
+
+
diff --git a/src/Umbraco.Web.UI/web.Template.Release.config b/src/Umbraco.Web.UI/web.Template.Release.config
index 3a244ea9f6..388480b0c3 100644
--- a/src/Umbraco.Web.UI/web.Template.Release.config
+++ b/src/Umbraco.Web.UI/web.Template.Release.config
@@ -23,6 +23,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs b/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs
index a9e52fbb2f..8b742b1b2c 100644
--- a/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/ApplicationCacheRefresher.cs
@@ -27,16 +27,19 @@ namespace Umbraco.Web.Cache
public override void RefreshAll()
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
+ base.RefreshAll();
}
public override void Refresh(int id)
{
Remove(id);
+ base.Refresh(id);
}
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationsCacheKey);
+ base.Remove(id);
}
}
diff --git a/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs b/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs
index ba2306bfe4..e267b441a2 100644
--- a/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/ApplicationTreeCacheRefresher.cs
@@ -27,16 +27,19 @@ namespace Umbraco.Web.Cache
public override void RefreshAll()
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
+ base.RefreshAll();
}
public override void Refresh(int id)
{
Remove(id);
+ base.Refresh(id);
}
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(CacheKeys.ApplicationTreeCacheKey);
+ base.Remove(id);
}
}
diff --git a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
index a9b01fb6e5..469463d7a2 100644
--- a/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
+++ b/src/Umbraco.Web/Cache/CacheRefresherEventHandler.cs
@@ -131,19 +131,40 @@ namespace Umbraco.Web.Cache
MediaService.Moving += MediaServiceMoving;
MediaService.Trashing += MediaServiceTrashing;
- ContentService.Created += ContentServiceCreated;
+ //Bind to content events - this is for unpublished content syncing across servers (primarily for examine)
+
+ ContentService.Saved += ContentServiceSaved;
+ ContentService.Deleted += ContentServiceDeleted;
ContentService.Copied += ContentServiceCopied;
+ //NOTE: we do not listen for the trashed event because there is no cache to update for content in that case since
+ // the unpublishing event handles that, and for examine with unpublished content indexes, we want to keep that data
+ // in the index, it's not until it's complete deleted that we want to remove it.
+
+ //public access events
+ Access.AfterSave += Access_AfterSave;
}
+ #region Public access event handlers
+
+ static void Access_AfterSave(Access sender, SaveEventArgs e)
+ {
+ DistributedCache.Instance.RefreshPublicAccess();
+ }
+
+ #endregion
+
#region Content service event handlers
///
- /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the
- /// case then we need to clear all user permissions cache.
+ /// Handles cache refreshgi for when content is copied
///
///
///
- static void ContentServiceCopied(IContentService sender, Core.Events.CopyEventArgs e)
+ ///
+ /// When an entity is copied new permissions may be assigned to it based on it's parent, if that is the
+ /// case then we need to clear all user permissions cache.
+ ///
+ static void ContentServiceCopied(IContentService sender, CopyEventArgs e)
{
//check if permissions have changed
var permissionsChanged = ((Content)e.Copy).WasPropertyDirty("PermissionsChanged");
@@ -151,23 +172,63 @@ namespace Umbraco.Web.Cache
{
DistributedCache.Instance.RefreshAllUserPermissionsCache();
}
+
+ //run the un-published cache refresher
+ DistributedCache.Instance.RefreshUnpublishedPageCache(e.Copy);
}
///
- /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the
- /// case then we need to clear all user permissions cache.
+ /// Handles cache refreshing for when content is deleted (not unpublished)
///
///
///
- static void ContentServiceCreated(IContentService sender, Core.Events.NewEventArgs e)
+ static void ContentServiceDeleted(IContentService sender, DeleteEventArgs e)
{
- //check if permissions have changed
- var permissionsChanged = ((Content)e.Entity).WasPropertyDirty("PermissionsChanged");
- if (permissionsChanged)
+ DistributedCache.Instance.RemoveUnpublishedPageCache(e.DeletedEntities.ToArray());
+ }
+
+ ///
+ /// Handles cache refreshing for when content is saved (not published)
+ ///
+ ///
+ ///
+ ///
+ /// When an entity is saved we need to notify other servers about the change in order for the Examine indexes to
+ /// stay up-to-date for unpublished content.
+ ///
+ /// When an entity is created new permissions may be assigned to it based on it's parent, if that is the
+ /// case then we need to clear all user permissions cache.
+ ///
+ static void ContentServiceSaved(IContentService sender, SaveEventArgs e)
+ {
+ var clearUserPermissions = false;
+ e.SavedEntities.ForEach(x =>
+ {
+ //check if it is new
+ if (x.IsNewEntity())
+ {
+ //check if permissions have changed
+ var permissionsChanged = ((Content)x).WasPropertyDirty("PermissionsChanged");
+ if (permissionsChanged)
+ {
+ clearUserPermissions = true;
+ }
+ }
+ });
+
+ if (clearUserPermissions)
{
DistributedCache.Instance.RefreshAllUserPermissionsCache();
}
- }
+
+ //filter out the entities that have only been saved (not newly published) since
+ // newly published ones will be synced with the published page cache refresher
+ var unpublished = e.SavedEntities.Where(x => x.JustPublished() == false);
+ //run the un-published cache refresher
+ DistributedCache.Instance.RefreshUnpublishedPageCache(unpublished.ToArray());
+ }
+
+
#endregion
#region ApplicationTree event handlers
@@ -451,12 +512,12 @@ namespace Umbraco.Web.Cache
InvalidateCacheForPermissionsChange(sender);
}
- void UserServiceSavedUser(IUserService sender, Core.Events.SaveEventArgs e)
+ static void UserServiceSavedUser(IUserService sender, SaveEventArgs e)
{
e.SavedEntities.ForEach(x => DistributedCache.Instance.RefreshUserCache(x.Id));
}
- void UserServiceDeletedUser(IUserService sender, Core.Events.DeleteEventArgs e)
+ static void UserServiceDeletedUser(IUserService sender, DeleteEventArgs e)
{
e.DeletedEntities.ForEach(x => DistributedCache.Instance.RemoveUserCache(x.Id));
}
@@ -545,22 +606,22 @@ namespace Umbraco.Web.Cache
#endregion
#region Media event handlers
- static void MediaServiceTrashing(IMediaService sender, Core.Events.MoveEventArgs e)
+ static void MediaServiceTrashing(IMediaService sender, MoveEventArgs e)
{
- DistributedCache.Instance.RemoveMediaCache(e.Entity);
+ DistributedCache.Instance.RemoveMediaCache(false, e.Entity);
}
- static void MediaServiceMoving(IMediaService sender, Core.Events.MoveEventArgs e)
+ static void MediaServiceMoving(IMediaService sender, MoveEventArgs e)
{
DistributedCache.Instance.RefreshMediaCache(e.Entity);
}
- static void MediaServiceDeleting(IMediaService sender, Core.Events.DeleteEventArgs e)
+ static void MediaServiceDeleting(IMediaService sender, DeleteEventArgs e)
{
- DistributedCache.Instance.RemoveMediaCache(e.DeletedEntities.ToArray());
+ DistributedCache.Instance.RemoveMediaCache(true, e.DeletedEntities.ToArray());
}
- static void MediaServiceSaved(IMediaService sender, Core.Events.SaveEventArgs e)
+ static void MediaServiceSaved(IMediaService sender, SaveEventArgs e)
{
DistributedCache.Instance.RefreshMediaCache(e.SavedEntities.ToArray());
}
@@ -568,27 +629,21 @@ namespace Umbraco.Web.Cache
#region Member event handlers
- static void MemberServiceDeleted(IMemberService sender, Core.Events.DeleteEventArgs e)
+ static void MemberServiceDeleted(IMemberService sender, DeleteEventArgs e)
{
- foreach (var m in e.DeletedEntities.ToArray())
- {
- DistributedCache.Instance.RemoveMemberCache(m.Id);
- }
+ DistributedCache.Instance.RemoveMemberCache(e.DeletedEntities.ToArray());
}
- static void MemberServiceSaved(IMemberService sender, Core.Events.SaveEventArgs e)
+ static void MemberServiceSaved(IMemberService sender, SaveEventArgs e)
{
- foreach (var m in e.SavedEntities.ToArray())
- {
- DistributedCache.Instance.RefreshMemberCache(m.Id);
- }
+ DistributedCache.Instance.RefreshMemberCache(e.SavedEntities.ToArray());
}
#endregion
#region Member group event handlers
- static void MemberGroupService_Deleted(IMemberGroupService sender, Core.Events.DeleteEventArgs e)
+ static void MemberGroupService_Deleted(IMemberGroupService sender, DeleteEventArgs e)
{
foreach (var m in e.DeletedEntities.ToArray())
{
@@ -596,7 +651,7 @@ namespace Umbraco.Web.Cache
}
}
- static void MemberGroupService_Saved(IMemberGroupService sender, Core.Events.SaveEventArgs e)
+ static void MemberGroupService_Saved(IMemberGroupService sender, SaveEventArgs e)
{
foreach (var m in e.SavedEntities.ToArray())
{
diff --git a/src/Umbraco.Web/Cache/DistributedCache.cs b/src/Umbraco.Web/Cache/DistributedCache.cs
index b66dd2174a..c993129859 100644
--- a/src/Umbraco.Web/Cache/DistributedCache.cs
+++ b/src/Umbraco.Web/Cache/DistributedCache.cs
@@ -39,6 +39,7 @@ namespace Umbraco.Web.Cache
public const string ApplicationCacheRefresherId = "B15F34A1-BC1D-4F8B-8369-3222728AB4C8";
public const string TemplateRefresherId = "DD12B6A0-14B9-46e8-8800-C154F74047C8";
public const string PageCacheRefresherId = "27AB3022-3DFA-47b6-9119-5945BC88FD66";
+ public const string UnpublishedPageCacheRefresherId = "55698352-DFC5-4DBE-96BD-A4A0F6F77145";
public const string MemberCacheRefresherId = "E285DF34-ACDC-4226-AE32-C0CB5CF388DA";
public const string MemberGroupCacheRefresherId = "187F236B-BD21-4C85-8A7C-29FBA3D6C00C";
public const string MediaCacheRefresherId = "B29286DD-2D40-4DDB-B325-681226589FEC";
@@ -53,6 +54,7 @@ namespace Umbraco.Web.Cache
public const string StylesheetPropertyCacheRefresherId = "2BC7A3A4-6EB1-4FBC-BAA3-C9E7B6D36D38";
public const string DataTypeCacheRefresherId = "35B16C25-A17E-45D7-BC8F-EDAB1DCC28D2";
public const string DictionaryCacheRefresherId = "D1D7E227-F817-4816-BFE9-6C39B6152884";
+ public const string PublicAccessCacheRefresherId = "1DB08769-B104-4F8B-850E-169CAC1DF2EC";
#endregion
@@ -220,4 +222,4 @@ namespace Umbraco.Web.Cache
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
index faede0bffc..7263776ae5 100644
--- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
+++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
@@ -13,6 +13,17 @@ namespace Umbraco.Web.Cache
///
internal static class DistributedCacheExtensions
{
+ #region Public access
+
+ public static void RefreshPublicAccess(this DistributedCache dc)
+ {
+ dc.RefreshByJson(new Guid(DistributedCache.PublicAccessCacheRefresherId),
+ PublicAccessCacheRefresher.SerializeToJsonPayload(
+ Access.GetXmlDocumentCopy()));
+ }
+
+ #endregion
+
#region Application tree cache
public static void RefreshAllApplicationTreeCache(this DistributedCache dc)
{
@@ -123,8 +134,7 @@ namespace Umbraco.Web.Cache
}
#endregion
-
-
+
#region Data type cache
///
/// Refreshes the cache amongst servers for a data type
@@ -232,15 +242,58 @@ namespace Umbraco.Web.Cache
public static void RemovePageCache(this DistributedCache dc, int documentId)
{
dc.Remove(new Guid(DistributedCache.PageCacheRefresherId), documentId);
- }
+ }
+
+ ///
+ /// invokes the unpublished page cache refresher
+ ///
+ ///
+ ///
+ public static void RefreshUnpublishedPageCache(this DistributedCache dc, params IContent[] content)
+ {
+ dc.Refresh(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content);
+ }
+
+ ///
+ /// invokes the unpublished page cache refresher
+ ///
+ ///
+ ///
+ public static void RemoveUnpublishedPageCache(this DistributedCache dc, params IContent[] content)
+ {
+ dc.Remove(new Guid(DistributedCache.UnpublishedPageCacheRefresherId), x => x.Id, content);
+ }
+
#endregion
#region Member cache
+
+ ///
+ /// Refreshes the cache among servers for a member
+ ///
+ ///
+ ///
+ public static void RefreshMemberCache(this DistributedCache dc, params IMember[] members)
+ {
+ dc.Refresh(new Guid(DistributedCache.MemberCacheRefresherId), x => x.Id, members);
+ }
+
+ ///
+ /// Removes the cache among servers for a member
+ ///
+ ///
+ ///
+ public static void RemoveMemberCache(this DistributedCache dc, params IMember[] members)
+ {
+ dc.Remove(new Guid(DistributedCache.MemberCacheRefresherId), x => x.Id, members);
+ }
+
///
/// Refreshes the cache among servers for a member
///
///
///
+ [Obsolete("Use the RefreshMemberCache with strongly typed IMember objects instead")]
public static void RefreshMemberCache(this DistributedCache dc, int memberId)
{
dc.Refresh(new Guid(DistributedCache.MemberCacheRefresherId), memberId);
@@ -251,6 +304,7 @@ namespace Umbraco.Web.Cache
///
///
///
+ [Obsolete("Use the RemoveMemberCache with strongly typed IMember objects instead")]
public static void RemoveMemberCache(this DistributedCache dc, int memberId)
{
dc.Remove(new Guid(DistributedCache.MemberCacheRefresherId), memberId);
@@ -291,7 +345,7 @@ namespace Umbraco.Web.Cache
public static void RefreshMediaCache(this DistributedCache dc, params IMedia[] media)
{
dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
- MediaCacheRefresher.SerializeToJsonPayload(media));
+ MediaCacheRefresher.SerializeToJsonPayload(MediaCacheRefresher.OperationType.Saved, media));
}
///
@@ -304,6 +358,7 @@ namespace Umbraco.Web.Cache
/// to clear all of the cache but the media item will be removed before the other servers can
/// look it up. Only here for legacy purposes.
///
+ [Obsolete("Ensure to clear with other RemoveMediaCache overload")]
public static void RemoveMediaCache(this DistributedCache dc, int mediaId)
{
dc.Remove(new Guid(DistributedCache.MediaCacheRefresherId), mediaId);
@@ -313,11 +368,14 @@ namespace Umbraco.Web.Cache
/// Removes the cache amongst servers for media items
///
///
+ ///
///
- public static void RemoveMediaCache(this DistributedCache dc, params IMedia[] media)
+ public static void RemoveMediaCache(this DistributedCache dc, bool isPermanentlyDeleted, params IMedia[] media)
{
- dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
- MediaCacheRefresher.SerializeToJsonPayload(media));
+ dc.RefreshByJson(new Guid(DistributedCache.MediaCacheRefresherId),
+ MediaCacheRefresher.SerializeToJsonPayload(
+ isPermanentlyDeleted ? MediaCacheRefresher.OperationType.Deleted : MediaCacheRefresher.OperationType.Trashed,
+ media));
}
#endregion
diff --git a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
index 53abacc7a5..a2de17626f 100644
--- a/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/MediaCacheRefresher.cs
@@ -1,15 +1,16 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.Web.Script.Serialization;
using Umbraco.Core;
using Umbraco.Core.Cache;
+using Umbraco.Core.IO;
using Umbraco.Core.Models;
using umbraco.interfaces;
using System.Linq;
namespace Umbraco.Web.Cache
{
-
///
/// A cache refresher to ensure media cache is updated
///
@@ -19,13 +20,13 @@ namespace Umbraco.Web.Cache
public class MediaCacheRefresher : JsonCacheRefresherBase
{
#region Static helpers
-
+
///
/// Converts the json to a JsonPayload object
///
///
///
- private static JsonPayload[] DeserializeFromJsonPayload(string json)
+ internal static JsonPayload[] DeserializeFromJsonPayload(string json)
{
var serializer = new JavaScriptSerializer();
var jsonObject = serializer.Deserialize(json);
@@ -35,12 +36,13 @@ namespace Umbraco.Web.Cache
///
/// Creates the custom Json payload used to refresh cache amongst the servers
///
+ ///
///
///
- internal static string SerializeToJsonPayload(params IMedia[] media)
+ internal static string SerializeToJsonPayload(OperationType operation, params IMedia[] media)
{
var serializer = new JavaScriptSerializer();
- var items = media.Select(FromMedia).ToArray();
+ var items = media.Select(x => FromMedia(x, operation)).ToArray();
var json = serializer.Serialize(items);
return json;
}
@@ -49,15 +51,17 @@ namespace Umbraco.Web.Cache
/// Converts a macro to a jsonPayload object
///
///
+ ///
///
- private static JsonPayload FromMedia(IMedia media)
+ internal static JsonPayload FromMedia(IMedia media, OperationType operation)
{
if (media == null) return null;
var payload = new JsonPayload
{
Id = media.Id,
- Path = media.Path
+ Path = media.Path,
+ Operation = operation
};
return payload;
}
@@ -66,10 +70,18 @@ namespace Umbraco.Web.Cache
#region Sub classes
- private class JsonPayload
+ internal enum OperationType
+ {
+ Saved,
+ Trashed,
+ Deleted
+ }
+
+ internal class JsonPayload
{
public string Path { get; set; }
public int Id { get; set; }
+ public OperationType Operation { get; set; }
}
#endregion
@@ -97,13 +109,15 @@ namespace Umbraco.Web.Cache
public override void Refresh(int id)
{
- ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
+ ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id), OperationType.Saved));
base.Refresh(id);
}
public override void Remove(int id)
- {
- ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id)));
+ {
+ ClearCache(FromMedia(ApplicationContext.Current.Services.MediaService.GetById(id),
+ //NOTE: we'll just default to trashed for this one.
+ OperationType.Trashed));
base.Remove(id);
}
@@ -121,7 +135,7 @@ namespace Umbraco.Web.Cache
string.Format("{0}_{1}_True", CacheKeys.MediaCacheKey, idPart));
// Also clear calls that only query this specific item!
- if (idPart == payload.Id.ToString())
+ if (idPart == payload.Id.ToString(CultureInfo.InvariantCulture))
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(
string.Format("{0}_{1}", CacheKeys.MediaCacheKey, payload.Id));
diff --git a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs b/src/Umbraco.Web/Cache/MemberCacheRefresher.cs
index 2b740e826d..97afd092a7 100644
--- a/src/Umbraco.Web/Cache/MemberCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/MemberCacheRefresher.cs
@@ -14,7 +14,7 @@ namespace Umbraco.Web.Cache
///
/// This is not intended to be used directly in your code and it should be sealed but due to legacy code we cannot seal it.
///
- public class MemberCacheRefresher : CacheRefresherBase
+ public class MemberCacheRefresher : TypedCacheRefresherBase
{
protected override MemberCacheRefresher Instance
@@ -44,6 +44,18 @@ namespace Umbraco.Web.Cache
base.Remove(id);
}
+ public override void Refresh(IMember instance)
+ {
+ ClearCache(instance.Id);
+ base.Refresh(instance);
+ }
+
+ public override void Remove(IMember instance)
+ {
+ ClearCache(instance.Id);
+ base.Remove(instance);
+ }
+
private void ClearCache(int id)
{
ApplicationContext.Current.ApplicationCache.ClearPartialViewCache();
diff --git a/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs b/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs
new file mode 100644
index 0000000000..0c700e9dfa
--- /dev/null
+++ b/src/Umbraco.Web/Cache/PublicAccessCacheRefresher.cs
@@ -0,0 +1,77 @@
+using System;
+using System.Xml;
+using Newtonsoft.Json;
+using umbraco.cms.businesslogic.web;
+using Umbraco.Core;
+using Umbraco.Core.Cache;
+
+namespace Umbraco.Web.Cache
+{
+ public sealed class PublicAccessCacheRefresher : JsonCacheRefresherBase
+ {
+ #region Static helpers
+
+ internal static JsonPayload DeserializeFromJsonPayload(string json)
+ {
+ return JsonConvert.DeserializeObject(json);
+ }
+
+ internal static string SerializeToJsonPayload(XmlDocument doc)
+ {
+ return JsonConvert.SerializeObject(FromXml(doc));
+ }
+
+ internal static JsonPayload FromXml(XmlDocument doc)
+ {
+ if (doc == null) return null;
+
+ var payload = new JsonPayload
+ {
+ XmlContent = doc.OuterXml
+ };
+ return payload;
+ }
+
+ #endregion
+
+ #region Sub classes
+
+ internal class JsonPayload
+ {
+ public string XmlContent { get; set; }
+ }
+
+ #endregion
+
+ protected override PublicAccessCacheRefresher Instance
+ {
+ get { return this; }
+ }
+
+ public override Guid UniqueIdentifier
+ {
+ get { return new Guid(DistributedCache.PublicAccessCacheRefresherId); }
+ }
+
+ public override string Name
+ {
+ get { return "Public access cache refresher"; }
+ }
+
+ public override void Refresh(string jsonPayload)
+ {
+ if (jsonPayload.IsNullOrWhiteSpace()) return;
+ var deserialized = DeserializeFromJsonPayload(jsonPayload);
+ if (deserialized == null) return;
+ var xDoc = new XmlDocument();
+ xDoc.LoadXml(deserialized.XmlContent);
+ ClearCache(xDoc);
+ base.Refresh(jsonPayload);
+ }
+
+ private void ClearCache(XmlDocument xDoc)
+ {
+ Access.UpdateInMemoryDocument(xDoc);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs
new file mode 100644
index 0000000000..4413a61e9b
--- /dev/null
+++ b/src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs
@@ -0,0 +1,33 @@
+using System;
+using Umbraco.Core.Cache;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Cache
+{
+ ///
+ /// A cache refresher used for non-published content, this is primarily to notify Examine indexes to update
+ ///
+ public sealed class UnpublishedPageCacheRefresher : TypedCacheRefresherBase
+ {
+ protected override UnpublishedPageCacheRefresher Instance
+ {
+ get { return this; }
+ }
+
+ public override Guid UniqueIdentifier
+ {
+ get { return new Guid(DistributedCache.UnpublishedPageCacheRefresherId); }
+ }
+
+ public override string Name
+ {
+ get { return "Unpublished Page Refresher"; }
+ }
+
+ //NOTE: There is no functionality for this cache refresher, it is here simply to emit events on each server for which examine
+ // binds to. We could put the Examine index functionality in here but we've kept it all in the ExamineEvents class so that all of
+ // the logic is in one place. In the future we may put the examine logic in a cache refresher instead (that would make sense) but we'd
+ // want to get this done before making more cache refreshers:
+ // http://issues.umbraco.org/issue/U4-2633
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Cache/UserCacheRefresher.cs b/src/Umbraco.Web/Cache/UserCacheRefresher.cs
index dda11ee2c4..deb36ae894 100644
--- a/src/Umbraco.Web/Cache/UserCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/UserCacheRefresher.cs
@@ -32,11 +32,13 @@ namespace Umbraco.Web.Cache
RuntimeCacheProvider.Current.Clear(typeof(IUser));
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey);
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey);
+ base.RefreshAll();
}
public override void Refresh(int id)
{
Remove(id);
+ base.Refresh(id);
}
public override void Remove(int id)
@@ -48,6 +50,8 @@ namespace Umbraco.Web.Cache
//we need to clear all UserContextCacheKey since we cannot invalidate based on ID since the cache is done so based
//on the current contextId stored in the database
ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserContextCacheKey);
+
+ base.Remove(id);
}
}
diff --git a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
index fdafdedb09..e61ea0d7e0 100644
--- a/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/UserPermissionsCacheRefresher.cs
@@ -31,17 +31,20 @@ namespace Umbraco.Web.Cache
public override void RefreshAll()
{
- ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey);
+ ApplicationContext.Current.ApplicationCache.ClearCacheByKeySearch(CacheKeys.UserPermissionsCacheKey);
+ base.RefreshAll();
}
public override void Refresh(int id)
{
Remove(id);
+ base.Refresh(id);
}
public override void Remove(int id)
{
ApplicationContext.Current.ApplicationCache.ClearCacheItem(string.Format("{0}{1}", CacheKeys.UserPermissionsCacheKey, id));
+ base.Remove(id);
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs b/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs
index 7c4628dbe3..1a726da815 100644
--- a/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs
+++ b/src/Umbraco.Web/Cache/UserTypeCacheRefresher.cs
@@ -29,16 +29,19 @@ namespace Umbraco.Web.Cache
public override void RefreshAll()
{
RuntimeCacheProvider.Current.Clear(typeof (IUserType));
+ base.RefreshAll();
}
public override void Refresh(int id)
{
RuntimeCacheProvider.Current.Delete(typeof(IUserType), id);
+ base.Refresh(id);
}
public override void Remove(int id)
{
RuntimeCacheProvider.Current.Delete(typeof(IUserType), id);
+ base.Remove(id);
}
}
diff --git a/src/Umbraco.Web/Controllers/UmbLoginController.cs b/src/Umbraco.Web/Controllers/UmbLoginController.cs
index 33d657cc3f..0d4ad8aa4a 100644
--- a/src/Umbraco.Web/Controllers/UmbLoginController.cs
+++ b/src/Umbraco.Web/Controllers/UmbLoginController.cs
@@ -32,9 +32,9 @@ namespace Umbraco.Web.Controllers
}
//redirect to current page by default
- TempData.Add("LoginSuccess", true);
- //return RedirectToCurrentUmbracoPage();
- return RedirectToCurrentUmbracoUrl();
+ TempData["LoginSuccess"] = true;
+ return RedirectToCurrentUmbracoPage();
+ //return RedirectToCurrentUmbracoUrl();
}
}
}
diff --git a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs
index a4081413c3..e1d9a7aab1 100644
--- a/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs
+++ b/src/Umbraco.Web/Controllers/UmbLoginStatusController.cs
@@ -30,7 +30,7 @@ namespace Umbraco.Web.Controllers
}
//redirect to current page by default
- TempData.Add("LogoutSuccess", true);
+ TempData["LogoutSuccess"] = true;
return RedirectToCurrentUmbracoPage();
}
}
diff --git a/src/Umbraco.Web/Controllers/UmbProfileController.cs b/src/Umbraco.Web/Controllers/UmbProfileController.cs
index 03c5ee91ff..dfcf17a543 100644
--- a/src/Umbraco.Web/Controllers/UmbProfileController.cs
+++ b/src/Umbraco.Web/Controllers/UmbProfileController.cs
@@ -16,7 +16,8 @@ namespace Umbraco.Web.Controllers
[HttpPost]
public ActionResult HandleUpdateProfile([Bind(Prefix = "profileModel")] ProfileModel model)
{
- if (Membership.Provider.IsUmbracoMembershipProvider() == false)
+ var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
+ if (provider.IsUmbracoMembershipProvider() == false)
{
throw new NotSupportedException("Profile editing with the " + typeof(UmbProfileController) + " is not supported when not using the default Umbraco membership provider");
}
@@ -41,7 +42,7 @@ namespace Umbraco.Web.Controllers
}
//redirect to current page by default
- TempData.Add("ProfileUpdateSuccess", true);
+ TempData["ProfileUpdateSuccess"] = true;
return RedirectToCurrentUmbracoPage();
}
}
diff --git a/src/Umbraco.Web/Controllers/UmbRegisterController.cs b/src/Umbraco.Web/Controllers/UmbRegisterController.cs
index c661c24f69..dea1135131 100644
--- a/src/Umbraco.Web/Controllers/UmbRegisterController.cs
+++ b/src/Umbraco.Web/Controllers/UmbRegisterController.cs
@@ -33,7 +33,7 @@ namespace Umbraco.Web.Controllers
return Redirect(model.RedirectUrl);
}
//redirect to current page by default
- TempData.Add("FormSuccess", true);
+ TempData["FormSuccess"] = true;
return RedirectToCurrentUmbracoPage();
case MembershipCreateStatus.InvalidUserName:
ModelState.AddModelError((model.UsernameIsEmail || model.Username == null)
diff --git a/src/Umbraco.Web/Install/Steps/Database.cs b/src/Umbraco.Web/Install/Steps/Database.cs
index ff3363947a..a20c07a122 100644
--- a/src/Umbraco.Web/Install/Steps/Database.cs
+++ b/src/Umbraco.Web/Install/Steps/Database.cs
@@ -1,8 +1,9 @@
using System;
+using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
-using Umbraco.Web.Install;
+using Umbraco.Core.Logging;
namespace Umbraco.Web.Install.Steps
{
@@ -32,22 +33,39 @@ namespace Umbraco.Web.Install.Steps
//here we determine if the installer should skip this step...
public override bool Completed()
{
+
// Fresh installs don't have a version number so this step cannot be complete yet
- if (string.IsNullOrEmpty(Umbraco.Core.Configuration.GlobalSettings.ConfigurationStatus))
+ if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus))
{
//Even though the ConfigurationStatus is blank we try to determine the version if we can connect to the database
var result = ApplicationContext.Current.DatabaseContext.ValidateDatabaseSchema();
var determinedVersion = result.DetermineInstalledVersion();
if (determinedVersion.Equals(new Version(0, 0, 0)))
- return false;
+ {
+ // Now that we know this is a brand new Umbraco install, turn membership providers' "useLegacyEncoding" off
+ DisableMembershipProviderLegacyEncoding();
+ return false;
+ }
return UmbracoVersion.Current < determinedVersion;
}
- var configuredVersion = new Version(Umbraco.Core.Configuration.GlobalSettings.ConfigurationStatus);
+ var configuredVersion = new Version(GlobalSettings.ConfigurationStatus);
var targetVersion = UmbracoVersion.Current;
return targetVersion < configuredVersion;
}
+
+ private static void DisableMembershipProviderLegacyEncoding()
+ {
+ if (GlobalSettings.UmbracoMembershipProviderLegacyEncoding)
+ GlobalSettings.UmbracoMembershipProviderLegacyEncoding = false;
+
+ if (GlobalSettings.UmbracoUsersMembershipProviderLegacyEncoding)
+ GlobalSettings.UmbracoUsersMembershipProviderLegacyEncoding = false;
+
+
+ LogHelper.Info("Updated Umbraco membership providers, set useLegacyEncoding to false.");
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Models/DynamicPublishedContent.cs b/src/Umbraco.Web/Models/DynamicPublishedContent.cs
index ea0b12b97b..eb476f50a0 100644
--- a/src/Umbraco.Web/Models/DynamicPublishedContent.cs
+++ b/src/Umbraco.Web/Models/DynamicPublishedContent.cs
@@ -329,37 +329,9 @@ namespace Umbraco.Web.Models
// can handle properties only when using the proper casing. So what this
// does is ensure that any casing is supported.
- Func> getMember =
- memberAlias =>
- {
- try
- {
- return Attempt.Succeed(
- content.GetType().InvokeMember(memberAlias,
- System.Reflection.BindingFlags.GetProperty |
- System.Reflection.BindingFlags.Instance |
- System.Reflection.BindingFlags.Public,
- null,
- content,
- null));
- }
- catch (MissingMethodException ex)
- {
- return Attempt.Fail(ex);
- }
- };
+ var attempt = content.GetType().GetMemberIgnoreCase(content, alias);
- //try with the current casing
- var attempt = getMember(alias);
- if (!attempt.Success)
- {
- //if we cannot get with the current alias, try changing it's case
- attempt = alias[0].IsUpperCase()
- ? getMember(alias.ToCleanString(CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.CamelCase))
- : getMember(alias.ToCleanString(CleanStringType.Ascii | CleanStringType.ConvertCase | CleanStringType.PascalCase));
- }
-
- return !attempt.Success
+ return attempt.Success == false || attempt.Result == null
? null
: new PropertyResult(alias, attempt.Result, Guid.Empty, PropertyResultType.ReflectedProperty);
}
diff --git a/src/Umbraco.Web/Models/ProfileModel.cs b/src/Umbraco.Web/Models/ProfileModel.cs
index 7ce1593c4b..ee1ea71ac4 100644
--- a/src/Umbraco.Web/Models/ProfileModel.cs
+++ b/src/Umbraco.Web/Models/ProfileModel.cs
@@ -4,14 +4,25 @@ using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
+using System.Web.Mvc;
+using System.Xml;
+using System.Xml.Linq;
using umbraco.cms.businesslogic.member;
using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.PublishedCache;
using Umbraco.Web.Security;
namespace Umbraco.Web.Models
{
+ ///
+ /// A readonly member profile model
+ ///
+ [ModelBinder(typeof(ProfileModelBinder))]
public class ProfileModel : PostRedirectModel
{
+
public static ProfileModel CreateModel()
{
var model = new ProfileModel(false);
@@ -24,7 +35,7 @@ namespace Umbraco.Web.Models
if (doLookup)
{
var helper = new MembershipHelper(ApplicationContext.Current, new HttpContextWrapper(HttpContext.Current));
- var model = helper.CreateProfileModel();
+ var model = helper.GetCurrentMemberProfileModel();
MemberProperties = model.MemberProperties;
}
}
@@ -49,7 +60,6 @@ namespace Umbraco.Web.Models
/// The member's member type alias
///
[ReadOnly(true)]
- [Obsolete("This is not used and will be removed from the codebase in future versions")]
public string MemberTypeAlias { get; set; }
[ReadOnly(true)]
@@ -90,5 +100,16 @@ namespace Umbraco.Web.Models
///
public List MemberProperties { get; set; }
+ ///
+ /// A custom model binder for MVC because the default ctor performs a lookup!
+ ///
+ internal class ProfileModelBinder : DefaultModelBinder
+ {
+ protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
+ {
+ return ProfileModel.CreateModel();
+ }
+
+ }
}
}
diff --git a/src/Umbraco.Web/Models/PublishedContentBase.cs b/src/Umbraco.Web/Models/PublishedContentBase.cs
index 3db343c8db..2ca1978c29 100644
--- a/src/Umbraco.Web/Models/PublishedContentBase.cs
+++ b/src/Umbraco.Web/Models/PublishedContentBase.cs
@@ -50,7 +50,7 @@ namespace Umbraco.Web.Models
_url = prop.Value.ToString();
break;
default:
- throw new ArgumentOutOfRangeException();
+ throw new NotSupportedException();
}
return _url;
diff --git a/src/Umbraco.Web/Models/RegisterModel.cs b/src/Umbraco.Web/Models/RegisterModel.cs
index a65e7a990b..ad26aca17f 100644
--- a/src/Umbraco.Web/Models/RegisterModel.cs
+++ b/src/Umbraco.Web/Models/RegisterModel.cs
@@ -4,12 +4,14 @@ using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
+using System.Web.Mvc;
using umbraco.cms.businesslogic.member;
using Umbraco.Core;
using Umbraco.Web.Security;
namespace Umbraco.Web.Models
{
+ [ModelBinder(typeof(RegisterModelBinder))]
public class RegisterModel : PostRedirectModel
{
///
@@ -58,6 +60,7 @@ namespace Umbraco.Web.Models
///
/// The member type alias to use to register the member
///
+ [Editable(false)]
public string MemberTypeAlias { get; set; }
///
@@ -90,5 +93,16 @@ namespace Umbraco.Web.Models
///
public bool LoginOnSuccess { get; set; }
+ ///
+ /// A custom model binder for MVC because the default ctor performs a lookup!
+ ///
+ internal class RegisterModelBinder : DefaultModelBinder
+ {
+ protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
+ {
+ return RegisterModel.CreateModel();
+ }
+ }
+
}
}
diff --git a/src/Umbraco.Web/Models/UmbracoProperty.cs b/src/Umbraco.Web/Models/UmbracoProperty.cs
index ad907df1a0..93f0238e9f 100644
--- a/src/Umbraco.Web/Models/UmbracoProperty.cs
+++ b/src/Umbraco.Web/Models/UmbracoProperty.cs
@@ -1,12 +1,40 @@
-namespace Umbraco.Web.Models
+using System.ComponentModel;
+using System.ComponentModel.DataAnnotations;
+using System.Xml;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+
+namespace Umbraco.Web.Models
{
///
/// A simple representation of an Umbraco property
///
public class UmbracoProperty
{
+ [Editable(false)]
public string Alias { get; set; }
+
+ //NOTE: This has to be a string currently, if it is an object it will bind as an array which we don't want.
+ // If we want to have this as an 'object' with a true type on it, we have to create a custom model binder
+ // for an UmbracoProperty and then bind with the correct type based on the property type for this alias. This
+ // would be a bit long winded and perhaps unnecessary. The reason is because it is always posted as a string anyways
+ // and when we set this value on the property object that gets sent to the database we do a TryConvertTo to the
+ // real type anyways.
+
+ [DataType(DataType.Text)]
public string Value { get; set; }
+
+ [ReadOnly(true)]
public string Name { get; set; }
+
+ //TODO: Perhaps one day we'll ship with our own EditorTempates but for now developers can just render their own inside the view
+
+ /////
+ ///// This can dynamically be set to a custom template name to change
+ ///// the editor type for this property
+ /////
+ //[ReadOnly(true)]
+ //public string EditorTemplate { get; set; }
+
}
}
diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs
index 087f124c6a..03e31f34c4 100644
--- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs
+++ b/src/Umbraco.Web/Mvc/ControllerExtensions.cs
@@ -7,6 +7,22 @@ namespace Umbraco.Web.Mvc
{
internal static class ControllerExtensions
{
+ internal static object GetDataTokenInViewContextHierarchy(this ControllerContext controllerContext, string dataTokenName)
+ {
+ if (controllerContext.RouteData.DataTokens.ContainsKey(dataTokenName))
+ {
+ return controllerContext.RouteData.DataTokens[dataTokenName];
+ }
+
+ if (controllerContext.ParentActionViewContext != null)
+ {
+ //recurse!
+ return controllerContext.ParentActionViewContext.GetDataTokenInViewContextHierarchy(dataTokenName);
+ }
+
+ return null;
+ }
+
///
/// Return the controller name from the controller type
///
diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs
index 6fb3610411..e205966f78 100644
--- a/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs
+++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoPageResult.cs
@@ -8,43 +8,6 @@ using Umbraco.Web.Routing;
namespace Umbraco.Web.Mvc
{
///
- /// Redirects to the current URL rendering an Umbraco page
- ///
- ///
- /// this is useful if you need to redirect
- /// to the current page but the current page is actually a rewritten URL normally done with something like
- /// Server.Transfer.
- ///
- public class RedirectToUmbracoUrlResult : ActionResult
- {
- private readonly UmbracoContext _umbracoContext;
-
- ///
- /// Creates a new RedirectToUmbracoResult
- ///
- ///
- public RedirectToUmbracoUrlResult(UmbracoContext umbracoContext)
- {
- _umbracoContext = umbracoContext;
- }
-
- public override void ExecuteResult(ControllerContext context)
- {
- if (context == null) throw new ArgumentNullException("context");
-
- if (context.IsChildAction)
- {
- throw new InvalidOperationException("Cannot redirect from a Child Action");
- }
-
- var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery;
- context.Controller.TempData.Keep();
-
- context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
- }
- }
-
- ///
/// Redirects to an Umbraco page by Id or Entity
///
public class RedirectToUmbracoPageResult : ActionResult
diff --git a/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs
new file mode 100644
index 0000000000..fc472c0255
--- /dev/null
+++ b/src/Umbraco.Web/Mvc/RedirectToUmbracoUrlResult.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Web.Mvc;
+
+namespace Umbraco.Web.Mvc
+{
+ ///
+ /// Redirects to the current URL rendering an Umbraco page
+ ///
+ ///
+ /// this is useful if you need to redirect
+ /// to the current page but the current page is actually a rewritten URL normally done with something like
+ /// Server.Transfer.
+ ///
+ public class RedirectToUmbracoUrlResult : ActionResult
+ {
+ private readonly UmbracoContext _umbracoContext;
+
+ ///
+ /// Creates a new RedirectToUmbracoResult
+ ///
+ ///
+ public RedirectToUmbracoUrlResult(UmbracoContext umbracoContext)
+ {
+ _umbracoContext = umbracoContext;
+ }
+
+ public override void ExecuteResult(ControllerContext context)
+ {
+ if (context == null) throw new ArgumentNullException("context");
+
+ if (context.IsChildAction)
+ {
+ throw new InvalidOperationException("Cannot redirect from a Child Action");
+ }
+
+ var destinationUrl = _umbracoContext.OriginalRequestUrl.PathAndQuery;
+ context.Controller.TempData.Keep();
+
+ context.HttpContext.Response.Redirect(destinationUrl, endResponse: false);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Mvc/RenderActionInvoker.cs b/src/Umbraco.Web/Mvc/RenderActionInvoker.cs
index 4fa9702a61..f4efc6a570 100644
--- a/src/Umbraco.Web/Mvc/RenderActionInvoker.cs
+++ b/src/Umbraco.Web/Mvc/RenderActionInvoker.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using System.Web.Mvc;
namespace Umbraco.Web.Mvc
@@ -25,7 +26,13 @@ namespace Umbraco.Web.Mvc
//check if the controller is an instance of IRenderMvcController
if (controllerContext.Controller is IRenderMvcController)
{
- return new ReflectedActionDescriptor(controllerContext.Controller.GetType().GetMethod("Index"), "Index", controllerDescriptor);
+ return new ReflectedActionDescriptor(
+ controllerContext.Controller.GetType().GetMethods()
+ .First(x => x.Name == "Index" &&
+ x.GetCustomAttributes(typeof (NonActionAttribute), false).Any() == false),
+ "Index",
+ controllerDescriptor);
+
}
}
return ad;
diff --git a/src/Umbraco.Web/Mvc/RenderViewEngine.cs b/src/Umbraco.Web/Mvc/RenderViewEngine.cs
index 650794b3a2..1e49314429 100644
--- a/src/Umbraco.Web/Mvc/RenderViewEngine.cs
+++ b/src/Umbraco.Web/Mvc/RenderViewEngine.cs
@@ -94,19 +94,17 @@ namespace Umbraco.Web.Mvc
///
private bool ShouldFindView(ControllerContext controllerContext, bool isPartial)
{
+ var umbracoToken = controllerContext.GetDataTokenInViewContextHierarchy("umbraco");
+
//first check if we're rendering a partial view for the back office, or surface controller, etc...
//anything that is not IUmbracoRenderModel as this should only pertain to Umbraco views.
- if (isPartial
- && controllerContext.RouteData.DataTokens.ContainsKey("umbraco")
- && !(controllerContext.RouteData.DataTokens["umbraco"] is RenderModel))
+ if (isPartial && umbracoToken is RenderModel)
{
return true;
}
//only find views if we're rendering the umbraco front end
- if (controllerContext.RouteData.DataTokens.ContainsKey("umbraco")
- && controllerContext.RouteData.DataTokens["umbraco"] != null
- && controllerContext.RouteData.DataTokens["umbraco"] is RenderModel)
+ if (umbracoToken is RenderModel)
{
return true;
}
diff --git a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
index 373e103a56..b7220b7cc1 100644
--- a/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
+++ b/src/Umbraco.Web/Mvc/UmbracoViewPageOfTModel.cs
@@ -141,6 +141,7 @@ namespace Umbraco.Web.Mvc
// maps model
protected override void SetViewData(ViewDataDictionary viewData)
{
+ // if view data contains no model, nothing to do
var source = viewData.Model;
if (source == null)
{
@@ -148,6 +149,8 @@ namespace Umbraco.Web.Mvc
return;
}
+ // get the type of the view data model (what we have)
+ // get the type of this view model (what we want)
var sourceType = source.GetType();
var targetType = typeof (TModel);
@@ -160,13 +163,15 @@ namespace Umbraco.Web.Mvc
// try to grab the content
// if no content is found, return, nothing we can do
- var sourceContent = source as IPublishedContent;
+ var sourceContent = source as IPublishedContent; // check if what we have is an IPublishedContent
if (sourceContent == null && sourceType.Implements())
{
+ // else check if it's an IRenderModel => get the content
sourceContent = ((IRenderModel)source).Content;
}
if (sourceContent == null)
{
+ // else check if we can convert it to a content
var attempt = source.TryConvertTo();
if (attempt.Success) sourceContent = attempt.Result;
}
diff --git a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
index a80aaa41b3..f020dc6097 100644
--- a/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/IPublishedContentCache.cs
@@ -33,5 +33,53 @@ namespace Umbraco.Web.PublishedCache
/// The route.
/// The value of overrides the context.
string GetRouteById(UmbracoContext umbracoContext, bool preview, int contentId);
+
+ ///
+ /// Creates a content fragment.
+ ///
+ /// The content type alias.
+ /// The content property raw values.
+ /// A value indicating whether the fragment is previewing.
+ /// A value indicating whether the fragment is managed by the cache.
+ /// The newly created content fragment.
+ //
+ // notes
+ //
+ // in XmlPublishedCache, IPublishedContent instances are not meant to survive longer
+ // that a request or else we cannot guarantee that the converted property values will
+ // be properly managed - because XmlPublishedProperty just stores the result of the
+ // conversion locally.
+ //
+ // in DrippingPublishedCache, IPublishedContent instances are meant to survive for as
+ // long as the content itself has not been modified, and the property respects the
+ // converter's indication ie whether the converted value should be cached at
+ // .Content - cache until the content changes
+ // .ContentCache - cache until any content changes
+ // .Request - cache for the current request
+ //
+ // a fragment can be either "detached" or "managed".
+ // detached: created from code, managed by code, converted property values are
+ // cached within the fragment itself for as long as the fragment lives
+ // managed: created from a property converter as part of a content, managed by
+ // the cache, converted property values can be cached...
+ //
+ // XmlPublishedCache: same as content properties, store the result of the
+ // conversion locally, neither content nor fragments should survive longer
+ // than a request
+ // DrippingPublishedCache: depends
+ // .Content: cache within the fragment
+ // .ContentCache, .Request: cache within the cache
+ //
+ // in the latter case, use a fragment-owned guid as the cache key. because we
+ // don't really have any other choice. this opens potential memory leaks: if the
+ // fragment is re-created on each request and has a property that caches its
+ // converted value at .ContentCache level then we'll flood that cache with data
+ // that's never removed (as long as no content is edited).
+ //
+ // so a requirement should be that any converter that creates fragment, should
+ // be marked .Content -- and nothing else
+ //
+ IPublishedContent CreateFragment(string contentTypeAlias, IDictionary dataValues,
+ bool isPreviewing, bool managed);
}
}
diff --git a/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs
new file mode 100644
index 0000000000..deafcaab40
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/MemberPublishedContent.cs
@@ -0,0 +1,219 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Web.Security;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Models;
+
+namespace Umbraco.Web.PublishedCache
+{
+ ///
+ /// Exposes a member object as IPublishedContent
+ ///
+ internal class MemberPublishedContent : PublishedContentBase
+ {
+
+ private readonly IMember _member;
+ private readonly MembershipUser _membershipUser;
+ private readonly List _properties;
+ private readonly PublishedContentType _publishedMemberType;
+
+ public MemberPublishedContent(IMember member, MembershipUser membershipUser)
+ {
+ if (member == null) throw new ArgumentNullException("member");
+ if (membershipUser == null) throw new ArgumentNullException("membershipUser");
+
+ _member = member;
+ _membershipUser = membershipUser;
+ _properties = new List();
+ _publishedMemberType = PublishedContentType.Get(PublishedItemType.Member, _member.ContentTypeAlias);
+ if (_publishedMemberType == null)
+ {
+ throw new InvalidOperationException("Could not get member type with alias " + _member.ContentTypeAlias);
+ }
+ foreach (var propType in _publishedMemberType.PropertyTypes)
+ {
+ var val = _member.Properties.Any(x => x.Alias == propType.PropertyTypeAlias) == false
+ ? string.Empty
+ : _member.Properties[propType.PropertyTypeAlias].Value;
+ _properties.Add(new RawValueProperty(propType, val ?? string.Empty));
+ }
+ }
+
+ #region Membership provider member properties
+ public string Email
+ {
+ get { return _membershipUser.Email; }
+ }
+ public string UserName
+ {
+ get { return _membershipUser.UserName; }
+ }
+ public string PasswordQuestion
+ {
+ get { return _membershipUser.PasswordQuestion; }
+ }
+ public string Comments
+ {
+ get { return _membershipUser.Comment; }
+ }
+ public bool IsApproved
+ {
+ get { return _membershipUser.IsApproved; }
+ }
+ public bool IsLockedOut
+ {
+ get { return _membershipUser.IsLockedOut; }
+ }
+ public DateTime LastLockoutDate
+ {
+ get { return _membershipUser.LastLockoutDate; }
+ }
+ public DateTime CreationDate
+ {
+ get { return _membershipUser.CreationDate; }
+ }
+ public DateTime LastLoginDate
+ {
+ get { return _membershipUser.LastLoginDate; }
+ }
+ public DateTime LastActivityDate
+ {
+ get { return _membershipUser.LastActivityDate; }
+ }
+ public DateTime LastPasswordChangedDate
+ {
+ get { return _membershipUser.LastPasswordChangedDate; }
+ }
+ #endregion
+
+ #region IPublishedContent
+ public override PublishedItemType ItemType
+ {
+ get { return PublishedItemType.Member; }
+ }
+
+ public override bool IsDraft
+ {
+ get { return false; }
+ }
+
+ public override IPublishedContent Parent
+ {
+ get { return null; }
+ }
+
+ public override IEnumerable Children
+ {
+ get { return Enumerable.Empty(); }
+ }
+
+ public override ICollection Properties
+ {
+ get { return _properties; }
+ }
+
+ public override IPublishedProperty GetProperty(string alias, bool recurse)
+ {
+ if (recurse)
+ {
+ throw new NotSupportedException();
+ }
+ return GetProperty(alias);
+ }
+
+ public override IPublishedProperty GetProperty(string alias)
+ {
+ return _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
+ }
+
+ public override PublishedContentType ContentType
+ {
+ get { return _publishedMemberType; }
+ }
+
+ public override int Id
+ {
+ get { return _member.Id; }
+ }
+
+ public override int TemplateId
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override int SortOrder
+ {
+ get { return 0; }
+ }
+
+ public override string Name
+ {
+ get { return _member.Name; }
+ }
+
+ public override string UrlName
+ {
+ get { throw new NotSupportedException(); }
+ }
+
+ public override string DocumentTypeAlias
+ {
+ get { return _member.ContentTypeAlias; }
+ }
+
+ public override int DocumentTypeId
+ {
+ get { return _member.ContentType.Id; }
+ }
+
+ public override string WriterName
+ {
+ get { return _member.GetCreatorProfile().Name; }
+ }
+
+ public override string CreatorName
+ {
+ get { return _member.GetCreatorProfile().Name; }
+ }
+
+ public override int WriterId
+ {
+ get { return _member.CreatorId; }
+ }
+
+ public override int CreatorId
+ {
+ get { return _member.CreatorId; }
+ }
+
+ public override string Path
+ {
+ get { return _member.Path; }
+ }
+
+ public override DateTime CreateDate
+ {
+ get { return _member.CreateDate; }
+ }
+
+ public override DateTime UpdateDate
+ {
+ get { return _member.UpdateDate; }
+ }
+
+ public override Guid Version
+ {
+ get { return _member.Version; }
+ }
+
+ public override int Level
+ {
+ get { return _member.Level; }
+ }
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Web/PublishedCache/RawValueProperty.cs b/src/Umbraco.Web/PublishedCache/RawValueProperty.cs
new file mode 100644
index 0000000000..5fa95f127b
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/RawValueProperty.cs
@@ -0,0 +1,46 @@
+using System;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Web.PublishedCache
+{
+ ///
+ /// A published property base that uses a raw object value
+ ///
+ internal class RawValueProperty : PublishedPropertyBase
+ {
+ private readonly object _dbVal; //the value in the db
+ private readonly Lazy _sourceValue;
+ private readonly Lazy _objectValue;
+ private readonly Lazy _xpathValue;
+
+ ///
+ /// Gets the raw value of the property.
+ ///
+ public override object DataValue { get { return _dbVal; } }
+
+ public override bool HasValue
+ {
+ get { return _dbVal != null && _dbVal.ToString().Trim().Length > 0; }
+ }
+
+ public override object Value { get { return _objectValue.Value; } }
+ public override object XPathValue { get { return _xpathValue.Value; } }
+
+ public RawValueProperty(PublishedPropertyType propertyType, object propertyData)
+ : this(propertyType)
+ {
+ if (propertyData == null)
+ throw new ArgumentNullException("propertyData");
+ _dbVal = propertyData;
+ }
+
+ public RawValueProperty(PublishedPropertyType propertyType)
+ : base(propertyType)
+ {
+ _dbVal = null;
+ _sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_dbVal, false));
+ _objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, false));
+ _xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, false));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
index 82a5b6f85a..2fe9c13c21 100644
--- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs
@@ -460,5 +460,15 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
}
#endregion
+
+ #region Fragments
+
+ public IPublishedContent CreateFragment(string contentTypeAlias, IDictionary dataValues,
+ bool isPreviewing, bool managed)
+ {
+ return new PublishedFragment(contentTypeAlias, dataValues, isPreviewing, managed);
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs
new file mode 100644
index 0000000000..860f9f043f
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.PublishedContent;
+using Umbraco.Web.Models;
+
+namespace Umbraco.Web.PublishedCache.XmlPublishedCache
+{
+ class PublishedFragment : PublishedContentBase
+ {
+ private readonly PublishedContentType _contentType;
+ private readonly IPublishedProperty[] _properties;
+
+ public PublishedFragment(string contentTypeAlias, IDictionary dataValues,
+ bool isPreviewing, bool managed)
+ {
+ IsPreviewing = isPreviewing;
+ _contentType = PublishedContentType.Get(PublishedItemType.Content, contentTypeAlias);
+
+ // we don't care about managed because in both cases, XmlPublishedCache stores
+ // converted property values in the IPublishedContent, which is not meant to
+ // survive the request
+
+ var dataValues2 = new Dictionary();
+ foreach (var kvp in dataValues)
+ dataValues2[kvp.Key.ToLowerInvariant()] = kvp.Value;
+
+ _properties = _contentType.PropertyTypes
+ .Select(x =>
+ {
+ object dataValue;
+ return dataValues2.TryGetValue(x.PropertyTypeAlias.ToLowerInvariant(), out dataValue)
+ ? new PublishedFragmentProperty(x, this, dataValue)
+ : new PublishedFragmentProperty(x, this);
+ })
+ .Cast()
+ .ToArray();
+ }
+
+ #region IPublishedContent
+
+ public override int Id
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int DocumentTypeId
+ {
+ get { return _contentType.Id; }
+ }
+
+ public override string DocumentTypeAlias
+ {
+ get { return _contentType.Alias; }
+ }
+
+ public override PublishedItemType ItemType
+ {
+ get { return PublishedItemType.Content; }
+ }
+
+ public override string Name
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int Level
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string Path
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int SortOrder
+ {
+ // note - could a published fragment have a sort order?
+ get { throw new NotImplementedException(); }
+ }
+
+ public override Guid Version
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int TemplateId
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string UrlName
+ {
+ get { return string.Empty; }
+ }
+
+ public override DateTime CreateDate
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override DateTime UpdateDate
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int CreatorId
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string CreatorName
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override int WriterId
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override string WriterName
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override bool IsDraft
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override IPublishedContent Parent
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override IEnumerable Children
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ public override ICollection Properties
+ {
+ get { return _properties; }
+ }
+
+ public override IPublishedProperty GetProperty(string alias)
+ {
+ return _properties.FirstOrDefault(x => x.PropertyTypeAlias.InvariantEquals(alias));
+ }
+
+ public override PublishedContentType ContentType
+ {
+ get { return _contentType; }
+ }
+
+ #endregion
+
+ #region Internal
+
+ // used by PublishedFragmentProperty
+ internal bool IsPreviewing { get; private set; }
+
+ #endregion
+ }
+}
diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs
new file mode 100644
index 0000000000..7ae10aebdd
--- /dev/null
+++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragmentProperty.cs
@@ -0,0 +1,43 @@
+using System;
+using Umbraco.Core.Models.PublishedContent;
+
+namespace Umbraco.Web.PublishedCache.XmlPublishedCache
+{
+ class PublishedFragmentProperty : PublishedPropertyBase
+ {
+ private readonly object _dataValue;
+ private readonly PublishedFragment _content;
+
+ private readonly Lazy _sourceValue;
+ private readonly Lazy _objectValue;
+ private readonly Lazy _xpathValue;
+
+ public PublishedFragmentProperty(PublishedPropertyType propertyType, PublishedFragment content)
+ : this(propertyType, content, null)
+ { }
+
+ public PublishedFragmentProperty(PublishedPropertyType propertyType, PublishedFragment content, object dataValue)
+ : base(propertyType)
+ {
+ _dataValue = dataValue;
+ _content = content;
+
+ _sourceValue = new Lazy(() => PropertyType.ConvertDataToSource(_dataValue, _content.IsPreviewing));
+ _objectValue = new Lazy(() => PropertyType.ConvertSourceToObject(_sourceValue.Value, _content.IsPreviewing));
+ _xpathValue = new Lazy(() => PropertyType.ConvertSourceToXPath(_sourceValue.Value, _content.IsPreviewing));
+ }
+
+ public override bool HasValue
+ {
+ get { return _dataValue != null && ((_dataValue is string) == false || string.IsNullOrWhiteSpace((string)_dataValue) == false); }
+ }
+
+ public override object DataValue
+ {
+ get { return _dataValue; }
+ }
+
+ public override object Value { get { return _objectValue.Value; } }
+ public override object XPathValue { get { return _xpathValue.Value; } }
+ }
+}
diff --git a/src/Umbraco.Web/Routing/DomainHelper.cs b/src/Umbraco.Web/Routing/DomainHelper.cs
index 85a1c5f66f..9f64b51e04 100644
--- a/src/Umbraco.Web/Routing/DomainHelper.cs
+++ b/src/Umbraco.Web/Routing/DomainHelper.cs
@@ -152,12 +152,21 @@ namespace Umbraco.Web.Routing
}
else
{
- // look for the first domain that would be the base of the hint
- var hintWithSlash = current.EndPathWithSlash();
+ // look for the first domain that would be the base of the current url
+ // ie current is www.example.com/foo/bar, look for domain www.example.com
+ var currentWithSlash = current.EndPathWithSlash();
domainAndUri = domainsAndUris
- .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(hintWithSlash));
+ .FirstOrDefault(d => d.Uri.EndPathWithSlash().IsBaseOf(currentWithSlash));
+ if (domainAndUri != null) return domainAndUri;
+
+ // look for the first domain that the current url would be the base of
+ // ie current is www.example.com, look for domain www.example.com/foo/bar
+ domainAndUri = domainsAndUris
+ .FirstOrDefault(d => currentWithSlash.IsBaseOf(d.Uri.EndPathWithSlash()));
+ if (domainAndUri != null) return domainAndUri;
+
// if none matches, then try to run the filter to pick a domain
- if (domainAndUri == null && filter != null)
+ if (filter != null)
{
domainAndUri = filter(domainsAndUris);
// if still nothing, pick the first one?
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequest.cs b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
index 424ffa1b52..9ab312e33e 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequest.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequest.cs
@@ -312,6 +312,16 @@ namespace Umbraco.Web.Routing
TemplateModel = template;
}
+ ///
+ /// Resets the template.
+ ///
+ /// The RenderingEngine becomes unknown.
+ public void ResetTemplate()
+ {
+ EnsureWriteable();
+ TemplateModel = null;
+ }
+
///
/// Gets a value indicating whether the content request has a template.
///
diff --git a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
index 40fa86b67b..da8bd37393 100644
--- a/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
+++ b/src/Umbraco.Web/Routing/PublishedContentRequestEngine.cs
@@ -7,6 +7,7 @@ using System.IO;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
+using Umbraco.Core.Security;
using UmbracoSettings = Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Web.Configuration;
@@ -520,7 +521,8 @@ namespace Umbraco.Web.Routing
System.Web.Security.MembershipUser user = null;
try
{
- user = System.Web.Security.Membership.GetUser();
+ var provider = MembershipProviderExtensions.GetMembersMembershipProvider();
+ user = provider.GetCurrentUser();
}
catch (ArgumentException)
{
diff --git a/src/Umbraco.Web/Routing/RoutingContext.cs b/src/Umbraco.Web/Routing/RoutingContext.cs
index bb52a43193..b999e2241a 100644
--- a/src/Umbraco.Web/Routing/RoutingContext.cs
+++ b/src/Umbraco.Web/Routing/RoutingContext.cs
@@ -1,3 +1,4 @@
+using System;
using System.Collections.Generic;
using Umbraco.Web.PublishedCache;
@@ -9,6 +10,10 @@ namespace Umbraco.Web.Routing
///
public class RoutingContext
{
+ private readonly Lazy _urlProvider;
+ private readonly Lazy> _publishedContentFinders;
+ private readonly Lazy _publishedContentLastChanceFinder;
+
///
/// Initializes a new instance of the class.
///
@@ -23,9 +28,21 @@ namespace Umbraco.Web.Routing
UrlProvider urlProvider)
{
UmbracoContext = umbracoContext;
- PublishedContentFinders = contentFinders;
- PublishedContentLastChanceFinder = contentLastChanceFinder;
- UrlProvider = urlProvider;
+ _publishedContentFinders = new Lazy>(() => contentFinders, false);
+ _publishedContentLastChanceFinder = new Lazy(() => contentLastChanceFinder, false);
+ _urlProvider = new Lazy(() => urlProvider, false);
+ }
+
+ internal RoutingContext(
+ UmbracoContext umbracoContext,
+ Lazy> contentFinders,
+ Lazy contentLastChanceFinder,
+ Lazy urlProvider)
+ {
+ UmbracoContext = umbracoContext;
+ _publishedContentFinders = contentFinders;
+ _publishedContentLastChanceFinder = contentLastChanceFinder;
+ _urlProvider = urlProvider;
}
///
@@ -33,19 +50,28 @@ namespace Umbraco.Web.Routing
///
public UmbracoContext UmbracoContext { get; private set; }
- ///
- /// Gets the published content finders.
- ///
- internal IEnumerable PublishedContentFinders { get; private set; }
+ ///
+ /// Gets the published content finders.
+ ///
+ internal IEnumerable PublishedContentFinders
+ {
+ get { return _publishedContentFinders.Value; }
+ }
- ///
- /// Gets the published content last chance finder.
- ///
- internal IContentFinder PublishedContentLastChanceFinder { get; private set; }
+ ///
+ /// Gets the published content last chance finder.
+ ///
+ internal IContentFinder PublishedContentLastChanceFinder
+ {
+ get { return _publishedContentLastChanceFinder.Value; }
+ }
- ///
- /// Gets the urls provider.
- ///
- public UrlProvider UrlProvider { get; private set; }
+ ///
+ /// Gets the urls provider.
+ ///
+ public UrlProvider UrlProvider
+ {
+ get { return _urlProvider.Value; }
+ }
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Web/Search/ExamineEvents.cs b/src/Umbraco.Web/Search/ExamineEvents.cs
index 102038a7c0..c04920e289 100644
--- a/src/Umbraco.Web/Search/ExamineEvents.cs
+++ b/src/Umbraco.Web/Search/ExamineEvents.cs
@@ -1,4 +1,5 @@
using System;
+using System.Globalization;
using System.Linq;
using System.Security;
using System.Xml;
@@ -7,9 +8,13 @@ using Examine;
using Examine.LuceneEngine;
using Lucene.Net.Documents;
using Umbraco.Core;
+using Umbraco.Core.Cache;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Services;
+using Umbraco.Core.Sync;
+using Umbraco.Web.Cache;
using UmbracoExamine;
using umbraco;
using umbraco.BusinessLogic;
@@ -50,23 +55,13 @@ namespace Umbraco.Web.Search
if (registeredProviders == 0)
return;
- MediaService.Saved += MediaServiceSaved;
- MediaService.Deleted += MediaServiceDeleted;
- MediaService.Moved += MediaServiceMoved;
- MediaService.Trashed += MediaServiceTrashed;
-
- ContentService.Saved += ContentServiceSaved;
- ContentService.Deleted += ContentServiceDeleted;
- ContentService.Moved += ContentServiceMoved;
- ContentService.Trashed += ContentServiceTrashed;
-
- //These should only fire for providers that DONT have SupportUnpublishedContent set to true
- content.AfterUpdateDocumentCache += ContentAfterUpdateDocumentCache;
- content.AfterClearDocumentCache += ContentAfterClearDocumentCache;
-
- Member.AfterSave += MemberAfterSave;
- Member.AfterDelete += MemberAfterDelete;
-
+ //Bind to distributed cache events - this ensures that this logic occurs on ALL servers that are taking part
+ // in a load balanced environment.
+ CacheRefresherBase.CacheUpdated += UnpublishedPageCacheRefresherCacheUpdated;
+ CacheRefresherBase.CacheUpdated += PublishedPageCacheRefresherCacheUpdated;
+ CacheRefresherBase.CacheUpdated += MediaCacheRefresherCacheUpdated;
+ CacheRefresherBase.CacheUpdated += MemberCacheRefresherCacheUpdated;
+
var contentIndexer = ExamineManager.Instance.IndexProviderCollection["InternalIndexer"] as UmbracoContentIndexer;
if (contentIndexer != null)
{
@@ -80,113 +75,245 @@ namespace Umbraco.Web.Search
}
[SecuritySafeCritical]
- static void ContentServiceTrashed(IContentService sender, Core.Events.MoveEventArgs e)
+ static void MemberCacheRefresherCacheUpdated(MemberCacheRefresher sender, CacheRefresherEventArgs e)
+ {
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.MemberService.GetById((int)e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForMember(c1);
+ }
+ break;
+ case MessageType.RemoveById:
+
+ // This is triggered when the item is permanently deleted
+
+ DeleteIndexForEntity((int)e.MessageObject, false);
+ break;
+ case MessageType.RefreshByInstance:
+ var c3 = e.MessageObject as IMember;
+ if (c3 != null)
+ {
+ ReIndexForMember(c3);
+ }
+ break;
+ case MessageType.RemoveByInstance:
+
+ // This is triggered when the item is permanently deleted
+
+ var c4 = e.MessageObject as IMember;
+ if (c4 != null)
+ {
+ DeleteIndexForEntity(c4.Id, false);
+ }
+ break;
+ case MessageType.RefreshAll:
+ case MessageType.RefreshByJson:
+ default:
+ //We don't support these, these message types will not fire for unpublished content
+ break;
+ }
+ }
+
+ ///
+ /// Handles index management for all media events - basically handling saving/copying/trashing/deleting
+ ///
+ ///
+ ///
+ [SecuritySafeCritical]
+ static void MediaCacheRefresherCacheUpdated(MediaCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexConent(e.Entity);
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForMedia(c1, c1.Trashed == false);
+ }
+ break;
+ case MessageType.RemoveById:
+ var c2 = ApplicationContext.Current.Services.MediaService.GetById((int)e.MessageObject);
+ if (c2 != null)
+ {
+ //This is triggered when the item has trashed.
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c2.Id, true);
+
+ //We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForMedia(c2, false);
+ }
+ break;
+ case MessageType.RefreshByJson:
+
+ var jsonPayloads = MediaCacheRefresher.DeserializeFromJsonPayload((string)e.MessageObject);
+ if (jsonPayloads.Any())
+ {
+ foreach (var payload in jsonPayloads)
+ {
+ switch (payload.Operation)
+ {
+ case MediaCacheRefresher.OperationType.Saved:
+ var media = ApplicationContext.Current.Services.MediaService.GetById(payload.Id);
+ if (media != null)
+ {
+ ReIndexForMedia(media, media.Trashed == false);
+ }
+ break;
+ case MediaCacheRefresher.OperationType.Trashed:
+ //keep if trashed for indexes supporting unpublished
+ DeleteIndexForEntity(payload.Id, true);
+ break;
+ case MediaCacheRefresher.OperationType.Deleted:
+ //permanently remove from all indexes
+ DeleteIndexForEntity(payload.Id, false);
+ break;
+ default:
+ throw new ArgumentOutOfRangeException();
+ }
+ }
+ }
+
+ break;
+ case MessageType.RefreshByInstance:
+ case MessageType.RemoveByInstance:
+ case MessageType.RefreshAll:
+ default:
+ //We don't support these, these message types will not fire for media
+ break;
+ }
}
+ ///
+ /// Handles index management for all published content events - basically handling published/unpublished
+ ///
+ ///
+ ///
+ ///
+ /// This will execute on all servers taking part in load balancing
+ ///
[SecuritySafeCritical]
- static void MediaServiceTrashed(IMediaService sender, Core.Events.MoveEventArgs e)
+ static void PublishedPageCacheRefresherCacheUpdated(PageCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexMedia(e.Entity);
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForContent(c1, true);
+ }
+ break;
+ case MessageType.RemoveById:
+
+ //This is triggered when the item has been unpublished or trashed (which also performs an unpublish).
+
+ var c2 = ApplicationContext.Current.Services.ContentService.GetById((int)e.MessageObject);
+ if (c2 != null)
+ {
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c2.Id, true);
+
+ // We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForContent(c2, false);
+ }
+ break;
+ case MessageType.RefreshByInstance:
+ var c3 = e.MessageObject as IContent;
+ if (c3 != null)
+ {
+ ReIndexForContent(c3, true);
+ }
+ break;
+ case MessageType.RemoveByInstance:
+
+ //This is triggered when the item has been unpublished or trashed (which also performs an unpublish).
+
+ var c4 = e.MessageObject as IContent;
+ if (c4 != null)
+ {
+ // So we need to delete the index from all indexes not supporting unpublished content.
+
+ DeleteIndexForEntity(c4.Id, true);
+
+ // We then need to re-index this item for all indexes supporting unpublished content
+
+ ReIndexForContent(c4, false);
+ }
+ break;
+ case MessageType.RefreshAll:
+ case MessageType.RefreshByJson:
+ default:
+ //We don't support these for examine indexing
+ break;
+ }
}
+ ///
+ /// Handles index management for all unpublished content events - basically handling saving/copying/deleting
+ ///
+ ///
+ ///
+ ///
+ /// This will execute on all servers taking part in load balancing
+ ///
[SecuritySafeCritical]
- static void ContentServiceMoved(IContentService sender, Umbraco.Core.Events.MoveEventArgs e)
+ static void UnpublishedPageCacheRefresherCacheUpdated(UnpublishedPageCacheRefresher sender, CacheRefresherEventArgs e)
{
- IndexConent(e.Entity);
- }
+ switch (e.MessageType)
+ {
+ case MessageType.RefreshById:
+ var c1 = ApplicationContext.Current.Services.ContentService.GetById((int) e.MessageObject);
+ if (c1 != null)
+ {
+ ReIndexForContent(c1, false);
+ }
+ break;
+ case MessageType.RemoveById:
+
+ // This is triggered when the item is permanently deleted
+
+ DeleteIndexForEntity((int)e.MessageObject, false);
+ break;
+ case MessageType.RefreshByInstance:
+ var c3 = e.MessageObject as IContent;
+ if (c3 != null)
+ {
+ ReIndexForContent(c3, false);
+ }
+ break;
+ case MessageType.RemoveByInstance:
- [SecuritySafeCritical]
- static void ContentServiceDeleted(IContentService sender, Umbraco.Core.Events.DeleteEventArgs e)
- {
- e.DeletedEntities.ForEach(
- content =>
- ExamineManager.Instance.DeleteFromIndex(
- content.Id.ToString(),
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)));
- }
+ // This is triggered when the item is permanently deleted
- [SecuritySafeCritical]
- static void ContentServiceSaved(IContentService sender, Umbraco.Core.Events.SaveEventArgs e)
- {
- e.SavedEntities.ForEach(IndexConent);
- }
-
- [SecuritySafeCritical]
- static void MediaServiceMoved(IMediaService sender, Umbraco.Core.Events.MoveEventArgs e)
- {
- IndexMedia(e.Entity);
- }
-
- [SecuritySafeCritical]
- static void MediaServiceDeleted(IMediaService sender, Umbraco.Core.Events.DeleteEventArgs e)
- {
- e.DeletedEntities.ForEach(
- media =>
- ExamineManager.Instance.DeleteFromIndex(
- media.Id.ToString(),
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler)));
- }
-
- [SecuritySafeCritical]
- static void MediaServiceSaved(IMediaService sender, Umbraco.Core.Events.SaveEventArgs e)
- {
- e.SavedEntities.ForEach(IndexMedia);
+ var c4 = e.MessageObject as IContent;
+ if (c4 != null)
+ {
+ DeleteIndexForEntity(c4.Id, false);
+ }
+ break;
+ case MessageType.RefreshAll:
+ case MessageType.RefreshByJson:
+ default:
+ //We don't support these, these message types will not fire for unpublished content
+ break;
+ }
}
[SecuritySafeCritical]
- private static void MemberAfterSave(Member sender, SaveEventArgs e)
+ private static void ReIndexForMember(IMember member)
{
- //ensure that only the providers are flagged to listen execute
- var xml = ExamineXmlExtensions.ToXElement(sender.ToXml(new System.Xml.XmlDocument(), false));
- var providers = ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => x.EnableDefaultEventHandler);
- ExamineManager.Instance.ReIndexNode(xml, IndexTypes.Member, providers);
- }
-
- [SecuritySafeCritical]
- private static void MemberAfterDelete(Member sender, DeleteEventArgs e)
- {
- var nodeId = sender.Id.ToString();
-
- //ensure that only the providers are flagged to listen execute
- ExamineManager.Instance.DeleteFromIndex(nodeId,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => x.EnableDefaultEventHandler));
- }
-
- ///
- /// Only Update indexes for providers that dont SupportUnpublishedContent
- ///
- ///
- ///
- [SecuritySafeCritical]
- private static void ContentAfterUpdateDocumentCache(Document sender, DocumentCacheEventArgs e)
- {
- //ensure that only the providers that have DONT unpublishing support enabled
- //that are also flagged to listen
- ExamineManager.Instance.ReIndexNode(ToXDocument(sender, true).Root, IndexTypes.Content,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => !x.SupportUnpublishedContent
- && x.EnableDefaultEventHandler));
- }
-
- ///
- /// Only update indexes for providers that don't SupportUnpublishedContnet
- ///
- ///
- ///
- [SecuritySafeCritical]
- private static void ContentAfterClearDocumentCache(Document sender, DocumentCacheEventArgs e)
- {
- var nodeId = sender.Id.ToString();
- //ensure that only the providers that DONT have unpublishing support enabled
- //that are also flagged to listen
- ExamineManager.Instance.DeleteFromIndex(nodeId,
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => !x.SupportUnpublishedContent
- && x.EnableDefaultEventHandler));
+ ExamineManager.Instance.ReIndexNode(
+ member.ToXml(), IndexTypes.Member,
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+ //ensure that only the providers are flagged to listen execute
+ .Where(x => x.EnableDefaultEventHandler));
}
///
@@ -210,28 +337,62 @@ namespace Umbraco.Web.Search
}
}
-
- private static void IndexMedia(IMedia sender)
+ [SecuritySafeCritical]
+ private static void ReIndexForMedia(IMedia sender, bool isMediaPublished)
{
ExamineManager.Instance.ReIndexNode(
- sender.ToXml(), "media",
- ExamineManager.Instance.IndexProviderCollection.OfType().Where(x => x.EnableDefaultEventHandler));
+ sender.ToXml(), IndexTypes.Media,
+ ExamineManager.Instance.IndexProviderCollection.OfType()
+
+ //Index this item for all indexers if the media is not trashed, otherwise if the item is trashed
+ // then only index this for indexers supporting unpublished media
+
+ .Where(x => isMediaPublished || (x.SupportUnpublishedContent))
+ .Where(x => x.EnableDefaultEventHandler));
}
- private static void IndexConent(IContent sender)
- {
- //only index this content if the indexer supports unpublished content. that is because the
- // content.AfterUpdateDocumentCache will handle anything being published and will only index against indexers
- // that only support published content.
- // NOTE: The events for publishing have changed slightly from 6.0 to 6.1 and are streamlined in 6.1. Before
- // this event would fire before publishing, then again after publishing. Now the save event fires once before
- // publishing and that is all.
-
- ExamineManager.Instance.ReIndexNode(
- sender.ToXml(), "content",
- ExamineManager.Instance.IndexProviderCollection.OfType()
- .Where(x => x.SupportUnpublishedContent && x.EnableDefaultEventHandler));
- }
+ ///