Merge pull request #3429 from umbraco/temp8-231-send-to-publish-with-variants
v8 - send to publish with variants
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,33 +475,28 @@ namespace Umbraco.Core.Models
|
||||
/// <remarks>
|
||||
/// Overriden to deal with specific object instances
|
||||
/// </remarks>
|
||||
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;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<int>();
|
||||
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<int>();
|
||||
clonedEntity._contentTypeComposition = ContentTypeComposition.Select(x => (IContentTypeComposition)x.DeepClone()).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used by inheritors to modify the DeepCloning logic
|
||||
/// </summary>
|
||||
/// <param name="clone"></param>
|
||||
protected virtual void PerformDeepClone(object clone)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string>();
|
||||
clone._removedProperties = new List<string>();
|
||||
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<string>();
|
||||
clonedEntity._removedProperties = new List<string>();
|
||||
clonedEntity._properties = (MacroPropertyCollection)Properties.DeepClone();
|
||||
//re-assign the event handler
|
||||
clonedEntity._properties.CollectionChanged += clonedEntity.PropertiesChanged;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -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<string, object>();
|
||||
var cloneAdditionalData = clonedEntity._additionalData = new Dictionary<string, object>();
|
||||
|
||||
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<IReadOnlyUserGroup>(_userGroups);
|
||||
clone._allowedSections = _allowedSections != null ? new List<string>(_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<IReadOnlyUserGroup>(_userGroups);
|
||||
clonedEntity._allowedSections = _allowedSections != null ? new List<string>(_allowedSections) : null;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
/// <summary>
|
||||
/// This will either be an HTML or text based summary depending on the email type being sent
|
||||
/// </summary>
|
||||
public string Summary { get; }
|
||||
public string EditedUser { get; }
|
||||
public string SiteUrl { get; }
|
||||
}
|
||||
}
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -424,22 +424,17 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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<int>(() => PropertyGroupId.Value);
|
||||
clonedEntity._propertyGroupId = new Lazy<int>(() => PropertyGroupId.Value);
|
||||
}
|
||||
//this shouldn't really be needed since we're not tracking
|
||||
clone.ResetDirtyProperties(false);
|
||||
//re-enable tracking
|
||||
clone.EnableChangeTracking();
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,12 +312,12 @@ namespace Umbraco.Core.Services
|
||||
/// <summary>
|
||||
/// Sorts documents.
|
||||
/// </summary>
|
||||
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
|
||||
OperationResult Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
/// <summary>
|
||||
/// Sorts documents.
|
||||
/// </summary>
|
||||
bool Sort(IEnumerable<int> ids, int userId = 0, bool raiseEvents = true);
|
||||
OperationResult Sort(IEnumerable<int> ids, int userId = 0, bool raiseEvents = true);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends the notifications for the specified user regarding the specified node and action.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="operatingUser"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="actionName"></param>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="createSubject"></param>
|
||||
/// <param name="createBody"></param>
|
||||
void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody);
|
||||
|
||||
/// <summary>
|
||||
/// Sends the notifications for the specified user regarding the specified nodes and action.
|
||||
/// </summary>
|
||||
@@ -37,9 +22,9 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="http"></param>
|
||||
/// <param name="createSubject"></param>
|
||||
/// <param name="createBody"></param>
|
||||
void SendNotifications(IUser operatingUser, IEnumerable<IUmbracoEntity> entities, string action, string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody);
|
||||
void SendNotifications(IUser operatingUser, IEnumerable<IContent> entities, string action, string actionName, Uri siteUri,
|
||||
Func<(IUser user, NotificationEmailSubjectParams subject), string> createSubject,
|
||||
Func<(IUser user, NotificationEmailBodyParams body, bool isHtml), string> createBody);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the notifications for the user
|
||||
|
||||
@@ -1906,17 +1906,19 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <param name="items"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true)
|
||||
/// <returns>Result indicating what action was taken when handling the command.</returns>
|
||||
public OperationResult Sort(IEnumerable<IContent> 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
|
||||
/// <param name="ids"></param>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="raiseEvents"></param>
|
||||
/// <returns>True if sorting succeeded, otherwise False</returns>
|
||||
public bool Sort(IEnumerable<int> ids, int userId = 0, bool raiseEvents = true)
|
||||
/// <returns>Result indicating what action was taken when handling the command.</returns>
|
||||
public OperationResult Sort(IEnumerable<int> 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<IContent>(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<IContent>();
|
||||
var saved = new List<IContent>();
|
||||
@@ -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<IContent>(x, TreeChangeTypes.RefreshNode)).ToEventArgs());
|
||||
@@ -1996,7 +2009,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
scope.Events.Dispatch(Published, this, new PublishEventArgs<IContent>(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
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, DeleteRevisionsEventArgs> DeletedVersions;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Sorting
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SaveEventArgs<IContent>> Sorting;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs after Sorting
|
||||
/// </summary>
|
||||
public static event TypedEventHandler<IContentService, SaveEventArgs<IContent>> Sorted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs before Save
|
||||
/// </summary>
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sends the notifications for the specified user regarding the specified node and action.
|
||||
/// </summary>
|
||||
/// <param name="entity"></param>
|
||||
/// <param name="operatingUser"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="actionName"></param>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="createSubject"></param>
|
||||
/// <param name="createBody"></param>
|
||||
/// <remarks>
|
||||
/// Currently this will only work for Content entities!
|
||||
/// </remarks>
|
||||
public void SendNotifications(IUser operatingUser, IUmbracoEntity entity, string action, string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the previous version to the latest version of the content item if there is one
|
||||
/// </summary>
|
||||
@@ -131,20 +65,14 @@ namespace Umbraco.Core.Services.Implement
|
||||
/// <param name="operatingUser"></param>
|
||||
/// <param name="action"></param>
|
||||
/// <param name="actionName"></param>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="siteUri"></param>
|
||||
/// <param name="createSubject"></param>
|
||||
/// <param name="createBody"></param>
|
||||
/// <remarks>
|
||||
/// Currently this will only work for Content entities!
|
||||
/// </remarks>
|
||||
public void SendNotifications(IUser operatingUser, IEnumerable<IUmbracoEntity> entities, string action, string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> createBody)
|
||||
public void SendNotifications(IUser operatingUser, IEnumerable<IContent> 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<IContent> == false)
|
||||
throw new NotSupportedException();
|
||||
|
||||
var entitiesL = entities as List<IContent> ?? entities.Cast<IContent>().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<int, IContentBase>();
|
||||
|
||||
// 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
|
||||
/// <param name="content"></param>
|
||||
/// <param name="oldDoc"></param>
|
||||
/// <param name="actionName">The action readable name - currently an action is just a single letter, this is the name associated with the letter </param>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="siteUri"></param>
|
||||
/// <param name="createSubject">Callback to create the mail subject</param>
|
||||
/// <param name="createBody">Callback to create the mail body</param>
|
||||
private NotificationRequest CreateNotificationRequest(IUser performingUser, IUser mailingUser, IContentBase content, IContentBase oldDoc,
|
||||
string actionName, HttpContextBase http,
|
||||
Func<IUser, string[], string> createSubject,
|
||||
Func<IUser, string[], string> 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("<table style=\"width: 100 %; \">");
|
||||
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("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;border-bottom: 1px solid #CCC'>");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;border-bottom: 1px solid #CCC'>");
|
||||
summary.Append(newText);
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
}
|
||||
summary.Append("</table>");
|
||||
}
|
||||
|
||||
}
|
||||
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("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Note: </th>");
|
||||
summary.Append(
|
||||
"<td style='text-align: left; vertical-align: top;'> <span style='background-color:red;'>Red for deleted characters</span> <span style='background-color:yellow;'>Yellow for inserted characters</span></td>");
|
||||
summary.Append("</tr>");
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> New ");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(ReplaceLinks(CompareText(oldText, newText, true, false, "<span style='background-color:yellow;'>", string.Empty), http.Request));
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'> Old ");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(ReplaceLinks(CompareText(newText, oldText, true, false, "<span style='background-color:red;'>", string.Empty), http.Request));
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
//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("<ul>");
|
||||
foreach (var culture in culturesChanged)
|
||||
{
|
||||
summary.Append("<li>");
|
||||
summary.Append(culture);
|
||||
summary.Append("</li>");
|
||||
}
|
||||
summary.Append("</ul>");
|
||||
}
|
||||
else
|
||||
{
|
||||
summary.Append("<tr>");
|
||||
summary.Append("<th style='text-align: left; vertical-align: top; width: 25%;'>");
|
||||
summary.Append(p.PropertyType.Name);
|
||||
summary.Append("</th>");
|
||||
summary.Append("<td style='text-align: left; vertical-align: top;'>");
|
||||
summary.Append(newText);
|
||||
summary.Append("</td>");
|
||||
summary.Append("</tr>");
|
||||
//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(
|
||||
"<tr><td colspan=\"2\" style=\"border-bottom: 1px solid #CCC; font-size: 2px;\"> </td></tr>");
|
||||
}
|
||||
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(@"<html><head>
|
||||
</head>
|
||||
<body style='font-family: Trebuchet MS, arial, sans-serif; font-color: black;'>
|
||||
", 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
|
||||
/// <param name="oldString">The old string.</param>
|
||||
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(""", "\"");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares the text.
|
||||
/// </summary>
|
||||
/// <param name="oldText">The old text.</param>
|
||||
/// <param name="newText">The new text.</param>
|
||||
/// <param name="displayInsertedText">if set to <c>true</c> [display inserted text].</param>
|
||||
/// <param name="displayDeletedText">if set to <c>true</c> [display deleted text].</param>
|
||||
/// <param name="insertedStyle">The inserted style.</param>
|
||||
/// <param name="deletedStyle">The deleted style.</param>
|
||||
/// <returns></returns>
|
||||
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("</span>");
|
||||
}
|
||||
|
||||
// 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("</span>");
|
||||
} // 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!
|
||||
|
||||
|
||||
@@ -395,6 +395,8 @@
|
||||
<Compile Include="Models\Identity\IdentityMapperProfile.cs" />
|
||||
<Compile Include="Models\Membership\MemberExportModel.cs" />
|
||||
<Compile Include="Models\Membership\MemberExportProperty.cs" />
|
||||
<Compile Include="Models\NotificationEmailBodyParams.cs" />
|
||||
<Compile Include="Models\NotificationEmailSubjectParams.cs" />
|
||||
<Compile Include="Models\PathValidationExtensions.cs" />
|
||||
<Compile Include="Models\Entities\TreeEntityBase.cs" />
|
||||
<Compile Include="Models\PropertyTagsExtensions.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()
|
||||
{
|
||||
|
||||
@@ -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<IUrlSegmentProvider> urlSegmentProviders,
|
||||
TypeLoader typeLoader,
|
||||
@@ -159,10 +161,11 @@ namespace Umbraco.Tests.TestHelpers
|
||||
var runtimeState = Mock.Of<IRuntimeState>();
|
||||
var idkMap = new IdkMap(scopeProvider);
|
||||
|
||||
var localizationService = GetLazyService<ILocalizationService>(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDictionaryRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<ILanguageRepository>(c)));
|
||||
var userService = GetLazyService<IUserService>(container, c => new UserService(scopeProvider, logger, eventMessagesFactory, runtimeState, GetRepo<IUserRepository>(c), GetRepo<IUserGroupRepository>(c),globalSettings));
|
||||
var dataTypeService = GetLazyService<IDataTypeService>(container, c => new DataTypeService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDataTypeRepository>(c), GetRepo<IDataTypeContainerRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IContentTypeRepository>(c)));
|
||||
var contentService = GetLazyService<IContentService>(container, c => new ContentService(scopeProvider, logger, eventMessagesFactory, mediaFileSystem, GetRepo<IDocumentRepository>(c), GetRepo<IEntityRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IContentTypeRepository>(c), GetRepo<IDocumentBlueprintRepository>(c), GetRepo<ILanguageRepository>(c)));
|
||||
var notificationService = GetLazyService<INotificationService>(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, logger, GetRepo<INotificationsRepository>(c),globalSettings));
|
||||
var notificationService = GetLazyService<INotificationService>(container, c => new NotificationService(scopeProvider, userService.Value, contentService.Value, localizationService.Value, logger, GetRepo<INotificationsRepository>(c), globalSettings, umbracoSettings.Content));
|
||||
var serverRegistrationService = GetLazyService<IServerRegistrationService>(container, c => new ServerRegistrationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IServerRegistrationRepository>(c)));
|
||||
var memberGroupService = GetLazyService<IMemberGroupService>(container, c => new MemberGroupService(scopeProvider, logger, eventMessagesFactory, GetRepo<IMemberGroupRepository>(c)));
|
||||
var memberService = GetLazyService<IMemberService>(container, c => new MemberService(scopeProvider, logger, eventMessagesFactory, memberGroupService.Value, mediaFileSystem, GetRepo<IMemberRepository>(c), GetRepo<IMemberTypeRepository>(c), GetRepo<IMemberGroupRepository>(c), GetRepo<IAuditRepository>(c)));
|
||||
@@ -170,7 +173,6 @@ namespace Umbraco.Tests.TestHelpers
|
||||
var contentTypeService = GetLazyService<IContentTypeService>(container, c => new ContentTypeService(scopeProvider, logger, eventMessagesFactory, contentService.Value, GetRepo<IContentTypeRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IDocumentTypeContainerRepository>(c), GetRepo<IEntityRepository>(c)));
|
||||
var mediaTypeService = GetLazyService<IMediaTypeService>(container, c => new MediaTypeService(scopeProvider, logger, eventMessagesFactory, mediaService.Value, GetRepo<IMediaTypeRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IMediaTypeContainerRepository>(c), GetRepo<IEntityRepository>(c)));
|
||||
var fileService = GetLazyService<IFileService>(container, c => new FileService(scopeProvider, logger, eventMessagesFactory, GetRepo<IStylesheetRepository>(c), GetRepo<IScriptRepository>(c), GetRepo<ITemplateRepository>(c), GetRepo<IPartialViewRepository>(c), GetRepo<IPartialViewMacroRepository>(c), GetRepo<IAuditRepository>(c)));
|
||||
var localizationService = GetLazyService<ILocalizationService>(container, c => new LocalizationService(scopeProvider, logger, eventMessagesFactory, GetRepo<IDictionaryRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<ILanguageRepository>(c)));
|
||||
|
||||
var memberTypeService = GetLazyService<IMemberTypeService>(container, c => new MemberTypeService(scopeProvider, logger, eventMessagesFactory, memberService.Value, GetRepo<IMemberTypeRepository>(c), GetRepo<IAuditRepository>(c), GetRepo<IEntityRepository>(c)));
|
||||
var entityService = GetLazyService<IEntityService>(container, c => new EntityService(
|
||||
|
||||
+66
-62
@@ -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);
|
||||
|
||||
})();
|
||||
|
||||
@@ -102,11 +102,8 @@
|
||||
|
||||
//It was decided to just put these messages into the normal status messages.
|
||||
|
||||
var msg = "Unauthorized access to URL: <br/><i>" + rejection.config.url.split('?')[0] + "</i>";
|
||||
if (rejection.config.data) {
|
||||
msg += "<br/> with data: <br/><i>" + angular.toJson(rejection.config.data) + "</i><br/>Contact your administrator for information.";
|
||||
}
|
||||
|
||||
var msg = "Unauthorized access to URL: <br/><i>" + rejection.config.url.split('?')[0] + "</i><br/>Contact your administrator for information.";
|
||||
|
||||
notificationsService.error("Authorization error", msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
})();
|
||||
})();
|
||||
|
||||
@@ -342,7 +342,6 @@
|
||||
<Content Include="Umbraco\Config\Lang\nb.xml" />
|
||||
<Content Include="Umbraco\Config\Lang\sv.xml" />
|
||||
<Content Include="Umbraco\Dialogs\republish.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\SendPublish.aspx" />
|
||||
<Content Include="Umbraco\Developer\Packages\directoryBrowser.aspx" />
|
||||
<Content Include="Umbraco\Developer\Packages\editPackage.aspx" />
|
||||
<Content Include="Umbraco\Dialogs\protectPage.aspx" />
|
||||
@@ -420,7 +419,6 @@
|
||||
</None>
|
||||
<Content Include="Umbraco\Masterpages\umbracoPage.Master" />
|
||||
<Content Include="Umbraco\Masterpages\umbracoDialog.Master" />
|
||||
<Content Include="Umbraco\Webservices\CheckForUpgrade.asmx" />
|
||||
<Content Include="Umbraco\Create\simple.ascx" />
|
||||
<Content Include="Umbraco\Developer\Macros\editMacro.aspx" />
|
||||
<Content Include="Umbraco\Js\dualSelectBox.js" />
|
||||
@@ -430,10 +428,6 @@
|
||||
<SubType>Form</SubType>
|
||||
</Content>
|
||||
<!--<Content Include="Umbraco\users\PermissionEditor.aspx" />-->
|
||||
<Content Include="Umbraco\Webservices\ajax.js" />
|
||||
<Content Include="Umbraco\Webservices\codeEditorSave.asmx" />
|
||||
<Content Include="Umbraco\Webservices\legacyAjaxCalls.asmx" />
|
||||
<Content Include="Umbraco\Webservices\nodeSorter.asmx" />
|
||||
<Content Include="Umbraco\Masterpages\default.Master" />
|
||||
<None Include="web.Template.config">
|
||||
<SubType>Designer</SubType>
|
||||
|
||||
@@ -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
|
||||
]]></key>
|
||||
<key alias="mailBodyVariantSummary">The following languages have been modified %0%</key>
|
||||
<key alias="mailBodyHtml"><![CDATA[
|
||||
<html>
|
||||
<head>
|
||||
@@ -1005,9 +1008,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
</table>
|
||||
<p style='color: #392F54; font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 15px;'>
|
||||
<h3>Update summary:</h3>
|
||||
<table style="width: 100%;">
|
||||
%6%
|
||||
</table>
|
||||
%6%
|
||||
</p>
|
||||
<p style='color: #392F54; font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 15px;'>
|
||||
Have a nice day!<br /><br />
|
||||
@@ -1028,6 +1029,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
</body>
|
||||
</html>
|
||||
]]></key>
|
||||
<key alias="mailBodyVariantHtmlSummary"><![CDATA[<p>The following languages have been modified:</p>
|
||||
%0%
|
||||
]]></key>
|
||||
<key alias="mailSubject">[%0%] Notification about %1% performed on %2%</key>
|
||||
<key alias="notifications">Notifications</key>
|
||||
</area>
|
||||
|
||||
@@ -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
|
||||
]]></key>
|
||||
<key alias="mailBodyVariantSummary">The following languages have been modified %0%</key>
|
||||
<key alias="mailBodyHtml"><![CDATA[
|
||||
<html>
|
||||
<head>
|
||||
@@ -1027,9 +1030,7 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
</table>
|
||||
<p style='color: #392F54; font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 15px;'>
|
||||
<h3>Update summary:</h3>
|
||||
<table style="width: 100%;">
|
||||
%6%
|
||||
</table>
|
||||
%6%
|
||||
</p>
|
||||
<p style='color: #392F54; font-family: sans-serif; font-size: 14px; font-weight: normal; margin: 0 0 15px;'>
|
||||
Have a nice day!<br /><br />
|
||||
@@ -1050,6 +1051,9 @@ To manage your website, simply open the Umbraco back office and start adding con
|
||||
</body>
|
||||
</html>
|
||||
]]></key>
|
||||
<key alias="mailBodyVariantHtmlSummary"><![CDATA[<p>The following languages have been modified:</p>
|
||||
%0%
|
||||
]]></key>
|
||||
<key alias="mailSubject">[%0%] Notification about %1% performed on %2%</key>
|
||||
<key alias="notifications">Notifications</key>
|
||||
</area>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<%@ Page language="c#" Codebehind="SendPublish.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.SendPublish" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>umbraco - <%=Services.TextService.Localize("editContentSendToPublish")%></title>
|
||||
<link href="../css/umbracoGui.css" type="text/css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body style="padding: 10px;">
|
||||
<h3><img src="../images/publish.gif" alt="Republish" align="absmiddle" style="float:left; margin-top: 3px; margin-right: 5px"/> <%=Services.TextService.Localize("editContentSendToPublishText")%></h3>
|
||||
<br/>
|
||||
<a href="#" onclick="javascript:UmbClientMgr.closeModalWindow();" style="margin-left: 30px" class="guiDialogNormal"><%=Services.TextService.Localize("closewindow")%></a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="CheckForUpgrade.asmx.cs" Class="umbraco.presentation.webservices.CheckForUpgrade" %>
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="c#" Codebehind="Developer.asmx.cs" Class="umbraco.webservices.Developer" %>
|
||||
@@ -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. -----
|
||||
|
||||
/// <summary>The root object for the proxies to webservices.</summary>
|
||||
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. -----
|
||||
|
||||
/// <summary>The root object for the ajax engine.</summary>
|
||||
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 -----
|
||||
|
||||
///<summary>Start an AJAX action by entering it into the queue</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Start an AJAX action by entering it into the queue</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Check, if the next AJAX action can start.
|
||||
///This is an internal method that should not be called from external.</summary>
|
||||
///<remarks>for private use only.<remarks>
|
||||
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
|
||||
|
||||
|
||||
///<summary>The delay time of an action is over.</summary>
|
||||
ajax.EndWait = function() {
|
||||
ajax.timer = null;
|
||||
ajax._next(true);
|
||||
} // ajax.EndWait
|
||||
|
||||
|
||||
///<summary>The current action timed out.</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Finish an AJAX Action the normal way</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Finish an AJAX Action with an exception</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Clear the current and all pending AJAX actions.</summary>
|
||||
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 = "<img style='vertical-align:bottom' src='" + path + "ajax-loader.gif'> 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
|
||||
|
||||
|
||||
///<summary>Execute a soap call.
|
||||
///Build the xml for the call of a soap method of a webservice
|
||||
///and post it to the server.</summary>
|
||||
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 = "<?xml version='1.0' encoding='utf-8'?>"
|
||||
+ "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>"
|
||||
+ "<soap:Body>"
|
||||
+ "<" + 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, "<").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 = "<string>" + args[n].join("</string><string>") + "</string>";
|
||||
|
||||
} else if (typ[1] == "int[]") {
|
||||
val = "<int>" + args[n].join("</int><int>") + "</int>";
|
||||
|
||||
} else if (typ[1] == "float[]") {
|
||||
val = "<float>" + args[n].join("</float><float>") + "</float>";
|
||||
|
||||
} else if (typ[1] == "bool[]") {
|
||||
val = "<boolean>" + args[n].join("</boolean><boolean>") + "</boolean>";
|
||||
|
||||
} // if
|
||||
soap += "<" + typ[0] + ">" + val + "</" + typ[0] + ">"
|
||||
} // for
|
||||
|
||||
// envelope end
|
||||
soap += "</" + p.fname + ">"
|
||||
+ "</soap:Body>"
|
||||
+ "</soap:Envelope>";
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
///<summary>Callback method for a webservice call that dispatches the response to servive.func or service.onException.</summary>
|
||||
///<remarks>for private use only.<remarks>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Callback method to show the result of a soap call in an alert box.</summary>
|
||||
///<remarks>To set up a debug output in an alert box use:
|
||||
///proxies.service.method.corefunc = proxies.alertResult;</remarks>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Show all the details of the returned data of a webservice call.
|
||||
///Use this method for debugging transmission problems.</summary>
|
||||
///<remarks>To set up a debug output in an alert box use:
|
||||
///proxies.service.method.corefunc = proxies.alertResponseText;</remarks>
|
||||
proxies.alertResponseText = function () {
|
||||
if (proxies.xmlhttp.readyState == 4)
|
||||
alert("Status:" + proxies.xmlhttp.status + "\nRESULT:" + proxies.xmlhttp.responseText);
|
||||
} // proxies.alertResponseText
|
||||
|
||||
|
||||
///<summary>show the details about an exception.</summary>
|
||||
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
|
||||
|
||||
|
||||
///<summary>Get a browser specific implementation of the XMLHttpRequest object.</summary>
|
||||
// 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
|
||||
|
||||
|
||||
///<summary>Get a browser specific implementation of the XMLDOM object, containing a XML document.</summary>
|
||||
///<param name="xmlText">the xml document as string.</param>
|
||||
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
|
||||
|
||||
|
||||
///<summary>show the details of a javascript object.</summary>
|
||||
///<remarks>This helps a lot while developing and debugging.</remarks>
|
||||
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 -----
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="codeEditorSave.asmx.cs" Class="umbraco.presentation.webservices.codeEditorSave" %>
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="legacyAjaxCalls.asmx.cs" Class="umbraco.presentation.webservices.legacyAjaxCalls" %>
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="nodeSorter.asmx.cs" Class="umbraco.presentation.webservices.nodeSorter" %>
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="c#" Codebehind="templates.asmx.cs" Class="umbraco.webservices.templates" %>
|
||||
@@ -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<ActionToPublish>());
|
||||
base.Compose(composition);
|
||||
composition.Container.RegisterSingleton<Notifier>();
|
||||
}
|
||||
|
||||
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<ActionToPublish>(), args.Entity);
|
||||
|
||||
//Send notifications for the published action
|
||||
ContentService.Published += (sender, args) =>
|
||||
{
|
||||
foreach (var content in args.PublishedEntities)
|
||||
notificationService.SendNotification(content, actions.GetAction<ActionToPublish>());
|
||||
};
|
||||
ContentService.Published += (sender, args) => notifier.Notify(actions.GetAction<ActionPublish>(), 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<IContent>();
|
||||
var updatedEntities = new List<IContent>();
|
||||
|
||||
//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<ActionNew>());
|
||||
notificationService.SendNotification(updatedEntities, actions.GetAction<ActionUpdate>());
|
||||
};
|
||||
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<ActionDelete>());
|
||||
};
|
||||
|
||||
ContentService.Deleted += (sender, args) => notifier.Notify(actions.GetAction<ActionDelete>(), args.DeletedEntities.ToArray());
|
||||
|
||||
//Send notifications for the unpublish action
|
||||
ContentService.Unpublished += (sender, args) =>
|
||||
ContentService.Unpublished += (sender, args) => notifier.Notify(actions.GetAction<ActionUnpublish>(), args.PublishedEntities.ToArray());
|
||||
}
|
||||
|
||||
private void ContentServiceSorted(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs<IContent> 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<ActionSort>(), new[] { parent });
|
||||
}
|
||||
|
||||
private void ContentServiceSaved(Notifier notifier, IContentService sender, Core.Events.SaveEventArgs<IContent> args, ActionCollection actions)
|
||||
{
|
||||
var newEntities = new List<IContent>();
|
||||
var updatedEntities = new List<IContent>();
|
||||
|
||||
//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<ActionUnpublish>());
|
||||
};
|
||||
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<ActionNew>(), newEntities.ToArray());
|
||||
notifier.Notify(actions.GetAction<ActionUpdate>(), updatedEntities.ToArray());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This class is used to send the notifications
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="umbracoContextAccessor"></param>
|
||||
/// <param name="notificationService"></param>
|
||||
/// <param name="userService"></param>
|
||||
/// <param name="textService"></param>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="contentConfig"></param>
|
||||
/// <param name="logger"></param>
|
||||
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<IContent> 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
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
private readonly ManifestParser _manifestParser;
|
||||
private readonly UmbracoFeatures _features;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private BackOfficeUserManager<BackOfficeIdentityUser> _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
|
||||
|
||||
@@ -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<ContentController>("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<ActionSort>(), UmbracoContext, Services.TextService, GlobalSettings);
|
||||
}
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -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<IUmbracoEntity> 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<IUmbracoEntity> 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<IUmbracoEntity> 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<IUmbracoEntity> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@ namespace Umbraco.Web.Trees
|
||||
internal static Attempt<LegacyUrlAction> GetUrlAndTitleFromLegacyAction(IAction action, string nodeId, string nodeType, string nodeName, string currentSection)
|
||||
{
|
||||
switch (action)
|
||||
|
||||
{
|
||||
case ActionNew actionNew:
|
||||
return Attempt.Succeed(
|
||||
|
||||
@@ -762,7 +762,6 @@
|
||||
<Compile Include="Models\TagModel.cs" />
|
||||
<Compile Include="Models\UpgradeCheckResponse.cs" />
|
||||
<Compile Include="Models\PasswordChangedModel.cs" />
|
||||
<Compile Include="NotificationServiceExtensions.cs" />
|
||||
<Compile Include="PropertyEditors\ColorPickerConfigurationEditor.cs" />
|
||||
<Compile Include="PropertyEditors\EmailAddressPropertyEditor.cs" />
|
||||
<Compile Include="PropertyEditors\ImageCropperPropertyEditor.cs" />
|
||||
@@ -1268,13 +1267,6 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\republish.aspx.designer.cs">
|
||||
<DependentUpon>republish.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\SendPublish.aspx.cs">
|
||||
<DependentUpon>SendPublish.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\dialogs\SendPublish.aspx.designer.cs">
|
||||
<DependentUpon>SendPublish.aspx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx.cs">
|
||||
<DependentUpon>editPackage.aspx</DependentUpon>
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
@@ -1287,19 +1279,6 @@
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\ItemRenderer.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\Macro.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\templateControls\ContentType.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\webservices\ajaxHelpers.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\webservices\CheckForUpgrade.asmx.cs">
|
||||
<DependentUpon>CheckForUpgrade.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\webservices\legacyAjaxCalls.asmx.cs">
|
||||
<DependentUpon>legacyAjaxCalls.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="umbraco.presentation\umbraco\webservices\nodeSorter.asmx.cs">
|
||||
<DependentUpon>nodeSorter.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UmbracoApplication.cs" />
|
||||
<Compile Include="UmbracoContext.cs" />
|
||||
<Compile Include="UmbracoModule.cs" />
|
||||
@@ -1311,9 +1290,6 @@
|
||||
</Compile>
|
||||
<Compile Include="TagsController.cs" />
|
||||
<Compile Include="UmbracoAuthorizedHttpHandler.cs" />
|
||||
<Compile Include="umbraco.presentation\umbraco\webservices\UmbracoAuthorizedWebService.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="UmbracoHttpHandler.cs" />
|
||||
<Compile Include="UmbracoWebService.cs">
|
||||
<SubType>Component</SubType>
|
||||
@@ -1340,10 +1316,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="UI\JavaScript\PreviewInitialize.js" />
|
||||
<Content Include="umbraco.presentation\umbraco\webservices\CheckForUpgrade.asmx" />
|
||||
<!--<Content Include="umbraco.presentation\umbraco\users\PermissionEditor.aspx" />-->
|
||||
<Content Include="umbraco.presentation\umbraco\webservices\legacyAjaxCalls.asmx" />
|
||||
<Content Include="umbraco.presentation\umbraco\webservices\nodeSorter.asmx" />
|
||||
<Content Include="PublishedCache\NuCache\notes.txt" />
|
||||
<Content Include="umbraco.presentation\umbraco\developer\RelationTypes\EditRelationType.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
@@ -1361,7 +1334,6 @@
|
||||
<Content Include="umbraco.presentation\umbraco\dialogs\republish.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Content>
|
||||
<Content Include="umbraco.presentation\umbraco\dialogs\SendPublish.aspx" />
|
||||
<Content Include="umbraco.presentation\umbraco\developer\Packages\editPackage.aspx">
|
||||
<SubType>ASPXCodeBehind</SubType>
|
||||
</Content>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<%@ Page language="c#" Codebehind="SendPublish.aspx.cs" AutoEventWireup="True" Inherits="umbraco.dialogs.SendPublish" %>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>umbraco - <%=Services.TextService.Localize("editContentSendToPublish")%></title>
|
||||
<link href="../css/umbracoGui.css" type="text/css" rel="stylesheet"/>
|
||||
</head>
|
||||
<body style="padding: 10px;">
|
||||
<h3><img src="../images/publish.gif" alt="Republish" align="absmiddle" style="float:left; margin-top: 3px; margin-right: 5px"/> <%=Services.TextService.Localize("editContentSendToPublishText")%></h3>
|
||||
<br/>
|
||||
<a href="#" onclick="javascript:UmbClientMgr.closeModalWindow();" style="margin-left: 30px" class="guiDialogNormal"><%=Services.TextService.Localize("closewindow")%></a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,39 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Actions;
|
||||
using Umbraco.Web.Composing;
|
||||
|
||||
|
||||
namespace umbraco.dialogs
|
||||
{
|
||||
/// <summary>
|
||||
/// Runs all action handlers for the ActionToPublish action for the document with
|
||||
/// the corresponding document id passed in by query string
|
||||
/// </summary>
|
||||
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<ActionToPublish>());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// 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.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace umbraco.dialogs {
|
||||
|
||||
|
||||
public partial class SendPublish {
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="CheckForUpgrade.asmx.cs" Class="umbraco.presentation.webservices.CheckForUpgrade" %>
|
||||
@@ -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>
|
||||
/// Summary description for CheckForUpgrade
|
||||
/// </summary>
|
||||
[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));
|
||||
}
|
||||
}
|
||||
}
|
||||
-97
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract web service class that has the methods and properties to correct validate an Umbraco user
|
||||
/// </summary>
|
||||
public abstract class UmbracoAuthorizedWebService : UmbracoWebService
|
||||
{
|
||||
private bool _hasValidated = false;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the umbraco context id is valid
|
||||
/// </summary>
|
||||
/// <param name="currentUmbracoUserContextId"></param>
|
||||
/// <returns></returns>
|
||||
protected bool ValidateUserContextId(string currentUmbracoUserContextId)
|
||||
{
|
||||
return UmbracoContext.Security.ValidateCurrentUser();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the username/password credentials are valid
|
||||
/// </summary>
|
||||
/// <param name="username"></param>
|
||||
/// <param name="password"></param>
|
||||
/// <returns></returns>
|
||||
protected bool ValidateCredentials(string username, string password)
|
||||
{
|
||||
return UmbracoContext.Security.ValidateBackOfficeCredentials(username, password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates the user for access to a certain application
|
||||
/// </summary>
|
||||
/// <param name="app">The application alias.</param>
|
||||
/// <param name="throwExceptions">true if an exception should be thrown if authorization fails</param>
|
||||
/// <returns></returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified user as access to the app
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
protected bool UserHasAppAccess(string app, IUser user)
|
||||
{
|
||||
return Security.UserHasSectionAccess(app, user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the specified user by username as access to the app
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="username"></param>
|
||||
/// <returns></returns>
|
||||
protected bool UserHasAppAccess(string app, string username)
|
||||
{
|
||||
return Security.UserHasSectionAccess(app, username);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a valid logged in user and that ssl is enabled if required
|
||||
/// </summary>
|
||||
/// <param name="throwExceptions">true if an exception should be thrown if authorization fails</param>
|
||||
/// <returns></returns>
|
||||
protected bool AuthorizeRequest(bool throwExceptions = false)
|
||||
{
|
||||
var result = Security.AuthorizeRequest(throwExceptions);
|
||||
return result == ValidateRequestAttempt.Success;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="legacyAjaxCalls.asmx.cs" Class="umbraco.presentation.webservices.legacyAjaxCalls" %>
|
||||
@@ -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>
|
||||
/// Summary description for legacyAjaxCalls
|
||||
/// </summary>
|
||||
[WebService(Namespace = "http://umbraco.org/webservices")]
|
||||
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
|
||||
[ToolboxItem(false)]
|
||||
[ScriptService]
|
||||
public class legacyAjaxCalls : UmbracoAuthorizedWebService
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="nodeId"></param>
|
||||
/// <param name="alias"></param>
|
||||
/// <param name="nodeType"></param>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Permanently deletes a document/media object.
|
||||
/// Used to remove an item from the recycle bin.
|
||||
/// </summary>
|
||||
/// <param name="nodeId"></param>
|
||||
/// <param name="nodeType"></param>
|
||||
[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<%@ WebService Language="C#" CodeBehind="nodeSorter.asmx.cs" Class="umbraco.presentation.webservices.nodeSorter" %>
|
||||
@@ -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>
|
||||
/// Summary description for nodeSorter
|
||||
/// </summary>
|
||||
[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<SortNode>();
|
||||
|
||||
// "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<IMedia>();
|
||||
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<nodeSorter>(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<int>();
|
||||
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<ActionSort>(), UmbracoContext, Services.TextService, GlobalSettings);
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Current.Logger.Error<nodeSorter>(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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user