Merge remote-tracking branch 'origin/v8/dev' into v8/feature/nucache-perf
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
<dependency id="Serilog.Sinks.Map" version="[1.0.0,1.999999)" />
|
||||
<dependency id="Serilog.Sinks.Async" version="[1.3.0,1.999999)" />
|
||||
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
|
||||
<dependency id="NPoco" version="[3.9.4,3.999999)" />
|
||||
<dependency id="NPoco" version="[4.0.3,4.999999)" />
|
||||
</group>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@@ -19,4 +19,4 @@ using System.Resources;
|
||||
|
||||
// these are FYI and changed automatically
|
||||
[assembly: AssemblyFileVersion("8.11.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.11.0-rc")]
|
||||
[assembly: AssemblyInformationalVersion("8.11.0")]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows clearing all event handlers
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue"></typeparam>
|
||||
public class EventClearingObservableCollection<TValue> : ObservableCollection<TValue>, INotifyCollectionChanged
|
||||
{
|
||||
public EventClearingObservableCollection()
|
||||
{
|
||||
}
|
||||
|
||||
public EventClearingObservableCollection(List<TValue> list) : base(list)
|
||||
{
|
||||
}
|
||||
|
||||
public EventClearingObservableCollection(IEnumerable<TValue> collection) : base(collection)
|
||||
{
|
||||
}
|
||||
|
||||
// need to explicitly implement with event accessor syntax in order to override in order to to clear
|
||||
// c# events are weird, they do not behave the same way as other c# things that are 'virtual',
|
||||
// a good article is here: https://medium.com/@unicorn_dev/virtual-events-in-c-something-went-wrong-c6f6f5fbe252
|
||||
// and https://stackoverflow.com/questions/2268065/c-sharp-language-design-explicit-interface-implementation-of-an-event
|
||||
private NotifyCollectionChangedEventHandler _changed;
|
||||
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
|
||||
{
|
||||
add { _changed += value; }
|
||||
remove { _changed -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all event handlers for the <see cref="CollectionChanged"/> event
|
||||
/// </summary>
|
||||
public void ClearCollectionChangedEvents() => _changed = null;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// An ObservableDictionary
|
||||
/// </summary>
|
||||
@@ -15,7 +18,7 @@ namespace Umbraco.Core.Collections
|
||||
/// </remarks>
|
||||
/// <typeparam name="TValue">The type of elements contained in the BindableCollection</typeparam>
|
||||
/// <typeparam name="TKey">The type of the indexing key</typeparam>
|
||||
public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>, IReadOnlyDictionary<TKey, TValue>, IDictionary<TKey, TValue>
|
||||
public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>, IReadOnlyDictionary<TKey, TValue>, IDictionary<TKey, TValue>, INotifyCollectionChanged
|
||||
{
|
||||
protected Dictionary<TKey, int> Indecies { get; }
|
||||
protected Func<TValue, TKey> KeySelector { get; }
|
||||
@@ -74,6 +77,22 @@ namespace Umbraco.Core.Collections
|
||||
|
||||
#endregion
|
||||
|
||||
// need to explicitly implement with event accessor syntax in order to override in order to to clear
|
||||
// c# events are weird, they do not behave the same way as other c# things that are 'virtual',
|
||||
// a good article is here: https://medium.com/@unicorn_dev/virtual-events-in-c-something-went-wrong-c6f6f5fbe252
|
||||
// and https://stackoverflow.com/questions/2268065/c-sharp-language-design-explicit-interface-implementation-of-an-event
|
||||
private NotifyCollectionChangedEventHandler _changed;
|
||||
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
|
||||
{
|
||||
add { _changed += value; }
|
||||
remove { _changed -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all <see cref="CollectionChanged"/> event handlers
|
||||
/// </summary>
|
||||
public void ClearCollectionChangedEvents() => _changed = null;
|
||||
|
||||
public bool ContainsKey(TKey key)
|
||||
{
|
||||
return Indecies.ContainsKey(key);
|
||||
|
||||
@@ -98,10 +98,15 @@ namespace Umbraco.Core.Models
|
||||
set
|
||||
{
|
||||
if (_schedule != null)
|
||||
_schedule.CollectionChanged -= ScheduleCollectionChanged;
|
||||
{
|
||||
_schedule.ClearCollectionChangedEvents();
|
||||
}
|
||||
|
||||
SetPropertyValueAndDetectChanges(value, ref _schedule, nameof(ContentSchedule));
|
||||
if (_schedule != null)
|
||||
{
|
||||
_schedule.CollectionChanged += ScheduleCollectionChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,10 +228,16 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_publishInfos != null) _publishInfos.CollectionChanged -= PublishNamesCollectionChanged;
|
||||
if (_publishInfos != null)
|
||||
{
|
||||
_publishInfos.ClearCollectionChangedEvents();
|
||||
}
|
||||
|
||||
_publishInfos = value;
|
||||
if (_publishInfos != null)
|
||||
{
|
||||
_publishInfos.CollectionChanged += PublishNamesCollectionChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +332,7 @@ namespace Umbraco.Core.Models
|
||||
else
|
||||
Properties.EnsurePropertyTypes(contentType.CompositionPropertyTypes);
|
||||
|
||||
Properties.CollectionChanged -= PropertiesChanged; // be sure not to double add
|
||||
Properties.ClearCollectionChangedEvents(); // be sure not to double add
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
}
|
||||
|
||||
@@ -438,7 +449,7 @@ namespace Umbraco.Core.Models
|
||||
//if culture infos exist then deal with event bindings
|
||||
if (clonedContent._publishInfos != null)
|
||||
{
|
||||
clonedContent._publishInfos.CollectionChanged -= PublishNamesCollectionChanged; //clear this event handler if any
|
||||
clonedContent._publishInfos.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedContent._publishInfos = (ContentCultureInfosCollection)_publishInfos.DeepClone(); //manually deep clone
|
||||
clonedContent._publishInfos.CollectionChanged += clonedContent.PublishNamesCollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
@@ -446,7 +457,7 @@ namespace Umbraco.Core.Models
|
||||
//if properties exist then deal with event bindings
|
||||
if (clonedContent._schedule != null)
|
||||
{
|
||||
clonedContent._schedule.CollectionChanged -= ScheduleCollectionChanged; //clear this event handler if any
|
||||
clonedContent._schedule.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedContent._schedule = (ContentScheduleCollection)_schedule.DeepClone(); //manually deep clone
|
||||
clonedContent._schedule.CollectionChanged += clonedContent.ScheduleCollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
|
||||
@@ -138,7 +138,11 @@ namespace Umbraco.Core.Models
|
||||
get => _properties;
|
||||
set
|
||||
{
|
||||
if (_properties != null) _properties.CollectionChanged -= PropertiesChanged;
|
||||
if (_properties != null)
|
||||
{
|
||||
_properties.ClearCollectionChangedEvents();
|
||||
}
|
||||
|
||||
_properties = value;
|
||||
_properties.CollectionChanged += PropertiesChanged;
|
||||
}
|
||||
@@ -173,10 +177,15 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
set
|
||||
{
|
||||
if (_cultureInfos != null) _cultureInfos.CollectionChanged -= CultureInfosCollectionChanged;
|
||||
if (_cultureInfos != null)
|
||||
{
|
||||
_cultureInfos.ClearCollectionChangedEvents();
|
||||
}
|
||||
_cultureInfos = value;
|
||||
if (_cultureInfos != null)
|
||||
{
|
||||
_cultureInfos.CollectionChanged += CultureInfosCollectionChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +488,7 @@ namespace Umbraco.Core.Models
|
||||
//if culture infos exist then deal with event bindings
|
||||
if (clonedContent._cultureInfos != null)
|
||||
{
|
||||
clonedContent._cultureInfos.CollectionChanged -= CultureInfosCollectionChanged; //clear this event handler if any
|
||||
clonedContent._cultureInfos.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedContent._cultureInfos = (ContentCultureInfosCollection)_cultureInfos.DeepClone(); //manually deep clone
|
||||
clonedContent._cultureInfos.CollectionChanged += clonedContent.CultureInfosCollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
@@ -487,7 +496,7 @@ namespace Umbraco.Core.Models
|
||||
//if properties exist then deal with event bindings
|
||||
if (clonedContent._properties != null)
|
||||
{
|
||||
clonedContent._properties.CollectionChanged -= PropertiesChanged; //clear this event handler if any
|
||||
clonedContent._properties.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedContent._properties = (PropertyCollection)_properties.DeepClone(); //manually deep clone
|
||||
clonedContent._properties.CollectionChanged += clonedContent.PropertiesChanged; //re-assign correct event handler
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Umbraco.Core.Models
|
||||
public ContentCultureInfosCollection()
|
||||
: base(x => x.Culture, StringComparer.InvariantCultureIgnoreCase)
|
||||
{ }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds or updates a <see cref="ContentCultureInfos"/> instance.
|
||||
/// </summary>
|
||||
|
||||
@@ -14,6 +14,11 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Clears all <see cref="CollectionChanged"/> event handlers
|
||||
/// </summary>
|
||||
public void ClearCollectionChangedEvents() => CollectionChanged = null;
|
||||
|
||||
private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
@@ -262,7 +262,10 @@ namespace Umbraco.Core.Models
|
||||
set
|
||||
{
|
||||
if (_noGroupPropertyTypes != null)
|
||||
_noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged;
|
||||
{
|
||||
_noGroupPropertyTypes.ClearCollectionChangedEvents();
|
||||
}
|
||||
|
||||
_noGroupPropertyTypes = new PropertyTypeCollection(SupportsPublishing, value);
|
||||
_noGroupPropertyTypes.CollectionChanged += PropertyTypesChanged;
|
||||
PropertyTypesChanged(_noGroupPropertyTypes, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
||||
@@ -480,14 +483,14 @@ namespace Umbraco.Core.Models
|
||||
// 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
|
||||
|
||||
clonedEntity._noGroupPropertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
|
||||
clonedEntity._noGroupPropertyTypes.ClearCollectionChangedEvents(); //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 (clonedEntity._propertyGroups != null)
|
||||
{
|
||||
clonedEntity._propertyGroups.CollectionChanged -= PropertyGroupsChanged; //clear this event handler if any
|
||||
clonedEntity._propertyGroups.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedEntity._propertyGroups = (PropertyGroupCollection) _propertyGroups.DeepClone(); //manually deep clone
|
||||
clonedEntity._propertyGroups.CollectionChanged += clonedEntity.PropertyGroupsChanged; //re-assign correct event handler
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
@@ -62,7 +63,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
_culture = Current.Configs.Global().DefaultUILanguage; // TODO: inject
|
||||
|
||||
// must initialize before setting groups
|
||||
_roles = new ObservableCollection<IdentityUserRole<string>>();
|
||||
_roles = new EventClearingObservableCollection<IdentityUserRole<string>>();
|
||||
_roles.CollectionChanged += _roles_CollectionChanged;
|
||||
|
||||
// use the property setters - they do more than just setting a field
|
||||
@@ -223,7 +224,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
_groups = value;
|
||||
|
||||
//now clear all roles and re-add them
|
||||
_roles.CollectionChanged -= _roles_CollectionChanged;
|
||||
_roles.ClearCollectionChangedEvents();
|
||||
_roles.Clear();
|
||||
foreach (var identityUserRole in _groups.Select(x => new IdentityUserRole<string>
|
||||
{
|
||||
@@ -306,7 +307,7 @@ namespace Umbraco.Core.Models.Identity
|
||||
_beingDirty.OnPropertyChanged(nameof(Roles));
|
||||
}
|
||||
|
||||
private readonly ObservableCollection<IdentityUserRole<string>> _roles;
|
||||
private readonly EventClearingObservableCollection<IdentityUserRole<string>> _roles;
|
||||
|
||||
/// <summary>
|
||||
/// helper method to easily add a role without having to deal with IdentityUserRole{T}
|
||||
|
||||
@@ -77,7 +77,7 @@ namespace Umbraco.Core.Models
|
||||
else
|
||||
Properties.EnsurePropertyTypes(contentType.CompositionPropertyTypes);
|
||||
|
||||
Properties.CollectionChanged -= PropertiesChanged; // be sure not to double add
|
||||
Properties.ClearCollectionChangedEvents(); // be sure not to double add
|
||||
Properties.CollectionChanged += PropertiesChanged;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +169,8 @@ namespace Umbraco.Core.Models
|
||||
/// </summary>
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
public void ClearCollectionChangedEvents() => CollectionChanged = null;
|
||||
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
@@ -66,7 +66,10 @@ namespace Umbraco.Core.Models
|
||||
set
|
||||
{
|
||||
if (_propertyTypes != null)
|
||||
_propertyTypes.CollectionChanged -= PropertyTypesChanged;
|
||||
{
|
||||
_propertyTypes.ClearCollectionChangedEvents();
|
||||
}
|
||||
|
||||
_propertyTypes = value;
|
||||
|
||||
// since we're adding this collection to this group,
|
||||
@@ -100,7 +103,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
if (clonedEntity._propertyTypes != null)
|
||||
{
|
||||
clonedEntity._propertyTypes.CollectionChanged -= PropertyTypesChanged; //clear this event handler if any
|
||||
clonedEntity._propertyTypes.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
clonedEntity._propertyTypes = (PropertyTypeCollection) _propertyTypes.DeepClone(); //manually deep clone
|
||||
clonedEntity._propertyTypes.CollectionChanged += clonedEntity.PropertyTypesChanged; //re-assign correct event handler
|
||||
}
|
||||
|
||||
@@ -160,6 +160,11 @@ namespace Umbraco.Core.Models
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Clears all <see cref="CollectionChanged"/> event handlers
|
||||
/// </summary>
|
||||
public void ClearCollectionChangedEvents() => CollectionChanged = null;
|
||||
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
@@ -162,7 +162,11 @@ namespace Umbraco.Core.Models
|
||||
}
|
||||
|
||||
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears all <see cref="CollectionChanged"/> event handlers
|
||||
/// </summary>
|
||||
public void ClearCollectionChangedEvents() => CollectionChanged = null;
|
||||
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
CollectionChanged?.Invoke(this, args);
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Collections.ObjectModel;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Collections;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Core.Models
|
||||
@@ -12,7 +13,7 @@ namespace Umbraco.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class PublicAccessEntry : EntityBase
|
||||
{
|
||||
private readonly ObservableCollection<PublicAccessRule> _ruleCollection;
|
||||
private readonly EventClearingObservableCollection<PublicAccessRule> _ruleCollection;
|
||||
private int _protectedNodeId;
|
||||
private int _noAccessNodeId;
|
||||
private int _loginNodeId;
|
||||
@@ -28,7 +29,7 @@ namespace Umbraco.Core.Models
|
||||
NoAccessNodeId = noAccessNode.Id;
|
||||
_protectedNodeId = protectedNode.Id;
|
||||
|
||||
_ruleCollection = new ObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection = new EventClearingObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged;
|
||||
|
||||
foreach (var rule in _ruleCollection)
|
||||
@@ -44,7 +45,7 @@ namespace Umbraco.Core.Models
|
||||
NoAccessNodeId = noAccessNodeId;
|
||||
_protectedNodeId = protectedNodeId;
|
||||
|
||||
_ruleCollection = new ObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection = new EventClearingObservableCollection<PublicAccessRule>(ruleCollection);
|
||||
_ruleCollection.CollectionChanged += _ruleCollection_CollectionChanged;
|
||||
|
||||
foreach (var rule in _ruleCollection)
|
||||
@@ -148,7 +149,7 @@ namespace Umbraco.Core.Models
|
||||
|
||||
if (cloneEntity._ruleCollection != null)
|
||||
{
|
||||
cloneEntity._ruleCollection.CollectionChanged -= _ruleCollection_CollectionChanged; //clear this event handler if any
|
||||
cloneEntity._ruleCollection.ClearCollectionChangedEvents(); //clear this event handler if any
|
||||
cloneEntity._ruleCollection.CollectionChanged += cloneEntity._ruleCollection_CollectionChanged; //re-assign correct event handler
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +167,23 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
return dto == null ? null : Map(dto);
|
||||
}
|
||||
|
||||
public IRedirectUrl GetMostRecentUrl(string url, string culture)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(culture)) return GetMostRecentUrl(url);
|
||||
var urlHash = url.GenerateHash<SHA1>();
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash &&
|
||||
(x.Culture == culture.ToLower() || x.Culture == string.Empty))
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc);
|
||||
var dtos = Database.Fetch<RedirectUrlDto>(sql);
|
||||
var dto = dtos.FirstOrDefault(f => f.Culture == culture.ToLower());
|
||||
|
||||
if (dto == null)
|
||||
dto = dtos.FirstOrDefault(f => f.Culture == string.Empty);
|
||||
|
||||
return dto == null ? null : Map(dto);
|
||||
}
|
||||
|
||||
public IEnumerable<IRedirectUrl> GetContentUrls(Guid contentKey)
|
||||
{
|
||||
var sql = GetBaseQuery(false)
|
||||
@@ -208,17 +225,5 @@ JOIN umbracoNode ON umbracoRedirectUrl.contentKey=umbracoNode.uniqueID");
|
||||
var rules = result.Items.Select(Map);
|
||||
return rules;
|
||||
}
|
||||
|
||||
public IRedirectUrl GetMostRecentUrl(string url, string culture)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(culture)) return GetMostRecentUrl(url);
|
||||
var urlHash = url.GenerateHash<SHA1>();
|
||||
var sql = GetBaseQuery(false)
|
||||
.Where<RedirectUrlDto>(x => x.Url == url && x.UrlHash == urlHash && x.Culture == culture.ToLower())
|
||||
.OrderByDescending<RedirectUrlDto>(x => x.CreateDateUtc);
|
||||
var dtos = Database.Fetch<RedirectUrlDto>(sql);
|
||||
var dto = dtos.FirstOrDefault();
|
||||
return dto == null ? null : Map(dto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.GetByUsername(username, includeSecurityData: false);
|
||||
}
|
||||
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -258,7 +258,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
// this method must exist in this service as an implementation (legacy)
|
||||
void IMembershipMemberService<IUser>.SetLastLogin(string username, DateTime date)
|
||||
{
|
||||
throw new NotSupportedException("This method is not implemented or supported for users");
|
||||
Logger.Warn<UserService>("This method is not implemented. Using membership providers users is not advised, use ASP.NET Identity instead. See issue #9224 for more information.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -717,7 +717,7 @@ namespace Umbraco.Core.Services.Implement
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.Get(id, includeSecurityData: false);
|
||||
}
|
||||
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,8 +128,10 @@
|
||||
</Compile>
|
||||
-->
|
||||
<Compile Include="AssemblyExtensions.cs" />
|
||||
<Compile Include="Collections\EventClearingObservableCollection.cs" />
|
||||
<Compile Include="Constants-SqlTemplates.cs" />
|
||||
<Compile Include="Exceptions\UnattendedInstallException.cs" />
|
||||
<Compile Include="Collections\EventClearingObservableCollection.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\ContentTypeDto80.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\PropertyDataDto80.cs" />
|
||||
<Compile Include="Migrations\Upgrade\V_8_0_0\Models\PropertyTypeDto80.cs" />
|
||||
|
||||
@@ -2171,7 +2171,7 @@ namespace Umbraco.Tests.Integration
|
||||
[Test]
|
||||
public void HasInitialContent()
|
||||
{
|
||||
Assert.AreEqual(4, ServiceContext.ContentService.Count());
|
||||
Assert.AreEqual(5, ServiceContext.ContentService.Count());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -343,7 +343,7 @@ namespace Umbraco.Tests.Services
|
||||
}
|
||||
|
||||
// Assert
|
||||
Assert.AreEqual(24, contentService.Count());
|
||||
Assert.AreEqual(25, contentService.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -1415,7 +1415,7 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
// Arrange
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 5);
|
||||
var content = contentService.GetById(NodeDto.NodeIdSeed + 6);
|
||||
|
||||
// Act
|
||||
var published = contentService.SaveAndPublish(content, userId: Constants.Security.SuperUserId);
|
||||
@@ -1687,7 +1687,7 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
Assert.AreNotEqual(-20, content.ParentId);
|
||||
Assert.IsFalse(content.Trashed);
|
||||
Assert.AreEqual(3, descendants.Count);
|
||||
Assert.AreEqual(4, descendants.Count);
|
||||
Assert.IsFalse(descendants.Any(x => x.Path.StartsWith("-1,-20,")));
|
||||
Assert.IsFalse(descendants.Any(x => x.Trashed));
|
||||
|
||||
@@ -1700,7 +1700,7 @@ namespace Umbraco.Tests.Services
|
||||
|
||||
Assert.AreEqual(-20, content.ParentId);
|
||||
Assert.IsTrue(content.Trashed);
|
||||
Assert.AreEqual(3, descendants.Count);
|
||||
Assert.AreEqual(4, descendants.Count);
|
||||
Assert.IsTrue(descendants.All(x => x.Path.StartsWith("-1,-20,")));
|
||||
Assert.True(descendants.All(x => x.Trashed));
|
||||
|
||||
@@ -1987,7 +1987,7 @@ namespace Umbraco.Tests.Services
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var temp = contentService.GetById(NodeDto.NodeIdSeed + 2);
|
||||
Assert.AreEqual("Home", temp.Name);
|
||||
Assert.AreEqual(2, contentService.CountChildren(temp.Id));
|
||||
Assert.AreEqual(3, contentService.CountChildren(temp.Id));
|
||||
|
||||
// Act
|
||||
var copy = contentService.Copy(temp, temp.ParentId, false, true, Constants.Security.SuperUserId);
|
||||
@@ -1997,7 +1997,7 @@ namespace Umbraco.Tests.Services
|
||||
Assert.That(copy, Is.Not.Null);
|
||||
Assert.That(copy.Id, Is.Not.EqualTo(content.Id));
|
||||
Assert.AreNotSame(content, copy);
|
||||
Assert.AreEqual(2, contentService.CountChildren(copy.Id));
|
||||
Assert.AreEqual(3, contentService.CountChildren(copy.Id));
|
||||
|
||||
var child = contentService.GetById(NodeDto.NodeIdSeed + 3);
|
||||
var childCopy = contentService.GetPagedChildren(copy.Id, 0, 500, out var total).First();
|
||||
@@ -2013,7 +2013,7 @@ namespace Umbraco.Tests.Services
|
||||
var contentService = ServiceContext.ContentService;
|
||||
var temp = contentService.GetById(NodeDto.NodeIdSeed + 2);
|
||||
Assert.AreEqual("Home", temp.Name);
|
||||
Assert.AreEqual(2, contentService.CountChildren(temp.Id));
|
||||
Assert.AreEqual(3, contentService.CountChildren(temp.Id));
|
||||
|
||||
// Act
|
||||
var copy = contentService.Copy(temp, temp.ParentId, false, false, Constants.Security.SuperUserId);
|
||||
|
||||
@@ -477,7 +477,7 @@ namespace Umbraco.Tests.Services
|
||||
var entities = service.GetAll(UmbracoObjectTypes.Document).ToArray();
|
||||
|
||||
Assert.That(entities.Any(), Is.True);
|
||||
Assert.That(entities.Length, Is.EqualTo(4));
|
||||
Assert.That(entities.Length, Is.EqualTo(5));
|
||||
Assert.That(entities.Any(x => x.Trashed), Is.True);
|
||||
}
|
||||
|
||||
@@ -490,7 +490,7 @@ namespace Umbraco.Tests.Services
|
||||
var entities = service.GetAll(objectTypeId).ToArray();
|
||||
|
||||
Assert.That(entities.Any(), Is.True);
|
||||
Assert.That(entities.Length, Is.EqualTo(4));
|
||||
Assert.That(entities.Length, Is.EqualTo(5));
|
||||
Assert.That(entities.Any(x => x.Trashed), Is.True);
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ namespace Umbraco.Tests.Services
|
||||
var entities = service.GetAll<IContent>().ToArray();
|
||||
|
||||
Assert.That(entities.Any(), Is.True);
|
||||
Assert.That(entities.Length, Is.EqualTo(4));
|
||||
Assert.That(entities.Length, Is.EqualTo(5));
|
||||
Assert.That(entities.Any(x => x.Trashed), Is.True);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Tests.Testing;
|
||||
@@ -19,11 +16,14 @@ namespace Umbraco.Tests.Services
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
|
||||
public class RedirectUrlServiceTests : TestWithSomeContentBase
|
||||
{
|
||||
private IContent _testPage;
|
||||
private IContent _altTestPage;
|
||||
private string _url = "blah";
|
||||
private string _cultureA = "en";
|
||||
private string _cultureB = "de";
|
||||
private IContent _firstSubPage;
|
||||
private IContent _secondSubPage;
|
||||
private IContent _thirdSubPage;
|
||||
private readonly string _url = "blah";
|
||||
private readonly string _urlAlt = "alternativeUrl";
|
||||
private readonly string _cultureEnglish = "en";
|
||||
private readonly string _cultureGerman = "de";
|
||||
private readonly string _unusedCulture = "es";
|
||||
public override void CreateTestData()
|
||||
{
|
||||
base.CreateTestData();
|
||||
@@ -33,22 +33,34 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
var repository = new RedirectUrlRepository((IScopeAccessor)provider, AppCaches.Disabled, Mock.Of<ILogger>());
|
||||
var rootContent = ServiceContext.ContentService.GetRootContent().FirstOrDefault();
|
||||
var subPages = ServiceContext.ContentService.GetPagedChildren(rootContent.Id, 0, 2, out _).ToList();
|
||||
_testPage = subPages[0];
|
||||
_altTestPage = subPages[1];
|
||||
var subPages = ServiceContext.ContentService.GetPagedChildren(rootContent.Id, 0, 3, out _).ToList();
|
||||
_firstSubPage = subPages[0];
|
||||
_secondSubPage = subPages[1];
|
||||
_thirdSubPage = subPages[2];
|
||||
|
||||
|
||||
|
||||
repository.Save(new RedirectUrl
|
||||
{
|
||||
ContentKey = _testPage.Key,
|
||||
ContentKey = _firstSubPage.Key,
|
||||
Url = _url,
|
||||
Culture = _cultureA
|
||||
Culture = _cultureEnglish
|
||||
});
|
||||
Thread.Sleep(1000); //Added delay to ensure timestamp difference as sometimes they seem to have the same timestamp
|
||||
repository.Save(new RedirectUrl
|
||||
{
|
||||
ContentKey = _altTestPage.Key,
|
||||
ContentKey = _secondSubPage.Key,
|
||||
Url = _url,
|
||||
Culture = _cultureB
|
||||
Culture = _cultureGerman
|
||||
});
|
||||
Thread.Sleep(1000);
|
||||
repository.Save(new RedirectUrl
|
||||
{
|
||||
ContentKey = _thirdSubPage.Key,
|
||||
Url = _urlAlt,
|
||||
Culture = string.Empty
|
||||
});
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
}
|
||||
@@ -64,7 +76,7 @@ namespace Umbraco.Tests.Services
|
||||
{
|
||||
var redirectUrlService = ServiceContext.RedirectUrlService;
|
||||
var redirect = redirectUrlService.GetMostRecentRedirectUrl(_url);
|
||||
Assert.AreEqual(redirect.ContentId, _altTestPage.Id);
|
||||
Assert.AreEqual(redirect.ContentId, _secondSubPage.Id);
|
||||
|
||||
}
|
||||
|
||||
@@ -72,8 +84,17 @@ namespace Umbraco.Tests.Services
|
||||
public void Can_Get_Most_Recent_RedirectUrl_With_Culture()
|
||||
{
|
||||
var redirectUrlService = ServiceContext.RedirectUrlService;
|
||||
var redirect = redirectUrlService.GetMostRecentRedirectUrl(_url, _cultureA);
|
||||
Assert.AreEqual(redirect.ContentId, _testPage.Id);
|
||||
var redirect = redirectUrlService.GetMostRecentRedirectUrl(_url, _cultureEnglish);
|
||||
Assert.AreEqual(redirect.ContentId, _firstSubPage.Id);
|
||||
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Can_Get_Most_Recent_RedirectUrl_With_Culture_When_No_CultureVariant_Exists()
|
||||
{
|
||||
var redirectUrlService = ServiceContext.RedirectUrlService;
|
||||
var redirect = redirectUrlService.GetMostRecentRedirectUrl(_urlAlt, _unusedCulture);
|
||||
Assert.AreEqual(redirect.ContentId, _thirdSubPage.Id);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@ namespace Umbraco.Tests.Services
|
||||
Content subpage2 = MockedContent.CreateSimpleContent(contentType, "Text Page 2", textpage.Id);
|
||||
ServiceContext.ContentService.Save(subpage2, 0);
|
||||
|
||||
Content subpage3 = MockedContent.CreateSimpleContent(contentType, "Text Page 3", textpage.Id);
|
||||
ServiceContext.ContentService.Save(subpage3, 0);
|
||||
|
||||
//Create and Save Content "Text Page Deleted" based on "umbTextpage" -> 1064
|
||||
Content trashed = MockedContent.CreateSimpleContent(contentType, "Text Page Deleted", -20);
|
||||
trashed.Trashed = true;
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
<PackageReference Include="Moq">
|
||||
<Version>4.14.5</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Moq" Version="4.10.1" />
|
||||
<PackageReference Include="NPoco" Version="4.0.3" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="3.12.0" />
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
|
||||
syncTreeNode($scope.content, data.path, false, args.reloadChildren);
|
||||
|
||||
eventsService.emit("content.saved", { content: $scope.content, action: args.action });
|
||||
eventsService.emit("content.saved", { content: $scope.content, action: args.action, valid: true });
|
||||
|
||||
resetNestedFieldValiation(fieldsToRollback);
|
||||
ensureDirtyIsSetIfAnyVariantIsDirty();
|
||||
@@ -474,8 +474,15 @@
|
||||
return $q.when(data);
|
||||
},
|
||||
function (err) {
|
||||
|
||||
|
||||
syncTreeNode($scope.content, $scope.content.path);
|
||||
|
||||
if (err.status === 400 && err.data) {
|
||||
// content was saved but is invalid.
|
||||
eventsService.emit("content.saved", { content: $scope.content, action: args.action, valid: false });
|
||||
}
|
||||
|
||||
resetNestedFieldValiation(fieldsToRollback);
|
||||
|
||||
return $q.reject(err);
|
||||
@@ -981,7 +988,7 @@
|
||||
$scope.appChanged = function (activeApp) {
|
||||
|
||||
$scope.activeApp = activeApp;
|
||||
|
||||
|
||||
_.forEach($scope.content.apps, function (app) {
|
||||
app.active = false;
|
||||
if (app.alias === $scope.activeApp.alias) {
|
||||
|
||||
@@ -10,18 +10,14 @@ angular.module("umbraco.directives")
|
||||
}
|
||||
};
|
||||
|
||||
//check if there's a value for the attribute, if there is and it's false then we conditionally don't
|
||||
//use auto focus.
|
||||
if (attrs.umbAutoFocus) {
|
||||
attrs.$observe("umbAutoFocus", function (newVal) {
|
||||
var enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
|
||||
if (enabled) {
|
||||
$timeout(function() {
|
||||
update();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
attrs.$observe("umbAutoFocus", function (newVal) {
|
||||
var enabled = (newVal === "false" || newVal === 0 || newVal === false) ? false : true;
|
||||
if (enabled) {
|
||||
$timeout(function() {
|
||||
update();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
function UmbRadiobuttonController($timeout) {
|
||||
function UmbRadiobuttonController($timeout, localizationService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ function angularHelper($q) {
|
||||
var requiredFormProps = ["$error", "$name", "$dirty", "$pristine", "$valid", "$submitted", "$pending"];
|
||||
|
||||
function collectAllFormErrorsRecursively(formCtrl, allErrors) {
|
||||
// skip if the control is already added to the array
|
||||
if (allErrors.indexOf(formCtrl) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// loop over the error dictionary (see https://docs.angularjs.org/api/ng/type/form.FormController#$error)
|
||||
var keys = Object.keys(formCtrl.$error);
|
||||
if (keys.length === 0) {
|
||||
@@ -31,6 +36,7 @@ function angularHelper($q) {
|
||||
allErrors.push(ctrl); // add the error
|
||||
return;
|
||||
}
|
||||
|
||||
// recurse with the sub form
|
||||
collectAllFormErrorsRecursively(ctrl, allErrors);
|
||||
}
|
||||
@@ -43,6 +49,7 @@ function angularHelper($q) {
|
||||
}
|
||||
|
||||
function isForm(obj) {
|
||||
|
||||
// a method to check that the collection of object prop names contains the property name expected
|
||||
function allPropertiesExist(objectPropNames) {
|
||||
//ensure that every required property name exists on the current object
|
||||
@@ -89,9 +96,9 @@ function angularHelper($q) {
|
||||
|
||||
/**
|
||||
* Method used to re-run the $parsers for a given ngModel
|
||||
* @param {} scope
|
||||
* @param {} ngModel
|
||||
* @returns {}
|
||||
* @param {} scope
|
||||
* @param {} ngModel
|
||||
* @returns {}
|
||||
*/
|
||||
revalidateNgModel: function (scope, ngModel) {
|
||||
this.safeApply(scope, function() {
|
||||
@@ -103,8 +110,8 @@ function angularHelper($q) {
|
||||
|
||||
/**
|
||||
* Execute a list of promises sequentially. Unlike $q.all which executes all promises at once, this will execute them in sequence.
|
||||
* @param {} promises
|
||||
* @returns {}
|
||||
* @param {} promises
|
||||
* @returns {}
|
||||
*/
|
||||
executeSequentialPromises: function (promises) {
|
||||
|
||||
@@ -178,7 +185,7 @@ function angularHelper($q) {
|
||||
//NOTE: There isn't a way in angular to get a reference to the current form object since the form object
|
||||
// is just defined as a property of the scope when it is named but you'll always need to know the name which
|
||||
// isn't very convenient. If we want to watch for validation changes we need to get a form reference.
|
||||
// The way that we detect the form object is a bit hackerific in that we detect all of the required properties
|
||||
// The way that we detect the form object is a bit hackerific in that we detect all of the required properties
|
||||
// that exist on a form object.
|
||||
//
|
||||
//The other way to do it in a directive is to require "^form", but in a controller the only other way to do it
|
||||
@@ -239,7 +246,7 @@ function angularHelper($q) {
|
||||
$submitted: false,
|
||||
$pending: undefined,
|
||||
$addControl: Utilities.noop,
|
||||
$removeControl: Utilities.noop,
|
||||
$removeControl: Utilities.noop,
|
||||
$setValidity: Utilities.noop,
|
||||
$setDirty: Utilities.noop,
|
||||
$setPristine: Utilities.noop,
|
||||
|
||||
@@ -84,7 +84,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
//when true, the url will change but it won't actually re-route
|
||||
//this is merely here for compatibility, if only the content/media/members used this service we'd prob be ok but tons of editors
|
||||
//use this service unfortunately and probably packages too.
|
||||
args.softRedirect = false;
|
||||
args.softRedirect = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +123,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
self.handleSaveError({
|
||||
showNotifications: args.showNotifications,
|
||||
softRedirect: args.softRedirect,
|
||||
err: err
|
||||
err: err,
|
||||
rebindCallback: function () {
|
||||
// if the error contains data, we want to map that back as we want to continue editing this save. Especially important when the content is new as the returned data will contain ID etc.
|
||||
if(err.data) {
|
||||
rebindCallback.apply(self, [args.content, err.data]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//update editor state to what is current
|
||||
@@ -298,7 +304,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
}
|
||||
|
||||
// if publishing is allowed also allow schedule publish
|
||||
// we add this manually becuase it doesn't have a permission so it wont
|
||||
// we add this manually becuase it doesn't have a permission so it wont
|
||||
// get picked up by the loop through permissions
|
||||
if (_.contains(args.content.allowedActions, "U")) {
|
||||
buttons.subButtons.push(createButtonDefinition("SCHEDULE"));
|
||||
@@ -622,7 +628,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
if (!args.err) {
|
||||
throw "args.err cannot be null";
|
||||
}
|
||||
|
||||
|
||||
//When the status is a 400 status with a custom header: X-Status-Reason: Validation failed, we have validation errors.
|
||||
//Otherwise the error is probably due to invalid data (i.e. someone mucking around with the ids or something).
|
||||
//Or, some strange server error
|
||||
@@ -640,7 +646,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
|
||||
if (!this.redirectToCreatedContent(args.err.data.id, args.softRedirect) || args.softRedirect) {
|
||||
// If we are not redirecting it's because this is not newly created content, else in some cases we are
|
||||
// soft-redirecting which means the URL will change but the route wont (i.e. creating content).
|
||||
// soft-redirecting which means the URL will change but the route wont (i.e. creating content).
|
||||
|
||||
// In this case we need to detect what properties have changed and re-bind them with the server data.
|
||||
if (args.rebindCallback && angular.isFunction(args.rebindCallback)) {
|
||||
@@ -687,7 +693,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
if (!this.redirectToCreatedContent(args.redirectId ? args.redirectId : args.savedContent.id, args.softRedirect) || args.softRedirect) {
|
||||
|
||||
// If we are not redirecting it's because this is not newly created content, else in some cases we are
|
||||
// soft-redirecting which means the URL will change but the route wont (i.e. creating content).
|
||||
// soft-redirecting which means the URL will change but the route wont (i.e. creating content).
|
||||
|
||||
// In this case we need to detect what properties have changed and re-bind them with the server data.
|
||||
if (args.rebindCallback && angular.isFunction(args.rebindCallback)) {
|
||||
@@ -723,7 +729,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
|
||||
navigationService.setSoftRedirect();
|
||||
}
|
||||
//change to new path
|
||||
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
|
||||
$location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id);
|
||||
//don't add a browser history for this
|
||||
$location.replace();
|
||||
return true;
|
||||
|
||||
@@ -63,6 +63,11 @@
|
||||
border-left-color: @white;
|
||||
}
|
||||
|
||||
.umb-button__progress.-black {
|
||||
border-color: rgba(255, 255, 255, 0.4);
|
||||
border-left-color: @black;
|
||||
}
|
||||
|
||||
.umb-button__success,
|
||||
.umb-button__error {
|
||||
position: absolute;
|
||||
|
||||
@@ -104,19 +104,18 @@
|
||||
margin: 0 auto;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.umb-card-grid li {
|
||||
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
width: 100px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.umb-card-grid.-six-in-row li {
|
||||
@@ -142,18 +141,20 @@
|
||||
.umb-card-grid .umb-card-grid-item {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
//height: 100%;
|
||||
padding-top: 100%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px 5px;
|
||||
border-radius: @baseBorderRadius * 2;
|
||||
transition: background-color 120ms;
|
||||
font-size: 13px;
|
||||
line-height: 1.3em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
> span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
bottom: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
align-items: center;
|
||||
flex: 0 0 30px;
|
||||
margin-right: 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.umb-checkbox-list__item-icon {
|
||||
@@ -44,6 +45,17 @@
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.umb-checkbox-list__item-icon-wrapper {
|
||||
position: relative;
|
||||
|
||||
.umb-button__progress {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-left: -10px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-checkbox-list__item-text {
|
||||
font-size: 14px;
|
||||
margin-bottom: 0;
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
.umb-nested-content-property-container {
|
||||
position: relative;
|
||||
|
||||
&:not(:last-child){
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
@@ -54,19 +55,19 @@
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.umb-nested-content__item--single > .umb-nested-content__content {
|
||||
.umb-nested-content__item--single {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.umb-nested-content__item--single > .umb-nested-content__content > .umb-pane {
|
||||
margin: 0;
|
||||
> .umb-nested-content__content {
|
||||
> .umb-pane {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-nested-content__header-bar {
|
||||
|
||||
cursor: pointer;
|
||||
background-color: @white;
|
||||
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
@@ -78,21 +79,19 @@
|
||||
padding-right: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-nested-content__heading {
|
||||
display: flex;
|
||||
padding: 15px;
|
||||
line-height: 20px;
|
||||
position: relative;
|
||||
padding: 15px 5px;
|
||||
color:@ui-option-type;
|
||||
color: @ui-option-type;
|
||||
|
||||
&:hover {
|
||||
color:@ui-option-type-hover;
|
||||
color: @ui-option-type-hover;
|
||||
}
|
||||
|
||||
.umb-nested-content__item-icon {
|
||||
position: absolute;
|
||||
margin-top: -3px;
|
||||
font-size: 22px;
|
||||
}
|
||||
@@ -106,10 +105,9 @@
|
||||
padding-left: 5px;
|
||||
|
||||
&.--has-icon {
|
||||
padding-left: 30px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-nested-content__icons {
|
||||
@@ -125,13 +123,16 @@
|
||||
.umb-nested-content__item--active > .umb-nested-content__header-bar {
|
||||
.umb-nested-content__heading {
|
||||
background-color: @ui-active;
|
||||
|
||||
&:hover {
|
||||
color:@ui-option-type;
|
||||
color: @ui-option-type;
|
||||
}
|
||||
|
||||
.umb-nested-content__item-name {
|
||||
padding-right: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-nested-content__icons {
|
||||
background-color: @ui-active;
|
||||
&:before {
|
||||
@@ -140,8 +141,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.umb-nested-content__header-bar:hover .umb-nested-content__icons,
|
||||
.umb-nested-content__header-bar:focus .umb-nested-content__icons,
|
||||
.umb-nested-content__header-bar:focus-within .umb-nested-content__icons,
|
||||
@@ -149,8 +148,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.umb-nested-content__icon {
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
@@ -180,9 +177,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.umb-nested-content__footer-bar {
|
||||
margin-top: 20px;
|
||||
}
|
||||
@@ -212,7 +206,6 @@
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
|
||||
.umb-nested-content__content {
|
||||
border-top: 1px solid transparent;
|
||||
border-bottom: 1px solid transparent;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
var oldModel = null;
|
||||
|
||||
vm.showConfirmSubmit = false;
|
||||
vm.loading = false;
|
||||
vm.loadingAlias = null;
|
||||
|
||||
vm.isSelected = isSelected;
|
||||
vm.openContentType = openContentType;
|
||||
@@ -57,31 +57,13 @@
|
||||
$location.path(url);
|
||||
}
|
||||
|
||||
function selectCompositeContentType(compositeContentType) {
|
||||
|
||||
vm.loading = true;
|
||||
function selectCompositeContentType(compositeContentType) {
|
||||
vm.loadingAlias = compositeContentType.contentType.alias
|
||||
|
||||
var contentType = compositeContentType.contentType;
|
||||
|
||||
$scope.model.selectCompositeContentType(contentType).then(function (response) {
|
||||
|
||||
Utilities.forEach(vm.availableGroups, function (group) {
|
||||
|
||||
Utilities.forEach(group.compositeContentTypes, function (obj) {
|
||||
if (obj.allowed === false) {
|
||||
obj.selected = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
|
||||
}, function () {
|
||||
$timeout(function () {
|
||||
vm.loading = false;
|
||||
}, 500);
|
||||
vm.loadingAlias = null;
|
||||
});
|
||||
|
||||
// Check if the template is already selected.
|
||||
|
||||
@@ -42,10 +42,6 @@
|
||||
<localize key="contentTypeEditor_compositionInUse"></localize>
|
||||
</umb-empty-state>
|
||||
|
||||
<div ng-show="vm.loading" style="min-height: 20px; position: absolute; left: 0; right: 0; z-index: 1;">
|
||||
<umb-load-indicator></umb-load-indicator>
|
||||
</div>
|
||||
|
||||
<div ng-if="model.availableCompositeContentTypes.length === 0 && model.totalContentTypes > 1 && model.whereCompositionUsed.length > 0">
|
||||
<h5><localize key="contentTypeEditor_compositionUsageHeading"></localize></h5>
|
||||
<p><localize key="contentTypeEditor_compositionUsageSpecification"></localize></p>
|
||||
@@ -68,18 +64,21 @@
|
||||
</li>
|
||||
<li class="umb-checkbox-list__item"
|
||||
ng-repeat="compositeContentType in group.compositeContentTypes | orderBy:'contentType.name' | filter:searchTerm"
|
||||
ng-class="{'-disabled': compositeContentType.allowed === false || compositeContentType.inherited, '-selected': compositeContentType.selected}">
|
||||
ng-class="{'-disabled': (compositeContentType.allowed === false && !compositeContentType.selected) || compositeContentType.inherited, '-selected': compositeContentType.selected}">
|
||||
|
||||
<div class="umb-checkbox-list__item-checkbox" ng-class="{'-selected': compositeContentType.selected }">
|
||||
<umb-checkbox input-id="umb-overlay-comp-{{compositeContentType.contentType.key}}"
|
||||
model="compositeContentType.selected"
|
||||
on-change="vm.selectCompositeContentType(compositeContentType)"
|
||||
disabled="compositeContentType.allowed === false || compositeContentType.inherited">
|
||||
disabled="(compositeContentType.allowed === false && !compositeContentType.selected) || compositeContentType.inherited || vm.loadingAlias">
|
||||
</umb-checkbox>
|
||||
</div>
|
||||
|
||||
<label for="umb-overlay-comp-{{compositeContentType.contentType.key}}" class="umb-checkbox-list__item-text" ng-class="{'-faded': compositeContentType.allowed === false}">
|
||||
<i class="{{ compositeContentType.contentType.icon }} umb-checkbox-list__item-icon"></i>
|
||||
<label for="umb-overlay-comp-{{compositeContentType.contentType.key}}" class="umb-checkbox-list__item-text" ng-class="{'-faded': compositeContentType.allowed === false && !compositeContentType.selected}">
|
||||
<div class="umb-checkbox-list__item-icon-wrapper">
|
||||
<i class="{{ compositeContentType.contentType.icon }} umb-checkbox-list__item-icon" ng-class="{'o-0': compositeContentType.contentType.alias === vm.loadingAlias}"></i>
|
||||
<div ng-if="compositeContentType.contentType.alias === vm.loadingAlias" class="umb-button__progress -black umb-checkbox-list__item-checkbox-progress"></div>
|
||||
</div>
|
||||
{{ compositeContentType.contentType.name }}
|
||||
<span class="umb-checkbox-list__item-caption" ng-if="compositeContentType.inherited">(inherited)</span>
|
||||
</label>
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
function ItemPickerOverlay($scope, localizationService) {
|
||||
|
||||
$scope.filter = {
|
||||
searchTerm: ''
|
||||
};
|
||||
|
||||
function onInit() {
|
||||
$scope.model.hideSubmitButton = true;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="form-search" ng-if="model.filter !== false" style="margin-bottom: 15px;">
|
||||
<i class="icon-search" aria-hidden="true"></i>
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
ng-model="filter.searchTerm"
|
||||
class="umb-search-field search-query input-block-level -full-width-input"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_filter"
|
||||
@@ -10,7 +10,7 @@
|
||||
no-dirty-check />
|
||||
</div>
|
||||
|
||||
<div class="umb-overlay__section-header" ng-if="(model.pasteItems | filter:searchTerm).length > 0">
|
||||
<div class="umb-overlay__section-header" ng-if="(model.pasteItems | filter:filter.searchTerm).length > 0">
|
||||
<h5><localize key="content_createFromClipboard">Paste from clipboard</localize></h5>
|
||||
<button ng-if="model.clickClearPaste" ng-click="model.clickClearPaste($event)" alt="Clear clipboard for entries accepted in this context.">
|
||||
<i class="icon-trash" aria-hidden="true"></i>
|
||||
@@ -18,7 +18,7 @@
|
||||
</div>
|
||||
|
||||
<ul class="umb-card-grid" ng-class="{'-three-in-row': model.availableItems.length < 7, '-four-in-row': model.availableItems.length >= 7}">
|
||||
<li ng-repeat="pasteItem in model.pasteItems | filter:searchTerm">
|
||||
<li ng-repeat="pasteItem in model.pasteItems | filter:filter.searchTerm">
|
||||
<button type="button" class="umb-card-grid-item btn-reset" ng-click="model.clickPasteItem(pasteItem)">
|
||||
<span>
|
||||
<i class="{{ pasteItem.icon }}" aria-hidden="true"></i>
|
||||
@@ -28,12 +28,12 @@
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="umb-overlay__section-header" ng-if="model.pasteItems.length > 0 && (model.availableItems | filter:searchTerm).length > 0">
|
||||
<div class="umb-overlay__section-header" ng-if="model.pasteItems.length > 0 && (model.availableItems | filter:filter.searchTerm).length > 0">
|
||||
<h5><localize key="content_createEmpty">Create new</localize></h5>
|
||||
</div>
|
||||
|
||||
<ul class="umb-card-grid" ng-class="{'-three-in-row': model.availableItems.length < 7, '-four-in-row': model.availableItems.length >= 7}">
|
||||
<li ng-repeat="availableItem in model.availableItems | compareArrays:model.selectedItems:'alias' | orderBy:model.orderBy | filter:searchTerm">
|
||||
<li ng-repeat="availableItem in model.availableItems | compareArrays:model.selectedItems:'alias' | orderBy:model.orderBy | filter:filter.searchTerm">
|
||||
<button type="button" class="umb-card-grid-item btn-reset" ng-click="selectItem(availableItem)">
|
||||
<span ng-mouseover="showTooltip(availableItem, $event)" ng-mouseleave="hideTooltip()">
|
||||
<i class="{{ availableItem.icon }}" aria-hidden="true"></i>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="umb-node-preview" ng-class="{'umb-node-preview--sortable': sortable, 'umb-node-preview--unpublished': published === false }">
|
||||
<div class="flex"> <!-- div keeps icon and nodename from wrapping -->
|
||||
<umb-icon ng-if="icon" icon="{{icon}}" class="umb-node-preview__icon"></umb-icon>
|
||||
<umb-icon ng-if="icon" icon="{{icon}}" class="umb-node-preview__icon {{icon}}"></umb-icon>
|
||||
|
||||
<div class="umb-node-preview__content">
|
||||
|
||||
|
||||
Reference in New Issue
Block a user