diff --git a/src/Umbraco.Core/Models/Content.cs b/src/Umbraco.Core/Models/Content.cs
index 3e5becf021..b048998691 100644
--- a/src/Umbraco.Core/Models/Content.cs
+++ b/src/Umbraco.Core/Models/Content.cs
@@ -467,6 +467,11 @@ namespace Umbraco.Core.Models
{
base.ResetDirtyProperties(rememberDirty);
+ if (Template != null)
+ Template.ResetDirtyProperties(rememberDirty);
+ if (ContentType != null)
+ ContentType.ResetDirtyProperties(rememberDirty);
+
// take care of the published state
_publishedState = _published ? PublishedState.Published : PublishedState.Unpublished;
@@ -499,28 +504,23 @@ namespace Umbraco.Core.Models
return clone;
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (Content) base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var clonedContent = (Content)clone;
//need to manually clone this since it's not settable
- clone._contentType = (IContentType) ContentType.DeepClone();
+ clonedContent._contentType = (IContentType) ContentType.DeepClone();
//if culture infos exist then deal with event bindings
- if (clone._publishInfos != null)
+ if (clonedContent._publishInfos != null)
{
- clone._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
- clone._publishInfos = (ContentCultureInfosCollection) _publishInfos.DeepClone(); //manually deep clone
- clone._publishInfos.CollectionChanged += clone.PublishNamesCollectionChanged; //re-assign correct event handler
+ clonedContent._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
+ clonedContent._publishInfos = (ContentCultureInfosCollection) _publishInfos.DeepClone(); //manually deep clone
+ clonedContent._publishInfos.CollectionChanged += clonedContent.PublishNamesCollectionChanged; //re-assign correct event handler
}
-
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+
}
}
}
diff --git a/src/Umbraco.Core/Models/ContentBase.cs b/src/Umbraco.Core/Models/ContentBase.cs
index 863374726d..7e70238d2f 100644
--- a/src/Umbraco.Core/Models/ContentBase.cs
+++ b/src/Umbraco.Core/Models/ContentBase.cs
@@ -475,33 +475,28 @@ namespace Umbraco.Core.Models
///
/// Overriden to deal with specific object instances
///
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (ContentBase) base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var clonedContent = (ContentBase)clone;
//if culture infos exist then deal with event bindings
- if (clone._cultureInfos != null)
+ if (clonedContent._cultureInfos != null)
{
- clone._cultureInfos.CollectionChanged -= CultureInfosCollectionChanged; //clear this event handler if any
- clone._cultureInfos = (ContentCultureInfosCollection) _cultureInfos.DeepClone(); //manually deep clone
- clone._cultureInfos.CollectionChanged += clone.CultureInfosCollectionChanged; //re-assign correct event handler
+ clonedContent._cultureInfos.CollectionChanged -= CultureInfosCollectionChanged; //clear this event handler if any
+ clonedContent._cultureInfos = (ContentCultureInfosCollection) _cultureInfos.DeepClone(); //manually deep clone
+ clonedContent._cultureInfos.CollectionChanged += clonedContent.CultureInfosCollectionChanged; //re-assign correct event handler
}
//if properties exist then deal with event bindings
- if (clone._properties != null)
+ if (clonedContent._properties != null)
{
- clone._properties.CollectionChanged -= PropertiesChanged; //clear this event handler if any
- clone._properties = (PropertyCollection) _properties.DeepClone(); //manually deep clone
- clone._properties.CollectionChanged += clone.PropertiesChanged; //re-assign correct event handler
+ clonedContent._properties.CollectionChanged -= PropertiesChanged; //clear this event handler if any
+ clonedContent._properties = (PropertyCollection) _properties.DeepClone(); //manually deep clone
+ clonedContent._properties.CollectionChanged += clonedContent.PropertiesChanged; //re-assign correct event handler
}
-
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+
}
}
}
diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs
index 9f848c6d14..caa63d7526 100644
--- a/src/Umbraco.Core/Models/ContentTypeBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeBase.cs
@@ -467,35 +467,29 @@ namespace Umbraco.Core.Models
}
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (ContentTypeBase) base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var clonedEntity = (ContentTypeBase) clone;
- if (clone._noGroupPropertyTypes != null)
+ if (clonedEntity._noGroupPropertyTypes != null)
{
//need to manually wire up the event handlers for the property type collections - we've ensured
// its ignored from the auto-clone process because its return values are unions, not raw and
// we end up with duplicates, see: http://issues.umbraco.org/issue/U4-4842
- clone._noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
- clone._noGroupPropertyTypes = (PropertyTypeCollection) _noGroupPropertyTypes.DeepClone(); //manually deep clone
- clone._noGroupPropertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
+ clonedEntity._noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
+ clonedEntity._noGroupPropertyTypes = (PropertyTypeCollection) _noGroupPropertyTypes.DeepClone(); //manually deep clone
+ clonedEntity._noGroupPropertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
}
- if (clone._propertyGroups != null)
+ if (clonedEntity._propertyGroups != null)
{
- clone._propertyGroups.CollectionChanged -= PropertyGroupsChanged; //clear this event handler if any
- clone._propertyGroups = (PropertyGroupCollection) _propertyGroups.DeepClone(); //manually deep clone
- clone._propertyGroups.CollectionChanged += clone.PropertyGroupsChanged; //re-assign correct event handler
+ clonedEntity._propertyGroups.CollectionChanged -= PropertyGroupsChanged; //clear this event handler if any
+ clonedEntity._propertyGroups = (PropertyGroupCollection) _propertyGroups.DeepClone(); //manually deep clone
+ clonedEntity._propertyGroups.CollectionChanged += clonedEntity.PropertyGroupsChanged; //re-assign correct event handler
}
-
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
}
public IContentType DeepCloneWithResetIdentities(string alias)
diff --git a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
index 2f455083c0..08b9f74802 100644
--- a/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
+++ b/src/Umbraco.Core/Models/ContentTypeCompositionBase.cs
@@ -311,20 +311,15 @@ namespace Umbraco.Core.Models
.Union(ContentTypeComposition.SelectMany(x => x.CompositionIds()));
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (ContentTypeCompositionBase)base.DeepClone();
- //turn off change tracking
- clone.DisableChangeTracking();
- //need to manually assign since this is an internal field and will not be automatically mapped
- clone.RemovedContentTypeKeyTracker = new List();
- clone._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
+ base.PerformDeepClone(clone);
- return clone;
+ var clonedEntity = (ContentTypeCompositionBase)clone;
+
+ //need to manually assign since this is an internal field and will not be automatically mapped
+ clonedEntity.RemovedContentTypeKeyTracker = new List();
+ clonedEntity._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
}
}
}
diff --git a/src/Umbraco.Core/Models/DictionaryTranslation.cs b/src/Umbraco.Core/Models/DictionaryTranslation.cs
index 2105e8057c..c3b5a8a3b2 100644
--- a/src/Umbraco.Core/Models/DictionaryTranslation.cs
+++ b/src/Umbraco.Core/Models/DictionaryTranslation.cs
@@ -104,23 +104,14 @@ namespace Umbraco.Core.Models
set { SetPropertyValueAndDetectChanges(value, ref _value, Ps.Value.ValueSelector); }
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (DictionaryTranslation)base.DeepClone();
+ base.PerformDeepClone(clone);
+
+ var clonedEntity = (DictionaryTranslation)clone;
// clear fields that were memberwise-cloned and that we don't want to clone
- clone._language = null;
-
- // turn off change tracking
- clone.DisableChangeTracking();
-
- // this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
-
- // re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+ clonedEntity._language = null;
}
}
}
diff --git a/src/Umbraco.Core/Models/Entities/EntityBase.cs b/src/Umbraco.Core/Models/Entities/EntityBase.cs
index 0b69586abf..5c6f943c60 100644
--- a/src/Umbraco.Core/Models/Entities/EntityBase.cs
+++ b/src/Umbraco.Core/Models/Entities/EntityBase.cs
@@ -159,7 +159,7 @@ namespace Umbraco.Core.Models.Entities
}
}
- public virtual object DeepClone()
+ public object DeepClone()
{
// memberwise-clone (ie shallow clone) the entity
var unused = Key; // ensure that 'this' has a key, before cloning
@@ -169,20 +169,29 @@ namespace Umbraco.Core.Models.Entities
clone.InstanceId = Guid.NewGuid();
#endif
- // clear changes (ensures the clone has its own dictionaries)
- // then disable change tracking
- clone.ResetDirtyProperties(false);
+ //disable change tracking while we deep clone IDeepCloneable properties
clone.DisableChangeTracking();
// deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
- // clear changes again (just to be sure, because we were not tracking)
- // then enable change tracking
+ PerformDeepClone(clone);
+
+ // clear changes (ensures the clone has its own dictionaries)
clone.ResetDirtyProperties(false);
+
+ //re-enable change tracking
clone.EnableChangeTracking();
return clone;
}
+
+ ///
+ /// Used by inheritors to modify the DeepCloning logic
+ ///
+ ///
+ protected virtual void PerformDeepClone(object clone)
+ {
+ }
}
}
diff --git a/src/Umbraco.Core/Models/File.cs b/src/Umbraco.Core/Models/File.cs
index 2e85b13261..2f8e021f4c 100644
--- a/src/Umbraco.Core/Models/File.cs
+++ b/src/Umbraco.Core/Models/File.cs
@@ -156,26 +156,17 @@ namespace Umbraco.Core.Models
clone._alias = Alias;
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (File) base.DeepClone();
+ base.PerformDeepClone(clone);
+
+ var clonedFile = (File)clone;
// clear fields that were memberwise-cloned and that we don't want to clone
- clone._content = null;
-
- // turn off change tracking
- clone.DisableChangeTracking();
+ clonedFile._content = null;
// ...
- DeepCloneNameAndAlias(clone);
-
- // this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
-
- // re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+ DeepCloneNameAndAlias(clonedFile);
}
}
}
diff --git a/src/Umbraco.Core/Models/Macro.cs b/src/Umbraco.Core/Models/Macro.cs
index 6e68bda439..5ef49305ac 100644
--- a/src/Umbraco.Core/Models/Macro.cs
+++ b/src/Umbraco.Core/Models/Macro.cs
@@ -284,22 +284,18 @@ namespace Umbraco.Core.Models
get { return _properties; }
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (Macro)base.DeepClone();
- //turn off change tracking
- clone.DisableChangeTracking();
- clone._addedProperties = new List();
- clone._removedProperties = new List();
- clone._properties = (MacroPropertyCollection)Properties.DeepClone();
- //re-assign the event handler
- clone._properties.CollectionChanged += clone.PropertiesChanged;
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
+ base.PerformDeepClone(clone);
- return clone;
+ var clonedEntity = (Macro)clone;
+
+ clonedEntity._addedProperties = new List();
+ clonedEntity._removedProperties = new List();
+ clonedEntity._properties = (MacroPropertyCollection)Properties.DeepClone();
+ //re-assign the event handler
+ clonedEntity._properties.CollectionChanged += clonedEntity.PropertiesChanged;
+
}
}
}
diff --git a/src/Umbraco.Core/Models/Member.cs b/src/Umbraco.Core/Models/Member.cs
index 7576f01ce0..38927898cf 100644
--- a/src/Umbraco.Core/Models/Member.cs
+++ b/src/Umbraco.Core/Models/Member.cs
@@ -598,20 +598,15 @@ namespace Umbraco.Core.Models
return true;
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (Member)base.DeepClone();
- //turn off change tracking
- clone.DisableChangeTracking();
+ base.PerformDeepClone(clone);
+
+ var clonedEntity = (Member)clone;
+
//need to manually clone this since it's not settable
- clone._contentType = (IMemberType)ContentType.DeepClone();
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
-
+ clonedEntity._contentType = (IMemberType)ContentType.DeepClone();
+
}
///
diff --git a/src/Umbraco.Core/Models/Membership/User.cs b/src/Umbraco.Core/Models/Membership/User.cs
index 9066674193..0694194996 100644
--- a/src/Umbraco.Core/Models/Membership/User.cs
+++ b/src/Umbraco.Core/Models/Membership/User.cs
@@ -448,18 +448,19 @@ namespace Umbraco.Core.Models.Membership
[DoNotClone]
internal object AdditionalDataLock { get { return _additionalDataLock; } }
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (User)base.DeepClone();
- //turn off change tracking
- clone.DisableChangeTracking();
+ base.PerformDeepClone(clone);
+
+ var clonedEntity = (User)clone;
+
//manually clone the start node props
- clone._startContentIds = _startContentIds.ToArray();
- clone._startMediaIds = _startMediaIds.ToArray();
+ clonedEntity._startContentIds = _startContentIds.ToArray();
+ clonedEntity._startMediaIds = _startMediaIds.ToArray();
// this value has been cloned and points to the same object
// which obviously is bad - needs to point to a new object
- clone._additionalDataLock = new object();
+ clonedEntity._additionalDataLock = new object();
if (_additionalData != null)
{
@@ -467,7 +468,7 @@ namespace Umbraco.Core.Models.Membership
// changing one clone impacts all of them - so we need to reset it with a fresh
// dictionary that will contain the same values - and, if some values are deep
// cloneable, they should be deep-cloned too
- var cloneAdditionalData = clone._additionalData = new Dictionary();
+ var cloneAdditionalData = clonedEntity._additionalData = new Dictionary();
lock (_additionalDataLock)
{
@@ -480,15 +481,9 @@ namespace Umbraco.Core.Models.Membership
}
//need to create new collections otherwise they'll get copied by ref
- clone._userGroups = new HashSet(_userGroups);
- clone._allowedSections = _allowedSections != null ? new List(_allowedSections) : null;
- //re-create the event handler
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
+ clonedEntity._userGroups = new HashSet(_userGroups);
+ clonedEntity._allowedSections = _allowedSections != null ? new List(_allowedSections) : null;
+
}
///
diff --git a/src/Umbraco.Core/Models/NotificationEmailBodyParams.cs b/src/Umbraco.Core/Models/NotificationEmailBodyParams.cs
new file mode 100644
index 0000000000..e85284fe5a
--- /dev/null
+++ b/src/Umbraco.Core/Models/NotificationEmailBodyParams.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace Umbraco.Core.Models
+{
+ public class NotificationEmailBodyParams
+ {
+ public NotificationEmailBodyParams(string recipientName, string action, string itemName, string itemId, string itemUrl, string editedUser, string siteUrl, string summary)
+ {
+ RecipientName = recipientName ?? throw new ArgumentNullException(nameof(recipientName));
+ Action = action ?? throw new ArgumentNullException(nameof(action));
+ ItemName = itemName ?? throw new ArgumentNullException(nameof(itemName));
+ ItemId = itemId ?? throw new ArgumentNullException(nameof(itemId));
+ ItemUrl = itemUrl ?? throw new ArgumentNullException(nameof(itemUrl));
+ Summary = summary ?? throw new ArgumentNullException(nameof(summary));
+ EditedUser = editedUser ?? throw new ArgumentNullException(nameof(editedUser));
+ SiteUrl = siteUrl ?? throw new ArgumentNullException(nameof(siteUrl));
+ }
+
+ public string RecipientName { get; }
+ public string Action { get; }
+ public string ItemName { get; }
+ public string ItemId { get; }
+ public string ItemUrl { get; }
+
+ ///
+ /// This will either be an HTML or text based summary depending on the email type being sent
+ ///
+ public string Summary { get; }
+ public string EditedUser { get; }
+ public string SiteUrl { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/NotificationEmailSubjectParams.cs b/src/Umbraco.Core/Models/NotificationEmailSubjectParams.cs
new file mode 100644
index 0000000000..07b26dbcc1
--- /dev/null
+++ b/src/Umbraco.Core/Models/NotificationEmailSubjectParams.cs
@@ -0,0 +1,23 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Umbraco.Core.Models
+{
+
+ public class NotificationEmailSubjectParams
+ {
+ public NotificationEmailSubjectParams(string siteUrl, string action, string itemName)
+ {
+ SiteUrl = siteUrl ?? throw new ArgumentNullException(nameof(siteUrl));
+ Action = action ?? throw new ArgumentNullException(nameof(action));
+ ItemName = itemName ?? throw new ArgumentNullException(nameof(itemName));
+ }
+
+ public string SiteUrl { get; }
+ public string Action { get; }
+ public string ItemName { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Models/Property.cs b/src/Umbraco.Core/Models/Property.cs
index 8a97dc2cfc..0c71544111 100644
--- a/src/Umbraco.Core/Models/Property.cs
+++ b/src/Umbraco.Core/Models/Property.cs
@@ -392,21 +392,14 @@ namespace Umbraco.Core.Models
return PropertyType.IsPropertyValueValid(value);
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (Property) base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var clonedEntity = (Property)clone;
//need to manually assign since this is a readonly property
- clone.PropertyType = (PropertyType) PropertyType.DeepClone();
-
- //re-enable tracking
- clone.ResetDirtyProperties(false); // not needed really, since we're not tracking
- clone.EnableChangeTracking();
-
- return clone;
+ clonedEntity.PropertyType = (PropertyType) PropertyType.DeepClone();
}
}
}
diff --git a/src/Umbraco.Core/Models/PropertyGroup.cs b/src/Umbraco.Core/Models/PropertyGroup.cs
index 6c1f2e5c61..1d0b949932 100644
--- a/src/Umbraco.Core/Models/PropertyGroup.cs
+++ b/src/Umbraco.Core/Models/PropertyGroup.cs
@@ -100,24 +100,18 @@ namespace Umbraco.Core.Models
return baseHash ^ nameHash;
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (PropertyGroup)base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var clonedEntity = (PropertyGroup)clone;
- if (clone._propertyTypes != null)
+ if (clonedEntity._propertyTypes != null)
{
- clone._propertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
- clone._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
- clone._propertyTypes.CollectionChanged += clone.PropertyTypesChanged; //re-assign correct event handler
+ clonedEntity._propertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
+ clonedEntity._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
+ clonedEntity._propertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
}
-
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
}
}
}
diff --git a/src/Umbraco.Core/Models/PropertyType.cs b/src/Umbraco.Core/Models/PropertyType.cs
index a34fdb04ed..d44e7d464f 100644
--- a/src/Umbraco.Core/Models/PropertyType.cs
+++ b/src/Umbraco.Core/Models/PropertyType.cs
@@ -424,22 +424,17 @@ namespace Umbraco.Core.Models
}
///
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (PropertyType)base.DeepClone();
- //turn off change tracking
- clone.DisableChangeTracking();
+ base.PerformDeepClone(clone);
+
+ var clonedEntity = (PropertyType)clone;
+
//need to manually assign the Lazy value as it will not be automatically mapped
if (PropertyGroupId != null)
{
- clone._propertyGroupId = new Lazy(() => PropertyGroupId.Value);
+ clonedEntity._propertyGroupId = new Lazy(() => PropertyGroupId.Value);
}
- //this shouldn't really be needed since we're not tracking
- clone.ResetDirtyProperties(false);
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
}
}
}
diff --git a/src/Umbraco.Core/Models/PublicAccessEntry.cs b/src/Umbraco.Core/Models/PublicAccessEntry.cs
index 7fd0849e27..e93dc56e35 100644
--- a/src/Umbraco.Core/Models/PublicAccessEntry.cs
+++ b/src/Umbraco.Core/Models/PublicAccessEntry.cs
@@ -153,23 +153,17 @@ namespace Umbraco.Core.Models
}
}
- public override object DeepClone()
+ protected override void PerformDeepClone(object clone)
{
- var clone = (PublicAccessEntry) base.DeepClone();
+ base.PerformDeepClone(clone);
- //turn off change tracking
- clone.DisableChangeTracking();
+ var cloneEntity = (PublicAccessEntry)clone;
- if (clone._ruleCollection != null)
+ if (cloneEntity._ruleCollection != null)
{
- clone._ruleCollection.CollectionChanged -= _ruleCollection_CollectionChanged; //clear this event handler if any
- clone._ruleCollection.CollectionChanged += clone._ruleCollection_CollectionChanged; //re-assign correct event handler
+ cloneEntity._ruleCollection.CollectionChanged -= _ruleCollection_CollectionChanged; //clear this event handler if any
+ cloneEntity._ruleCollection.CollectionChanged += cloneEntity._ruleCollection_CollectionChanged; //re-assign correct event handler
}
-
- //re-enable tracking
- clone.EnableChangeTracking();
-
- return clone;
}
}
}
diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs
index 6cfc923ebe..275957e453 100644
--- a/src/Umbraco.Core/Services/IContentService.cs
+++ b/src/Umbraco.Core/Services/IContentService.cs
@@ -312,12 +312,12 @@ namespace Umbraco.Core.Services
///
/// Sorts documents.
///
- bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true);
+ OperationResult Sort(IEnumerable items, int userId = 0, bool raiseEvents = true);
///
/// Sorts documents.
///
- bool Sort(IEnumerable ids, int userId = 0, bool raiseEvents = true);
+ OperationResult Sort(IEnumerable ids, int userId = 0, bool raiseEvents = true);
#endregion
diff --git a/src/Umbraco.Core/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs
index 3af603a31c..a990b1e0ff 100644
--- a/src/Umbraco.Core/Services/INotificationService.cs
+++ b/src/Umbraco.Core/Services/INotificationService.cs
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using System.Web;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Membership;
@@ -13,20 +12,6 @@ namespace Umbraco.Core.Services
{
public interface INotificationService : IService
{
- ///
- /// Sends the notifications for the specified user regarding the specified node and action.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
- Func createSubject,
- Func createBody);
-
///
/// Sends the notifications for the specified user regarding the specified nodes and action.
///
@@ -37,9 +22,9 @@ namespace Umbraco.Core.Services
///
///
///
- void SendNotifications(IUser operatingUser, IEnumerable entities, string action, string actionName, HttpContextBase http,
- Func createSubject,
- Func createBody);
+ void SendNotifications(IUser operatingUser, IEnumerable entities, string action, string actionName, Uri siteUri,
+ Func<(IUser user, NotificationEmailSubjectParams subject), string> createSubject,
+ Func<(IUser user, NotificationEmailBodyParams body, bool isHtml), string> createBody);
///
/// Gets the notifications for the user
diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs
index 386c76db88..2b585cebad 100644
--- a/src/Umbraco.Core/Services/Implement/ContentService.cs
+++ b/src/Umbraco.Core/Services/Implement/ContentService.cs
@@ -1906,17 +1906,19 @@ namespace Umbraco.Core.Services.Implement
///
///
///
- /// True if sorting succeeded, otherwise False
- public bool Sort(IEnumerable items, int userId = 0, bool raiseEvents = true)
+ /// Result indicating what action was taken when handling the command.
+ public OperationResult Sort(IEnumerable items, int userId = 0, bool raiseEvents = true)
{
+ var evtMsgs = EventMessagesFactory.Get();
+
var itemsA = items.ToArray();
- if (itemsA.Length == 0) return true;
+ if (itemsA.Length == 0) return new OperationResult(OperationResultType.NoOperation, evtMsgs);
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
- var ret = Sort(scope, itemsA, userId, raiseEvents);
+ var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents);
scope.Complete();
return ret;
}
@@ -1933,28 +1935,38 @@ namespace Umbraco.Core.Services.Implement
///
///
///
- /// True if sorting succeeded, otherwise False
- public bool Sort(IEnumerable ids, int userId = 0, bool raiseEvents = true)
+ /// Result indicating what action was taken when handling the command.
+ public OperationResult Sort(IEnumerable ids, int userId = 0, bool raiseEvents = true)
{
+ var evtMsgs = EventMessagesFactory.Get();
+
var idsA = ids.ToArray();
- if (idsA.Length == 0) return true;
+ if (idsA.Length == 0) return new OperationResult(OperationResultType.NoOperation, evtMsgs);
using (var scope = ScopeProvider.CreateScope())
{
scope.WriteLock(Constants.Locks.ContentTree);
var itemsA = GetByIds(idsA).ToArray();
- var ret = Sort(scope, itemsA, userId, raiseEvents);
+ var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents);
scope.Complete();
return ret;
}
}
- private bool Sort(IScope scope, IContent[] itemsA, int userId, bool raiseEvents)
+ private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages evtMsgs, bool raiseEvents)
{
var saveEventArgs = new SaveEventArgs(itemsA);
- if (raiseEvents && scope.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
- return false;
+ if (raiseEvents)
+ {
+ //raise cancelable sorting event
+ if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs, nameof(Sorting)))
+ return OperationResult.Cancel(evtMsgs);
+
+ //raise saving event (this one cannot be canceled)
+ saveEventArgs.CanCancel = false;
+ scope.Events.Dispatch(Saving, this, saveEventArgs, nameof(Saving));
+ }
var published = new List();
var saved = new List();
@@ -1986,8 +1998,9 @@ namespace Umbraco.Core.Services.Implement
if (raiseEvents)
{
- saveEventArgs.CanCancel = false;
- scope.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
+ //first saved, then sorted
+ scope.Events.Dispatch(Saved, this, saveEventArgs, nameof(Saved));
+ scope.Events.Dispatch(Sorted, this, saveEventArgs, nameof(Sorted));
}
scope.Events.Dispatch(TreeChanged, this, saved.Select(x => new TreeChange(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
@@ -1996,7 +2009,7 @@ namespace Umbraco.Core.Services.Implement
scope.Events.Dispatch(Published, this, new PublishEventArgs(published, false, false), "Published");
Audit(AuditType.Sort, userId, 0, "Sorting content performed by user");
- return true;
+ return OperationResult.Succeed(evtMsgs);
}
#endregion
@@ -2071,6 +2084,16 @@ namespace Umbraco.Core.Services.Implement
///
public static event TypedEventHandler DeletedVersions;
+ ///
+ /// Occurs before Sorting
+ ///
+ public static event TypedEventHandler> Sorting;
+
+ ///
+ /// Occurs after Sorting
+ ///
+ public static event TypedEventHandler> Sorted;
+
///
/// Occurs before Save
///
diff --git a/src/Umbraco.Core/Services/Implement/NotificationService.cs b/src/Umbraco.Core/Services/Implement/NotificationService.cs
index cc76374715..ef2bfafcf6 100644
--- a/src/Umbraco.Core/Services/Implement/NotificationService.cs
+++ b/src/Umbraco.Core/Services/Implement/NotificationService.cs
@@ -6,8 +6,8 @@ using System.Linq;
using System.Net.Mail;
using System.Text;
using System.Threading;
-using System.Web;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
@@ -24,91 +24,25 @@ namespace Umbraco.Core.Services.Implement
private readonly IScopeProvider _uowProvider;
private readonly IUserService _userService;
private readonly IContentService _contentService;
+ private readonly ILocalizationService _localizationService;
private readonly INotificationsRepository _notificationsRepository;
private readonly IGlobalSettings _globalSettings;
+ private readonly IContentSection _contentSection;
private readonly ILogger _logger;
- public NotificationService(IScopeProvider provider, IUserService userService, IContentService contentService, ILogger logger,
- INotificationsRepository notificationsRepository, IGlobalSettings globalSettings)
+ public NotificationService(IScopeProvider provider, IUserService userService, IContentService contentService, ILocalizationService localizationService,
+ ILogger logger, INotificationsRepository notificationsRepository, IGlobalSettings globalSettings, IContentSection contentSection)
{
_notificationsRepository = notificationsRepository;
_globalSettings = globalSettings;
+ _contentSection = contentSection;
_uowProvider = provider ?? throw new ArgumentNullException(nameof(provider));
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
+ _localizationService = localizationService;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
- ///
- /// Sends the notifications for the specified user regarding the specified node and action.
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// Currently this will only work for Content entities!
- ///
- public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
- Func createSubject,
- Func createBody)
- {
- if (entity is IContent == false)
- throw new NotSupportedException();
-
- var content = (IContent) entity;
-
- // lazily get previous version
- IContentBase prevVersion = null;
-
- // do not load *all* users in memory at once
- // do not load notifications *per user* (N+1 select)
- // cannot load users & notifications in 1 query (combination btw User2AppDto and User2NodeNotifyDto)
- // => get batches of users, get all their notifications in 1 query
- // re. users:
- // users being (dis)approved = not an issue, filtered in memory not in SQL
- // users being modified or created = not an issue, ordering by ID, as long as we don't *insert* low IDs
- // users being deleted = not an issue for GetNextUsers
- var id = Constants.Security.SuperUserId;
- var nodeIds = content.Path.Split(',').Select(int.Parse).ToArray();
- const int pagesz = 400; // load batches of 400 users
- do
- {
- // users are returned ordered by id, notifications are returned ordered by user id
- var users = ((UserService) _userService).GetNextUsers(id, pagesz).Where(x => x.IsApproved).ToList();
- var notifications = GetUsersNotifications(users.Select(x => x.Id), action, nodeIds, Constants.ObjectTypes.Document).ToList();
- if (notifications.Count == 0) break;
-
- var i = 0;
- foreach (var user in users)
- {
- // continue if there's no notification for this user
- if (notifications[i].UserId != user.Id) continue; // next user
-
- // lazy load prev version
- if (prevVersion == null)
- {
- prevVersion = GetPreviousVersion(entity.Id);
- }
-
- // queue notification
- var req = CreateNotificationRequest(operatingUser, user, content, prevVersion, actionName, http, createSubject, createBody);
- Enqueue(req);
-
- // skip other notifications for this user
- while (i < notifications.Count && notifications[i++].UserId == user.Id) ;
- if (i >= notifications.Count) break; // break if no more notifications
- }
-
- // load more users if any
- id = users.Count == pagesz ? users.Last().Id + 1 : -1;
-
- } while (id > 0);
- }
-
///
/// Gets the previous version to the latest version of the content item if there is one
///
@@ -131,20 +65,14 @@ namespace Umbraco.Core.Services.Implement
///
///
///
- ///
+ ///
///
///
- ///
- /// Currently this will only work for Content entities!
- ///
- public void SendNotifications(IUser operatingUser, IEnumerable entities, string action, string actionName, HttpContextBase http,
- Func createSubject,
- Func createBody)
+ public void SendNotifications(IUser operatingUser, IEnumerable entities, string action, string actionName, Uri siteUri,
+ Func<(IUser user, NotificationEmailSubjectParams subject), string> createSubject,
+ Func<(IUser user, NotificationEmailBodyParams body, bool isHtml), string> createBody)
{
- if (entities is IEnumerable == false)
- throw new NotSupportedException();
-
- var entitiesL = entities as List ?? entities.Cast().ToList();
+ var entitiesL = entities.ToList();
//exit if there are no entities
if (entitiesL.Count == 0) return;
@@ -156,7 +84,7 @@ namespace Umbraco.Core.Services.Implement
var prevVersionDictionary = new Dictionary();
// see notes above
- var id = 0;
+ var id = Constants.Security.SuperUserId;
const int pagesz = 400; // load batches of 400 users
do
{
@@ -185,7 +113,7 @@ namespace Umbraco.Core.Services.Implement
}
// queue notification
- var req = CreateNotificationRequest(operatingUser, user, content, prevVersionDictionary[content.Id], actionName, http, createSubject, createBody);
+ var req = CreateNotificationRequest(operatingUser, user, content, prevVersionDictionary[content.Id], actionName, siteUri, createSubject, createBody);
Enqueue(req);
}
@@ -350,118 +278,141 @@ namespace Umbraco.Core.Services.Implement
///
///
/// The action readable name - currently an action is just a single letter, this is the name associated with the letter
- ///
+ ///
/// Callback to create the mail subject
/// Callback to create the mail body
- private NotificationRequest CreateNotificationRequest(IUser performingUser, IUser mailingUser, IContentBase content, IContentBase oldDoc,
- string actionName, HttpContextBase http,
- Func createSubject,
- Func createBody)
+ private NotificationRequest CreateNotificationRequest(IUser performingUser, IUser mailingUser, IContent content, IContentBase oldDoc,
+ string actionName,
+ Uri siteUri,
+ Func<(IUser user, NotificationEmailSubjectParams subject), string> createSubject,
+ Func<(IUser user, NotificationEmailBodyParams body, bool isHtml), string> createBody)
{
if (performingUser == null) throw new ArgumentNullException("performingUser");
if (mailingUser == null) throw new ArgumentNullException("mailingUser");
if (content == null) throw new ArgumentNullException("content");
- if (http == null) throw new ArgumentNullException("http");
+ if (siteUri == null) throw new ArgumentNullException("siteUri");
if (createSubject == null) throw new ArgumentNullException("createSubject");
if (createBody == null) throw new ArgumentNullException("createBody");
// build summary
var summary = new StringBuilder();
- var props = content.Properties.ToArray();
- foreach (var p in props)
+
+ if (content.ContentType.VariesByNothing())
{
- //fixme doesn't take into account variants
-
- var newText = p.GetValue() != null ? p.GetValue().ToString() : "";
- var oldText = newText;
-
- // check if something was changed and display the changes otherwise display the fields
- if (oldDoc.Properties.Contains(p.PropertyType.Alias))
+ if (!_contentSection.DisableHtmlEmail)
{
- var oldProperty = oldDoc.Properties[p.PropertyType.Alias];
- oldText = oldProperty.GetValue() != null ? oldProperty.GetValue().ToString() : "";
+ //create the html summary for invariant content
- // replace html with char equivalent
- ReplaceHtmlSymbols(ref oldText);
- ReplaceHtmlSymbols(ref newText);
+ //list all of the property values like we used to
+ summary.Append("");
+ foreach (var p in content.Properties)
+ {
+ //fixme doesn't take into account variants
+
+ var newText = p.GetValue() != null ? p.GetValue().ToString() : "";
+ var oldText = newText;
+
+ // check if something was changed and display the changes otherwise display the fields
+ if (oldDoc.Properties.Contains(p.PropertyType.Alias))
+ {
+ var oldProperty = oldDoc.Properties[p.PropertyType.Alias];
+ oldText = oldProperty.GetValue() != null ? oldProperty.GetValue().ToString() : "";
+
+ // replace html with char equivalent
+ ReplaceHtmlSymbols(ref oldText);
+ ReplaceHtmlSymbols(ref newText);
+ }
+
+ //show the values
+ summary.Append("");
+ summary.Append("");
+ summary.Append(p.PropertyType.Name);
+ summary.Append(" ");
+ summary.Append("");
+ summary.Append(newText);
+ summary.Append(" ");
+ summary.Append(" ");
+ }
+ summary.Append("
");
}
+
+ }
+ else if (content.ContentType.VariesByCulture())
+ {
+ //it's variant, so detect what cultures have changed
-
- // make sure to only highlight changes done using TinyMCE editor... other changes will be displayed using default summary
- // TODO: We should probably allow more than just tinymce??
- if ((p.PropertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.TinyMce)
- && string.CompareOrdinal(oldText, newText) != 0)
+ if (!_contentSection.DisableHtmlEmail)
{
- summary.Append("");
- summary.Append(" Note: ");
- summary.Append(
- " Red for deleted characters Yellow for inserted characters ");
- summary.Append(" ");
- summary.Append("");
- summary.Append(" New ");
- summary.Append(p.PropertyType.Name);
- summary.Append(" ");
- summary.Append("");
- summary.Append(ReplaceLinks(CompareText(oldText, newText, true, false, "", string.Empty), http.Request));
- summary.Append(" ");
- summary.Append(" ");
- summary.Append("");
- summary.Append(" Old ");
- summary.Append(p.PropertyType.Name);
- summary.Append(" ");
- summary.Append("");
- summary.Append(ReplaceLinks(CompareText(newText, oldText, true, false, "", string.Empty), http.Request));
- summary.Append(" ");
- summary.Append(" ");
+ //Create the html based summary (ul of culture names)
+
+ var culturesChanged = content.CultureInfos.Where(x => x.Value.WasDirty())
+ .Select(x => x.Key)
+ .Select(_localizationService.GetLanguageByIsoCode)
+ .WhereNotNull()
+ .Select(x => x.CultureName);
+ summary.Append("");
+ foreach (var culture in culturesChanged)
+ {
+ summary.Append("");
+ summary.Append(culture);
+ summary.Append(" ");
+ }
+ summary.Append(" ");
}
else
{
- summary.Append("");
- summary.Append("");
- summary.Append(p.PropertyType.Name);
- summary.Append(" ");
- summary.Append("");
- summary.Append(newText);
- summary.Append(" ");
- summary.Append(" ");
+ //Create the text based summary (csv of culture names)
+
+ var culturesChanged = string.Join(", ", content.CultureInfos.Where(x => x.Value.WasDirty())
+ .Select(x => x.Key)
+ .Select(_localizationService.GetLanguageByIsoCode)
+ .WhereNotNull()
+ .Select(x => x.CultureName));
+
+ summary.Append("'");
+ summary.Append(culturesChanged);
+ summary.Append("'");
}
- summary.Append(
- " ");
+ }
+ else
+ {
+ //not supported yet...
+ throw new NotSupportedException();
}
- string protocol = _globalSettings.UseHttps ? "https" : "http";
+ var protocol = _globalSettings.UseHttps ? "https" : "http";
+ var subjectVars = new NotificationEmailSubjectParams(
+ string.Concat(siteUri.Authority, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
+ actionName,
+ content.Name);
- string[] subjectVars = {
- string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
- actionName,
- content.Name
- };
- string[] bodyVars = {
- mailingUser.Name,
- actionName,
- content.Name,
- performingUser.Name,
- string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
- content.Id.ToString(CultureInfo.InvariantCulture), summary.ToString(),
- string.Format("{2}://{0}/{1}",
- string.Concat(http.Request.ServerVariables["SERVER_NAME"], ":", http.Request.Url.Port),
- //TODO: RE-enable this so we can have a nice url
- /*umbraco.library.NiceUrl(documentObject.Id))*/
- string.Concat(content.Id, ".aspx"),
- protocol)
-
- };
+ var bodyVars = new NotificationEmailBodyParams(
+ mailingUser.Name,
+ actionName,
+ content.Name,
+ content.Id.ToString(CultureInfo.InvariantCulture),
+ string.Format("{2}://{0}/{1}",
+ string.Concat(siteUri.Authority),
+ //TODO: RE-enable this so we can have a nice url
+ /*umbraco.library.NiceUrl(documentObject.Id))*/
+ string.Concat(content.Id, ".aspx"),
+ protocol),
+ performingUser.Name,
+ string.Concat(siteUri.Authority, IOHelper.ResolveUrl(SystemDirectories.Umbraco)),
+ summary.ToString());
// create the mail message
- var mail = new MailMessage(UmbracoConfig.For.UmbracoSettings().Content.NotificationEmailAddress, mailingUser.Email);
+ var mail = new MailMessage(_contentSection.NotificationEmailAddress, mailingUser.Email);
// populate the message
- mail.Subject = createSubject(mailingUser, subjectVars);
- if (UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail)
+
+
+ mail.Subject = createSubject((mailingUser, subjectVars));
+ if (_contentSection.DisableHtmlEmail)
{
mail.IsBodyHtml = false;
- mail.Body = createBody(mailingUser, bodyVars);
+ mail.Body = createBody((user: mailingUser, body: bodyVars, false));
}
else
{
@@ -470,14 +421,14 @@ namespace Umbraco.Core.Services.Implement
string.Concat(@"
-", createBody(mailingUser, bodyVars));
+", createBody((user: mailingUser, body: bodyVars, true)));
}
// nh, issue 30724. Due to hardcoded http strings in resource files, we need to check for https replacements here
// adding the server name to make sure we don't replace external links
if (_globalSettings.UseHttps && string.IsNullOrEmpty(mail.Body) == false)
{
- string serverName = http.Request.ServerVariables["SERVER_NAME"];
+ string serverName = siteUri.Host;
mail.Body = mail.Body.Replace(
string.Format("http://{0}", serverName),
string.Format("https://{0}", serverName));
@@ -486,12 +437,10 @@ namespace Umbraco.Core.Services.Implement
return new NotificationRequest(mail, actionName, mailingUser.Name, mailingUser.Email);
}
- private string ReplaceLinks(string text, HttpRequestBase request)
+ private string ReplaceLinks(string text, Uri siteUri)
{
var sb = new StringBuilder(_globalSettings.UseHttps ? "https://" : "http://");
- sb.Append(request.ServerVariables["SERVER_NAME"]);
- sb.Append(":");
- sb.Append(request.Url.Port);
+ sb.Append(siteUri.Authority);
sb.Append("/");
var domain = sb.ToString();
text = text.Replace("href=\"/", "href=\"" + domain);
@@ -505,6 +454,7 @@ namespace Umbraco.Core.Services.Implement
/// The old string.
private static void ReplaceHtmlSymbols(ref string oldString)
{
+ if (oldString.IsNullOrWhiteSpace()) return;
oldString = oldString.Replace(" ", " ");
oldString = oldString.Replace("’", "'");
oldString = oldString.Replace("&", "&");
@@ -512,69 +462,7 @@ namespace Umbraco.Core.Services.Implement
oldString = oldString.Replace("”", "”");
oldString = oldString.Replace(""", "\"");
}
-
- ///
- /// Compares the text.
- ///
- /// The old text.
- /// The new text.
- /// if set to true [display inserted text].
- /// if set to true [display deleted text].
- /// The inserted style.
- /// The deleted style.
- ///
- private static string CompareText(string oldText, string newText, bool displayInsertedText,
- bool displayDeletedText, string insertedStyle, string deletedStyle)
- {
- var sb = new StringBuilder();
- var diffs = Diff.DiffText1(oldText, newText);
-
- int pos = 0;
- for (var n = 0; n < diffs.Length; n++)
- {
- var it = diffs[n];
-
- // write unchanged chars
- while ((pos < it.StartB) && (pos < newText.Length))
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
-
- // write deleted chars
- if (displayDeletedText && it.DeletedA > 0)
- {
- sb.Append(deletedStyle);
- for (var m = 0; m < it.DeletedA; m++)
- {
- sb.Append(oldText[it.StartA + m]);
- } // for
- sb.Append("");
- }
-
- // write inserted chars
- if (displayInsertedText && pos < it.StartB + it.InsertedB)
- {
- sb.Append(insertedStyle);
- while (pos < it.StartB + it.InsertedB)
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
- sb.Append("");
- } // if
- } // while
-
- // write rest of unchanged chars
- while (pos < newText.Length)
- {
- sb.Append(newText[pos]);
- pos++;
- } // while
-
- return sb.ToString();
- }
-
+
// manage notifications
// ideally, would need to use IBackgroundTasks - but they are not part of Core!
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index acda9ef589..ac115e843b 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -395,6 +395,8 @@
+
+
diff --git a/src/Umbraco.Tests/Models/ContentTests.cs b/src/Umbraco.Tests/Models/ContentTests.cs
index 807231730b..31110d7196 100644
--- a/src/Umbraco.Tests/Models/ContentTests.cs
+++ b/src/Umbraco.Tests/Models/ContentTests.cs
@@ -271,8 +271,13 @@ namespace Umbraco.Tests.Models
// Arrange
var contentType = MockedContentTypes.CreateTextpageContentType();
contentType.Id = 99;
+ contentType.Variations = ContentVariation.Culture;
var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
+ content.SetCultureName("Hello", "en-US");
+ content.SetCultureName("World", "es-ES");
+ content.PublishCulture("en-US");
+
// should not try to clone something that's not Published or Unpublished
// (and in fact it will not work)
// but we cannot directly set the state to Published - hence this trick
@@ -301,6 +306,8 @@ namespace Umbraco.Tests.Models
content.UpdateDate = DateTime.Now;
content.WriterId = 23;
+
+
// Act
var clone = (Content)content.DeepClone();
@@ -349,6 +356,22 @@ namespace Umbraco.Tests.Models
Assert.AreEqual(clone.Properties[index], content.Properties[index]);
}
+ Assert.AreNotSame(clone.PublishCultureInfos, content.PublishCultureInfos);
+ Assert.AreEqual(clone.PublishCultureInfos.Count, content.PublishCultureInfos.Count);
+ foreach (var key in content.PublishCultureInfos.Keys)
+ {
+ Assert.AreNotSame(clone.PublishCultureInfos[key], content.PublishCultureInfos[key]);
+ Assert.AreEqual(clone.PublishCultureInfos[key], content.PublishCultureInfos[key]);
+ }
+
+ Assert.AreNotSame(clone.CultureInfos, content.CultureInfos);
+ Assert.AreEqual(clone.CultureInfos.Count, content.CultureInfos.Count);
+ foreach (var key in content.CultureInfos.Keys)
+ {
+ Assert.AreNotSame(clone.CultureInfos[key], content.CultureInfos[key]);
+ Assert.AreEqual(clone.CultureInfos[key], content.CultureInfos[key]);
+ }
+
//This double verifies by reflection
var allProps = clone.GetType().GetProperties();
foreach (var propertyInfo in allProps)
@@ -369,6 +392,87 @@ namespace Umbraco.Tests.Models
Assert.IsTrue(asDirty.IsPropertyDirty("Properties"));
}
+ [Test]
+ public void Remember_Dirty_Properties()
+ {
+ // Arrange
+ var contentType = MockedContentTypes.CreateTextpageContentType();
+ contentType.Id = 99;
+ contentType.Variations = ContentVariation.Culture;
+ var content = MockedContent.CreateTextpageContent(contentType, "Textpage", -1);
+
+ content.SetCultureName("Hello", "en-US");
+ content.SetCultureName("World", "es-ES");
+ content.PublishCulture("en-US");
+
+ var i = 200;
+ foreach (var property in content.Properties)
+ {
+ property.Id = ++i;
+ }
+ content.Id = 10;
+ content.CreateDate = DateTime.Now;
+ content.CreatorId = 22;
+ content.ExpireDate = DateTime.Now;
+ content.Key = Guid.NewGuid();
+ content.Level = 3;
+ content.Path = "-1,4,10";
+ content.ReleaseDate = DateTime.Now;
+ content.SortOrder = 5;
+ content.Template = new Template((string)"Test Template", (string)"testTemplate")
+ {
+ Id = 88
+ };
+
+ content.Trashed = true;
+ content.UpdateDate = DateTime.Now;
+ content.WriterId = 23;
+
+ content.Template.UpdateDate = DateTime.Now; //update a child object
+ content.ContentType.UpdateDate = DateTime.Now; //update a child object
+
+ // Act
+ content.ResetDirtyProperties();
+
+ // Assert
+ Assert.IsTrue(content.WasDirty());
+ Assert.IsTrue(content.WasPropertyDirty("Id"));
+ Assert.IsTrue(content.WasPropertyDirty("CreateDate"));
+ Assert.IsTrue(content.WasPropertyDirty("CreatorId"));
+ Assert.IsTrue(content.WasPropertyDirty("ExpireDate"));
+ Assert.IsTrue(content.WasPropertyDirty("Key"));
+ Assert.IsTrue(content.WasPropertyDirty("Level"));
+ Assert.IsTrue(content.WasPropertyDirty("Path"));
+ Assert.IsTrue(content.WasPropertyDirty("ReleaseDate"));
+ Assert.IsTrue(content.WasPropertyDirty("SortOrder"));
+ Assert.IsTrue(content.WasPropertyDirty("Template"));
+ Assert.IsTrue(content.WasPropertyDirty("Trashed"));
+ Assert.IsTrue(content.WasPropertyDirty("UpdateDate"));
+ Assert.IsTrue(content.WasPropertyDirty("WriterId"));
+ foreach (var prop in content.Properties)
+ {
+ Assert.IsTrue(prop.WasDirty());
+ Assert.IsTrue(prop.WasPropertyDirty("Id"));
+ }
+ Assert.IsTrue(content.WasPropertyDirty("CultureInfos"));
+ foreach(var culture in content.CultureInfos)
+ {
+ Assert.IsTrue(culture.Value.WasDirty());
+ Assert.IsTrue(culture.Value.WasPropertyDirty("Name"));
+ Assert.IsTrue(culture.Value.WasPropertyDirty("Date"));
+ }
+ Assert.IsTrue(content.WasPropertyDirty("PublishCultureInfos"));
+ foreach (var culture in content.PublishCultureInfos)
+ {
+ Assert.IsTrue(culture.Value.WasDirty());
+ Assert.IsTrue(culture.Value.WasPropertyDirty("Name"));
+ Assert.IsTrue(culture.Value.WasPropertyDirty("Date"));
+ }
+ //verify child objects were reset too
+ Assert.IsTrue(content.Template.WasPropertyDirty("UpdateDate"));
+ Assert.IsTrue(content.ContentType.WasPropertyDirty("UpdateDate"));
+ }
+
[Test]
public void Can_Serialize_Without_Error()
{
diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
index 6d58a49f04..4529c4f1ef 100644
--- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs
+++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs
@@ -10,6 +10,7 @@ using Umbraco.Core;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -106,6 +107,7 @@ namespace Umbraco.Tests.TestHelpers
CacheHelper cache,
ILogger logger,
IGlobalSettings globalSettings,
+ IUmbracoSettingsSection umbracoSettings,
IEventMessagesFactory eventMessagesFactory,
IEnumerable urlSegmentProviders,
TypeLoader typeLoader,
@@ -159,10 +161,11 @@ namespace Umbraco.Tests.TestHelpers
var runtimeState = Mock.Of();
var idkMap = new IdkMap(scopeProvider);
+ var localizationService = GetLazyService(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c)));
var userService = GetLazyService(container, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo(c), GetRepo(c),globalSettings));
var dataTypeService = GetLazyService(container, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
var contentService = GetLazyService(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
- var notificationService = GetLazyService(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, logger, GetRepo(c),globalSettings));
+ var notificationService = GetLazyService(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, GetRepo(c), globalSettings, umbracoSettings.Content));
var serverRegistrationService = GetLazyService(container, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
var memberGroupService = GetLazyService(container, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo(c)));
var memberService = GetLazyService(container, c => new MemberService(scopeProvider, logger, eventMessagesFactory, memberGroupService.Value, mediaFileSystem, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
@@ -170,7 +173,6 @@ namespace Umbraco.Tests.TestHelpers
var contentTypeService = GetLazyService(container, c => new ContentTypeService(scopeProvider, logger, eventMessagesFactory, contentService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
var mediaTypeService = GetLazyService(container, c => new MediaTypeService(scopeProvider, logger, eventMessagesFactory, mediaService.Value, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
var fileService = GetLazyService(container, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c), GetRepo(c)));
- var localizationService = GetLazyService(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c), GetRepo(c)));
var memberTypeService = GetLazyService(container, c => new MemberTypeService(scopeProvider, logger, eventMessagesFactory, memberService.Value, GetRepo(c), GetRepo(c), GetRepo(c)));
var entityService = GetLazyService(container, c => new EntityService(
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
index 097602fe20..d5a21e0ba6 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/validation/valsubview.directive.js
@@ -5,74 +5,78 @@
* @description Used to show validation warnings for a editor sub view to indicate that the section content has validation errors in its data.
* In order for this directive to work, the valFormManager directive must be placed on the containing form.
**/
-(function() {
- 'use strict';
+(function () {
+ 'use strict';
- function valSubViewDirective() {
+ function valSubViewDirective() {
- function controller($scope, $element) {
- //expose api
- return {
- valStatusChanged: function(args) {
- if (!args.form.$valid) {
- var subViewContent = $element.find(".ng-invalid");
+ function controller($scope, $element) {
+ //expose api
+ return {
+ valStatusChanged: function (args) {
- if (subViewContent.length > 0) {
- $scope.model.hasError = true;
- $scope.model.errorClass = args.showValidation ? 'show-validation' : null;
- } else {
- $scope.model.hasError = false;
- $scope.model.errorClass = null;
+ //TODO: Verify this is correct, does $scope.model ever exist?
+ if ($scope.model) {
+ if (!args.form.$valid) {
+ var subViewContent = $element.find(".ng-invalid");
+
+ if (subViewContent.length > 0) {
+ $scope.model.hasError = true;
+ $scope.model.errorClass = args.showValidation ? 'show-validation' : null;
+ } else {
+ $scope.model.hasError = false;
+ $scope.model.errorClass = null;
+ }
+ }
+ else {
+ $scope.model.hasError = false;
+ $scope.model.errorClass = null;
+ }
+ }
+ }
}
- }
- else {
- $scope.model.hasError = false;
- $scope.model.errorClass = null;
- }
}
- }
+
+ function link(scope, el, attr, ctrl) {
+
+ //if there are no containing form or valFormManager controllers, then we do nothing
+ if (!ctrl || !angular.isArray(ctrl) || ctrl.length !== 2 || !ctrl[0] || !ctrl[1]) {
+ return;
+ }
+
+ var valFormManager = ctrl[1];
+ scope.model.hasError = false;
+
+ //listen for form validation changes
+ valFormManager.onValidationStatusChanged(function (evt, args) {
+ if (!args.form.$valid) {
+
+ var subViewContent = el.find(".ng-invalid");
+
+ if (subViewContent.length > 0) {
+ scope.model.hasError = true;
+ } else {
+ scope.model.hasError = false;
+ }
+
+ }
+ else {
+ scope.model.hasError = false;
+ }
+ });
+
+ }
+
+ var directive = {
+ require: ['?^^form', '?^^valFormManager'],
+ restrict: "A",
+ link: link,
+ controller: controller
+ };
+
+ return directive;
}
- function link(scope, el, attr, ctrl) {
-
- //if there are no containing form or valFormManager controllers, then we do nothing
- if (!ctrl || !angular.isArray(ctrl) || ctrl.length !== 2 || !ctrl[0] || !ctrl[1]) {
- return;
- }
-
- var valFormManager = ctrl[1];
- scope.model.hasError = false;
-
- //listen for form validation changes
- valFormManager.onValidationStatusChanged(function (evt, args) {
- if (!args.form.$valid) {
-
- var subViewContent = el.find(".ng-invalid");
-
- if (subViewContent.length > 0) {
- scope.model.hasError = true;
- } else {
- scope.model.hasError = false;
- }
-
- }
- else {
- scope.model.hasError = false;
- }
- });
-
- }
-
- var directive = {
- require: ['?^^form', '?^^valFormManager'],
- restrict: "A",
- link: link,
- controller: controller
- };
-
- return directive;
- }
-
- angular.module('umbraco.directives').directive('valSubView', valSubViewDirective);
+ angular.module('umbraco.directives').directive('valSubView', valSubViewDirective);
})();
diff --git a/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js b/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js
index b636a0a51c..13d5fbc057 100644
--- a/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js
+++ b/src/Umbraco.Web.UI.Client/src/common/interceptors/security.interceptor.js
@@ -102,11 +102,8 @@
//It was decided to just put these messages into the normal status messages.
- var msg = "Unauthorized access to URL: " + rejection.config.url.split('?')[0] + " ";
- if (rejection.config.data) {
- msg += " with data: " + angular.toJson(rejection.config.data) + " Contact your administrator for information.";
- }
-
+ var msg = "Unauthorized access to URL: " + rejection.config.url.split('?')[0] + " Contact your administrator for information.";
+
notificationsService.error("Authorization error", msg);
}
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js
index 4581b78c8c..53759d7d97 100644
--- a/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/content/content.sort.controller.js
@@ -1,7 +1,7 @@
(function () {
"use strict";
- function ContentSortController($scope, $filter, contentResource, navigationService) {
+ function ContentSortController($scope, $filter, $routeParams, contentResource, navigationService) {
var vm = this;
var parentId = $scope.currentNode.parentId ? $scope.currentNode.parentId : "-1";
@@ -30,7 +30,7 @@
function onInit() {
vm.loading = true;
- contentResource.getChildren(id)
+ contentResource.getChildren(id, { cultureName: $routeParams.cculture ? $routeParams.cculture : $routeParams.mculture })
.then(function(data){
vm.children = data.items;
vm.loading = false;
@@ -79,4 +79,4 @@
}
angular.module("umbraco").controller("Umbraco.Editors.Content.SortController", ContentSortController);
-})();
\ No newline at end of file
+})();
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
index d2867acfc9..c108cffcf4 100644
--- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
+++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj
@@ -342,7 +342,6 @@
-
@@ -420,7 +419,6 @@
-
@@ -430,10 +428,6 @@
Form
-
-
-
-
Designer
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index 11a32a9703..cb9c87c8d9 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -946,10 +946,13 @@ To manage your website, simply open the Umbraco back office and start adding con
Go to http://%4%/#/content/content/edit/%5% to edit.
+ %6%
+
Have a nice day!
Cheers from the Umbraco robot
]]>
+ The following languages have been modified %0%
@@ -1005,9 +1008,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Update summary:
-
+ %6%
Have a nice day!
@@ -1028,6 +1029,9 @@ To manage your website, simply open the Umbraco back office and start adding con
]]>
+ The following languages have been modified:
+ %0%
+ ]]>
[%0%] Notification about %1% performed on %2%
Notifications
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index 6a29fc0c0c..a7a7c7d8e5 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -968,10 +968,13 @@ To manage your website, simply open the Umbraco back office and start adding con
Go to http://%4%/#/content/content/edit/%5% to edit.
+ %6%
+
Have a nice day!
Cheers from the Umbraco robot
]]>
+ The following languages have been modified %0%
@@ -1027,9 +1030,7 @@ To manage your website, simply open the Umbraco back office and start adding con
Update summary:
-
+ %6%
Have a nice day!
@@ -1050,6 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con
]]>
+ The following languages have been modified:
+ %0%
+ ]]>
[%0%] Notification about %1% performed on %2%
Notifications
diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/SendPublish.aspx b/src/Umbraco.Web.UI/Umbraco/dialogs/SendPublish.aspx
deleted file mode 100644
index 8b190d575c..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/dialogs/SendPublish.aspx
+++ /dev/null
@@ -1,13 +0,0 @@
-<%@ Page language="c#" Codebehind="SendPublish.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.SendPublish" %>
-
-
-
- umbraco - <%=Services.TextService.Localize("editContentSendToPublish")%>
-
-
-
- <%=Services.TextService.Localize("editContentSendToPublishText")%>
-
- <%=Services.TextService.Localize("closewindow")%>
-
-
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/CheckForUpgrade.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/CheckForUpgrade.asmx
deleted file mode 100644
index 226022e0fd..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/CheckForUpgrade.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="CheckForUpgrade.asmx.cs" Class="umbraco.presentation.webservices.CheckForUpgrade" %>
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/Developer.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/Developer.asmx
deleted file mode 100644
index b98e33ef54..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/Developer.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="c#" Codebehind="Developer.asmx.cs" Class="umbraco.webservices.Developer" %>
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/ajax.js b/src/Umbraco.Web.UI/Umbraco/webservices/ajax.js
deleted file mode 100644
index 868ccf6650..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/ajax.js
+++ /dev/null
@@ -1,805 +0,0 @@
-// ajax.js
-// Common Javascript methods and global objects
-// Ajax framework for Internet Explorer (6.0, ...) and Firefox (1.0, ...)
-// Copyright by Matthias Hertel, http://www.mathertel.de
-// This work is licensed under a Creative Commons Attribution 2.0 Germany License.
-// See http://creativecommons.org/licenses/by/2.0/de/
-// More information on: http://ajaxaspects.blogspot.com/ and http://ajaxaspekte.blogspot.com/
-// -----
-// 05.06.2005 created by Matthias Hertel.
-// 19.06.2005 minor corrections to webservices.
-// 25.06.2005 ajax action queue and timing.
-// 02.07.2005 queue up actions fixed.
-// 10.07.2005 ajax.timeout
-// 10.07.2005 a option object that is passed from ajax.Start() to prepare() is also queued.
-// 10.07.2005 a option object that is passed from ajax.Start() to prepare(), finish()
-// and onException() is also queued.
-// 12.07.2005 correct xml encoding when CallSoap()
-// 20.07.2005 more datatypes and XML Documents
-// 20.07.2005 more datatypes and XML Documents fixed
-// 06.08.2005 caching implemented.
-// 07.08.2005 bugs fixed, when queuing without a delay time.
-// 04.09.2005 bugs fixed, when entering non-multiple actions.
-// 07.09.2005 proxies.IsActive added
-// 27.09.2005 fixed error in handling bool as a datatype
-// 13.12.2005 WebServices with arrays on strings, ints, floats and booleans - still undocumented
-// 27.12.2005 fixed: empty string return values enabled.
-// 27.12.2005 enable the late binding of proxy methods.
-// 21.01.2006 void return bug fixed.
-// 18.02.2006 typo: Finsh -> Finish.
-// 25.02.2006 better xmlhttp request object retrieval, see http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
-// 22.04.2006 progress indicator added.
-// 28.01.2006 void return bug fixed again?
-// 09.03.2006 enable late binding of prepare and finish methods by using an expression.
-// 14.07.2006 Safari Browser Version 2.03/Mac OS X 10.4. compatibility: xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue
-// 10.08.2006 date to xml format fixed by Kars Veling
-// 16.09.2006 .postUrl
-
-// ----- global variable for the proxies to webservices. -----
-
-/// The root object for the proxies to webservices.
-var proxies = new Object();
-
-proxies.current = null; // the current active webservice call.
-proxies.xmlhttp = null; // The current active xmlhttp object.
-
-
-// ----- global variable for the ajax engine. -----
-
-/// The root object for the ajax engine.
-var ajax = new Object();
-
-ajax.current = null; /// The current active AJAX action.
-ajax.option = null; /// The options for the current active AJAX action.
-
-ajax.queue = new Array(); /// The pending AJAX actions.
-ajax.options = new Array(); /// The options for the pending AJAX actions.
-
-ajax.timer = null; /// The timer for delayed actions.
-
-ajax.progress = false; /// show a progress indicator
-ajax.progressTimer = null; /// a timer-object that help displaying the progress indicator not too often.
-
-// ----- AJAX engine and actions implementation -----
-
-///Start an AJAX action by entering it into the queue
-ajax.Start = function (action, options) {
- ajax.Add(action, options);
- // check if the action should start
- if ((ajax.current == null) && (ajax.timer == null))
- ajax._next(false);
-} // ajax.Start
-
-
-///Start an AJAX action by entering it into the queue
-ajax.Add = function (action, options) {
- if (action == null) {
- alert("ajax.Start: Argument action must be set.");
- return;
- } // if
-
- // enable the late binding of the methods by using a string that is evaluated.
- if (typeof(action.call) == "string") action.call = eval(action.call);
- if (typeof(action.prepare) == "string") action.prepare = eval(action.prepare);
- if (typeof(action.finish) == "string") action.finish = eval(action.finish);
-
- if ((action.queueClear != null) && (action.queueClear == true)) {
- ajax.queue = new Array();
- ajax.options = new Array();
-
- } else if ((ajax.queue.length > 0) && ((action.queueMultiple == null) || (action.queueMultiple == false))) {
- // remove existing action entries from the queue and clear a running timer
- if ((ajax.timer != null) && (ajax.queue[0] == action)) {
- window.clearTimeout(ajax.timer);
- ajax.timer = null;
- } // if
-
- var n = 0;
- while (n < ajax.queue.length) {
- if (ajax.queue[n] == action) {
- ajax.queue.splice(n, 1);
- ajax.options.splice(n, 1);
- } else {
- n++;
- } // if
- } // while
- } // if
-
- if ((action.queueTop == null) || (action.queueTop == false)) {
- // to the end.
- ajax.queue.push(action);
- ajax.options.push(options);
-
- } else {
- // to the top
- ajax.queue.unshift(action);
- ajax.options.unshift(options);
- } // if
-} // ajax.Add
-
-
-///Check, if the next AJAX action can start.
-///This is an internal method that should not be called from external.
-///for private use only.
-ajax._next = function (forceStart) {
- var ca = null // current action
- var co = null // current opptions
- var data = null;
-
- if (ajax.current != null)
- return; // a call is active: wait more time
-
- if (ajax.timer != null)
- return; // a call is pendig: wait more time
-
- if (ajax.queue.length == 0)
- return; // nothing to do.
-
- ca = ajax.queue[0];
- co = ajax.options[0];
- if ((forceStart == true) || (ca.delay == null) || (ca.delay == 0)) {
- // start top action
- ajax.current = ca;
- ajax.queue.shift();
- ajax.option = co;
- ajax.options.shift();
-
- // get the data
- if (ca.prepare != null)
- try {
- data = ca.prepare(co);
- } catch (ex) { }
-
- if (ca.call != null) {
- ajax.StartProgress();
-
- // start the call
- ca.call.func = ajax.Finish;
- ca.call.onException = ajax.Exception;
- ca.call(data);
- // start timeout timer
- if (ca.timeout != null)
- ajax.timer = window.setTimeout(ajax.Cancel, ca.timeout * 1000);
-
- } else if (ca.postUrl != null) {
- // post raw data to URL
-
- } else {
- // no call
- ajax.Finish(data);
- } // if
-
- } else {
- // start a timer and wait
- ajax.timer = window.setTimeout(ajax.EndWait, ca.delay);
- } // if
-} // ajax._next
-
-
-///The delay time of an action is over.
-ajax.EndWait = function() {
- ajax.timer = null;
- ajax._next(true);
-} // ajax.EndWait
-
-
-///The current action timed out.
-ajax.Cancel = function() {
- proxies.cancel(false); // cancel the current webservice call.
- ajax.timer = null;
- ajax.current = null;
- ajax.option = null;
- ajax.EndProgress();
- window.setTimeout(ajax._next, 200); // give some to time to cancel the http connection.
-} // ajax.Cancel
-
-
-///Finish an AJAX Action the normal way
-ajax.Finish = function (data) {
- // clear timeout timer if set
- if (ajax.timer != null) {
- window.clearTimeout(ajax.timer);
- ajax.timer = null;
- } // if
-
- // use the data
- try {
- if ((ajax.current != null) && (ajax.current.finish != null))
- ajax.current.finish(data, ajax.option);
- } catch (ex) { }
- // reset the running action
- ajax.current = null;
- ajax.option = null;
- ajax.EndProgress();
- ajax._next(false)
-} // ajax.Finish
-
-
-///Finish an AJAX Action with an exception
-ajax.Exception = function (ex) {
- // use the data
- if (ajax.current.onException != null)
- ajax.current.onException(ex, ajax.option);
-
- // reset the running action
- ajax.current = null;
- ajax.option = null;
- ajax.EndProgress();
-} // ajax.Exception
-
-
-///Clear the current and all pending AJAX actions.
-ajax.CancelAll = function () {
- ajax.Cancel();
- // clear all pending AJAX actions in the queue.
- ajax.queue = new Array();
- ajax.options = new Array();
-} // ajax.CancelAll
-
-
-// ----- show or hide a progress indicator -----
-
-// show a progress indicator if it takes longer...
-ajax.StartProgress = function() {
- ajax.progress = true;
- if (ajax.progressTimer != null)
- window.clearTimeout(ajax.progressTimer);
- ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 220);
-} // ajax.StartProgress
-
-
-// hide any progress indicator soon.
-ajax.EndProgress = function () {
- ajax.progress = false;
- if (ajax.progressTimer != null)
- window.clearTimeout(ajax.progressTimer);
- ajax.progressTimer = window.setTimeout(ajax.ShowProgress, 20);
-} // ajax.EndProgress
-
-
-// this function is called by a timer to show or hide a progress indicator
-ajax.ShowProgress = function() {
- ajax.progressTimer = null;
- var a = document.getElementById("AjaxProgressIndicator");
-
- if (ajax.progress && (a != null)) {
- // just display the existing object
- a.style.top = document.documentElement.scrollTop + 2 + "px";
- a.style.display = "";
-
- } else if (ajax.progress) {
-
- // find a relative link to the ajaxcore folder containing ajax.js
- var path = "../ajaxcore/"
- for (var n in document.scripts) {
- s = document.scripts[n].src;
- if ((s != null) && (s.length >= 7) && (s.substr(s.length -7).toLowerCase() == "ajax.js"))
- path = s.substr(0,s.length -7);
- } // for
-
- // create new standard progress object
- a = document.createElement("div");
- a.id = "AjaxProgressIndicator";
- a.style.position = "absolute";
- a.style.right = "2px";
- a.style.top = document.documentElement.scrollTop + 2 + "px";
- a.style.width = "98px";
- a.style.height = "16px"
- a.style.padding = "2px";
- a.style.verticalAlign = "bottom";
- a.style.backgroundColor="#51c77d";
-
- a.innerHTML = " please wait...";
- document.body.appendChild(a);
-
- } else if (a) {
- a.style.display="none";
- } // if
-} // ajax.ShowProgress
-
-
-// ----- simple http-POST server call -----
-
-ajax.postData = function (url, data, func) {
- var x = proxies._getXHR();
-
- // enable cookieless sessions:
- var cs = document.location.href.match(/\/\(.*\)\//);
- if (cs != null) {
- url = url.split('/');
- url[3] += cs[0].substr(0, cs[0].length-1);
- url = url.join('/');
- } // if
-
- x.open("POST", url, (func != null));
-
- if (func != null) {
- // async call with xmlhttp-object as parameter
- x.onreadystatechange = func;
- x.send(data);
-
- } else {
- // sync call
- x.send(soap);
- return(x.responseText);
- } // if
-} // ajax.postData
-
-
-///Execute a soap call.
-///Build the xml for the call of a soap method of a webservice
-///and post it to the server.
-proxies.callSoap = function (args) {
- var p = args.callee;
- var x = null;
-
- // check for existing cache-entry
- if (p._cache != null) {
- if ((p.params.length == 1) && (args.length == 1) && (p._cache[args[0]] != null)) {
- if (p.func != null) {
- p.func(p._cache[args[0]]);
- return(null);
- } else {
- return(p._cache[args[0]]);
- } // if
- } else {
- p._cachekey = args[0];
- }// if
- } // if
-
- proxies.current = p;
- x = proxies._getXHR();
- proxies.xmlhttp = x;
-
- // envelope start
- var soap = ""
- + ""
- + ""
- + "<" + p.fname + " xmlns='" + p.service.ns + "'>";
-
- // parameters
- for (n = 0; (n < p.params.length) && (n < args.length); n++) {
- var val = args[n];
- var typ = p.params[n].split(':');
-
- if ((typ.length == 1) || (typ[1] == "string")) {
- val = String(args[n]).replace(/&/g, "&").replace(//g, ">");
-
- } else if (typ[1] == "int") {
- val = parseInt(args[n]);
- } else if (typ[1] == "float") {
- val = parseFloat(args[n]);
-
- } else if ((typ[1] == "x") && (typeof(args[n]) == "string")) {
- val = args[n];
-
- } else if ((typ[1] == "x") && (typeof(XMLSerializer) != "undefined")) {
- val = (new XMLSerializer()).serializeToString(args[n].firstChild);
-
- } else if (typ[1] == "x") {
- val = args[n].xml;
-
- } else if ((typ[1] == "bool") && (typeof(args[n]) == "string")) {
- val = args[n].toLowerCase();
-
- } else if (typ[1] == "bool") {
- val = String(args[n]).toLowerCase();
-
- } else if (typ[1] == "date") {
- // calculate the xml format for datetime objects from a javascript date object
- var s, ret;
- ret = String(val.getFullYear());
- ret += "-";
- s = String(val.getMonth() + 1);
- ret += (s.length == 1 ? "0" + s : s);
- ret += "-";
- s = String(val.getDate());
- ret += (s.length == 1 ? "0" + s : s);
- ret += "T";
- s = String(val.getHours());
- ret += (s.length == 1 ? "0" + s : s);
- ret += ":";
- s = String(val.getMinutes());
- ret += (s.length == 1 ? "0" + s : s);
- ret += ":";
- s = String(val.getSeconds());
- ret += (s.length == 1 ? "0" + s : s);
- val = ret;
-
- } else if (typ[1] == "s[]") {
- val = "" + args[n].join(" ") + " ";
-
- } else if (typ[1] == "int[]") {
- val = "" + args[n].join(" ") + " ";
-
- } else if (typ[1] == "float[]") {
- val = "" + args[n].join(" ") + " ";
-
- } else if (typ[1] == "bool[]") {
- val = "" + args[n].join(" ") + " ";
-
- } // if
- soap += "<" + typ[0] + ">" + val + "" + typ[0] + ">"
- } // for
-
- // envelope end
- soap += "" + p.fname + ">"
- + " "
- + " ";
-
- // enable cookieless sessions:
- var u = p.service.url;
- var cs = document.location.href.match(/\/\(.*\)\//);
- if (cs != null) {
- u = p.service.url.split('/');
- u[3] += cs[0].substr(0, cs[0].length-1);
- u = u.join('/');
- } // if
-
- x.open("POST", u, (p.func != null));
- x.setRequestHeader("SOAPAction", p.action);
- x.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
-
- if (p.corefunc != null) {
- // async call with xmlhttp-object as parameter
- x.onreadystatechange = p.corefunc;
- x.send(soap);
-
- } else if (p.func != null) {
- // async call
- x.onreadystatechange = proxies._response;
- x.send(soap);
-
- } else {
- // sync call
- x.send(soap);
- return(proxies._response());
- } // if
-} // proxies.callSoap
-
-
-// cancel the running webservice call.
-// raise: set raise to false to prevent raising an exception
-proxies.cancel = function(raise) {
- var cc = proxies.current;
- var cx = proxies.xmlhttp;
-
- if (raise == null) raise == true;
-
- if (proxies.xmlhttp != null) {
- proxies.xmlhttp.onreadystatechange = function() { };
- proxies.xmlhttp.abort();
- if (raise && (proxies.current.onException != null))
- proxies.current.onException("WebService call was canceled.")
- proxies.current = null;
- proxies.xmlhttp = null;
- } // if
-} // proxies.cancel
-
-
-// px is a proxies.service.func object !
-proxies.EnableCache = function (px) {
- // attach an empty _cache object.
- px._cache = new Object();
-} // proxies.EnableCache
-
-
-// check, if a call is currently waiting for a result
-proxies.IsActive = function () {
- return(proxies.xmlhttp != null);
-} // proxies.IsActive
-
-
-///Callback method for a webservice call that dispatches the response to servive.func or service.onException.
-///for private use only.
-proxies._response = function () {
- var ret = null;
- var x = proxies.xmlhttp;
- var cc = proxies.current;
- var rtype = null;
-
- if ((cc.rtype.length > 0) && (cc.rtype[0] != null))
- rtype = cc.rtype[0].split(':');
-
- if ((x != null) && (x.readyState == 4)) {
- if (x.status == 200) {
- var xNode = null;
-
- if (rtype != null)
- xNode = x.responseXML.getElementsByTagName(rtype[0])[0];
-
- if (xNode == null) {
- ret = null;
-
- } else if (xNode.firstChild == null) { // 27.12.2005: empty string return values
- ret = ((rtype.length == 1) || (rtype[1] == "string") ? "" : null);
-
- } else if ((rtype.length == 1) || (rtype[1] == "string")) {
- ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
-
- } else if (rtype[1] == "bool") {
- ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
- ret = (ret == "true");
-
- } else if (rtype[1] == "int") {
- ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
- ret = parseInt(ret);
-
- } else if (rtype[1] == "float") {
- ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
- ret = parseFloat(ret);
-
- } else if ((rtype[1] == "x") && (typeof(XMLSerializer) != "undefined")) {
- ret = (new XMLSerializer()).serializeToString(xNode.firstChild);
- ret = ajax._getXMLDOM(ret);
-
- } else if ((rtype[1] == "ds") && (typeof(XMLSerializer) != "undefined")) {
- // ret = (new XMLSerializer()).serializeToString(xNode.firstChild.nextSibling.firstChild);
- ret = (new XMLSerializer()).serializeToString(xNode);
- ret = ajax._getXMLDOM(ret);
-
- } else if (rtype[1] == "x") {
- ret = xNode.firstChild.xml;
- ret = ajax._getXMLDOM(ret);
-
- } else if (rtype[1] == "ds") {
-// ret = xNode.firstChild.nextSibling.firstChild.xml;
- ret = xNode.xml;
- ret = ajax._getXMLDOM(ret);
-
- } else if (rtype[1] == "s[]") {
- // Array of strings
- ret = new Array();
- xNode = xNode.firstChild;
- while (xNode != null) {
- ret.push(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue);
- xNode = xNode.nextSibling;
- } // while
-
- } else if (rtype[1] == "int[]") {
- // Array of int
- ret = new Array();
- xNode = xNode.firstChild;
- while (xNode != null) {
- ret.push(parseInt(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue));
- xNode = xNode.nextSibling;
- } // while
-
- } else if (rtype[1] == "float[]") {
- // Array of float
- ret = new Array();
- xNode = xNode.firstChild;
- while (xNode != null) {
- ret.push(parseFloat(xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue));
- xNode = xNode.nextSibling;
- } // while
-
- } else if (rtype[1] == "bool[]") {
- // Array of bool
- ret = new Array();
- xNode = xNode.firstChild;
- while (xNode != null) {
- ret.push((xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue).toLowerCase() == "true");
- xNode = xNode.nextSibling;
- } // while
-
- } else {
- ret = xNode.textContent || xNode.innerText || xNode.text || xNode.childNodes[0].nodeValue;
- } // if
-
- // store to _cache
- if ((cc._cache != null) && (cc._cachekey != null)) {
- cc._cache[cc._cachekey] = ret;
- cc._cachekey = null;
- } // if
-
- proxies.xmlhttp = null;
- proxies.current = null;
-
- if (cc.func == null) {
- return(ret); // sync
- } else {
- cc.func(ret); // async
- return(null);
- } // if
-
- } else if (proxies.current.onException == null) {
- // no exception
-
- } else {
- // raise an exception
- ret = new Error();
-
- if (x.status == 404) {
- ret.message = "The webservice could not be found.";
-
- } else if (x.status == 500) {
- ret.name = "SoapException";
- var n = x.responseXML.documentElement.firstChild.firstChild.firstChild;
- while (n != null) {
- if (n.nodeName == "faultcode") ret.message = n.firstChild.nodeValue;
- if (n.nodeName == "faultstring") ret.description = n.firstChild.nodeValue;
- n = n.nextSibling;
- } // while
-
- } else if ((x.status == 502) || (x.status == 12031)) {
- ret.message = "The server could not be found.";
-
- } else {
- // no classified response.
- ret.message = "Result-Status:" + x.status + "\n" + x.responseText;
- } // if
- proxies.current.onException(ret);
- } // if
-
- proxies.xmlhttp = null;
- proxies.current = null;
- } // if
-} // proxies._response
-
-
-///Callback method to show the result of a soap call in an alert box.
-///To set up a debug output in an alert box use:
-///proxies.service.method.corefunc = proxies.alertResult;
-proxies.alertResult = function () {
- var x = proxies.xmlhttp;
-
- if (x.readyState == 4) {
- if (x.status == 200) {
- if (x.responseXML.documentElement.firstChild.firstChild.firstChild == null)
- alert("(no result)");
- else
- alert(x.responseXML.documentElement.firstChild.firstChild.firstChild.firstChild.nodeValue);
-
- } else if (x.status == 404) { alert("Error!\n\nThe webservice could not be found.");
-
- } else if (x.status == 500) {
- // a SoapException
- var ex = new Error();
- ex.name = "SoapException";
- var n = x.responseXML.documentElement.firstChild.firstChild.firstChild;
- while (n != null) {
- if (n.nodeName == "faultcode") ex.message = n.firstChild.nodeValue;
- if (n.nodeName == "faultstring") ex.description = n.firstChild.nodeValue;
- n = n.nextSibling;
- } // while
- alert("The server threw an exception.\n\n" + ex.message + "\n\n" + ex.description);
-
- } else if (x.status == 502) { alert("Error!\n\nThe server could not be found.");
-
- } else {
- // no classified response.
- alert("Result-Status:" + x.status + "\n" + x.responseText);
- } // if
-
- proxies.xmlhttp = null;
- proxies.current = null;
- } // if
-} // proxies.alertResult
-
-
-///Show all the details of the returned data of a webservice call.
-///Use this method for debugging transmission problems.
-///To set up a debug output in an alert box use:
-///proxies.service.method.corefunc = proxies.alertResponseText;
-proxies.alertResponseText = function () {
- if (proxies.xmlhttp.readyState == 4)
- alert("Status:" + proxies.xmlhttp.status + "\nRESULT:" + proxies.xmlhttp.responseText);
-} // proxies.alertResponseText
-
-
-///show the details about an exception.
-proxies.alertException = function(ex) {
- var s = "Exception:\n\n";
-
- if (ex.constructor == String) {
- s = ex;
- } else {
- if ((ex.name != null) && (ex.name != ""))
- s += "Type: " + ex.name + "\n\n";
-
- if ((ex.message != null) && (ex.message != ""))
- s += "Message:\n" + ex.message + "\n\n";
-
- if ((ex.description != null) && (ex.description != "") && (ex.message != ex.description))
- s += "Description:\n" + ex.description + "\n\n";
- } // if
- alert(s);
-} // proxies.alertException
-
-
-///Get a browser specific implementation of the XMLHttpRequest object.
-// from http://blogs.msdn.com/ie/archive/2006/01/23/516393.aspx
-proxies._getXHR = function () {
- var x = null;
- if (window.XMLHttpRequest) {
- // if IE7, Mozilla, Safari, etc: Use native object
- x = new XMLHttpRequest()
-
- } else if (window.ActiveXObject) {
- // ...otherwise, use the ActiveX control for IE5.x and IE6
- try { x = new ActiveXObject("Msxml2.XMLHTTP"); } catch (e) { }
- if (x == null)
- try { x = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) { }
- } // if
- return(x);
-} // proxies._getXHR
-
-
-///Get a browser specific implementation of the XMLDOM object, containing a XML document.
-/// the xml document as string.
-ajax._getXMLDOM = function (xmlText) {
- var obj = null;
-
- if ((document.implementation != null) && (typeof document.implementation.createDocument == "function")) {
- // Gecko / Mozilla / Firefox
- var parser = new DOMParser();
- obj = parser.parseFromString(xmlText, "text/xml");
-
- } else {
- // IE
- try {
- obj = new ActiveXObject("MSXML2.DOMDocument");
- } catch (e) { }
-
- if (obj == null) {
- try {
- obj = new ActiveXObject("Microsoft.XMLDOM");
- } catch (e) { }
- } // if
-
- if (obj != null) {
- obj.async = false;
- obj.validateOnParse = false;
- } // if
- obj.loadXML(xmlText);
- } // if
- return(obj);
-} // _getXMLDOM
-
-
-///show the details of a javascript object.
-///This helps a lot while developing and debugging.
-function inspectObj(obj) {
- var s = "InspectObj:";
-
- if (obj == null) {
- s = "(null)"; alert(s); return;
- } else if (obj.constructor == String) {
- s = "\"" + obj + "\"";
- } else if (obj.constructor == Array) {
- s += " _ARRAY";
- } else if (typeof(obj) == "function") {
- s += " [function]" + obj;
-
- } else if ((typeof(XMLSerializer) != "undefined") && (obj.constructor == XMLDocument)) {
- s = "[XMLDocument]:\n" + (new XMLSerializer()).serializeToString(obj.firstChild);
- alert(s); return;
-
- } else if ((obj.constructor == null) && (typeof(obj) == "object") && (obj.xml != null)) {
- s = "[XML]:\n" + obj.xml;
- alert(s); return;
- }
-
- for (p in obj) {
- try {
- if (obj[p] == null) {
- s += "\n" + String(p) + " (...)";
-
- } else if (typeof(obj[p]) == "function") {
- s += "\n" + String(p) + " [function]";
-
- } else if (obj[p].constructor == Array) {
- s += "\n" + String(p) + " [ARRAY]: " + obj[p];
- for (n = 0; n < obj[p].length; n++)
- s += "\n " + n + ": " + obj[p][n];
-
- } else {
- s += "\n" + String(p) + " [" + typeof(obj[p]) + "]: " + obj[p];
- } // if
- } catch (e) { s+= e;}
- } // for
- alert(s);
-} // inspectObj
-
-// ----- End -----
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/codeEditorSave.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/codeEditorSave.asmx
deleted file mode 100644
index 947a6fb889..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/codeEditorSave.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="codeEditorSave.asmx.cs" Class="umbraco.presentation.webservices.codeEditorSave" %>
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/legacyAjaxCalls.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/legacyAjaxCalls.asmx
deleted file mode 100644
index 5acc6e6022..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/legacyAjaxCalls.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="legacyAjaxCalls.asmx.cs" Class="umbraco.presentation.webservices.legacyAjaxCalls" %>
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/nodeSorter.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/nodeSorter.asmx
deleted file mode 100644
index ae09ffa463..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/nodeSorter.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="nodeSorter.asmx.cs" Class="umbraco.presentation.webservices.nodeSorter" %>
diff --git a/src/Umbraco.Web.UI/Umbraco/webservices/templates.asmx b/src/Umbraco.Web.UI/Umbraco/webservices/templates.asmx
deleted file mode 100644
index 4a6e4ee189..0000000000
--- a/src/Umbraco.Web.UI/Umbraco/webservices/templates.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="c#" Codebehind="templates.asmx.cs" Class="umbraco.webservices.templates" %>
diff --git a/src/Umbraco.Web/Components/NotificationsComponent.cs b/src/Umbraco.Web/Components/NotificationsComponent.cs
index 79f5ee9f6d..38ec283cfe 100644
--- a/src/Umbraco.Web/Components/NotificationsComponent.cs
+++ b/src/Umbraco.Web/Components/NotificationsComponent.cs
@@ -1,68 +1,195 @@
using System.Collections.Generic;
using Umbraco.Core;
+using Umbraco.Core.Logging;
using Umbraco.Core.Components;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
using Umbraco.Web.Actions;
-
+using Umbraco.Core.Composing;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Configuration.UmbracoSettings;
+using System.Linq;
+using Umbraco.Core.Models.Membership;
+using System;
+using System.Globalization;
namespace Umbraco.Web.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public sealed class NotificationsComponent : UmbracoComponentBase, IUmbracoCoreComponent
{
- public void Initialize(INotificationService notificationService, ActionCollection actions)
+ public override void Compose(Composition composition)
{
- ContentService.SentToPublish += (sender, args) =>
- notificationService.SendNotification(args.Entity, actions.GetAction());
+ base.Compose(composition);
+ composition.Container.RegisterSingleton();
+ }
+
+ public void Initialize(INotificationService notificationService, Notifier notifier, ActionCollection actions)
+ {
+ //Send notifications for the send to publish action
+ ContentService.SentToPublish += (sender, args) => notifier.Notify(actions.GetAction(), args.Entity);
//Send notifications for the published action
- ContentService.Published += (sender, args) =>
- {
- foreach (var content in args.PublishedEntities)
- notificationService.SendNotification(content, actions.GetAction());
- };
+ ContentService.Published += (sender, args) => notifier.Notify(actions.GetAction(), args.PublishedEntities.ToArray());
+
+ //Send notifications for the saved action
+ ContentService.Sorted += (sender, args) => ContentServiceSorted(notifier, sender, args, actions);
//Send notifications for the update and created actions
- ContentService.Saved += (sender, args) =>
- {
- var newEntities = new List();
- var updatedEntities = new List();
-
- //need to determine if this is updating or if it is new
- foreach (var entity in args.SavedEntities)
- {
- var dirty = (IRememberBeingDirty) entity;
- if (dirty.WasPropertyDirty("Id"))
- {
- //it's new
- newEntities.Add(entity);
- }
- else
- {
- //it's updating
- updatedEntities.Add(entity);
- }
- }
- notificationService.SendNotification(newEntities, actions.GetAction());
- notificationService.SendNotification(updatedEntities, actions.GetAction());
- };
+ ContentService.Saved += (sender, args) => ContentServiceSaved(notifier, sender, args, actions);
//Send notifications for the delete action
- ContentService.Deleted += (sender, args) =>
- {
- foreach (var content in args.DeletedEntities)
- notificationService.SendNotification(content, actions.GetAction());
- };
-
+ ContentService.Deleted += (sender, args) => notifier.Notify(actions.GetAction(), args.DeletedEntities.ToArray());
+
//Send notifications for the unpublish action
- ContentService.Unpublished += (sender, args) =>
+ ContentService.Unpublished += (sender, args) => notifier.Notify(actions.GetAction(), args.PublishedEntities.ToArray());
+ }
+
+ private void ContentServiceSorted(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs args, ActionCollection actions)
+ {
+ var parentId = args.SavedEntities.Select(x => x.ParentId).Distinct().ToList();
+ if (parentId.Count != 1) return; // this shouldn't happen, for sorting all entities will have the same parent id
+
+ // in this case there's nothing to report since if the root is sorted we can't report on a fake entity.
+ // this is how it was in v7, we can't report on root changes because you can't subscribe to root changes.
+ if (parentId[0] <= 0) return;
+
+ var parent = sender.GetById(parentId[0]);
+ if (parent == null) return; // this shouldn't happen
+
+ notifier.Notify(actions.GetAction(), new[] { parent });
+ }
+
+ private void ContentServiceSaved(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs args, ActionCollection actions)
+ {
+ var newEntities = new List();
+ var updatedEntities = new List();
+
+ //need to determine if this is updating or if it is new
+ foreach (var entity in args.SavedEntities)
{
- foreach (var content in args.PublishedEntities)
- notificationService.SendNotification(content, actions.GetAction());
- };
+ var dirty = (IRememberBeingDirty)entity;
+ if (dirty.WasPropertyDirty("Id"))
+ {
+ //it's new
+ newEntities.Add(entity);
+ }
+ else
+ {
+ //it's updating
+ updatedEntities.Add(entity);
+ }
+ }
+ notifier.Notify(actions.GetAction(), newEntities.ToArray());
+ notifier.Notify(actions.GetAction(), updatedEntities.ToArray());
+ }
+
+ ///
+ /// This class is used to send the notifications
+ ///
+ public sealed class Notifier
+ {
+ private readonly IUmbracoContextAccessor _umbracoContextAccessor;
+ private readonly IRuntimeState _runtimeState;
+ private readonly INotificationService _notificationService;
+ private readonly IUserService _userService;
+ private readonly ILocalizedTextService _textService;
+ private readonly IGlobalSettings _globalSettings;
+ private readonly IContentSection _contentConfig;
+ private readonly ILogger _logger;
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Notifier(IUmbracoContextAccessor umbracoContextAccessor, IRuntimeState runtimeState, INotificationService notificationService, IUserService userService, ILocalizedTextService textService, IGlobalSettings globalSettings, IContentSection contentConfig, ILogger logger)
+ {
+ _umbracoContextAccessor = umbracoContextAccessor;
+ _runtimeState = runtimeState;
+ _notificationService = notificationService;
+ _userService = userService;
+ _textService = textService;
+ _globalSettings = globalSettings;
+ _contentConfig = contentConfig;
+ _logger = logger;
+ }
+
+ public void Notify(IAction action, params IContent[] entities)
+ {
+ IUser user = null;
+ if (_umbracoContextAccessor.UmbracoContext != null)
+ {
+ user = _umbracoContextAccessor.UmbracoContext.Security.CurrentUser;
+ }
+
+ //if there is no current user, then use the admin
+ if (user == null)
+ {
+ _logger.Debug(typeof(Notifier), "There is no current Umbraco user logged in, the notifications will be sent from the administrator");
+ user = _userService.GetUserById(Constants.Security.SuperUserId);
+ if (user == null)
+ {
+ _logger.Warn(typeof(Notifier), "Notifications can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId);
+ return;
+ }
+ }
+
+ SendNotification(user, entities, action, _runtimeState.ApplicationUrl);
+ }
+
+ private void SendNotification(IUser sender, IEnumerable entities, IAction action, Uri siteUri)
+ {
+ if (sender == null) throw new ArgumentNullException(nameof(sender));
+ if (siteUri == null) throw new ArgumentNullException(nameof(siteUri));
+
+ //group by the content type variation since the emails will be different
+ foreach(var contentVariantGroup in entities.GroupBy(x => x.ContentType.Variations))
+ {
+ if (contentVariantGroup.Key == ContentVariation.CultureAndSegment || contentVariantGroup.Key == ContentVariation.Segment)
+ throw new NotSupportedException("Segments are not yet supported in Umbraco");
+
+ _notificationService.SendNotifications(
+ sender,
+ contentVariantGroup,
+ action.Letter.ToString(CultureInfo.InvariantCulture),
+ _textService.Localize("actions", action.Alias),
+ siteUri,
+ ((IUser user, NotificationEmailSubjectParams subject) x)
+ => _textService.Localize(
+ "notifications/mailSubject",
+ x.user.GetUserCulture(_textService, _globalSettings),
+ new[] { x.subject.SiteUrl, x.subject.Action, x.subject.ItemName }),
+ ((IUser user, NotificationEmailBodyParams body, bool isHtml) x)
+ => _textService.Localize(
+ x.isHtml ? "notifications/mailBodyHtml" : "notifications/mailBody",
+ x.user.GetUserCulture(_textService, _globalSettings),
+ new[]
+ {
+ x.body.RecipientName,
+ x.body.Action,
+ x.body.ItemName,
+ x.body.EditedUser,
+ x.body.SiteUrl,
+ x.body.ItemId,
+ //format the summary depending on if it's variant or not
+ contentVariantGroup.Key == ContentVariation.Culture
+ ? (x.isHtml ? _textService.Localize("notifications/mailBodyVariantHtmlSummary", new[]{ x.body.Summary }) : _textService.Localize("notifications/mailBodyVariantSummary", new []{ x.body.Summary }))
+ : x.body.Summary,
+ x.body.ItemUrl
+ }));
+ }
+ }
+
}
}
+
+
}
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index 308cab644c..4ed9251929 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -41,6 +41,7 @@ namespace Umbraco.Web.Editors
{
private readonly ManifestParser _manifestParser;
private readonly UmbracoFeatures _features;
+ private readonly IRuntimeState _runtimeState;
private BackOfficeUserManager _userManager;
private BackOfficeSignInManager _signInManager;
@@ -48,10 +49,11 @@ namespace Umbraco.Web.Editors
private const string TokenPasswordResetCode = "PasswordResetCode";
private static readonly string[] TempDataTokenNames = { TokenExternalSignInError, TokenPasswordResetCode };
- public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features)
+ public BackOfficeController(ManifestParser manifestParser, UmbracoFeatures features, IRuntimeState runtimeState)
{
_manifestParser = manifestParser;
_features = features;
+ _runtimeState = runtimeState;
}
protected BackOfficeSignInManager SignInManager => _signInManager ?? (_signInManager = OwinContext.GetBackOfficeSignInManager());
@@ -250,7 +252,7 @@ namespace Umbraco.Web.Editors
[MinifyJavaScriptResult(Order = 1)]
public JavaScriptResult ServerVariables()
{
- var serverVars = new BackOfficeServerVariables(Url, Current.RuntimeState, _features, GlobalSettings);
+ var serverVars = new BackOfficeServerVariables(Url, _runtimeState, _features, GlobalSettings);
//cache the result if debugging is disabled
var result = HttpContext.IsDebuggingEnabled
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs
index fe5675b2aa..5fddac0c14 100644
--- a/src/Umbraco.Web/Editors/ContentController.cs
+++ b/src/Umbraco.Web/Editors/ContentController.cs
@@ -1070,17 +1070,14 @@ namespace Umbraco.Web.Editors
var contentService = Services.ContentService;
// Save content with new sort order and update content xml in db accordingly
- if (contentService.Sort(sorted.IdSortOrder, Security.CurrentUser.Id) == false)
+ var sortResult = contentService.Sort(sorted.IdSortOrder, Security.CurrentUser.Id);
+ if (!sortResult.Success)
{
Logger.Warn("Content sorting failed, this was probably caused by an event being cancelled");
+ //TODO: Now you can cancel sorting, does the event messages bubble up automatically?
return Request.CreateValidationErrorResponse("Content sorting failed, this was probably caused by an event being cancelled");
}
- if (sorted.ParentId > 0)
- {
- Services.NotificationService.SendNotification(contentService.GetById(sorted.ParentId), Current.Actions.GetAction(), UmbracoContext, Services.TextService, GlobalSettings);
- }
-
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (Exception ex)
diff --git a/src/Umbraco.Web/NotificationServiceExtensions.cs b/src/Umbraco.Web/NotificationServiceExtensions.cs
deleted file mode 100644
index 4be7b4f091..0000000000
--- a/src/Umbraco.Web/NotificationServiceExtensions.cs
+++ /dev/null
@@ -1,146 +0,0 @@
-using System;
-using System.Globalization;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Core.Services;
-using Umbraco.Core.Models;
-
-using System.Collections.Generic;
-using Umbraco.Core.Models.Entities;
-using Umbraco.Web.Actions;
-using Umbraco.Web.Composing;
-
-namespace Umbraco.Web
-{
- // TODO: all of these require an UmbracoContext because currently to send the notifications we need an HttpContext, this is based on legacy code
- // for which probably requires updating so that these can be sent outside of the http context.
-
- internal static class NotificationServiceExtensions
- {
- internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action)
- {
- if (Current.UmbracoContext == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- return;
- }
- service.SendNotification(entity, action, Current.UmbracoContext, Current.Services.TextService, UmbracoConfig.For.GlobalSettings());
- }
-
- internal static void SendNotification(this INotificationService service, IEnumerable entities, IAction action)
- {
- if (Current.UmbracoContext == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- return;
- }
- service.SendNotification(entities, action, Current.UmbracoContext, Current.Services.TextService, UmbracoConfig.For.GlobalSettings());
- }
-
- //internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext)
- //{
- // if (umbracoContext == null)
- // {
- // Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- // return;
- // }
- // service.SendNotification(entity, action, umbracoContext);
- //}
-
- //internal static void SendNotification(this INotificationService service, IEnumerable entities, IAction action, UmbracoContext umbracoContext)
- //{
- // if (umbracoContext == null)
- // {
- // Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- // return;
- // }
- // service.SendNotification(entities, action, umbracoContext);
- //}
-
- internal static void SendNotification(this INotificationService service, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ILocalizedTextService textService, IGlobalSettings globalSettings)
- {
- if (umbracoContext == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- return;
- }
-
- var user = umbracoContext.Security.CurrentUser;
- var userService = Current.Services.UserService; // fixme inject
-
- //if there is no current user, then use the admin
- if (user == null)
- {
- Current.Logger.Debug(typeof(NotificationServiceExtensions), "There is no current Umbraco user logged in, the notifications will be sent from the administrator");
- user = userService.GetUserById(Constants.Security.SuperUserId);
- if (user == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Noticiations can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId);
- return;
- }
- }
- service.SendNotification(user, entity, action, umbracoContext, textService, globalSettings);
- }
-
- internal static void SendNotification(this INotificationService service, IEnumerable entities, IAction action, UmbracoContext umbracoContext, ILocalizedTextService textService, IGlobalSettings globalSettings)
- {
- if (umbracoContext == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Cannot send notifications, there is no current UmbracoContext");
- return;
- }
-
- var user = umbracoContext.Security.CurrentUser;
- var userService = Current.Services.UserService; // fixme inject
-
- //if there is no current user, then use the admin
- if (user == null)
- {
- Current.Logger.Debug(typeof(NotificationServiceExtensions), "There is no current Umbraco user logged in, the notifications will be sent from the administrator");
- user = userService.GetUserById(Constants.Security.SuperUserId);
- if (user == null)
- {
- Current.Logger.Warn(typeof(NotificationServiceExtensions), "Noticiations can not be sent, no admin user with id {SuperUserId} could be resolved", Constants.Security.SuperUserId);
- return;
- }
- }
- service.SendNotification(user, entities, action, umbracoContext, textService, globalSettings);
- }
-
- internal static void SendNotification(this INotificationService service, IUser sender, IUmbracoEntity entity, IAction action, UmbracoContext umbracoContext, ILocalizedTextService textService, IGlobalSettings globalSettings)
- {
- if (sender == null) throw new ArgumentNullException(nameof(sender));
- if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
-
- service.SendNotifications(
- sender,
- entity,
- action.Letter.ToString(CultureInfo.InvariantCulture),
- textService.Localize("actions", action.Alias),
- umbracoContext.HttpContext,
- (mailingUser, strings) => textService.Localize("notifications/mailSubject", mailingUser.GetUserCulture(textService, globalSettings), strings),
- (mailingUser, strings) => UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail
- ? textService.Localize("notifications/mailBody", mailingUser.GetUserCulture(textService, globalSettings), strings)
- : textService.Localize("notifications/mailBodyHtml", mailingUser.GetUserCulture(textService, globalSettings), strings));
- }
-
- internal static void SendNotification(this INotificationService service, IUser sender, IEnumerable entities, IAction action, UmbracoContext umbracoContext, ILocalizedTextService textService, IGlobalSettings globalSettings)
- {
- if (sender == null) throw new ArgumentNullException(nameof(sender));
- if (umbracoContext == null) throw new ArgumentNullException(nameof(umbracoContext));
-
- service.SendNotifications(
- sender,
- entities,
- action.Letter.ToString(CultureInfo.InvariantCulture),
- textService.Localize("actions", action.Alias),
- umbracoContext.HttpContext,
- (mailingUser, strings) => textService.Localize("notifications/mailSubject", mailingUser.GetUserCulture(textService, globalSettings), strings),
- (mailingUser, strings) => UmbracoConfig.For.UmbracoSettings().Content.DisableHtmlEmail
- ? textService.Localize("notifications/mailBody", mailingUser.GetUserCulture(textService, globalSettings), strings)
- : textService.Localize("notifications/mailBodyHtml", mailingUser.GetUserCulture(textService, globalSettings), strings));
- }
- }
-}
diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
index d0bfa360fa..85cebdf3f5 100644
--- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
+++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
@@ -48,6 +48,7 @@ namespace Umbraco.Web.Trees
internal static Attempt GetUrlAndTitleFromLegacyAction(IAction action, string nodeId, string nodeType, string nodeName, string currentSection)
{
switch (action)
+
{
case ActionNew actionNew:
return Attempt.Succeed(
diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj
index f17f6daf72..78215e0d1d 100644
--- a/src/Umbraco.Web/Umbraco.Web.csproj
+++ b/src/Umbraco.Web/Umbraco.Web.csproj
@@ -762,7 +762,6 @@
-
@@ -1268,13 +1267,6 @@
republish.aspx
-
- SendPublish.aspx
- ASPXCodeBehind
-
-
- SendPublish.aspx
-
editPackage.aspx
ASPXCodeBehind
@@ -1287,19 +1279,6 @@
-
-
- CheckForUpgrade.asmx
- Component
-
-
- legacyAjaxCalls.asmx
- Component
-
-
- nodeSorter.asmx
- Component
-
@@ -1311,9 +1290,6 @@
-
- Component
-
Component
@@ -1340,10 +1316,7 @@
-
-
-
ASPXCodeBehind
@@ -1361,7 +1334,6 @@
ASPXCodeBehind
-
ASPXCodeBehind
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx
deleted file mode 100644
index 8b190d575c..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx
+++ /dev/null
@@ -1,13 +0,0 @@
-<%@ Page language="c#" Codebehind="SendPublish.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.SendPublish" %>
-
-
-
- umbraco - <%=Services.TextService.Localize("editContentSendToPublish")%>
-
-
-
- <%=Services.TextService.Localize("editContentSendToPublishText")%>
-
- <%=Services.TextService.Localize("closewindow")%>
-
-
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs
deleted file mode 100644
index 1f4ba277b5..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using System;
-using Umbraco.Web;
-using Umbraco.Core;
-using Umbraco.Web.Actions;
-using Umbraco.Web.Composing;
-
-
-namespace umbraco.dialogs
-{
- ///
- /// Runs all action handlers for the ActionToPublish action for the document with
- /// the corresponding document id passed in by query string
- ///
- public partial class SendPublish : Umbraco.Web.UI.Pages.UmbracoEnsuredPage
- {
- public SendPublish()
- {
- CurrentApp = Constants.Applications.Content.ToString();
- }
-
- protected void Page_Load(object sender, EventArgs e)
- {
- if (!string.IsNullOrEmpty(Request.QueryString["id"]))
- {
- int docId;
- if (int.TryParse(Request.QueryString["id"], out docId))
- {
- //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here
- Services.NotificationService.SendNotification(
- Services.ContentService.GetById(docId), Current.Actions.GetAction());
- }
-
- }
-
- }
-
-
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.designer.cs
deleted file mode 100644
index 36e42291f7..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/SendPublish.aspx.designer.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:2.0.50727.4200
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace umbraco.dialogs {
-
-
- public partial class SendPublish {
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx
deleted file mode 100644
index 226022e0fd..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="CheckForUpgrade.asmx.cs" Class="umbraco.presentation.webservices.CheckForUpgrade" %>
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs
deleted file mode 100644
index 9007f9c41e..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/CheckForUpgrade.asmx.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Web;
-using System.Web.Services;
-using System.Web.Script.Services;
-using Umbraco.Core;
-using Umbraco.Web.WebServices;
-using Umbraco.Core.Configuration;
-using Umbraco.Web;
-using Umbraco.Web.Composing;
-using Umbraco.Web.Install;
-
-
-namespace umbraco.presentation.webservices
-{
- ///
- /// Summary description for CheckForUpgrade
- ///
- [WebService(Namespace = "http://umbraco.org/")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [System.ComponentModel.ToolboxItem(false)]
- [ScriptService]
- public class CheckForUpgrade : UmbracoAuthorizedWebService
- {
-
- [WebMethod]
- [ScriptMethod]
- public UpgradeResult CallUpgradeService()
- {
- if (!AuthorizeRequest()) return null;
-
- var check = new global::Umbraco.Web.org.umbraco.update.CheckForUpgrade();
- var result = check.CheckUpgrade(UmbracoVersion.Current.Major,
- UmbracoVersion.Current.Minor,
- UmbracoVersion.Current.Build,
- UmbracoVersion.CurrentComment);
- return new UpgradeResult(result.UpgradeType.ToString(), result.Comment, result.UpgradeUrl);
- }
-
- [WebMethod]
- [ScriptMethod]
- public void InstallStatus(bool isCompleted, string userAgent, string errorMsg)
- {
- bool isUpgrade = false;
- // if it's an upgrade, you'll need to be logged in before we allow this call
- if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) == false)
- {
- isUpgrade = true;
- try
- {
- AuthorizeRequest(true);
- }
- catch (Exception)
- {
- //we don't want to throw the exception back to JS
- return;
- }
- }
-
- // Check for current install Id
- Guid installId = Guid.NewGuid();
- var installCookie = Context.Request.GetCookieValue(Constants.Web.InstallerCookieName);
- if (string.IsNullOrEmpty(installCookie) == false)
- {
- if (Guid.TryParse(installCookie, out installId))
- {
- // check that it's a valid Guid
- if (installId == Guid.Empty)
- installId = Guid.NewGuid();
-
- }
- }
- Context.Response.Cookies.Set(new HttpCookie(Constants.Web.InstallerCookieName, installId.ToString()));
-
- string dbProvider = string.Empty;
- if (string.IsNullOrEmpty(GlobalSettings.ConfigurationStatus) == false)
- dbProvider = InstallHelper.GetDbProviderString(Current.SqlContext);
-
- var check = new global::Umbraco.Web.org.umbraco.update.CheckForUpgrade();
- check.Install(installId,
- isUpgrade,
- isCompleted,
- DateTime.Now,
- UmbracoVersion.Current.Major,
- UmbracoVersion.Current.Minor,
- UmbracoVersion.Current.Build,
- UmbracoVersion.CurrentComment,
- errorMsg,
- userAgent,
- dbProvider);
- }
- }
-
-
- public class UpgradeResult
- {
- public string UpgradeType { get; set; }
- public string UpgradeComment { get; set; }
- public string UpgradeUrl { get; set; }
-
- public UpgradeResult() { }
- public UpgradeResult(string upgradeType, string upgradeComment, string upgradeUrl)
- {
- UpgradeType = upgradeType;
- UpgradeComment = upgradeComment;
- UpgradeUrl = upgradeUrl + "?version=" + HttpContext.Current.Server.UrlEncode(UmbracoVersion.Current.ToString(3));
- }
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UmbracoAuthorizedWebService.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UmbracoAuthorizedWebService.cs
deleted file mode 100644
index 33c4831c40..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/UmbracoAuthorizedWebService.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Security;
-using Umbraco.Web.Security;
-using Umbraco.Core;
-using Umbraco.Core.Cache;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Core.Services;
-using Umbraco.Web;
-using Umbraco.Web.WebServices;
-
-namespace umbraco.presentation.webservices
-{
- ///
- /// An abstract web service class that has the methods and properties to correct validate an Umbraco user
- ///
- public abstract class UmbracoAuthorizedWebService : UmbracoWebService
- {
- private bool _hasValidated = false;
-
- ///
- /// Checks if the umbraco context id is valid
- ///
- ///
- ///
- protected bool ValidateUserContextId(string currentUmbracoUserContextId)
- {
- return UmbracoContext.Security.ValidateCurrentUser();
- }
-
- ///
- /// Checks if the username/password credentials are valid
- ///
- ///
- ///
- ///
- protected bool ValidateCredentials(string username, string password)
- {
- return UmbracoContext.Security.ValidateBackOfficeCredentials(username, password);
- }
-
- ///
- /// Validates the user for access to a certain application
- ///
- /// The application alias.
- /// true if an exception should be thrown if authorization fails
- ///
- protected bool AuthorizeRequest(string app, bool throwExceptions = false)
- {
- //ensure we have a valid user first!
- if (!AuthorizeRequest(throwExceptions)) return false;
-
- //if it is empty, don't validate
- if (app.IsNullOrWhiteSpace())
- {
- return true;
- }
- var hasAccess = UserHasAppAccess(app, Security.CurrentUser);
- if (!hasAccess && throwExceptions)
- throw new SecurityException("The user does not have access to the required application");
- return hasAccess;
- }
-
- ///
- /// Checks if the specified user as access to the app
- ///
- ///
- ///
- ///
- protected bool UserHasAppAccess(string app, IUser user)
- {
- return Security.UserHasSectionAccess(app, user);
- }
-
- ///
- /// Checks if the specified user by username as access to the app
- ///
- ///
- ///
- ///
- protected bool UserHasAppAccess(string app, string username)
- {
- return Security.UserHasSectionAccess(app, username);
- }
-
- ///
- /// Returns true if there is a valid logged in user and that ssl is enabled if required
- ///
- /// true if an exception should be thrown if authorization fails
- ///
- protected bool AuthorizeRequest(bool throwExceptions = false)
- {
- var result = Security.AuthorizeRequest(throwExceptions);
- return result == ValidateRequestAttempt.Success;
- }
-
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/ajaxHelpers.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/ajaxHelpers.cs
deleted file mode 100644
index 3e6ad7d221..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/ajaxHelpers.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using System;
-using System.Data;
-using System.Configuration;
-using System.Web;
-using System.Web.Security;
-using System.Web.UI;
-using System.Web.UI.WebControls;
-using System.Web.UI.WebControls.WebParts;
-using System.Web.UI.HtmlControls;
-using Umbraco.Core.IO;
-
-namespace umbraco.presentation.webservices
-{
- public class ajaxHelpers
- {
- public static void EnsureLegacyCalls(Page page)
- {
- var sm = ScriptManager.GetCurrent(page);
- var legacyPath = new ServiceReference(SystemDirectories.WebServices + "/legacyAjaxCalls.asmx");
-
- if (!sm.Services.Contains(legacyPath))
- sm.Services.Add(legacyPath);
- }
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx
deleted file mode 100644
index 5acc6e6022..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="legacyAjaxCalls.asmx.cs" Class="umbraco.presentation.webservices.legacyAjaxCalls" %>
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs
deleted file mode 100644
index 28ba66709a..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs
+++ /dev/null
@@ -1,141 +0,0 @@
-using System;
-using System.Web;
-using System.Web.Services;
-using System.ComponentModel;
-using System.Web.Script.Services;
-using Umbraco.Core;
-using Umbraco.Web;
-using Umbraco.Web.WebServices;
-using Umbraco.Core.Models.Membership;
-using Umbraco.Web.Composing;
-using Umbraco.Web._Legacy.UI;
-using Umbraco.Core.Services;
-
-namespace umbraco.presentation.webservices
-{
- ///
- /// Summary description for legacyAjaxCalls
- ///
- [WebService(Namespace = "http://umbraco.org/webservices")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [ToolboxItem(false)]
- [ScriptService]
- public class legacyAjaxCalls : UmbracoAuthorizedWebService
- {
- ///
- /// method to accept a string value for the node id. Used for tree's such as python
- /// and xslt since the file names are the node IDs
- ///
- ///
- ///
- ///
- [WebMethod]
- [ScriptMethod]
- public void Delete(string nodeId, string alias, string nodeType)
- {
- if (!AuthorizeRequest())
- return;
-
- //U4-2686 - alias is html encoded, make sure to decode
- alias = HttpUtility.HtmlDecode(alias);
-
- //check which parameters to pass depending on the types passed in
- int intNodeId;
- if (nodeType == "memberGroups")
- {
- LegacyDialogHandler.Delete(
- new HttpContextWrapper(HttpContext.Current),
- Security.CurrentUser,
- nodeType, 0, nodeId);
- }
- else if (int.TryParse(nodeId, out intNodeId) && nodeType != "member") // Fix for #26965 - numeric member login gets parsed as nodeId
- {
- LegacyDialogHandler.Delete(
- new HttpContextWrapper(HttpContext.Current),
- Security.CurrentUser,
- nodeType, intNodeId, alias);
- }
- else
- {
- LegacyDialogHandler.Delete(
- new HttpContextWrapper(HttpContext.Current),
- Security.CurrentUser,
- nodeType, 0, nodeId);
- }
- }
-
- ///
- /// Permanently deletes a document/media object.
- /// Used to remove an item from the recycle bin.
- ///
- ///
- ///
- [WebMethod]
- [ScriptMethod]
- public void DeleteContentPermanently(string nodeId, string nodeType)
- {
- int intNodeId;
- if (int.TryParse(nodeId, out intNodeId))
- {
- switch (nodeType)
- {
- case "media":
- case "mediaRecycleBin":
- //ensure user has access to media
- AuthorizeRequest(Constants.Applications.Media.ToString(), true);
- var media = Current.Services.MediaService.GetById(intNodeId);
- if (media != null)
- Current.Services.MediaService.Delete(media);
- break;
- case "content":
- case "contentRecycleBin":
- default:
- //ensure user has access to content
- AuthorizeRequest(Constants.Applications.Content.ToString(), true);
- var content = Current.Services.ContentService.GetById(intNodeId);
- if (content != null)
- Current.Services.ContentService.Delete(content);
- break;
- }
- }
- else
- {
- throw new ArgumentException("The nodeId argument could not be parsed to an integer");
- }
- }
-
- [WebMethod]
- [ScriptMethod]
- public void DisableUser(int userId)
- {
- AuthorizeRequest(Constants.Applications.Users.ToString(), true);
-
- var user = Services.UserService.GetUserById(userId);
- if (user == null) return;
-
- user.IsApproved = false;
- Services.UserService.Save(user);
- }
-
- [WebMethod]
- [ScriptMethod]
- public string NiceUrl(int nodeId)
- {
-
- AuthorizeRequest(true);
-
- var umbHelper = new UmbracoHelper(Current.UmbracoContext, Current.Services, Current.ApplicationCache);
-
- return umbHelper.Url(nodeId);
- }
-
- [WebMethod]
- [ScriptMethod]
- public string ProgressStatus(string Key)
- {
- AuthorizeRequest(true);
-
- return Application[Context.Request.GetItemAsString("key")].ToString();
- }
- }
-}
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx
deleted file mode 100644
index ae09ffa463..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx
+++ /dev/null
@@ -1 +0,0 @@
-<%@ WebService Language="C#" CodeBehind="nodeSorter.asmx.cs" Class="umbraco.presentation.webservices.nodeSorter" %>
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
deleted file mode 100644
index 5e0cb289f0..0000000000
--- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/nodeSorter.asmx.cs
+++ /dev/null
@@ -1,263 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using System.Web.Script.Services;
-using System.Web.Services;
-using Umbraco.Core;
-using Umbraco.Core.Configuration;
-using Umbraco.Core.Logging;
-using Umbraco.Core.Models;
-using Umbraco.Web;
-using Umbraco.Web.Actions;
-using Umbraco.Web.Composing;
-
-
-namespace umbraco.presentation.webservices
-{
- ///
- /// Summary description for nodeSorter
- ///
- [WebService(Namespace = "http://umbraco.org/")]
- [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
- [ToolboxItem(false)]
- [ScriptService]
- public class nodeSorter : UmbracoAuthorizedWebService
- {
- [WebMethod]
- public SortNode GetNodes(string ParentId, string App)
- {
- if (AuthorizeRequest())
- {
- var nodes = new List();
-
- // "hack for stylesheet"
- if (App == "settings")
- {
- var stylesheet = Services.FileService.GetStylesheetByName(ParentId.EnsureEndsWith(".css"));
- if (stylesheet == null) throw new InvalidOperationException("No stylesheet found by name " + ParentId);
-
- var sort = 0;
- foreach (var child in stylesheet.Properties)
- {
- nodes.Add(new SortNode(child.Name.GetHashCode(), sort, child.Name, DateTime.Now));
- sort++;
- }
-
- return new SortNode()
- {
- SortNodes = nodes.ToArray()
- };
- }
- else
- {
- var asInt = int.Parse(ParentId);
-
- var parent = new SortNode { Id = asInt };
-
- var entityService = Services.EntityService;
-
- // Root nodes?
- if (asInt == -1)
- {
- if (App == "media")
- {
- var rootMedia = entityService.GetRootEntities(UmbracoObjectTypes.Media);
- nodes.AddRange(rootMedia.Select(media => new SortNode(media.Id, media.SortOrder, media.Name, media.CreateDate)));
- }
- else
- {
- var rootContent = entityService.GetRootEntities(UmbracoObjectTypes.Document);
- nodes.AddRange(rootContent.Select(content => new SortNode(content.Id, content.SortOrder, content.Name, content.CreateDate)));
- }
- }
- else
- {
- var children = entityService.GetChildren(asInt);
- nodes.AddRange(children.Select(child => new SortNode(child.Id, child.SortOrder, child.Name, child.CreateDate)));
- }
-
-
- parent.SortNodes = nodes.ToArray();
-
- return parent;
- }
- }
-
- throw new ArgumentException("User not logged in");
- }
-
- public void UpdateSortOrder(int ParentId, string SortOrder)
- {
- UpdateSortOrder(ParentId.ToString(), SortOrder);
- }
-
- [WebMethod]
- public void UpdateSortOrder(string ParentId, string SortOrder)
- {
- if (AuthorizeRequest() == false) return;
- if (SortOrder.Trim().Length <= 0) return;
-
- var isContent = Context.Request.GetItemAsString("app") == "content" | Context.Request.GetItemAsString("app") == "";
- var isMedia = Context.Request.GetItemAsString("app") == "media";
-
- //ensure user is authorized for the app requested
- if (isContent && AuthorizeRequest(Constants.Applications.Content.ToString()) == false) return;
- if (isMedia && AuthorizeRequest(Constants.Applications.Media.ToString()) == false) return;
-
- var ids = SortOrder.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
- if (isContent)
- {
- SortContent(ids, int.Parse(ParentId));
- }
- else if (isMedia)
- {
- SortMedia(ids);
- }
- else
- {
- SortStylesheetProperties(ParentId, ids);
- }
- }
-
- private void SortMedia(string[] ids)
- {
- var mediaService = Services.MediaService;
- var sortedMedia = new List();
- try
- {
- for (var i = 0; i < ids.Length; i++)
- {
- var id = int.Parse(ids[i]);
- var m = mediaService.GetById(id);
- sortedMedia.Add(m);
- }
-
- // Save Media with new sort order and update content xml in db accordingly
- var sorted = mediaService.Sort(sortedMedia);
- }
- catch (Exception ex)
- {
- Current.Logger.Error(ex, "Could not update media sort order");
- }
- }
-
-
- private void SortStylesheetProperties(string stylesheetName, string[] names)
- {
- var stylesheet = Services.FileService.GetStylesheetByName(stylesheetName.EnsureEndsWith(".css"));
- if (stylesheet == null) throw new InvalidOperationException("No stylesheet found by name " + stylesheetName);
-
- var currProps = stylesheet.Properties.ToArray();
- //remove them all first
- foreach (var prop in currProps)
- {
- stylesheet.RemoveProperty(prop.Name);
- }
-
- //re-add them in the right order
- for (var i = 0; i < names.Length; i++)
- {
- var found = currProps.Single(x => x.Name == names[i]);
- stylesheet.AddProperty(found);
- }
-
- Services.FileService.SaveStylesheet(stylesheet);
- }
-
- private void SortContent(string[] ids, int parentId)
- {
- var contentService = Services.ContentService;
- try
- {
- // Save content with new sort order and update db+cache accordingly
- var intIds = new List();
- foreach (var stringId in ids)
- {
- int intId;
- if (int.TryParse(stringId, out intId))
- intIds.Add(intId);
- }
- var sorted = contentService.Sort(intIds.ToArray());
-
- // refresh sort order on cached xml
- // but no... this is not distributed - solely relying on content service & events should be enough
- //content.Instance.SortNodes(parentId);
-
- //send notifications! TODO: This should be put somewhere centralized instead of hard coded directly here
- if (parentId > 0)
- {
- Services.NotificationService.SendNotification(contentService.GetById(parentId), Current.Actions.GetAction(), UmbracoContext, Services.TextService, GlobalSettings);
- }
-
- }
- catch (Exception ex)
- {
- Current.Logger.Error(ex, "Could not update content sort order");
- }
- }
-
- }
-
- [Serializable]
- public class SortNode
- {
- public SortNode()
- {
- }
-
- private SortNode[] _sortNodes;
-
- public SortNode[] SortNodes
- {
- get { return _sortNodes; }
- set { _sortNodes = value; }
- }
-
- public int TotalNodes
- {
- get { return _sortNodes != null ? _sortNodes.Length : 0; }
- set { int test = value; }
- }
-
- public SortNode(int Id, int SortOrder, string Name, DateTime CreateDate)
- {
- _id = Id;
- _sortOrder = SortOrder;
- _name = Name;
- _createDate = CreateDate;
- }
-
- private DateTime _createDate;
-
- public DateTime CreateDate
- {
- get { return _createDate; }
- set { _createDate = value; }
- }
-
- private string _name;
-
- public string Name
- {
- get { return _name; }
- set { _name = value; }
- }
-
- private int _sortOrder;
-
- public int SortOrder
- {
- get { return _sortOrder; }
- set { _sortOrder = value; }
- }
-
- private int _id;
-
- public int Id
- {
- get { return _id; }
- set { _id = value; }
- }
- }
-}