diff --git a/src/Umbraco.Core/UmbracoApplicationBase.cs b/src/Umbraco.Core/UmbracoApplicationBase.cs index df241c2bd3..b08e0c9418 100644 --- a/src/Umbraco.Core/UmbracoApplicationBase.cs +++ b/src/Umbraco.Core/UmbracoApplicationBase.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Web; using System.Web.Hosting; +using System.Web.Mvc; using Umbraco.Core.Logging; namespace Umbraco.Core @@ -27,6 +28,9 @@ namespace Umbraco.Core /// internal void StartApplication(object sender, EventArgs e) { + //don't output the MVC version header (security) + MvcHandler.DisableMvcResponseHeader = true; + //boot up the application GetBootManager() .Initialize() diff --git a/src/Umbraco.Web.UI/umbraco/login.aspx b/src/Umbraco.Web.UI/umbraco/login.aspx index 93ca7a7034..47f65fb716 100644 --- a/src/Umbraco.Web.UI/umbraco/login.aspx +++ b/src/Umbraco.Web.UI/umbraco/login.aspx @@ -21,7 +21,7 @@ } body { - font-size: 11px; + font-size: 11px; width: 100%; font-family: Trebuchet MS, verdana, arial, Lucida Grande; text-align: center; @@ -70,7 +70,7 @@ - @@ -85,7 +85,7 @@ - diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index b32cbc1f0f..6561c6f5ab 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -18,10 +18,7 @@ namespace Umbraco.Web /// public class UmbracoApplication : UmbracoApplicationBase { - //don't output the MVC version header (security) - MvcHandler.DisableMvcResponseHeader = true; - - + protected override IBootManager GetBootManager() { return new WebBootManager(this); diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs index e1c23b4ad6..4ecebc4378 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentTypeControlNew.ascx.cs @@ -132,32 +132,32 @@ namespace umbraco.controls string originalAlias, string originalName, string newAlias, - string newName + string newName, string[] originalPropertyAliases) { SaveArgs = saveArgs; _originalAlias = originalAlias; _originalName = originalName; - NewAlias = newAlias; + _newAlias = newAlias; _originalPropertyAliases = originalPropertyAliases; - NewName = newName; + _newName = newName; } public SaveClickEventArgs SaveArgs { get; private set; } private readonly string _originalAlias; private readonly string _originalName; - public string NewAlias { get; private set; } - public string NewName { get; private set; } + private readonly string _newAlias; + private readonly string _newName; private readonly string[] _originalPropertyAliases; public bool HasAliasChanged() { - return (string.Compare(OriginalAlias, NewAlias, StringComparison.OrdinalIgnoreCase) != 0); + return (string.Compare(_originalAlias, _newAlias, StringComparison.OrdinalIgnoreCase) != 0); } public bool HasNameChanged() { - return (string.Compare(OriginalName, NewName, StringComparison.OrdinalIgnoreCase) != 0); + return (string.Compare(_originalName, _newName, StringComparison.OrdinalIgnoreCase) != 0); } /// @@ -339,7 +339,7 @@ namespace umbraco.controls // Only if the doctype alias changed, cause a regeneration of the xml cache file since // the xml element names will need to be updated to reflect the new alias - if (asyncState.HasAliasChanged(_contentType) || asyncState.HasAnyPropertyAliasChanged(_contentType)) + if (asyncState.HasAliasChanged() || asyncState.HasAnyPropertyAliasChanged(_contentType)) RegenerateXmlCaches(); Trace.Write("ContentTypeControlNew", "task completing"); @@ -1037,6 +1037,47 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); } } + /// + /// Called asynchronously in order to delete a content type property + /// + /// + /// + /// + /// + /// + private IAsyncResult BeginAsyncDeleteOperation(object sender, EventArgs e, AsyncCallback cb, object state) + { + Trace.Write("ContentTypeControlNew", "Start async operation"); + + //get the args from the async state + var args = (GenericPropertyWrapper)state; + + //start the task + var result = _asyncDeleteTask.BeginInvoke(args, cb, args); + return result; + } + + /// + /// Occurs once the async database delete operation has completed + /// + /// + /// + /// This updates the UI elements + /// + private void EndAsyncDeleteOperation(IAsyncResult ar) + { + Trace.Write("ContentTypeControlNew", "ending async operation"); + + // reload content type (due to caching) + LoadContentType(_contentType.Id); + BindDataGenericProperties(true); + + Trace.Write("ContentTypeControlNew", "async operation ended"); + + //complete it + _asyncDeleteTask.EndInvoke(ar); + } + /// /// Removes a PropertyType from the current ContentType when user clicks "red x" /// @@ -1044,20 +1085,35 @@ jQuery(document).ready(function() {{ refreshDropDowns(); }}); /// protected void gpw_Delete(object sender, EventArgs e) { - var gpw = (GenericPropertyWrapper)sender; + //Add the async operation to the page + Page.RegisterAsyncTask(new PageAsyncTask(BeginAsyncDeleteOperation, EndAsyncDeleteOperation, HandleAsyncSaveTimeout, (GenericPropertyWrapper)sender)); - if (_contentType.ContentTypeItem is IContentType || _contentType.ContentTypeItem is IMediaType) + //create the save task to be executed async + _asyncDeleteTask = genericPropertyWrapper => { - _contentType.ContentTypeItem.RemovePropertyType(gpw.PropertyType.Alias); - _contentType.Save(); - } + Trace.Write("ContentTypeControlNew", "executing task"); - gpw.GenricPropertyControl.PropertyType.delete(); + if (_contentType.ContentTypeItem is IContentType || _contentType.ContentTypeItem is IMediaType) + { + _contentType.ContentTypeItem.RemovePropertyType(genericPropertyWrapper.PropertyType.Alias); + _contentType.Save(); + } + + //delete the property + genericPropertyWrapper.GenricPropertyControl.PropertyType.delete(); + + //we need to re-generate the xml structures because we're removing a content type property + RegenerateXmlCaches(); + + Trace.Write("ContentTypeControlNew", "task completing"); + }; + + //execute the async tasks + Page.ExecuteRegisteredAsyncTasks(); - LoadContentType(_contentType.Id); - BindDataGenericProperties(true); } + #endregion #region "Tab" Pane diff --git a/src/umbraco.cms/businesslogic/ContentType.cs b/src/umbraco.cms/businesslogic/ContentType.cs index c11c26f0ee..2013b898f5 100644 --- a/src/umbraco.cms/businesslogic/ContentType.cs +++ b/src/umbraco.cms/businesslogic/ContentType.cs @@ -6,6 +6,7 @@ using System.Runtime.CompilerServices; using System.Linq; using System.Xml; using Umbraco.Core; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence.Caching; diff --git a/src/umbraco.cms/businesslogic/web/DocumentType.cs b/src/umbraco.cms/businesslogic/web/DocumentType.cs index a552176fd4..ef22568f8a 100644 --- a/src/umbraco.cms/businesslogic/web/DocumentType.cs +++ b/src/umbraco.cms/businesslogic/web/DocumentType.cs @@ -286,29 +286,49 @@ namespace umbraco.cms.businesslogic.web /// This will return all PUBLISHED content Ids that are of this content type or any descendant types as well. /// /// + /// + /// This will also work if we have multiple child content types (if one day we support that) + /// internal override IEnumerable GetContentIdsForContentType() { var ids = new List(); - int? currentContentTypeId = this.Id; - while (currentContentTypeId != null) + + var currentContentTypeIds = new[] { this.Id }; + while (currentContentTypeIds.Any()) { - //get all the content item ids of the current content type - using (var dr = SqlHelper.ExecuteReader(@"SELECT DISTINCT cmsDocument.nodeId FROM cmsDocument + var childContentTypeIds = new List(); + + //loop through each content type id + foreach (var currentContentTypeId in currentContentTypeIds) + { + //get all the content item ids of the current content type + using (var dr = SqlHelper.ExecuteReader(@"SELECT DISTINCT cmsDocument.nodeId FROM cmsDocument INNER JOIN cmsContent ON cmsContent.nodeId = cmsDocument.nodeId INNER JOIN cmsContentType ON cmsContent.contentType = cmsContentType.nodeId WHERE cmsContentType.nodeId = @contentTypeId AND cmsDocument.published = 1", - SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId))) - { - while (dr.Read()) + SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId))) { - ids.Add(dr.GetInt("nodeId")); + while (dr.Read()) + { + ids.Add(dr.GetInt("nodeId")); + } + dr.Close(); } - dr.Close(); + + //lookup the child content types if there are any and add the ids to the content type ids array + using (var reader = SqlHelper.ExecuteReader("SELECT childContentTypeId FROM cmsContentType2ContentType WHERE parentContentTypeId=@contentTypeId", + SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId))) + { + while (reader.Read()) + { + childContentTypeIds.Add(reader.GetInt("childContentTypeId")); + } + } + } - //lookup the child content type if there is one - currentContentTypeId = SqlHelper.ExecuteScalar("SELECT nodeId FROM cmsContentType WHERE masterContentType=@contentTypeId", - SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId)); + currentContentTypeIds = childContentTypeIds.ToArray(); + } return ids; } @@ -337,25 +357,40 @@ namespace umbraco.cms.businesslogic.web /// Clears all xml structures in the cmsContentXml table for the current content type and any of it's descendant types /// /// - /// This is not thread safe + /// This is not thread safe. + /// This will also work if we have multiple child content types (if one day we support that) /// internal override void ClearXmlStructuresForContent() { - int? currentContentTypeId = this.Id; - while (currentContentTypeId != null) + var currentContentTypeIds = new[] {this.Id}; + while (currentContentTypeIds.Any()) { - //Remove all items from the cmsContentXml table that are of this current content type - SqlHelper.ExecuteNonQuery(@"DELETE FROM cmsContentXml WHERE nodeId IN + var childContentTypeIds = new List(); + + //loop through each content type id + foreach (var currentContentTypeId in currentContentTypeIds) + { + //Remove all items from the cmsContentXml table that are of this current content type + SqlHelper.ExecuteNonQuery(@"DELETE FROM cmsContentXml WHERE nodeId IN (SELECT DISTINCT cmsContent.nodeId FROM cmsContent INNER JOIN cmsContentType ON cmsContent.contentType = cmsContentType.nodeId WHERE cmsContentType.nodeId = @contentTypeId)", - SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId)); + SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId)); - //lookup the child content type if there is one - currentContentTypeId = SqlHelper.ExecuteScalar("SELECT nodeId FROM cmsContentType WHERE masterContentType=@contentTypeId", - SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId)); + //lookup the child content types if there are any and add the ids to the content type ids array + using (var reader = SqlHelper.ExecuteReader("SELECT childContentTypeId FROM cmsContentType2ContentType WHERE parentContentTypeId=@contentTypeId", + SqlHelper.CreateParameter("@contentTypeId", currentContentTypeId))) + { + while (reader.Read()) + { + childContentTypeIds.Add(reader.GetInt("childContentTypeId")); + } + } + + } + + currentContentTypeIds = childContentTypeIds.ToArray(); } - } #endregion