Merge branch 'v8/contrib' into pr/5917
This commit is contained in:
@@ -28,15 +28,5 @@ namespace Umbraco.Web.Actions
|
||||
.WhereNotNull()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
internal IReadOnlyList<IAction> FromEntityPermission(EntityPermission entityPermission)
|
||||
{
|
||||
var actions = this.ToArray(); // no worry: internally, it's already an array
|
||||
return entityPermission.AssignedPermissions
|
||||
.Where(x => x.Length == 1)
|
||||
.SelectMany(x => actions.Where(y => y.Letter == x[0]))
|
||||
.WhereNotNull()
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
@@ -51,7 +52,10 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
foreach (var payload in payloads.Where(x => x.Id != default))
|
||||
{
|
||||
//By INT Id
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent>(payload.Id));
|
||||
//By GUID Key
|
||||
isolatedCache.Clear(RepositoryCacheKeys.GetKey<IContent>(payload.Key));
|
||||
|
||||
_idkMap.ClearCache(payload.Id);
|
||||
|
||||
@@ -97,13 +101,7 @@ namespace Umbraco.Web.Cache
|
||||
//if (Suspendable.PageCacheRefresher.CanUpdateDocumentCache)
|
||||
// ...
|
||||
|
||||
_publishedSnapshotService.Notify(payloads, out _, out var publishedChanged);
|
||||
|
||||
if (payloads.Any(x => x.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) || publishedChanged)
|
||||
{
|
||||
// when a public version changes
|
||||
AppCaches.ClearPartialViewCache();
|
||||
}
|
||||
NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, payloads);
|
||||
|
||||
base.Refresh(payloads);
|
||||
}
|
||||
@@ -111,40 +109,54 @@ namespace Umbraco.Web.Cache
|
||||
// these events should never trigger
|
||||
// everything should be PAYLOAD/JSON
|
||||
|
||||
public override void RefreshAll()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void RefreshAll() => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Refresh(int id) => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(Guid id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Refresh(Guid id) => throw new NotSupportedException();
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Remove(int id) => throw new NotSupportedException();
|
||||
|
||||
#endregion
|
||||
|
||||
#region Json
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes the publish snapshot service and if there are published changes ensures that partial view caches are refreshed too
|
||||
/// </summary>
|
||||
/// <param name="service"></param>
|
||||
/// <param name="appCaches"></param>
|
||||
/// <param name="payloads"></param>
|
||||
internal static void NotifyPublishedSnapshotService(IPublishedSnapshotService service, AppCaches appCaches, JsonPayload[] payloads)
|
||||
{
|
||||
service.Notify(payloads, out _, out var publishedChanged);
|
||||
|
||||
if (payloads.Any(x => x.ChangeTypes.HasType(TreeChangeTypes.RefreshAll)) || publishedChanged)
|
||||
{
|
||||
// when a public version changes
|
||||
appCaches.ClearPartialViewCache();
|
||||
}
|
||||
}
|
||||
|
||||
public class JsonPayload
|
||||
{
|
||||
[Obsolete("Use the constructor specifying a GUID instead, using this constructor will result in not refreshing all caches")]
|
||||
public JsonPayload(int id, TreeChangeTypes changeTypes)
|
||||
{
|
||||
Id = id;
|
||||
ChangeTypes = changeTypes;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
[JsonConstructor]
|
||||
public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes)
|
||||
{
|
||||
Id = id;
|
||||
Key = key;
|
||||
ChangeTypes = changeTypes;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
public Guid? Key { get; }
|
||||
public TreeChangeTypes ChangeTypes { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Changes;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
@@ -86,11 +84,8 @@ namespace Umbraco.Web.Cache
|
||||
// don't try to be clever - refresh all
|
||||
MemberCacheRefresher.RefreshMemberTypes(AppCaches);
|
||||
|
||||
// we have to refresh models before we notify the published snapshot
|
||||
// service of changes, else factories may try to rebuild models while
|
||||
// we are using the database to load content into caches
|
||||
|
||||
_publishedModelFactory.WithSafeLiveFactory(() =>
|
||||
// refresh the models and cache
|
||||
_publishedModelFactory.WithSafeLiveFactoryReset(() =>
|
||||
_publishedSnapshotService.Notify(payloads));
|
||||
|
||||
// now we can trigger the event
|
||||
|
||||
@@ -62,11 +62,9 @@ namespace Umbraco.Web.Cache
|
||||
TagsValueConverter.ClearCaches();
|
||||
SliderValueConverter.ClearCaches();
|
||||
|
||||
// we have to refresh models before we notify the published snapshot
|
||||
// service of changes, else factories may try to rebuild models while
|
||||
// we are using the database to load content into caches
|
||||
// refresh the models and cache
|
||||
|
||||
_publishedModelFactory.WithSafeLiveFactory(() =>
|
||||
_publishedModelFactory.WithSafeLiveFactoryReset(() =>
|
||||
_publishedSnapshotService.Notify(payloads));
|
||||
|
||||
base.Refresh(payloads);
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public static void RefreshAllContentCache(this DistributedCache dc)
|
||||
{
|
||||
var payloads = new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) };
|
||||
var payloads = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
|
||||
// note: refresh all content cache does refresh content types too
|
||||
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
|
||||
@@ -117,7 +117,7 @@ namespace Umbraco.Web.Cache
|
||||
if (changes.Length == 0) return;
|
||||
|
||||
var payloads = changes
|
||||
.Select(x => new ContentCacheRefresher.JsonPayload(x.Item.Id, x.ChangeTypes));
|
||||
.Select(x => new ContentCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(ContentCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
@@ -157,7 +157,7 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public static void RefreshAllMediaCache(this DistributedCache dc)
|
||||
{
|
||||
var payloads = new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) };
|
||||
var payloads = new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
|
||||
// note: refresh all media cache does refresh content types too
|
||||
dc.RefreshByPayload(MediaCacheRefresher.UniqueId, payloads);
|
||||
@@ -168,7 +168,7 @@ namespace Umbraco.Web.Cache
|
||||
if (changes.Length == 0) return;
|
||||
|
||||
var payloads = changes
|
||||
.Select(x => new MediaCacheRefresher.JsonPayload(x.Item.Id, x.ChangeTypes));
|
||||
.Select(x => new MediaCacheRefresher.JsonPayload(x.Item.Id, x.Item.Key, x.ChangeTypes));
|
||||
|
||||
dc.RefreshByPayload(MediaCacheRefresher.UniqueId, payloads);
|
||||
}
|
||||
@@ -266,13 +266,21 @@ namespace Umbraco.Web.Cache
|
||||
public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language)
|
||||
{
|
||||
if (language == null) return;
|
||||
dc.Refresh(LanguageCacheRefresher.UniqueId, language.Id);
|
||||
|
||||
var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode,
|
||||
language.WasPropertyDirty(nameof(ILanguage.IsoCode))
|
||||
? LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture
|
||||
: LanguageCacheRefresher.JsonPayload.LanguageChangeType.Update);
|
||||
|
||||
dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
|
||||
}
|
||||
|
||||
public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language)
|
||||
{
|
||||
if (language == null) return;
|
||||
dc.Remove(LanguageCacheRefresher.UniqueId, language.Id);
|
||||
|
||||
var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode, LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove);
|
||||
dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -47,25 +47,13 @@ namespace Umbraco.Web.Cache
|
||||
// these events should never trigger
|
||||
// everything should be PAYLOAD/JSON
|
||||
|
||||
public override void RefreshAll()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void RefreshAll() => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Refresh(int id) => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(Guid id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Refresh(Guid id) => throw new NotSupportedException();
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override void Remove(int id) => throw new NotSupportedException();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -5,16 +5,18 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Changes;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
using static Umbraco.Web.Cache.LanguageCacheRefresher.JsonPayload;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
{
|
||||
public sealed class LanguageCacheRefresher : CacheRefresherBase<LanguageCacheRefresher>
|
||||
public sealed class LanguageCacheRefresher : PayloadCacheRefresherBase<LanguageCacheRefresher, LanguageCacheRefresher.JsonPayload>
|
||||
|
||||
//CacheRefresherBase<LanguageCacheRefresher>
|
||||
{
|
||||
public LanguageCacheRefresher(AppCaches appCaches, IPublishedSnapshotService publishedSnapshotService, IDomainService domainService)
|
||||
public LanguageCacheRefresher(AppCaches appCaches, IPublishedSnapshotService publishedSnapshotService)
|
||||
: base(appCaches)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_domainService = domainService;
|
||||
}
|
||||
|
||||
#region Define
|
||||
@@ -23,7 +25,6 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public static readonly Guid UniqueId = Guid.Parse("3E0F95D8-0BE5-44B8-8394-2B8750B62654");
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IDomainService _domainService;
|
||||
|
||||
public override Guid RefresherUniqueId => UniqueId;
|
||||
|
||||
@@ -33,41 +34,118 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
#region Refresher
|
||||
|
||||
public override void Refresh(int id)
|
||||
public override void Refresh(JsonPayload[] payloads)
|
||||
{
|
||||
if (payloads.Length == 0) return;
|
||||
|
||||
var clearDictionary = false;
|
||||
var clearContent = false;
|
||||
|
||||
//clear all no matter what type of payload
|
||||
ClearAllIsolatedCacheByEntityType<ILanguage>();
|
||||
RefreshDomains(id);
|
||||
base.Refresh(id);
|
||||
|
||||
foreach (var payload in payloads)
|
||||
{
|
||||
switch (payload.ChangeType)
|
||||
{
|
||||
case LanguageChangeType.Update:
|
||||
clearDictionary = true;
|
||||
break;
|
||||
case LanguageChangeType.Remove:
|
||||
case LanguageChangeType.ChangeCulture:
|
||||
clearDictionary = true;
|
||||
clearContent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (clearDictionary)
|
||||
{
|
||||
ClearAllIsolatedCacheByEntityType<IDictionaryItem>();
|
||||
}
|
||||
|
||||
//if this flag is set, we will tell the published snapshot service to refresh ALL content and evict ALL IContent items
|
||||
if (clearContent)
|
||||
{
|
||||
//clear all domain caches
|
||||
RefreshDomains();
|
||||
ContentCacheRefresher.RefreshContentTypes(AppCaches); // we need to evict all IContent items
|
||||
//now refresh all nucache
|
||||
var clearContentPayload = new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) };
|
||||
ContentCacheRefresher.NotifyPublishedSnapshotService(_publishedSnapshotService, AppCaches, clearContentPayload);
|
||||
}
|
||||
|
||||
// then trigger event
|
||||
base.Refresh(payloads);
|
||||
}
|
||||
|
||||
public override void Remove(int id)
|
||||
{
|
||||
ClearAllIsolatedCacheByEntityType<ILanguage>();
|
||||
//if a language is removed, then all dictionary cache needs to be removed
|
||||
ClearAllIsolatedCacheByEntityType<IDictionaryItem>();
|
||||
RefreshDomains(id);
|
||||
base.Remove(id);
|
||||
}
|
||||
// these events should never trigger
|
||||
// everything should be PAYLOAD/JSON
|
||||
|
||||
public override void RefreshAll() => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(int id) => throw new NotSupportedException();
|
||||
|
||||
public override void Refresh(Guid id) => throw new NotSupportedException();
|
||||
|
||||
public override void Remove(int id) => throw new NotSupportedException();
|
||||
|
||||
#endregion
|
||||
|
||||
private void RefreshDomains(int langId)
|
||||
/// <summary>
|
||||
/// Clears all domain caches
|
||||
/// </summary>
|
||||
private void RefreshDomains()
|
||||
{
|
||||
var assignedDomains = _domainService.GetAll(true).Where(x => x.LanguageId.HasValue && x.LanguageId.Value == langId).ToList();
|
||||
ClearAllIsolatedCacheByEntityType<IDomain>();
|
||||
|
||||
if (assignedDomains.Count > 0)
|
||||
// note: must do what's above FIRST else the repositories still have the old cached
|
||||
// content and when the PublishedCachesService is notified of changes it does not see
|
||||
// the new content...
|
||||
|
||||
var payloads = new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) };
|
||||
_publishedSnapshotService.Notify(payloads);
|
||||
}
|
||||
|
||||
#region Json
|
||||
|
||||
public class JsonPayload
|
||||
{
|
||||
public JsonPayload(int id, string isoCode, LanguageChangeType changeType)
|
||||
{
|
||||
// TODO: this is duplicating the logic in DomainCacheRefresher BUT we cannot inject that into this because it it not registered explicitly in the container,
|
||||
// and we cannot inject the CacheRefresherCollection since that would be a circular reference, so what is the best way to call directly in to the
|
||||
// DomainCacheRefresher?
|
||||
Id = id;
|
||||
IsoCode = isoCode;
|
||||
ChangeType = changeType;
|
||||
}
|
||||
|
||||
ClearAllIsolatedCacheByEntityType<IDomain>();
|
||||
// note: must do what's above FIRST else the repositories still have the old cached
|
||||
// content and when the PublishedCachesService is notified of changes it does not see
|
||||
// the new content...
|
||||
// notify
|
||||
_publishedSnapshotService.Notify(assignedDomains.Select(x => new DomainCacheRefresher.JsonPayload(x.Id, DomainChangeTypes.Remove)).ToArray());
|
||||
public int Id { get; }
|
||||
public string IsoCode { get; }
|
||||
public LanguageChangeType ChangeType { get; }
|
||||
|
||||
public enum LanguageChangeType
|
||||
{
|
||||
/// <summary>
|
||||
/// A new languages has been added
|
||||
/// </summary>
|
||||
Add = 0,
|
||||
|
||||
/// <summary>
|
||||
/// A language has been deleted
|
||||
/// </summary>
|
||||
Remove = 1,
|
||||
|
||||
/// <summary>
|
||||
/// A language has been updated - but it's culture remains the same
|
||||
/// </summary>
|
||||
Update = 2,
|
||||
|
||||
/// <summary>
|
||||
/// A language has been updated - it's culture has changed
|
||||
/// </summary>
|
||||
ChangeCulture = 3
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ namespace Umbraco.Web.Cache
|
||||
return new[]
|
||||
{
|
||||
CacheKeys.MacroContentCacheKey, // macro render cache
|
||||
CacheKeys.MacroFromAliasCacheKey, // lookup macro by alias
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace Umbraco.Web.Cache
|
||||
// it *was* done for each pathId but really that does not make sense
|
||||
// only need to do it for the current media
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia>(payload.Id));
|
||||
mediaCache.Result.Clear(RepositoryCacheKeys.GetKey<IMedia>(payload.Key));
|
||||
|
||||
// remove those that are in the branch
|
||||
if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.RefreshBranch | TreeChangeTypes.Remove))
|
||||
@@ -104,14 +105,15 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public class JsonPayload
|
||||
{
|
||||
public JsonPayload(int id, TreeChangeTypes changeTypes)
|
||||
public JsonPayload(int id, Guid? key, TreeChangeTypes changeTypes)
|
||||
{
|
||||
Id = id;
|
||||
Key = key;
|
||||
ChangeTypes = changeTypes;
|
||||
}
|
||||
|
||||
public int Id { get; }
|
||||
|
||||
public Guid? Key { get; }
|
||||
public TreeChangeTypes ChangeTypes { get; }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.Cache
|
||||
@@ -8,11 +9,13 @@ namespace Umbraco.Web.Cache
|
||||
public sealed class TemplateCacheRefresher : CacheRefresherBase<TemplateCacheRefresher>
|
||||
{
|
||||
private readonly IdkMap _idkMap;
|
||||
private readonly IContentTypeCommonRepository _contentTypeCommonRepository;
|
||||
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap)
|
||||
public TemplateCacheRefresher(AppCaches appCaches, IdkMap idkMap, IContentTypeCommonRepository contentTypeCommonRepository)
|
||||
: base(appCaches)
|
||||
{
|
||||
_idkMap = idkMap;
|
||||
_contentTypeCommonRepository = contentTypeCommonRepository;
|
||||
}
|
||||
|
||||
#region Define
|
||||
@@ -45,6 +48,7 @@ namespace Umbraco.Web.Cache
|
||||
// it has an associated template.
|
||||
ClearAllIsolatedCacheByEntityType<IContent>();
|
||||
ClearAllIsolatedCacheByEntityType<IContentType>();
|
||||
_contentTypeCommonRepository.ClearCache();
|
||||
|
||||
base.Remove(id);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Compose;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Changes;
|
||||
using Umbraco.Core.Sync;
|
||||
@@ -58,8 +54,8 @@ namespace Umbraco.Web.Compose
|
||||
// note: refresh all content & media caches does refresh content types too
|
||||
var svc = Current.PublishedSnapshotService;
|
||||
svc.Notify(new[] { new DomainCacheRefresher.JsonPayload(0, DomainChangeTypes.RefreshAll) });
|
||||
svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _, out _);
|
||||
svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, TreeChangeTypes.RefreshAll) }, out _);
|
||||
svc.Notify(new[] { new ContentCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _, out _);
|
||||
svc.Notify(new[] { new MediaCacheRefresher.JsonPayload(0, null, TreeChangeTypes.RefreshAll) }, out _);
|
||||
},
|
||||
|
||||
//rebuild indexes if the server is not synced
|
||||
|
||||
@@ -204,14 +204,15 @@ namespace Umbraco.Web.Compose
|
||||
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));
|
||||
if (siteUri == null)
|
||||
{
|
||||
_logger.Warn(typeof(Notifier), "Notifications can not be sent, no site url is set (might be during boot process?)");
|
||||
return;
|
||||
}
|
||||
|
||||
//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,
|
||||
|
||||
@@ -182,6 +182,8 @@ namespace Umbraco.Web.Composing
|
||||
|
||||
public static DataEditorCollection DataEditors => CoreCurrent.DataEditors;
|
||||
|
||||
public static DataValueReferenceFactoryCollection DataValueReferenceFactories => CoreCurrent.DataValueReferenceFactories;
|
||||
|
||||
public static PropertyEditorCollection PropertyEditors => CoreCurrent.PropertyEditors;
|
||||
|
||||
public static ParameterEditorCollection ParameterEditors => CoreCurrent.ParameterEditors;
|
||||
|
||||
@@ -14,6 +14,7 @@ namespace Umbraco.Web.ContentApps
|
||||
|
||||
private ContentApp _contentApp;
|
||||
private ContentApp _mediaApp;
|
||||
private ContentApp _memberApp;
|
||||
|
||||
public ContentApp GetContentAppFor(object o, IEnumerable<IReadOnlyUserGroup> userGroups)
|
||||
{
|
||||
@@ -45,6 +46,16 @@ namespace Umbraco.Web.ContentApps
|
||||
case IMedia _:
|
||||
return null;
|
||||
|
||||
case IMember _:
|
||||
return _memberApp ?? (_memberApp = new ContentApp
|
||||
{
|
||||
Alias = "umbContent",
|
||||
Name = "Content",
|
||||
Icon = Constants.Icons.Content,
|
||||
View = "views/member/apps/content/content.html",
|
||||
Weight = Weight
|
||||
});
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Object type {o.GetType()} is not supported here.");
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Umbraco.Web.ContentApps
|
||||
|
||||
private ContentApp _contentApp;
|
||||
private ContentApp _mediaApp;
|
||||
private ContentApp _memberApp;
|
||||
|
||||
public ContentApp GetContentAppFor(object o, IEnumerable<IReadOnlyUserGroup> userGroups)
|
||||
{
|
||||
@@ -37,6 +38,15 @@ namespace Umbraco.Web.ContentApps
|
||||
View = "views/media/apps/info/info.html",
|
||||
Weight = Weight
|
||||
});
|
||||
case IMember _:
|
||||
return _memberApp ?? (_memberApp = new ContentApp
|
||||
{
|
||||
Alias = "umbInfo",
|
||||
Name = "Info",
|
||||
Icon = "icon-info",
|
||||
View = "views/member/apps/info/info.html",
|
||||
Weight = Weight
|
||||
});
|
||||
|
||||
default:
|
||||
throw new NotSupportedException($"Object type {o.GetType()} is not supported here.");
|
||||
|
||||
@@ -45,6 +45,8 @@ namespace Umbraco.Web.ContentApps
|
||||
entityType = "media";
|
||||
dtdId = Core.Constants.DataTypes.DefaultMediaListView;
|
||||
break;
|
||||
case IMember member:
|
||||
return null;
|
||||
default:
|
||||
throw new NotSupportedException($"Object type {o.GetType()} is not supported here.");
|
||||
}
|
||||
@@ -100,6 +102,15 @@ namespace Umbraco.Web.ContentApps
|
||||
if (configTabName != null && String.IsNullOrWhiteSpace(configTabName.ToString()) == false)
|
||||
contentApp.Name = configTabName.ToString();
|
||||
}
|
||||
|
||||
//Override Icon if icon is provided
|
||||
if (listViewConfig.ContainsKey("icon"))
|
||||
{
|
||||
var configIcon = listViewConfig["icon"];
|
||||
if (configIcon != null && String.IsNullOrWhiteSpace(configIcon.ToString()) == false)
|
||||
contentApp.Icon = configIcon.ToString();
|
||||
}
|
||||
|
||||
// if the list view is configured to show umbContent first, update the list view content app weight accordingly
|
||||
if(listViewConfig.ContainsKey("showContentFirst") &&
|
||||
listViewConfig["showContentFirst"]?.ToString().TryConvertTo<bool>().Result == true)
|
||||
|
||||
@@ -122,7 +122,20 @@ namespace Umbraco.Web.Dictionary
|
||||
//ensure it's stored/retrieved from request cache
|
||||
//NOTE: This is no longer necessary since these are cached at the runtime level, but we can leave it here for now.
|
||||
return _requestCache.GetCacheItem<ILanguage>(typeof (DefaultCultureDictionary).Name + "Culture" + Culture.Name,
|
||||
() => _localizationService.GetLanguageByIsoCode(Culture.Name));
|
||||
() => {
|
||||
// find a language that matches the current culture or any of its parent cultures
|
||||
var culture = Culture;
|
||||
while(culture != CultureInfo.InvariantCulture)
|
||||
{
|
||||
var language = _localizationService.GetLanguageByIsoCode(culture.Name);
|
||||
if(language != null)
|
||||
{
|
||||
return language;
|
||||
}
|
||||
culture = culture.Parent;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[StatusCodeResult(System.Net.HttpStatusCode.ServiceUnavailable)]
|
||||
public async Task<ActionResult> AuthorizeUpgrade()
|
||||
{
|
||||
return await RenderDefaultOrProcessExternalLoginAsync(
|
||||
@@ -189,7 +190,7 @@ namespace Umbraco.Web.Editors
|
||||
.ToDictionary(pv => pv.Key, pv =>
|
||||
pv.ToDictionary(pve => pve.valueAlias, pve => pve.value));
|
||||
|
||||
return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.Indented };
|
||||
return new JsonNetResult { Data = nestedDictionary, Formatting = Formatting.None };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -236,7 +237,7 @@ namespace Umbraco.Web.Editors
|
||||
GetAssetList,
|
||||
new TimeSpan(0, 2, 0));
|
||||
|
||||
return new JsonNetResult { Data = result, Formatting = Formatting.Indented };
|
||||
return new JsonNetResult { Data = result, Formatting = Formatting.None };
|
||||
}
|
||||
|
||||
[UmbracoAuthorize(Order = 0)]
|
||||
@@ -244,7 +245,7 @@ namespace Umbraco.Web.Editors
|
||||
public JsonNetResult GetGridConfig()
|
||||
{
|
||||
var gridConfig = Current.Configs.Grids();
|
||||
return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.Indented };
|
||||
return new JsonNetResult { Data = gridConfig.EditorsConfig.Editors, Formatting = Formatting.None };
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -309,7 +309,11 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
"webProfilingBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<WebProfilingController>(
|
||||
controller => controller.GetStatus())
|
||||
}
|
||||
},
|
||||
{
|
||||
"tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TinyMceController>(
|
||||
controller => controller.UploadImage())
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -49,18 +49,15 @@ namespace Umbraco.Web.Editors.Binders
|
||||
{
|
||||
foreach (var variant in model.Variants)
|
||||
{
|
||||
if (variant.Culture.IsNullOrWhiteSpace())
|
||||
{
|
||||
//map the property dto collection (no culture is passed to the mapping context so it will be invariant)
|
||||
variant.PropertyCollectionDto = Current.Mapper.Map<ContentPropertyCollectionDto>(model.PersistedContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//map the property dto collection with the culture of the current variant
|
||||
variant.PropertyCollectionDto = Current.Mapper.Map<ContentPropertyCollectionDto>(
|
||||
model.PersistedContent,
|
||||
context => context.SetCulture(variant.Culture));
|
||||
}
|
||||
//map the property dto collection with the culture of the current variant
|
||||
variant.PropertyCollectionDto = Current.Mapper.Map<ContentPropertyCollectionDto>(
|
||||
model.PersistedContent,
|
||||
context =>
|
||||
{
|
||||
// either of these may be null and that is ok, if it's invariant they will be null which is what is expected
|
||||
context.SetCulture(variant.Culture);
|
||||
context.SetSegment(variant.Segment);
|
||||
});
|
||||
|
||||
//now map all of the saved values to the dto
|
||||
_modelBinderHelper.MapPropertyValuesFromSaved(variant, variant.PropertyCollectionDto);
|
||||
@@ -87,6 +84,5 @@ namespace Umbraco.Web.Editors.Binders
|
||||
model.ParentId,
|
||||
contentType);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,7 +303,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content json for the content id
|
||||
/// Gets the content json for the content guid
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
@@ -323,7 +323,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content json for the content id
|
||||
/// Gets the content json for the content udi
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
@@ -341,7 +341,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty content item for the
|
||||
/// Gets an empty content item for the document type.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias"></param>
|
||||
/// <param name="parentId"></param>
|
||||
@@ -1679,9 +1679,9 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(response);
|
||||
}
|
||||
|
||||
var permission = Services.UserService.GetPermissions(Security.CurrentUser, node.Path);
|
||||
var assignedPermissions = Services.UserService.GetAssignedPermissions(Security.CurrentUser, node.Id);
|
||||
|
||||
if (permission.AssignedPermissions.Contains(ActionAssignDomain.ActionLetter.ToString(), StringComparer.Ordinal) == false)
|
||||
if (assignedPermissions.Contains(ActionAssignDomain.ActionLetter.ToString(), StringComparer.Ordinal) == false)
|
||||
{
|
||||
var response = Request.CreateResponse(HttpStatusCode.BadRequest);
|
||||
response.Content = new StringContent("You do not have permission to assign domains on that node.");
|
||||
@@ -1836,8 +1836,13 @@ namespace Umbraco.Web.Editors
|
||||
/// <param name="contentSave"></param>
|
||||
private void MapValuesForPersistence(ContentItemSave contentSave)
|
||||
{
|
||||
// inline method to determine if a property type varies
|
||||
bool Varies(Property property) => property.PropertyType.VariesByCulture();
|
||||
// inline method to determine the culture and segment to persist the property
|
||||
(string culture, string segment) PropertyCultureAndSegment(Property property, ContentVariantSave variant)
|
||||
{
|
||||
var culture = property.PropertyType.VariesByCulture() ? variant.Culture : null;
|
||||
var segment = property.PropertyType.VariesBySegment() ? variant.Segment : null;
|
||||
return (culture, segment);
|
||||
}
|
||||
|
||||
var variantIndex = 0;
|
||||
|
||||
@@ -1876,8 +1881,18 @@ namespace Umbraco.Web.Editors
|
||||
MapPropertyValuesForPersistence<IContent, ContentItemSave>(
|
||||
contentSave,
|
||||
propertyCollection,
|
||||
(save, property) => Varies(property) ? property.GetValue(variant.Culture) : property.GetValue(), //get prop val
|
||||
(save, property, v) => { if (Varies(property)) property.SetValue(v, variant.Culture); else property.SetValue(v); }, //set prop val
|
||||
(save, property) =>
|
||||
{
|
||||
// Get property value
|
||||
(var culture, var segment) = PropertyCultureAndSegment(property, variant);
|
||||
return property.GetValue(culture, segment);
|
||||
},
|
||||
(save, property, v) =>
|
||||
{
|
||||
// Set property value
|
||||
(var culture, var segment) = PropertyCultureAndSegment(property, variant);
|
||||
property.SetValue(v, culture, segment);
|
||||
},
|
||||
variant.Culture);
|
||||
|
||||
variantIndex++;
|
||||
@@ -1926,10 +1941,8 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
if (model.ParentId < 0)
|
||||
{
|
||||
//cannot move if the content item is not allowed at the root unless there are
|
||||
//none allowed at root (in which case all should be allowed at root)
|
||||
var contentTypeService = Services.ContentTypeService;
|
||||
if (toMove.ContentType.AllowedAsRoot == false && contentTypeService.GetAll().Any(ct => ct.AllowedAsRoot))
|
||||
//cannot move if the content item is not allowed at the root
|
||||
if (toMove.ContentType.AllowedAsRoot == false)
|
||||
{
|
||||
throw new HttpResponseException(
|
||||
Request.CreateNotificationValidationErrorResponse(
|
||||
@@ -2297,7 +2310,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var entry = Services.PublicAccessService.GetEntryForContent(content);
|
||||
if (entry == null)
|
||||
if (entry == null || entry.ProtectedNodeId != content.Id)
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.OK);
|
||||
}
|
||||
@@ -2379,7 +2392,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var entry = Services.PublicAccessService.GetEntryForContent(content);
|
||||
|
||||
if (entry == null)
|
||||
if (entry == null || entry.ProtectedNodeId != content.Id)
|
||||
{
|
||||
entry = new PublicAccessEntry(content, loginPage, errorPage, new List<PublicAccessRule>());
|
||||
|
||||
|
||||
@@ -17,9 +17,11 @@ using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Editors;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Packaging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
@@ -46,6 +48,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
private readonly IEntityXmlSerializer _serializer;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
|
||||
public ContentTypeController(IEntityXmlSerializer serializer,
|
||||
ICultureDictionaryFactory cultureDictionaryFactory,
|
||||
@@ -53,11 +56,13 @@ namespace Umbraco.Web.Editors
|
||||
IUmbracoContextAccessor umbracoContextAccessor,
|
||||
ISqlContext sqlContext, PropertyEditorCollection propertyEditors,
|
||||
ServiceContext services, AppCaches appCaches,
|
||||
IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper)
|
||||
IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper,
|
||||
IScopeProvider scopeProvider)
|
||||
: base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper)
|
||||
{
|
||||
_serializer = serializer;
|
||||
_propertyEditors = propertyEditors;
|
||||
_scopeProvider = scopeProvider;
|
||||
}
|
||||
|
||||
public int GetCount()
|
||||
@@ -65,6 +70,13 @@ namespace Umbraco.Web.Editors
|
||||
return Services.ContentTypeService.Count();
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.DocumentTypes)]
|
||||
public bool HasContentNodes(int id)
|
||||
{
|
||||
return Services.ContentTypeService.HasContentNodes(id);
|
||||
}
|
||||
|
||||
public DocumentTypeDisplay GetById(int id)
|
||||
{
|
||||
var ct = Services.ContentTypeService.Get(id);
|
||||
@@ -132,7 +144,7 @@ namespace Umbraco.Web.Editors
|
||||
[HttpPost]
|
||||
public HttpResponseMessage GetAvailableCompositeContentTypes(GetAvailableCompositionsFilter filter)
|
||||
{
|
||||
var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes)
|
||||
var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.DocumentType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement)
|
||||
.Select(x => new
|
||||
{
|
||||
contentType = x.Item1,
|
||||
@@ -409,11 +421,7 @@ namespace Umbraco.Web.Editors
|
||||
IEnumerable<IContentType> types;
|
||||
if (contentId == Constants.System.Root)
|
||||
{
|
||||
var allContentTypes = Services.ContentTypeService.GetAll().ToList();
|
||||
bool AllowedAsRoot(IContentType x) => x.AllowedAsRoot;
|
||||
types = allContentTypes.Any(AllowedAsRoot)
|
||||
? allContentTypes.Where(AllowedAsRoot).ToList()
|
||||
: allContentTypes;
|
||||
types = Services.ContentTypeService.GetAll().Where(x => x.AllowedAsRoot).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -424,11 +432,11 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var contentType = Services.ContentTypeBaseServices.GetContentTypeOf(contentItem);
|
||||
var ids = contentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray();
|
||||
var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray();
|
||||
|
||||
if (ids.Any() == false) return Enumerable.Empty<ContentTypeBasic>();
|
||||
|
||||
types = Services.ContentTypeService.GetAll(ids).ToList();
|
||||
types = Services.ContentTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList();
|
||||
}
|
||||
|
||||
var basics = types.Where(type => type.IsElement == false).Select(Mapper.Map<IContentType, ContentTypeBasic>).ToList();
|
||||
@@ -451,7 +459,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
}
|
||||
|
||||
return basics;
|
||||
return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -520,7 +528,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService,
|
||||
Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors);
|
||||
Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors, _scopeProvider);
|
||||
|
||||
var xd = new XmlDocument {XmlResolver = null};
|
||||
xd.Load(filePath);
|
||||
|
||||
@@ -53,11 +53,13 @@ namespace Umbraco.Web.Editors
|
||||
/// be looked up via the db, they need to be passed in.
|
||||
/// </param>
|
||||
/// <param name="contentTypeId"></param>
|
||||
/// <param name="isElement">Wether the composite content types should be applicable for an element type</param>
|
||||
/// <returns></returns>
|
||||
protected IEnumerable<Tuple<EntityBasic, bool>> PerformGetAvailableCompositeContentTypes(int contentTypeId,
|
||||
UmbracoObjectTypes type,
|
||||
string[] filterContentTypes,
|
||||
string[] filterPropertyTypes)
|
||||
string[] filterPropertyTypes,
|
||||
bool isElement)
|
||||
{
|
||||
IContentTypeComposition source = null;
|
||||
|
||||
@@ -98,7 +100,7 @@ namespace Umbraco.Web.Editors
|
||||
throw new ArgumentOutOfRangeException("The entity type was not a content type");
|
||||
}
|
||||
|
||||
var availableCompositions = Services.ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes, filterPropertyTypes);
|
||||
var availableCompositions = Services.ContentTypeService.GetAvailableCompositeContentTypes(source, allContentTypes, filterContentTypes, filterPropertyTypes, isElement);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace Umbraco.Web.Editors
|
||||
break;
|
||||
|
||||
case "OUR":
|
||||
urlPrefix = "https://our.umbraco.org/";
|
||||
urlPrefix = "https://our.umbraco.com/";
|
||||
break;
|
||||
|
||||
case "COM":
|
||||
|
||||
@@ -20,6 +20,7 @@ using Umbraco.Web.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using System.Web.Http.Controllers;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -34,6 +35,7 @@ namespace Umbraco.Web.Editors
|
||||
[PluginController("UmbracoApi")]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.DataTypes, Constants.Trees.DocumentTypes, Constants.Trees.MediaTypes, Constants.Trees.MemberTypes)]
|
||||
[EnableOverrideAuthorization]
|
||||
[DataTypeControllerConfiguration]
|
||||
public class DataTypeController : BackOfficeNotificationsController
|
||||
{
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
@@ -44,6 +46,19 @@ namespace Umbraco.Web.Editors
|
||||
_propertyEditors = propertyEditors;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures this controller with a custom action selector
|
||||
/// </summary>
|
||||
private class DataTypeControllerConfigurationAttribute : Attribute, IControllerConfiguration
|
||||
{
|
||||
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
|
||||
{
|
||||
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets data type by name
|
||||
/// </summary>
|
||||
@@ -70,6 +85,40 @@ namespace Umbraco.Web.Editors
|
||||
return Mapper.Map<IDataType, DataTypeDisplay>(dataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the datatype json for the datatype guid
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public DataTypeDisplay GetById(Guid id)
|
||||
{
|
||||
var dataType = Services.DataTypeService.GetDataType(id);
|
||||
if (dataType == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
return Mapper.Map<IDataType, DataTypeDisplay>(dataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the datatype json for the datatype udi
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public DataTypeDisplay GetById(Udi id)
|
||||
{
|
||||
var guidUdi = id as GuidUdi;
|
||||
if (guidUdi == null)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
var dataType = Services.DataTypeService.GetDataType(guidUdi.Guid);
|
||||
if (dataType == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
return Mapper.Map<IDataType, DataTypeDisplay>(dataType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a data type with a given ID
|
||||
/// </summary>
|
||||
@@ -283,6 +332,60 @@ namespace Umbraco.Web.Editors
|
||||
: Request.CreateNotificationValidationErrorResponse(result.Exception.Message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the references (usages) for the data type
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <returns></returns>
|
||||
public DataTypeReferences GetReferences(int id)
|
||||
{
|
||||
var result = new DataTypeReferences();
|
||||
var usages = Services.DataTypeService.GetReferences(id);
|
||||
|
||||
foreach(var groupOfEntityType in usages.GroupBy(x => x.Key.EntityType))
|
||||
{
|
||||
//get all the GUIDs for the content types to find
|
||||
var guidsAndPropertyAliases = groupOfEntityType.ToDictionary(i => ((GuidUdi)i.Key).Guid, i => i.Value);
|
||||
|
||||
if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType))
|
||||
result.DocumentTypes = GetContentTypeUsages(Services.ContentTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MediaType))
|
||||
result.MediaTypes = GetContentTypeUsages(Services.MediaTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
else if (groupOfEntityType.Key == ObjectTypes.GetUdiType(UmbracoObjectTypes.MemberType))
|
||||
result.MemberTypes = GetContentTypeUsages(Services.MemberTypeService.GetAll(guidsAndPropertyAliases.Keys), guidsAndPropertyAliases);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Maps the found content types and usages to the resulting model
|
||||
/// </summary>
|
||||
/// <param name="cts"></param>
|
||||
/// <param name="usages"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<DataTypeReferences.ContentTypeReferences> GetContentTypeUsages(
|
||||
IEnumerable<IContentTypeBase> cts,
|
||||
IReadOnlyDictionary<Guid, IEnumerable<string>> usages)
|
||||
{
|
||||
return cts.Select(x => new DataTypeReferences.ContentTypeReferences
|
||||
{
|
||||
Id = x.Id,
|
||||
Key = x.Key,
|
||||
Alias = x.Alias,
|
||||
Icon = x.Icon,
|
||||
Name = x.Name,
|
||||
Udi = new GuidUdi(ObjectTypes.GetUdiType(UmbracoObjectTypes.DocumentType), x.Key),
|
||||
//only select matching properties
|
||||
Properties = x.PropertyTypes.Where(p => usages[x.Key].InvariantContains(p.Alias))
|
||||
.Select(p => new DataTypeReferences.ContentTypeReferences.PropertyTypeReferences
|
||||
{
|
||||
Alias = p.Alias,
|
||||
Name = p.Name
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#region ReadOnly actions to return basic data - allow access for: content ,media, members, settings, developer
|
||||
/// <summary>
|
||||
/// Gets the content json for all data types
|
||||
|
||||
@@ -6,7 +6,7 @@ using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
internal class EditorValidatorCollection : BuilderCollectionBase<IEditorValidator>
|
||||
public class EditorValidatorCollection : BuilderCollectionBase<IEditorValidator>
|
||||
{
|
||||
public EditorValidatorCollection(IEnumerable<IEditorValidator> items)
|
||||
: base(items)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
internal class EditorValidatorCollectionBuilder : LazyCollectionBuilderBase<EditorValidatorCollectionBuilder, EditorValidatorCollection, IEditorValidator>
|
||||
public class EditorValidatorCollectionBuilder : LazyCollectionBuilderBase<EditorValidatorCollectionBuilder, EditorValidatorCollection, IEditorValidator>
|
||||
{
|
||||
protected override EditorValidatorCollectionBuilder This => this;
|
||||
}
|
||||
|
||||
@@ -206,22 +206,54 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of an entity
|
||||
/// </summary>
|
||||
/// <param name="udi">UDI of the entity to fetch URL for</param>
|
||||
/// <param name="culture">The culture to fetch the URL for</param>
|
||||
/// <returns>The URL or path to the item</returns>
|
||||
public HttpResponseMessage GetUrl(Udi udi, string culture = "*")
|
||||
{
|
||||
var intId = Services.EntityService.GetId(udi);
|
||||
if (!intId.Success)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
UmbracoEntityTypes entityType;
|
||||
switch(udi.EntityType)
|
||||
{
|
||||
case Constants.UdiEntityType.Document:
|
||||
entityType = UmbracoEntityTypes.Document;
|
||||
break;
|
||||
case Constants.UdiEntityType.Media:
|
||||
entityType = UmbracoEntityTypes.Media;
|
||||
break;
|
||||
case Constants.UdiEntityType.Member:
|
||||
entityType = UmbracoEntityTypes.Member;
|
||||
break;
|
||||
default:
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
return GetUrl(intId.Result, entityType, culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the url of an entity
|
||||
/// </summary>
|
||||
/// <param name="id">Int id of the entity to fetch URL for</param>
|
||||
/// <param name="type">The type of entity such as Document, Media, Member</param>
|
||||
/// <param name="culture">The culture to fetch the URL for</param>
|
||||
/// <returns>The URL or path to the item</returns>
|
||||
/// <remarks>
|
||||
/// We are not restricting this with security because there is no sensitive data
|
||||
/// </remarks>
|
||||
public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type)
|
||||
public HttpResponseMessage GetUrl(int id, UmbracoEntityTypes type, string culture = null)
|
||||
{
|
||||
culture = culture ?? ClientCulture();
|
||||
|
||||
var returnUrl = string.Empty;
|
||||
|
||||
if (type == UmbracoEntityTypes.Document)
|
||||
{
|
||||
var foundUrl = UmbracoContext.Url(id);
|
||||
var foundUrl = UmbracoContext.Url(id, culture);
|
||||
if (string.IsNullOrEmpty(foundUrl) == false && foundUrl != "#")
|
||||
{
|
||||
returnUrl = foundUrl;
|
||||
@@ -300,7 +332,9 @@ namespace Umbraco.Web.Editors
|
||||
[HttpGet]
|
||||
public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*")
|
||||
{
|
||||
var url = UmbracoContext.UrlProvider.GetUrl(id);
|
||||
culture = culture ?? ClientCulture();
|
||||
|
||||
var url = UmbracoContext.UrlProvider.GetUrl(id, culture: culture);
|
||||
var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture);
|
||||
return new UrlAndAnchors(url, anchorValues);
|
||||
}
|
||||
@@ -659,6 +693,9 @@ namespace Umbraco.Web.Editors
|
||||
if (pageSize <= 0)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
// re-normalize since NULL can be passed in
|
||||
filter = filter ?? string.Empty;
|
||||
|
||||
var objectType = ConvertToObjectType(type);
|
||||
if (objectType.HasValue)
|
||||
{
|
||||
|
||||
@@ -177,8 +177,12 @@ namespace Umbraco.Web.Editors
|
||||
var indexName = index.Name;
|
||||
|
||||
if (!(index is IIndexDiagnostics indexDiag))
|
||||
indexDiag = new GenericIndexDiagnostics(index);
|
||||
|
||||
{
|
||||
if (index is LuceneIndex luceneIndex)
|
||||
indexDiag = new LuceneIndexDiagnostics(luceneIndex, Logger);
|
||||
else
|
||||
indexDiag = new GenericIndexDiagnostics(index);
|
||||
}
|
||||
|
||||
var isHealth = indexDiag.IsHealthy();
|
||||
var properties = new Dictionary<string, object>
|
||||
|
||||
@@ -5,10 +5,10 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Editors.Filters
|
||||
@@ -43,8 +43,11 @@ namespace Umbraco.Web.Editors.Filters
|
||||
where TModelSave: IContentSave<TPersisted>
|
||||
where TModelWithProperties : IContentProperties<ContentPropertyBasic>
|
||||
{
|
||||
protected ContentModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
protected ContentModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor)
|
||||
{
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,6 +125,18 @@ namespace Umbraco.Web.Editors.Filters
|
||||
{
|
||||
var properties = modelWithProperties.Properties.ToDictionary(x => x.Alias, x => x);
|
||||
|
||||
// Retrieve default messages used for required and regex validatation. We'll replace these
|
||||
// if set with custom ones if they've been provided for a given property.
|
||||
var requiredDefaultMessages = new[]
|
||||
{
|
||||
_textService.Localize("validation", "invalidNull"),
|
||||
_textService.Localize("validation", "invalidEmpty")
|
||||
};
|
||||
var formatDefaultMessages = new[]
|
||||
{
|
||||
_textService.Localize("validation", "invalidPattern"),
|
||||
};
|
||||
|
||||
foreach (var p in dto.Properties)
|
||||
{
|
||||
var editor = p.PropertyEditor;
|
||||
@@ -141,7 +156,7 @@ namespace Umbraco.Web.Editors.Filters
|
||||
|
||||
var postedValue = postedProp.Value;
|
||||
|
||||
ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState);
|
||||
ValidatePropertyValue(model, modelWithProperties, editor, p, postedValue, modelState, requiredDefaultMessages, formatDefaultMessages);
|
||||
|
||||
}
|
||||
|
||||
@@ -157,22 +172,34 @@ namespace Umbraco.Web.Editors.Filters
|
||||
/// <param name="property"></param>
|
||||
/// <param name="postedValue"></param>
|
||||
/// <param name="modelState"></param>
|
||||
/// <param name="requiredDefaultMessages"></param>
|
||||
/// <param name="formatDefaultMessages"></param>
|
||||
protected virtual void ValidatePropertyValue(
|
||||
TModelSave model,
|
||||
TModelWithProperties modelWithProperties,
|
||||
IDataEditor editor,
|
||||
ContentPropertyDto property,
|
||||
object postedValue,
|
||||
ModelStateDictionary modelState)
|
||||
ModelStateDictionary modelState,
|
||||
string[] requiredDefaultMessages,
|
||||
string[] formatDefaultMessages)
|
||||
{
|
||||
// validate
|
||||
var valueEditor = editor.GetValueEditor(property.DataType.Configuration);
|
||||
foreach (var r in valueEditor.Validate(postedValue, property.IsRequired, property.ValidationRegExp))
|
||||
{
|
||||
// If we've got custom error messages, we'll replace the default ones that will have been applied in the call to Validate().
|
||||
if (property.IsRequired && !string.IsNullOrWhiteSpace(property.IsRequiredMessage) && requiredDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
r.ErrorMessage = property.IsRequiredMessage;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(property.ValidationRegExp) && !string.IsNullOrWhiteSpace(property.ValidationRegExpMessage) && formatDefaultMessages.Contains(r.ErrorMessage, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
r.ErrorMessage = property.ValidationRegExpMessage;
|
||||
}
|
||||
|
||||
modelState.AddPropertyError(r, property.Alias, property.Culture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Editors.Filters
|
||||
@@ -12,7 +10,7 @@ namespace Umbraco.Web.Editors.Filters
|
||||
/// </summary>
|
||||
internal class ContentSaveModelValidator : ContentModelValidator<IContent, ContentItemSave, ContentVariantSave>
|
||||
{
|
||||
public ContentSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
|
||||
public ContentSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor, textService)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,28 +23,30 @@ namespace Umbraco.Web.Editors.Filters
|
||||
/// </summary>
|
||||
internal sealed class ContentSaveValidationAttribute : ActionFilterAttribute
|
||||
{
|
||||
public ContentSaveValidationAttribute(): this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.ContentService, Current.Services.UserService, Current.Services.EntityService)
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
public ContentSaveValidationAttribute(): this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.ContentService, Current.Services.UserService, Current.Services.EntityService)
|
||||
{ }
|
||||
|
||||
public ContentSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IContentService contentService, IUserService userService, IEntityService entityService)
|
||||
public ContentSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IContentService contentService, IUserService userService, IEntityService entityService)
|
||||
{
|
||||
_logger = logger;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
|
||||
_userService = userService ?? throw new ArgumentNullException(nameof(userService));
|
||||
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
||||
}
|
||||
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IUserService _userService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
var model = (ContentItemSave)actionContext.ActionArguments["contentItem"];
|
||||
var contentItemValidator = new ContentSaveModelValidator(_logger, _umbracoContextAccessor);
|
||||
var contentItemValidator = new ContentSaveModelValidator(_logger, _umbracoContextAccessor, _textService);
|
||||
|
||||
if (!ValidateAtLeastOneVariantIsBeingSaved(model, actionContext)) return;
|
||||
if (!contentItemValidator.ValidateExistingContent(model, actionContext)) return;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System.Linq;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core;
|
||||
@@ -21,25 +20,27 @@ namespace Umbraco.Web.Editors.Filters
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IMediaService _mediaService;
|
||||
private readonly IEntityService _entityService;
|
||||
|
||||
public MediaItemSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MediaService, Current.Services.EntityService)
|
||||
public MediaItemSaveValidationAttribute() : this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MediaService, Current.Services.EntityService)
|
||||
{
|
||||
}
|
||||
|
||||
public MediaItemSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMediaService mediaService, IEntityService entityService)
|
||||
public MediaItemSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMediaService mediaService, IEntityService entityService)
|
||||
{
|
||||
_logger = logger;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_mediaService = mediaService;
|
||||
_entityService = entityService;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
_mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService));
|
||||
_entityService = entityService ?? throw new ArgumentNullException(nameof(entityService));
|
||||
}
|
||||
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
var model = (MediaItemSave)actionContext.ActionArguments["contentItem"];
|
||||
var contentItemValidator = new MediaSaveModelValidator(_logger, _umbracoContextAccessor);
|
||||
var contentItemValidator = new MediaSaveModelValidator(_logger, _umbracoContextAccessor, _textService);
|
||||
|
||||
if (ValidateUserAccess(model, actionContext))
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Editors.Filters
|
||||
@@ -9,8 +10,8 @@ namespace Umbraco.Web.Editors.Filters
|
||||
/// </summary>
|
||||
internal class MediaSaveModelValidator : ContentModelValidator<IMedia, MediaItemSave, IContentProperties<ContentPropertyBasic>>
|
||||
{
|
||||
public MediaSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor) : base(logger, umbracoContextAccessor)
|
||||
public MediaSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService) : base(logger, umbracoContextAccessor, textService)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,10 @@ namespace Umbraco.Web.Editors.Filters
|
||||
{
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
|
||||
public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService)
|
||||
: base(logger, umbracoContextAccessor)
|
||||
public MemberSaveModelValidator(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService)
|
||||
: base(logger, umbracoContextAccessor, textService)
|
||||
{
|
||||
_memberTypeService = memberTypeService;
|
||||
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Web.Http.Controllers;
|
||||
using System;
|
||||
using System.Web.Http.Controllers;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Services;
|
||||
@@ -14,23 +15,25 @@ namespace Umbraco.Web.Editors.Filters
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly IUmbracoContextAccessor _umbracoContextAccessor;
|
||||
private readonly ILocalizedTextService _textService;
|
||||
private readonly IMemberTypeService _memberTypeService;
|
||||
|
||||
public MemberSaveValidationAttribute()
|
||||
: this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.MemberTypeService)
|
||||
: this(Current.Logger, Current.UmbracoContextAccessor, Current.Services.TextService, Current.Services.MemberTypeService)
|
||||
{ }
|
||||
|
||||
public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, IMemberTypeService memberTypeService)
|
||||
public MemberSaveValidationAttribute(ILogger logger, IUmbracoContextAccessor umbracoContextAccessor, ILocalizedTextService textService, IMemberTypeService memberTypeService)
|
||||
{
|
||||
_logger = logger;
|
||||
_umbracoContextAccessor = umbracoContextAccessor;
|
||||
_memberTypeService = memberTypeService;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
_umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor));
|
||||
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
|
||||
_memberTypeService = memberTypeService ?? throw new ArgumentNullException(nameof(memberTypeService));
|
||||
}
|
||||
|
||||
public override void OnActionExecuting(HttpActionContext actionContext)
|
||||
{
|
||||
var model = (MemberSave)actionContext.ActionArguments["contentItem"];
|
||||
var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _memberTypeService);
|
||||
var contentItemValidator = new MemberSaveModelValidator(_logger, _umbracoContextAccessor, _textService, _memberTypeService);
|
||||
//now do each validation step
|
||||
if (contentItemValidator.ValidateExistingContent(model, actionContext))
|
||||
if (contentItemValidator.ValidateProperties(model, model, actionContext))
|
||||
|
||||
@@ -50,6 +50,14 @@ namespace Umbraco.Web.Editors.Filters
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
|
||||
return;
|
||||
}
|
||||
|
||||
if (persisted.Alias != userGroupSave.Alias && persisted.IsSystemUserGroup())
|
||||
{
|
||||
var message = $"User group with alias: {persisted.Alias} cannot be changed";
|
||||
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, message);
|
||||
return;
|
||||
}
|
||||
|
||||
//map the model to the persisted instance
|
||||
Mapper.Map(userGroupSave, persisted);
|
||||
break;
|
||||
|
||||
@@ -13,14 +13,23 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
var url = string.Format(baseUrl + "/Umbraco/Documentation/Lessons/GetContextHelpDocs?sectionAlias={0}&treeAlias={1}", section, tree);
|
||||
|
||||
if (_httpClient == null)
|
||||
_httpClient = new HttpClient();
|
||||
try
|
||||
{
|
||||
|
||||
//fetch dashboard json and parse to JObject
|
||||
var json = await _httpClient.GetStringAsync(url);
|
||||
var result = JsonConvert.DeserializeObject<List<HelpPage>>(json);
|
||||
if (result != null)
|
||||
return result;
|
||||
if (_httpClient == null)
|
||||
_httpClient = new HttpClient();
|
||||
|
||||
//fetch dashboard json and parse to JObject
|
||||
var json = await _httpClient.GetStringAsync(url);
|
||||
var result = JsonConvert.DeserializeObject<List<HelpPage>>(json);
|
||||
if (result != null)
|
||||
return result;
|
||||
|
||||
}
|
||||
catch (HttpRequestException rex)
|
||||
{
|
||||
Logger.Info(GetType(), $"Check your network connection, exception: {rex.Message}");
|
||||
}
|
||||
|
||||
return new List<HelpPage>();
|
||||
}
|
||||
|
||||
@@ -14,9 +14,7 @@ namespace Umbraco.Web.Editors
|
||||
// initialized with all IEditorValidator instances
|
||||
//
|
||||
// validation is used exclusively in ContentTypeControllerBase
|
||||
// the whole thing is internal at the moment, never released
|
||||
// and, there are no IEditorValidator implementation in Core
|
||||
// so... this all mechanism is basically useless
|
||||
// currently the only implementations are for Models Builder.
|
||||
|
||||
/// <summary>
|
||||
/// Provides a general object validator.
|
||||
|
||||
@@ -60,8 +60,27 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//redirect to ImageProcessor thumbnail with rnd generated from last modified time of original media file
|
||||
var response = Request.CreateResponse(HttpStatusCode.Found);
|
||||
var imageLastModified = _mediaFileSystem.GetLastModified(imagePath);
|
||||
response.Headers.Location = new Uri($"{imagePath}?rnd={imageLastModified:yyyyMMddHHmmss}&upscale=false&width={width}&animationprocessmode=first&mode=max", UriKind.Relative);
|
||||
|
||||
DateTimeOffset? imageLastModified = null;
|
||||
try
|
||||
{
|
||||
imageLastModified = _mediaFileSystem.GetLastModified(imagePath);
|
||||
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// if we get an exception here it's probably because the image path being requested is an image that doesn't exist
|
||||
// in the local media file system. This can happen if someone is storing an absolute path to an image online, which
|
||||
// is perfectly legal but in that case the media file system isn't going to resolve it.
|
||||
// so ignore and we won't set a last modified date.
|
||||
}
|
||||
|
||||
// TODO: When we abstract imaging for netcore, we are actually just going to be abstracting a URI builder for images, this
|
||||
// is one of those places where this can be used.
|
||||
|
||||
var rnd = imageLastModified.HasValue ? $"&rnd={imageLastModified:yyyyMMddHHmmss}" : string.Empty;
|
||||
|
||||
response.Headers.Location = new Uri($"{imagePath}?upscale=false&width={width}&animationprocessmode=first&mode=max{rnd}", UriKind.RelativeOrAbsolute);
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
@@ -99,24 +99,28 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
|
||||
// this is prone to race conditions but the service will not let us proceed anyways
|
||||
var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
|
||||
var existingByCulture = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
|
||||
|
||||
// the localization service might return the generic language even when queried for specific ones (e.g. "da" when queried for "da-DK")
|
||||
// - we need to handle that explicitly
|
||||
if (existing?.IsoCode != language.IsoCode)
|
||||
if (existingByCulture?.IsoCode != language.IsoCode)
|
||||
{
|
||||
existing = null;
|
||||
existingByCulture = null;
|
||||
}
|
||||
|
||||
if (existing != null && language.Id != existing.Id)
|
||||
if (existingByCulture != null && language.Id != existingByCulture.Id)
|
||||
{
|
||||
//someone is trying to create a language that already exist
|
||||
ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
|
||||
if (existing == null)
|
||||
var existingById = language.Id != default ? Services.LocalizationService.GetLanguageById(language.Id) : null;
|
||||
|
||||
if (existingById == null)
|
||||
{
|
||||
//Creating a new lang...
|
||||
|
||||
CultureInfo culture;
|
||||
try
|
||||
{
|
||||
@@ -141,38 +145,39 @@ namespace Umbraco.Web.Editors
|
||||
return Mapper.Map<Language>(newLang);
|
||||
}
|
||||
|
||||
existing.IsMandatory = language.IsMandatory;
|
||||
existingById.IsMandatory = language.IsMandatory;
|
||||
|
||||
// note that the service will prevent the default language from being "un-defaulted"
|
||||
// but does not hurt to test here - though the UI should prevent it too
|
||||
if (existing.IsDefault && !language.IsDefault)
|
||||
if (existingById.IsDefault && !language.IsDefault)
|
||||
{
|
||||
ModelState.AddModelError("IsDefault", "Cannot un-default the default language.");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
|
||||
existing.IsDefault = language.IsDefault;
|
||||
existing.FallbackLanguageId = language.FallbackLanguageId;
|
||||
existingById.IsDefault = language.IsDefault;
|
||||
existingById.FallbackLanguageId = language.FallbackLanguageId;
|
||||
existingById.IsoCode = language.IsoCode;
|
||||
|
||||
// modifying an existing language can create a fallback, verify
|
||||
// note that the service will check again, dealing with race conditions
|
||||
if (existing.FallbackLanguageId.HasValue)
|
||||
if (existingById.FallbackLanguageId.HasValue)
|
||||
{
|
||||
var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x);
|
||||
if (!languages.ContainsKey(existing.FallbackLanguageId.Value))
|
||||
if (!languages.ContainsKey(existingById.FallbackLanguageId.Value))
|
||||
{
|
||||
ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist.");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
if (CreatesCycle(existing, languages))
|
||||
if (CreatesCycle(existingById, languages))
|
||||
{
|
||||
ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path.");
|
||||
ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existingById.FallbackLanguageId.Value].IsoCode} would create a circular path.");
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
}
|
||||
|
||||
Services.LocalizationService.Save(existing);
|
||||
return Mapper.Map<Language>(existing);
|
||||
Services.LocalizationService.Save(existingById);
|
||||
return Mapper.Map<Language>(existingById);
|
||||
}
|
||||
|
||||
// see LocalizationService
|
||||
|
||||
@@ -130,5 +130,11 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
return _logViewer.DeleteSavedSearch(item.Name, item.Query);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public string GetLogLevel()
|
||||
{
|
||||
return _logViewer.GetLogLevel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,14 +135,19 @@ namespace Umbraco.Web.Editors
|
||||
// must have an active variation context!
|
||||
_variationContextAccessor.VariationContext = new VariationContext(culture);
|
||||
|
||||
var result = Request.CreateResponse();
|
||||
//need to create a specific content result formatted as HTML since this controller has been configured
|
||||
//with only json formatters.
|
||||
result.Content = new StringContent(
|
||||
_componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(),
|
||||
Encoding.UTF8,
|
||||
"text/html");
|
||||
return result;
|
||||
using (UmbracoContext.ForcedPreview(true))
|
||||
{
|
||||
|
||||
var result = Request.CreateResponse();
|
||||
//need to create a specific content result formatted as HTML since this controller has been configured
|
||||
//with only json formatters.
|
||||
result.Content = new StringContent(
|
||||
_componentRenderer.RenderMacro(pageId, m.Alias, macroParams).ToString(),
|
||||
Encoding.UTF8,
|
||||
"text/html");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
|
||||
@@ -19,6 +19,7 @@ using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using System.Web.Http.Controllers;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -28,6 +29,7 @@ namespace Umbraco.Web.Editors
|
||||
/// </summary>
|
||||
[PluginController("UmbracoApi")]
|
||||
[UmbracoTreeAuthorize(Constants.Trees.Macros)]
|
||||
[MacrosControllerConfiguration]
|
||||
public class MacrosController : BackOfficeNotificationsController
|
||||
{
|
||||
private readonly IMacroService _macroService;
|
||||
@@ -38,6 +40,19 @@ namespace Umbraco.Web.Editors
|
||||
_macroService = Services.MacroService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures this controller with a custom action selector
|
||||
/// </summary>
|
||||
private class MacrosControllerConfigurationAttribute : Attribute, IControllerConfiguration
|
||||
{
|
||||
public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor)
|
||||
{
|
||||
controllerSettings.Services.Replace(typeof(IHttpActionSelector), new ParameterSwapControllerActionSelector(
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new macro
|
||||
/// </summary>
|
||||
@@ -62,6 +77,11 @@ namespace Umbraco.Web.Editors
|
||||
return this.ReturnErrorResponse("Macro with this alias already exists");
|
||||
}
|
||||
|
||||
if (name == null || name.Length > 255)
|
||||
{
|
||||
return this.ReturnErrorResponse("Name cannnot be more than 255 characters in length.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var macro = new Macro
|
||||
@@ -92,39 +112,43 @@ namespace Umbraco.Web.Editors
|
||||
return this.ReturnErrorResponse($"Macro with id {id} does not exist");
|
||||
}
|
||||
|
||||
var macroDisplay = new MacroDisplay
|
||||
{
|
||||
Alias = macro.Alias,
|
||||
Id = macro.Id,
|
||||
Key = macro.Key,
|
||||
Name = macro.Name,
|
||||
CacheByPage = macro.CacheByPage,
|
||||
CacheByUser = macro.CacheByMember,
|
||||
CachePeriod = macro.CacheDuration,
|
||||
View = macro.MacroSource,
|
||||
RenderInEditor = !macro.DontRender,
|
||||
UseInEditor = macro.UseInEditor,
|
||||
Path = $"-1,{macro.Id}"
|
||||
};
|
||||
|
||||
var parameters = new List<MacroParameterDisplay>();
|
||||
|
||||
foreach (var param in macro.Properties.Values.OrderBy(x => x.SortOrder))
|
||||
{
|
||||
parameters.Add(new MacroParameterDisplay
|
||||
{
|
||||
Editor = param.EditorAlias,
|
||||
Key = param.Alias,
|
||||
Label = param.Name,
|
||||
Id = param.Id
|
||||
});
|
||||
}
|
||||
|
||||
macroDisplay.Parameters = parameters;
|
||||
var macroDisplay = MapToDisplay(macro);
|
||||
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public HttpResponseMessage GetById(Guid id)
|
||||
{
|
||||
var macro = _macroService.GetById(id);
|
||||
|
||||
if (macro == null)
|
||||
{
|
||||
return this.ReturnErrorResponse($"Macro with id {id} does not exist");
|
||||
}
|
||||
|
||||
var macroDisplay = MapToDisplay(macro);
|
||||
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public HttpResponseMessage GetById(Udi id)
|
||||
{
|
||||
var guidUdi = id as GuidUdi;
|
||||
if (guidUdi == null)
|
||||
this.ReturnErrorResponse($"Macro with id {id} does not exist");
|
||||
|
||||
var macro = _macroService.GetById(guidUdi.Guid);
|
||||
if (macro == null)
|
||||
{
|
||||
return this.ReturnErrorResponse($"Macro with id {id} does not exist");
|
||||
}
|
||||
|
||||
var macroDisplay = MapToDisplay(macro);
|
||||
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public HttpResponseMessage DeleteById(int id)
|
||||
@@ -149,6 +173,11 @@ namespace Umbraco.Web.Editors
|
||||
return this.ReturnErrorResponse($"No macro data found in request");
|
||||
}
|
||||
|
||||
if (macroDisplay.Name == null || macroDisplay.Name.Length > 255)
|
||||
{
|
||||
return this.ReturnErrorResponse("Name cannnot be more than 255 characters in length.");
|
||||
}
|
||||
|
||||
var macro = _macroService.GetById(int.Parse(macroDisplay.Id.ToString()));
|
||||
|
||||
if (macro == null)
|
||||
@@ -219,6 +248,39 @@ namespace Umbraco.Web.Editors
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, Current.ParameterEditors);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the available parameter editors grouped by their group.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="HttpResponseMessage"/>.
|
||||
/// </returns>
|
||||
public HttpResponseMessage GetGroupedParameterEditors()
|
||||
{
|
||||
var parameterEditors = Current.ParameterEditors.ToArray();
|
||||
|
||||
var grouped = parameterEditors
|
||||
.GroupBy(x => x.Group.IsNullOrWhiteSpace() ? "" : x.Group.ToLower())
|
||||
.OrderBy(x => x.Key)
|
||||
.ToDictionary(group => group.Key, group => group.OrderBy(d => d.Name).AsEnumerable());
|
||||
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, grouped);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get parameter editor by alias.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="HttpResponseMessage"/>.
|
||||
/// </returns>
|
||||
public HttpResponseMessage GetParameterEditorByAlias(string alias)
|
||||
{
|
||||
var parameterEditors = Current.ParameterEditors.ToArray();
|
||||
|
||||
var parameterEditor = parameterEditors.FirstOrDefault(x => x.Alias.InvariantEquals(alias));
|
||||
|
||||
return this.Request.CreateResponse(HttpStatusCode.OK, parameterEditor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a error response and optionally logs it
|
||||
/// </summary>
|
||||
@@ -280,7 +342,6 @@ namespace Umbraco.Web.Editors
|
||||
/// Finds partial view files in app plugin folders.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// The <see cref="IEnumerable"/>.
|
||||
/// </returns>
|
||||
private IEnumerable<string> FindPartialViewFilesInPluginFolders()
|
||||
{
|
||||
@@ -342,5 +403,29 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to map an <see cref="IMacro"/> instance to a <see cref="MacroDisplay"/>
|
||||
/// </summary>
|
||||
/// <param name="macro"></param>
|
||||
/// <returns></returns>
|
||||
private MacroDisplay MapToDisplay(IMacro macro)
|
||||
{
|
||||
var display = Mapper.Map<MacroDisplay>(macro);
|
||||
|
||||
var parameters = macro.Properties.Values
|
||||
.OrderBy(x => x.SortOrder)
|
||||
.Select(x => new MacroParameterDisplay()
|
||||
{
|
||||
Editor = x.EditorAlias,
|
||||
Key = x.Alias,
|
||||
Label = x.Name,
|
||||
Id = x.Id
|
||||
});
|
||||
|
||||
display.Parameters = parameters;
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.ContentApps;
|
||||
using Umbraco.Web.Editors.Binders;
|
||||
using Umbraco.Web.Editors.Filters;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -815,7 +816,7 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EntityNotFoundException(parentId, "The passed id doesn't exist");
|
||||
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.NotFound, "The passed id doesn't exist"));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -943,5 +944,31 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return hasPathAccess;
|
||||
}
|
||||
|
||||
public PagedResult<EntityBasic> GetPagedReferences(int id, string entityType, int pageNumber = 1, int pageSize = 100)
|
||||
{
|
||||
if (pageNumber <= 0 || pageSize <= 0)
|
||||
{
|
||||
throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero");
|
||||
}
|
||||
|
||||
var objectType = ObjectTypes.GetUmbracoObjectType(entityType);
|
||||
var udiType = ObjectTypes.GetUdiType(objectType);
|
||||
|
||||
var relations = Services.RelationService.GetPagedParentEntitiesByChildId(id, pageNumber - 1, pageSize, out var totalRecords, objectType);
|
||||
|
||||
return new PagedResult<EntityBasic>(totalRecords, pageNumber, pageSize)
|
||||
{
|
||||
Items = relations.Cast<ContentEntitySlim>().Select(rel => new EntityBasic
|
||||
{
|
||||
Id = rel.Id,
|
||||
Key = rel.Key,
|
||||
Udi = Udi.Create(udiType, rel.Key),
|
||||
Icon = rel.ContentTypeIcon,
|
||||
Name = rel.Name,
|
||||
Alias = rel.ContentTypeAlias
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Web.Composing;
|
||||
using IMediaType = Umbraco.Core.Models.IMediaType;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
@@ -109,7 +110,7 @@ namespace Umbraco.Web.Editors
|
||||
[HttpPost]
|
||||
public HttpResponseMessage GetAvailableCompositeMediaTypes(GetAvailableCompositionsFilter filter)
|
||||
{
|
||||
var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, filter.FilterContentTypes, filter.FilterPropertyTypes)
|
||||
var result = PerformGetAvailableCompositeContentTypes(filter.ContentTypeId, UmbracoObjectTypes.MediaType, filter.FilterContentTypes, filter.FilterPropertyTypes, filter.IsElement)
|
||||
.Select(x => new
|
||||
{
|
||||
contentType = x.Item1,
|
||||
@@ -135,12 +136,18 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
public MediaTypeDisplay GetEmpty(int parentId)
|
||||
{
|
||||
var ct = new MediaType(parentId)
|
||||
IMediaType mt;
|
||||
if (parentId != Constants.System.Root)
|
||||
{
|
||||
Icon = Constants.Icons.MediaImage
|
||||
};
|
||||
var parent = Services.MediaTypeService.Get(parentId);
|
||||
mt = parent != null ? new MediaType(parent, string.Empty) : new MediaType(parentId);
|
||||
}
|
||||
else
|
||||
mt = new MediaType(parentId);
|
||||
|
||||
var dto = Mapper.Map<IMediaType, MediaTypeDisplay>(ct);
|
||||
mt.Icon = Constants.Icons.MediaImage;
|
||||
|
||||
var dto = Mapper.Map<IMediaType, MediaTypeDisplay>(mt);
|
||||
return dto;
|
||||
}
|
||||
|
||||
@@ -233,11 +240,11 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
var contentType = Services.MediaTypeService.Get(contentItem.ContentTypeId);
|
||||
var ids = contentType.AllowedContentTypes.Select(x => x.Id.Value).ToArray();
|
||||
var ids = contentType.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value).ToArray();
|
||||
|
||||
if (ids.Any() == false) return Enumerable.Empty<ContentTypeBasic>();
|
||||
|
||||
types = Services.MediaTypeService.GetAll(ids).ToList();
|
||||
types = Services.MediaTypeService.GetAll(ids).OrderBy(c => ids.IndexOf(c.Id)).ToList();
|
||||
}
|
||||
|
||||
var basics = types.Select(Mapper.Map<IMediaType, ContentTypeBasic>).ToList();
|
||||
@@ -248,7 +255,7 @@ namespace Umbraco.Web.Editors
|
||||
basic.Description = TranslateItem(basic.Description);
|
||||
}
|
||||
|
||||
return basics.OrderBy(x => x.Name);
|
||||
return basics.OrderBy(c => contentId == Constants.System.Root ? c.Name : string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Umbraco.Web.Editors
|
||||
[FromUri]string[] filterContentTypes,
|
||||
[FromUri]string[] filterPropertyTypes)
|
||||
{
|
||||
var result = PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes)
|
||||
var result = PerformGetAvailableCompositeContentTypes(contentTypeId, UmbracoObjectTypes.MemberType, filterContentTypes, filterPropertyTypes, false)
|
||||
.Select(x => new
|
||||
{
|
||||
contentType = x.Item1,
|
||||
|
||||
@@ -3,12 +3,14 @@ using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web.Http;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
@@ -45,14 +47,28 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var relations = Services.RelationService.GetByRelationTypeId(relationType.Id);
|
||||
|
||||
var display = Mapper.Map<IRelationType, RelationTypeDisplay>(relationType);
|
||||
display.Relations = Mapper.MapEnumerable<IRelation, RelationDisplay>(relations);
|
||||
|
||||
|
||||
return display;
|
||||
}
|
||||
|
||||
public PagedResult<RelationDisplay> GetPagedResults(int id, int pageNumber = 1, int pageSize = 100)
|
||||
{
|
||||
|
||||
if (pageNumber <= 0 || pageSize <= 0)
|
||||
{
|
||||
throw new NotSupportedException("Both pageNumber and pageSize must be greater than zero");
|
||||
}
|
||||
|
||||
// Ordering do we need to pass through?
|
||||
var relations = Services.RelationService.GetPagedByRelationTypeId(id, pageNumber -1, pageSize, out long totalRecords);
|
||||
|
||||
return new PagedResult<RelationDisplay>(totalRecords, pageNumber, pageSize)
|
||||
{
|
||||
Items = relations.Select(x => Mapper.Map<RelationDisplay>(x))
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of object types which can be associated via relations.
|
||||
/// </summary>
|
||||
@@ -84,11 +100,7 @@ namespace Umbraco.Web.Editors
|
||||
/// <returns>A <see cref="HttpResponseMessage"/> containing the persisted relation type's ID.</returns>
|
||||
public HttpResponseMessage PostCreate(RelationTypeSave relationType)
|
||||
{
|
||||
var relationTypePersisted = new RelationType(relationType.ChildObjectType, relationType.ParentObjectType, relationType.Name.ToSafeAlias(true))
|
||||
{
|
||||
Name = relationType.Name,
|
||||
IsBidirectional = relationType.IsBidirectional
|
||||
};
|
||||
var relationTypePersisted = new RelationType(relationType.Name, relationType.Name.ToSafeAlias(true), relationType.IsBidirectional, relationType.ChildObjectType, relationType.ParentObjectType);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
110
src/Umbraco.Web/Editors/TinyMceController.cs
Normal file
110
src/Umbraco.Web/Editors/TinyMceController.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Http;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
[PluginController("UmbracoApi")]
|
||||
[UmbracoApplicationAuthorize(
|
||||
Constants.Applications.Content,
|
||||
Constants.Applications.Media,
|
||||
Constants.Applications.Members)]
|
||||
public class TinyMceController : UmbracoAuthorizedApiController
|
||||
{
|
||||
private IMediaService _mediaService;
|
||||
private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;
|
||||
|
||||
|
||||
public TinyMceController(IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider)
|
||||
{
|
||||
_mediaService = mediaService;
|
||||
_contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<HttpResponseMessage> UploadImage()
|
||||
{
|
||||
|
||||
if (Request.Content.IsMimeMultipartContent() == false)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
|
||||
}
|
||||
|
||||
// Create an unique folder path to help with concurrent users to avoid filename clash
|
||||
var imageTempPath = IOHelper.MapPath(SystemDirectories.TempImageUploads + "/" + Guid.NewGuid().ToString());
|
||||
|
||||
// Temp folderpath (Files come in as bodypart & will need to move/saved into imgTempPath
|
||||
var folderPath = IOHelper.MapPath(SystemDirectories.TempFileUploads);
|
||||
|
||||
// Ensure image temp path exists
|
||||
if(Directory.Exists(imageTempPath) == false)
|
||||
{
|
||||
Directory.CreateDirectory(imageTempPath);
|
||||
}
|
||||
|
||||
// File uploaded will be saved as bodypart into TEMP folder
|
||||
var provider = new MultipartFormDataStreamProvider(folderPath);
|
||||
var result = await Request.Content.ReadAsMultipartAsync(provider);
|
||||
|
||||
// Must have a file
|
||||
if (result.FileData.Count == 0)
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
// Should only have one file
|
||||
if (result.FileData.Count > 1)
|
||||
{
|
||||
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Only one file can be uploaded at a time");
|
||||
}
|
||||
|
||||
// Really we should only have one file per request to this endpoint
|
||||
var file = result.FileData[0];
|
||||
var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd();
|
||||
var safeFileName = fileName.ToSafeFileName();
|
||||
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower();
|
||||
|
||||
if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext) == false || Current.Configs.Settings().Content.ImageFileTypes.Contains(ext) == false)
|
||||
{
|
||||
// Throw some error - to say can't upload this IMG type
|
||||
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "This is not an image filetype extension that is approved");
|
||||
}
|
||||
|
||||
//var mediaItemName = fileName.ToFriendlyName();
|
||||
var currentFile = file.LocalFileName;
|
||||
var newFilePath = imageTempPath + IOHelper.DirSepChar + safeFileName;
|
||||
var relativeNewFilePath = IOHelper.GetRelativePath(newFilePath);
|
||||
|
||||
try
|
||||
{
|
||||
// Move the file from bodypart to a real filename
|
||||
// This is what we return from this API so RTE updates img src path
|
||||
// Until we fully persist & save the media item when persisting
|
||||
// If we find <img data-temp-img /> data attribute
|
||||
File.Move(currentFile, newFilePath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// IOException, PathTooLong, DirectoryNotFound, UnathorizedAccess
|
||||
Logger.Error<TinyMceController>(ex, "Error when trying to move {CurrentFilePath} to {NewFilePath}", currentFile, newFilePath);
|
||||
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, $"Error when trying to move {currentFile} to {newFilePath}", ex);
|
||||
}
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK, new { tmpLocation = relativeNewFilePath });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,29 +51,35 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
//collect all tour files in packages
|
||||
foreach (var plugin in Directory.EnumerateDirectories(IOHelper.MapPath(SystemDirectories.AppPlugins)))
|
||||
var appPlugins = IOHelper.MapPath(SystemDirectories.AppPlugins);
|
||||
if (Directory.Exists(appPlugins))
|
||||
{
|
||||
var pluginName = Path.GetFileName(plugin.TrimEnd('\\'));
|
||||
var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList();
|
||||
|
||||
//If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely
|
||||
var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null);
|
||||
if (isPluginFiltered) continue;
|
||||
|
||||
//combine matched package filters with filters not specific to a package
|
||||
var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList();
|
||||
|
||||
foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice"))
|
||||
foreach (var plugin in Directory.EnumerateDirectories(appPlugins))
|
||||
{
|
||||
foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours"))
|
||||
var pluginName = Path.GetFileName(plugin.TrimEnd('\\'));
|
||||
var pluginFilters = _filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName))
|
||||
.ToList();
|
||||
|
||||
//If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely
|
||||
var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null);
|
||||
if (isPluginFiltered) continue;
|
||||
|
||||
//combine matched package filters with filters not specific to a package
|
||||
var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList();
|
||||
|
||||
foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice"))
|
||||
{
|
||||
foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json"))
|
||||
foreach (var tourDir in Directory.EnumerateDirectories(backofficeDir, "tours"))
|
||||
{
|
||||
TryParseTourFile(tourFile, result, combinedFilters, aliasOnlyFilters, pluginName);
|
||||
foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json"))
|
||||
{
|
||||
TryParseTourFile(tourFile, result, combinedFilters, aliasOnlyFilters, pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Get all allowed sections for the current user
|
||||
var allowedSections = user.AllowedSections.ToList();
|
||||
|
||||
@@ -104,6 +110,39 @@ namespace Umbraco.Web.Editors
|
||||
return result.Except(toursToBeRemoved).OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tours for a specific doctype
|
||||
/// </summary>
|
||||
/// <param name="doctypeAlias">The documenttype alias</param>
|
||||
/// <returns>A <see cref="BackOfficeTour"/></returns>
|
||||
public IEnumerable<BackOfficeTour> GetToursForDoctype(string doctypeAlias)
|
||||
{
|
||||
var tourFiles = this.GetTours();
|
||||
|
||||
var doctypeAliasWithCompositions = new List<string>
|
||||
{
|
||||
doctypeAlias
|
||||
};
|
||||
|
||||
var contentType = this.Services.ContentTypeService.Get(doctypeAlias);
|
||||
|
||||
if (contentType != null)
|
||||
{
|
||||
doctypeAliasWithCompositions.AddRange(contentType.CompositionAliases());
|
||||
}
|
||||
|
||||
return tourFiles.SelectMany(x => x.Tours)
|
||||
.Where(x =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(x.ContentType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var contentTypes = x.ContentType.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(ct => ct.Trim());
|
||||
return contentTypes.Intersect(doctypeAliasWithCompositions).Any();
|
||||
});
|
||||
}
|
||||
|
||||
private void TryParseTourFile(string tourFile,
|
||||
ICollection<BackOfficeTourFile> result,
|
||||
List<BackOfficeTourFilter> filters,
|
||||
|
||||
@@ -154,8 +154,8 @@ namespace Umbraco.Web.Editors
|
||||
public HttpResponseMessage PostDeleteUserGroups([FromUri] int[] userGroupIds)
|
||||
{
|
||||
var userGroups = Services.UserService.GetAllUserGroups(userGroupIds)
|
||||
//never delete the admin group or translators group
|
||||
.Where(x => x.Alias != Constants.Security.AdminGroupAlias && x.Alias != Constants.Security.TranslatorGroupAlias)
|
||||
//never delete the admin group, sensitive data or translators group
|
||||
.Where(x => !x.IsSystemUserGroup())
|
||||
.ToArray();
|
||||
foreach (var userGroup in userGroups)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
@@ -25,6 +26,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Editors.Filters;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
@@ -105,7 +107,7 @@ namespace Umbraco.Web.Editors
|
||||
if (Current.Configs.Settings().Content.DisallowedUploadFiles.Contains(ext) == false)
|
||||
{
|
||||
//generate a path of known data, we don't want this path to be guessable
|
||||
user.Avatar = "UserAvatars/" + (user.Id + safeFileName).ToSHA1() + "." + ext;
|
||||
user.Avatar = "UserAvatars/" + (user.Id + safeFileName).GenerateHash<SHA1>() + "." + ext;
|
||||
|
||||
using (var fs = System.IO.File.OpenRead(file.LocalFileName))
|
||||
{
|
||||
@@ -543,34 +545,12 @@ namespace Umbraco.Web.Editors
|
||||
hasErrors = true;
|
||||
}
|
||||
|
||||
// if the found user has his email for username, we want to keep this synced when changing the email.
|
||||
// if the found user has their email for username, we want to keep this synced when changing the email.
|
||||
// we have already cross-checked above that the email isn't colliding with anything, so we can safely assign it here.
|
||||
if (Current.Configs.Settings().Security.UsernameIsEmail && found.Username == found.Email && userSave.Username != userSave.Email)
|
||||
{
|
||||
userSave.Username = userSave.Email;
|
||||
}
|
||||
|
||||
if (userSave.ChangePassword != null)
|
||||
{
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
|
||||
|
||||
//this will change the password and raise appropriate events
|
||||
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, userSave.ChangePassword, UserManager);
|
||||
if (passwordChangeResult.Success)
|
||||
{
|
||||
//need to re-get the user
|
||||
found = Services.UserService.GetUserById(intId.Result);
|
||||
}
|
||||
else
|
||||
{
|
||||
hasErrors = true;
|
||||
|
||||
foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames)
|
||||
{
|
||||
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasErrors)
|
||||
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
|
||||
@@ -586,6 +566,51 @@ namespace Umbraco.Web.Editors
|
||||
return display;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="changingPasswordModel"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ModelWithNotifications<string>> PostChangePassword(ChangingPasswordModel changingPasswordModel)
|
||||
{
|
||||
changingPasswordModel = changingPasswordModel ?? throw new ArgumentNullException(nameof(changingPasswordModel));
|
||||
|
||||
if (ModelState.IsValid == false)
|
||||
{
|
||||
throw new HttpResponseException(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState));
|
||||
}
|
||||
|
||||
var intId = changingPasswordModel.Id.TryConvertTo<int>();
|
||||
if (intId.Success == false)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var found = Services.UserService.GetUserById(intId.Result);
|
||||
if (found == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
var passwordChanger = new PasswordChanger(Logger, Services.UserService, UmbracoContext.HttpContext);
|
||||
var passwordChangeResult = await passwordChanger.ChangePasswordWithIdentityAsync(Security.CurrentUser, found, changingPasswordModel, UserManager);
|
||||
|
||||
if (passwordChangeResult.Success)
|
||||
{
|
||||
var result = new ModelWithNotifications<string>(passwordChangeResult.Result.ResetPassword);
|
||||
result.AddSuccessNotification(Services.TextService.Localize("general/success"), Services.TextService.Localize("user/passwordChangedGeneric"));
|
||||
return result;
|
||||
}
|
||||
|
||||
foreach (var memberName in passwordChangeResult.Result.ChangeError.MemberNames)
|
||||
{
|
||||
ModelState.AddModelError(memberName, passwordChangeResult.Result.ChangeError.ErrorMessage);
|
||||
}
|
||||
|
||||
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Disables the users with the given user ids
|
||||
/// </summary>
|
||||
|
||||
@@ -2,32 +2,95 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Umbraco.Core;
|
||||
using Examine.LuceneEngine.Providers;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Examine;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension methods for Examine
|
||||
/// Extension methods for Examine.
|
||||
/// </summary>
|
||||
public static class ExamineExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content from the <paramref name="cache" />.
|
||||
/// </summary>
|
||||
/// <param name="results">The search results.</param>
|
||||
/// <param name="cache">The cache to fetch the content from.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">cache</exception>
|
||||
/// <remarks>
|
||||
/// Search results are skipped if it can't be fetched from the <paramref name="cache" /> by its integer id.
|
||||
/// </remarks>
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedCache cache)
|
||||
{
|
||||
var list = new List<PublishedSearchResult>();
|
||||
if (cache == null) throw new ArgumentNullException(nameof(cache));
|
||||
|
||||
foreach (var result in results.OrderByDescending(x => x.Score))
|
||||
var publishedSearchResults = new List<PublishedSearchResult>();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (!int.TryParse(result.Id, out var intId)) continue; //invalid
|
||||
var content = cache.GetById(intId);
|
||||
if (content == null) continue; // skip if this doesn't exist in the cache
|
||||
|
||||
list.Add(new PublishedSearchResult(content, result.Score));
|
||||
|
||||
if (int.TryParse(result.Id, out var contentId) &&
|
||||
cache.GetById(contentId) is IPublishedContent content)
|
||||
{
|
||||
publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
return publishedSearchResults;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members from the <paramref name="snapshot" />.
|
||||
/// </summary>
|
||||
/// <param name="results">The search results.</param>
|
||||
/// <param name="snapshot">The snapshot.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="IEnumerable{PublishedSearchResult}" /> containing all content, media or members.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">snapshot</exception>
|
||||
/// <remarks>
|
||||
/// Search results are skipped if it can't be fetched from the respective cache by its integer id.
|
||||
/// </remarks>
|
||||
public static IEnumerable<PublishedSearchResult> ToPublishedSearchResults(this IEnumerable<ISearchResult> results, IPublishedSnapshot snapshot)
|
||||
{
|
||||
if (snapshot == null) throw new ArgumentNullException(nameof(snapshot));
|
||||
|
||||
var publishedSearchResults = new List<PublishedSearchResult>();
|
||||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (int.TryParse(result.Id, out var contentId) &&
|
||||
result.Values.TryGetValue(LuceneIndex.CategoryFieldName, out var indexType))
|
||||
{
|
||||
IPublishedContent content;
|
||||
switch (indexType)
|
||||
{
|
||||
case IndexTypes.Content:
|
||||
content = snapshot.Content.GetById(contentId);
|
||||
break;
|
||||
case IndexTypes.Media:
|
||||
content = snapshot.Media.GetById(contentId);
|
||||
break;
|
||||
case IndexTypes.Member:
|
||||
content = snapshot.Members.GetById(contentId);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
if (content != null)
|
||||
{
|
||||
publishedSearchResults.Add(new PublishedSearchResult(content, result.Score));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return publishedSearchResults;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
using System;
|
||||
using System.Web.Mvc.Html;
|
||||
using System.Web.Mvc;
|
||||
using System.IO;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using System.Web.Mvc.Html;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
|
||||
public static class GridTemplateExtensions
|
||||
{
|
||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedProperty property, string framework = "bootstrap3")
|
||||
@@ -26,18 +23,20 @@ namespace Umbraco.Web
|
||||
|
||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
||||
if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias));
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias));
|
||||
|
||||
return html.GetGridHtml(contentItem, propertyAlias, "bootstrap3");
|
||||
}
|
||||
|
||||
public static MvcHtmlString GetGridHtml(this HtmlHelper html, IPublishedContent contentItem, string propertyAlias, string framework)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
||||
if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias));
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias));
|
||||
|
||||
var view = "Grid/" + framework;
|
||||
var prop = contentItem.GetProperty(propertyAlias);
|
||||
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
|
||||
if (prop == null) throw new InvalidOperationException("No property type found with alias " + propertyAlias);
|
||||
var model = prop.GetValue();
|
||||
|
||||
var asString = model as string;
|
||||
@@ -54,23 +53,28 @@ namespace Umbraco.Web
|
||||
var view = "Grid/" + framework;
|
||||
return html.Partial(view, property.GetValue());
|
||||
}
|
||||
|
||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html)
|
||||
{
|
||||
return GetGridHtml(contentItem, html, "bodyText", "bootstrap3");
|
||||
}
|
||||
|
||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
||||
if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias));
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias));
|
||||
|
||||
return GetGridHtml(contentItem, html, propertyAlias, "bootstrap3");
|
||||
}
|
||||
|
||||
public static MvcHtmlString GetGridHtml(this IPublishedContent contentItem, HtmlHelper html, string propertyAlias, string framework)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentNullOrEmptyException(nameof(propertyAlias));
|
||||
if (propertyAlias == null) throw new ArgumentNullException(nameof(propertyAlias));
|
||||
if (string.IsNullOrWhiteSpace(propertyAlias)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(propertyAlias));
|
||||
|
||||
var view = "Grid/" + framework;
|
||||
var prop = contentItem.GetProperty(propertyAlias);
|
||||
if (prop == null) throw new NullReferenceException("No property type found with alias " + propertyAlias);
|
||||
if (prop == null) throw new InvalidOperationException("No property type found with alias " + propertyAlias);
|
||||
var model = prop.GetValue();
|
||||
|
||||
var asString = model as string;
|
||||
@@ -78,12 +82,5 @@ namespace Umbraco.Web
|
||||
|
||||
return html.Partial(view, model);
|
||||
}
|
||||
|
||||
private class FakeView : IView
|
||||
{
|
||||
public void Render(ViewContext viewContext, TextWriter writer)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
var response = request.GetResponse();
|
||||
|
||||
// Check first for header
|
||||
success = DoHttpHeadersContainHeader(response);
|
||||
success = HasMatchingHeader(response.Headers.AllKeys);
|
||||
|
||||
// If not found, and available, check for meta-tag
|
||||
if (success == false && _metaTagOptionAvailable)
|
||||
@@ -113,9 +113,9 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
};
|
||||
}
|
||||
|
||||
private bool DoHttpHeadersContainHeader(WebResponse response)
|
||||
private bool HasMatchingHeader(IEnumerable<string> headerKeys)
|
||||
{
|
||||
return response.Headers.AllKeys.Contains(_header);
|
||||
return headerKeys.Contains(_header, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private bool DoMetaTagsContainKeyForHeader(WebResponse response)
|
||||
@@ -127,7 +127,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
{
|
||||
var html = reader.ReadToEnd();
|
||||
var metaTags = ParseMetaTags(html);
|
||||
return metaTags.ContainsKey(_header);
|
||||
return HasMatchingHeader(metaTags.Keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -186,24 +186,22 @@ namespace Umbraco.Web.HealthCheck.Checks.Security
|
||||
}
|
||||
|
||||
var removeHeaderElement = customHeadersElement.Elements("remove")
|
||||
.SingleOrDefault(x => x.Attribute("name") != null &&
|
||||
x.Attribute("name")?.Value == _value);
|
||||
.SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_value, StringComparison.InvariantCultureIgnoreCase) == true);
|
||||
if (removeHeaderElement == null)
|
||||
{
|
||||
removeHeaderElement = new XElement("remove");
|
||||
removeHeaderElement.Add(new XAttribute("name", _header));
|
||||
customHeadersElement.Add(removeHeaderElement);
|
||||
customHeadersElement.Add(
|
||||
new XElement("remove",
|
||||
new XAttribute("name", _header)));
|
||||
}
|
||||
|
||||
var addHeaderElement = customHeadersElement.Elements("add")
|
||||
.SingleOrDefault(x => x.Attribute("name") != null &&
|
||||
x.Attribute("name").Value == _header);
|
||||
.SingleOrDefault(x => x.Attribute("name")?.Value.Equals(_header, StringComparison.InvariantCultureIgnoreCase) == true);
|
||||
if (addHeaderElement == null)
|
||||
{
|
||||
addHeaderElement = new XElement("add");
|
||||
addHeaderElement.Add(new XAttribute("name", _header));
|
||||
addHeaderElement.Add(new XAttribute("value", _value));
|
||||
customHeadersElement.Add(addHeaderElement);
|
||||
customHeadersElement.Add(
|
||||
new XElement("add",
|
||||
new XAttribute("name", _header),
|
||||
new XAttribute("value", _value)));
|
||||
}
|
||||
|
||||
doc.Save(configFile);
|
||||
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Web.Models;
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
using Core.Configuration;
|
||||
using Umbraco.Web.JavaScript;
|
||||
|
||||
/// <summary>
|
||||
/// HtmlHelper extensions for the back office
|
||||
@@ -118,6 +119,21 @@ namespace Umbraco.Web
|
||||
sb.AppendLine(JsonConvert.SerializeObject(resetCodeModel));
|
||||
sb.AppendLine(@"});");
|
||||
|
||||
return html.Raw(sb.ToString());
|
||||
}
|
||||
|
||||
public static IHtmlString AngularValueTinyMceAssets(this HtmlHelper html)
|
||||
{
|
||||
var ctx = new HttpContextWrapper(HttpContext.Current);
|
||||
var files = JsInitialization.OptimizeTinyMceScriptFiles(ctx);
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(@"app.value(""tinyMceAssets"",");
|
||||
sb.AppendLine(JsonConvert.SerializeObject(files));
|
||||
sb.AppendLine(@");");
|
||||
|
||||
|
||||
return html.Raw(sb.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,7 @@ using System.Web.Mvc;
|
||||
using System.Web.Mvc.Html;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.Security;
|
||||
using Current = Umbraco.Web.Composing.Current;
|
||||
@@ -68,7 +64,8 @@ namespace Umbraco.Web
|
||||
var htmlBadge =
|
||||
String.Format(Current.Configs.Settings().Content.PreviewBadge,
|
||||
IOHelper.ResolveUrl(SystemDirectories.Umbraco),
|
||||
Current.UmbracoContext.HttpContext.Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Path));
|
||||
Current.UmbracoContext.HttpContext.Server.UrlEncode(Current.UmbracoContext.HttpContext.Request.Path),
|
||||
Current.UmbracoContext.PublishedRequest.PublishedContent.Id);
|
||||
return new MvcHtmlString(htmlBadge);
|
||||
}
|
||||
return new MvcHtmlString("");
|
||||
@@ -169,8 +166,9 @@ namespace Umbraco.Web
|
||||
/// <returns></returns>
|
||||
public static IHtmlString Action(this HtmlHelper htmlHelper, string actionName, Type surfaceType)
|
||||
{
|
||||
if (actionName == null) throw new ArgumentNullException(nameof(actionName));
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName));
|
||||
if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType));
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName));
|
||||
|
||||
var routeVals = new RouteValueDictionary(new {area = ""});
|
||||
|
||||
@@ -221,7 +219,7 @@ namespace Umbraco.Web
|
||||
{
|
||||
_viewContext = viewContext;
|
||||
_method = method;
|
||||
_controllerName = controllerName;
|
||||
_controllerName = controllerName;
|
||||
_encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
|
||||
}
|
||||
|
||||
@@ -230,7 +228,7 @@ namespace Umbraco.Web
|
||||
private readonly FormMethod _method;
|
||||
private bool _disposed;
|
||||
private readonly string _encryptedString;
|
||||
private readonly string _controllerName;
|
||||
private readonly string _controllerName;
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
@@ -243,12 +241,12 @@ namespace Umbraco.Web
|
||||
|| _controllerName == "UmbProfile"
|
||||
|| _controllerName == "UmbLoginStatus"
|
||||
|| _controllerName == "UmbLogin")
|
||||
{
|
||||
{
|
||||
_viewContext.Writer.Write(AntiForgery.GetHtml().ToString());
|
||||
}
|
||||
}
|
||||
|
||||
//write out the hidden surface form routes
|
||||
_viewContext.Writer.Write("<input name='ufprt' type='hidden' value='" + _encryptedString + "' />");
|
||||
_viewContext.Writer.Write("<input name=\"ufprt\" type=\"hidden\" value=\"" + _encryptedString + "\" />");
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
@@ -355,8 +353,10 @@ namespace Umbraco.Web
|
||||
IDictionary<string, object> htmlAttributes,
|
||||
FormMethod method)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action));
|
||||
if (controllerName == null) throw new ArgumentNullException(nameof(controllerName));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName));
|
||||
|
||||
return html.BeginUmbracoForm(action, controllerName, "", additionalRouteVals, htmlAttributes, method);
|
||||
}
|
||||
@@ -374,8 +374,10 @@ namespace Umbraco.Web
|
||||
object additionalRouteVals,
|
||||
IDictionary<string, object> htmlAttributes)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action));
|
||||
if (controllerName == null) throw new ArgumentNullException(nameof(controllerName));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName));
|
||||
|
||||
return html.BeginUmbracoForm(action, controllerName, "", additionalRouteVals, htmlAttributes);
|
||||
}
|
||||
@@ -575,7 +577,9 @@ namespace Umbraco.Web
|
||||
IDictionary<string, object> htmlAttributes,
|
||||
FormMethod method)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentNullOrEmptyException(nameof(action));
|
||||
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
if (string.IsNullOrWhiteSpace(action)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(action));
|
||||
if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType));
|
||||
|
||||
var area = "";
|
||||
@@ -687,8 +691,10 @@ namespace Umbraco.Web
|
||||
IDictionary<string, object> htmlAttributes,
|
||||
FormMethod method)
|
||||
{
|
||||
if (string.IsNullOrEmpty(action)) throw new ArgumentNullOrEmptyException(nameof(action));
|
||||
if (string.IsNullOrEmpty(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
|
||||
if (action == null) throw new ArgumentNullException(nameof(action));
|
||||
if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action));
|
||||
if (controllerName == null) throw new ArgumentNullException(nameof(controllerName));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName));
|
||||
|
||||
var formAction = Current.UmbracoContext.OriginalRequestUrl.PathAndQuery;
|
||||
return html.RenderForm(formAction, method, htmlAttributes, controllerName, action, area, additionalRouteVals);
|
||||
|
||||
@@ -19,10 +19,26 @@ namespace Umbraco.Web
|
||||
/// Replaces text line breaks with HTML line breaks
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <returns>The text with text line breaks replaced with HTML line breaks (<br/>)</returns>
|
||||
/// <returns>The text with text line breaks replaced with HTML line breaks (<c><br /></c>).
|
||||
[Obsolete("This method doesn't HTML encode the text. Use ReplaceLineBreaks instead.")]
|
||||
public HtmlString ReplaceLineBreaksForHtml(string text)
|
||||
{
|
||||
return new HtmlString(text.Replace("\r\n", @"<br />").Replace("\n", @"<br />").Replace("\r", @"<br />"));
|
||||
return new HtmlString(text.Replace("\r\n", @"<br />").Replace("\n", @"<br />").Replace("\r", @"<br />"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// HTML encodes the text and replaces text line breaks with HTML line breaks.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <returns>The HTML encoded text with text line breaks replaced with HTML line breaks (<c><br /></c>).</returns>
|
||||
public IHtmlString ReplaceLineBreaks(string text)
|
||||
{
|
||||
var value = HttpUtility.HtmlEncode(text)?
|
||||
.Replace("\r\n", "<br />")
|
||||
.Replace("\r", "<br />")
|
||||
.Replace("\n", "<br />");
|
||||
|
||||
return new HtmlString(value);
|
||||
}
|
||||
|
||||
public HtmlString StripHtmlTags(string html, params string[] tags)
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Web.Http.Routing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.WebApi;
|
||||
@@ -53,7 +52,8 @@ namespace Umbraco.Web
|
||||
/// <returns></returns>
|
||||
public static string GetUmbracoApiService(this UrlHelper url, string actionName, Type apiControllerType, object id = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName));
|
||||
if (actionName == null) throw new ArgumentNullException(nameof(actionName));
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName));
|
||||
if (apiControllerType == null) throw new ArgumentNullException(nameof(apiControllerType));
|
||||
|
||||
var area = "";
|
||||
@@ -95,8 +95,10 @@ namespace Umbraco.Web
|
||||
/// <returns></returns>
|
||||
public static string GetUmbracoApiService(this UrlHelper url, string actionName, string controllerName, string area, object id = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentNullOrEmptyException(nameof(actionName));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentNullOrEmptyException(nameof(controllerName));
|
||||
if (actionName == null) throw new ArgumentNullException(nameof(actionName));
|
||||
if (string.IsNullOrWhiteSpace(actionName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(actionName));
|
||||
if (controllerName == null) throw new ArgumentNullException(nameof(controllerName));
|
||||
if (string.IsNullOrWhiteSpace(controllerName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(controllerName));
|
||||
|
||||
string routeName;
|
||||
if (area.IsNullOrWhiteSpace())
|
||||
|
||||
@@ -35,52 +35,63 @@ namespace Umbraco.Web
|
||||
/// <summary>
|
||||
/// Searches content.
|
||||
/// </summary>
|
||||
/// <param name="term">Term to search.</param>
|
||||
/// <param name="culture">Optional culture.</param>
|
||||
/// <param name="indexName">Optional index name.</param>
|
||||
/// <param name="term">The term to search.</param>
|
||||
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
|
||||
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
|
||||
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null);
|
||||
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName);
|
||||
|
||||
/// <summary>
|
||||
/// Searches content.
|
||||
/// </summary>
|
||||
/// <param name="term">Term to search.</param>
|
||||
/// <param name="skip">Numbers of items to skip.</param>
|
||||
/// <param name="take">Numbers of items to return.</param>
|
||||
/// <param name="totalRecords">Total number of matching items.</param>
|
||||
/// <param name="culture">Optional culture.</param>
|
||||
/// <param name="indexName">Optional index name.</param>
|
||||
/// <param name="term">The term to search.</param>
|
||||
/// <param name="skip">The amount of results to skip.</param>
|
||||
/// <param name="take">The amount of results to take/return.</param>
|
||||
/// <param name="totalRecords">The total amount of records.</param>
|
||||
/// <param name="culture">The culture (defaults to a culture insensitive search).</param>
|
||||
/// <param name="indexName">The name of the index to search (defaults to <see cref="Constants.UmbracoIndexes.ExternalIndexName" />).</param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
|
||||
/// When the <paramref name="culture" /> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null);
|
||||
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = Constants.UmbracoIndexes.ExternalIndexName);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the query and converts the results to PublishedSearchResult.
|
||||
/// Executes the query and converts the results to <see cref="PublishedSearchResult" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
IEnumerable<PublishedSearchResult> Search(IQueryExecutor query);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the query and converts the results to PublishedSearchResult.
|
||||
/// Executes the query and converts the results to <see cref="PublishedSearchResult" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
/// <param name="query">The query.</param>
|
||||
/// <param name="skip">The amount of results to skip.</param>
|
||||
/// <param name="take">The amount of results to take/return.</param>
|
||||
/// <param name="totalRecords">The total amount of records.</param>
|
||||
/// <returns>
|
||||
/// The search results.
|
||||
/// </returns>
|
||||
IEnumerable<PublishedSearchResult> Search(IQueryExecutor query, int skip, int take, out long totalRecords);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Migrations.Install;
|
||||
using Umbraco.Web.JavaScript;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Web.Security;
|
||||
|
||||
namespace Umbraco.Web.Install.Controllers
|
||||
@@ -35,6 +36,7 @@ namespace Umbraco.Web.Install.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[StatusCodeResult(System.Net.HttpStatusCode.ServiceUnavailable)]
|
||||
public ActionResult Index()
|
||||
{
|
||||
if (_runtime.Level == RuntimeLevel.Run)
|
||||
|
||||
@@ -45,8 +45,8 @@ namespace Umbraco.Web.Install
|
||||
/// <summary>
|
||||
/// This will test the directories for write access
|
||||
/// </summary>
|
||||
/// <param name="directories"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <param name="dirs"></param>
|
||||
/// <param name="errors"></param>
|
||||
/// <param name="writeCausesRestart">
|
||||
/// If this is false, the easiest way to test for write access is to write a temp file, however some folder will cause
|
||||
/// an App Domain restart if a file is written to the folder, so in that case we need to use the ACL APIs which aren't as
|
||||
|
||||
@@ -1,44 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Install
|
||||
{
|
||||
/// <summary>
|
||||
/// Used for steps to be able to return a json structure back to the UI
|
||||
/// Used for steps to be able to return a JSON structure back to the UI.
|
||||
/// </summary>
|
||||
internal class InstallException : Exception
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class InstallException : Exception
|
||||
{
|
||||
private readonly string _message;
|
||||
/// <summary>
|
||||
/// Gets the view.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The view.
|
||||
/// </value>
|
||||
public string View { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the view model.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The view model.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This object is not included when serializing.
|
||||
/// </remarks>
|
||||
public object ViewModel { get; private set; }
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get { return _message; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
public InstallException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public InstallException(string message)
|
||||
: this(message, "error", null)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="viewModel">The view model.</param>
|
||||
public InstallException(string message, object viewModel)
|
||||
: this(message, "error", viewModel)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
/// <param name="view">The view.</param>
|
||||
/// <param name="viewModel">The view model.</param>
|
||||
public InstallException(string message, string view, object viewModel)
|
||||
: base(message)
|
||||
{
|
||||
_message = message;
|
||||
View = view;
|
||||
ViewModel = viewModel;
|
||||
}
|
||||
|
||||
public InstallException(string message, object viewModel)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public InstallException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InstallException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected InstallException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
_message = message;
|
||||
View = "error";
|
||||
ViewModel = viewModel;
|
||||
View = info.GetString(nameof(View));
|
||||
}
|
||||
|
||||
public InstallException(string message)
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <exception cref="ArgumentNullException">info</exception>
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
_message = message;
|
||||
View = "error";
|
||||
ViewModel = null;
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
info.AddValue(nameof(View), View);
|
||||
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,8 @@ namespace Umbraco.Web.Install.InstallSteps
|
||||
var fileName = IOHelper.MapPath($"{SystemDirectories.Root}/web.config");
|
||||
var xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
|
||||
var systemWeb = xml.Root.DescendantsAndSelf("system.web").Single();
|
||||
// we only want to get the element that is under the root, (there may be more under <location> tags we don't want them)
|
||||
var systemWeb = xml.Root.Element("system.web");
|
||||
|
||||
// Update appSetting if it exists, or else create a new appSetting for the given key and value
|
||||
var machineKey = systemWeb.Descendants("machineKey").FirstOrDefault();
|
||||
|
||||
@@ -131,6 +131,17 @@ namespace Umbraco.Web.JavaScript
|
||||
return resources.Where(x => x.Type == JTokenType.String).Select(x => x.ToString());
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> GetTinyMceInitialization()
|
||||
{
|
||||
var resources = JsonConvert.DeserializeObject<JArray>(Resources.TinyMceInitialize);
|
||||
return resources.Where(x => x.Type == JTokenType.String).Select(x => x.ToString());
|
||||
}
|
||||
|
||||
internal static IEnumerable<string> OptimizeTinyMceScriptFiles(HttpContextBase httpContext)
|
||||
{
|
||||
return OptimizeScriptFiles(httpContext, GetTinyMceInitialization());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parses the JsResources.Main and replaces the replacement tokens accordingly.
|
||||
/// </summary>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
'lib/angular-route/angular-route.js',
|
||||
'lib/angular-cookies/angular-cookies.js',
|
||||
'lib/angular-aria/angular-aria.min.js',
|
||||
'lib/angular-touch/angular-touch.js',
|
||||
'lib/angular-sanitize/angular-sanitize.js',
|
||||
'lib/angular-animate/angular-animate.js',
|
||||
|
||||
12
src/Umbraco.Web/JavaScript/Resources.Designer.cs
generated
12
src/Umbraco.Web/JavaScript/Resources.Designer.cs
generated
@@ -146,5 +146,17 @@ namespace Umbraco.Web.JavaScript {
|
||||
return ResourceManager.GetString("ServerVariables", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to [
|
||||
/// '../lib/tinymce/tinymce.min.js',
|
||||
///]
|
||||
///.
|
||||
/// </summary>
|
||||
internal static string TinyMceInitialize {
|
||||
get {
|
||||
return ResourceManager.GetString("TinyMceInitialize", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,4 +130,7 @@
|
||||
<data name="ServerVariables" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>servervariables.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="TinyMceInitialize" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>TinyMceInitialize.js;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8</value>
|
||||
</data>
|
||||
</root>
|
||||
17
src/Umbraco.Web/JavaScript/TinyMceInitialize.js
Normal file
17
src/Umbraco.Web/JavaScript/TinyMceInitialize.js
Normal file
@@ -0,0 +1,17 @@
|
||||
[
|
||||
'lib/tinymce/tinymce.min.js',
|
||||
|
||||
'lib/tinymce/plugins/paste/plugin.min.js',
|
||||
'lib/tinymce/plugins/anchor/plugin.min.js',
|
||||
'lib/tinymce/plugins/charmap/plugin.min.js',
|
||||
'lib/tinymce/plugins/table/plugin.min.js',
|
||||
'lib/tinymce/plugins/lists/plugin.min.js',
|
||||
'lib/tinymce/plugins/advlist/plugin.min.js',
|
||||
'lib/tinymce/plugins/hr/plugin.min.js',
|
||||
'lib/tinymce/plugins/autolink/plugin.min.js',
|
||||
'lib/tinymce/plugins/directionality/plugin.min.js',
|
||||
'lib/tinymce/plugins/tabfocus/plugin.min.js',
|
||||
'lib/tinymce/plugins/searchreplace/plugin.min.js',
|
||||
'lib/tinymce/plugins/fullscreen/plugin.min.js',
|
||||
'lib/tinymce/plugins/noneditable/plugin.min.js'
|
||||
]
|
||||
@@ -186,7 +186,7 @@ namespace Umbraco.Web.Macros
|
||||
foreach (var prop in model.Properties)
|
||||
{
|
||||
var key = prop.Key.ToLowerInvariant();
|
||||
prop.Value = macroParams.ContainsKey(key)
|
||||
prop.Value = macroParams != null && macroParams.ContainsKey(key)
|
||||
? macroParams[key]?.ToString() ?? string.Empty
|
||||
: string.Empty;
|
||||
}
|
||||
@@ -197,7 +197,8 @@ namespace Umbraco.Web.Macros
|
||||
|
||||
public MacroContent Render(string macroAlias, IPublishedContent content, IDictionary<string, object> macroParams)
|
||||
{
|
||||
var m = _macroService.GetByAlias(macroAlias);
|
||||
var m = _appCaches.RuntimeCache.GetCacheItem(CacheKeys.MacroFromAliasCacheKey + macroAlias, () => _macroService.GetByAlias(macroAlias));
|
||||
|
||||
if (m == null)
|
||||
throw new InvalidOperationException("No macro found by alias " + macroAlias);
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Umbraco.Web.Media.EmbedProviders
|
||||
var imageHeight = GetXmlProperty(xmlDocument, "/oembed/height");
|
||||
var imageTitle = GetXmlProperty(xmlDocument, "/oembed/title");
|
||||
|
||||
return string.Format("<img src=\"{0}\" width\"{1}\" height=\"{2}\" alt=\"{3}\" />", imageUrl, imageWidth, imageHeight, HttpUtility.HtmlEncode(imageTitle));
|
||||
return string.Format("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", imageUrl, imageWidth, imageHeight, HttpUtility.HtmlEncode(imageTitle));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG/Exif file
|
||||
/// could not be understood.
|
||||
/// The exception that is thrown when the format of the JPEG/EXIF file could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidExifFileException : Exception
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class NotValidExifFileException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidExifFileException" /> class.
|
||||
/// </summary>
|
||||
public NotValidExifFileException()
|
||||
: base("Not a valid JPEG/Exif file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
: base("Not a valid JPEG/EXIF file.")
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidExifFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public NotValidExifFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidExifFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public NotValidExifFileException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidExifFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected NotValidExifFileException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when an invalid enum type is given to an
|
||||
/// ExifEnumProperty.
|
||||
/// </summary>
|
||||
internal class UnknownEnumTypeException : Exception
|
||||
{
|
||||
public UnknownEnumTypeException()
|
||||
: base("Unknown enum type.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public UnknownEnumTypeException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace Umbraco.Web.Media.Exif
|
||||
return new ExifInterOperability(tagid, 3, 1, ExifBitConverter.GetBytes((ushort)((object)mValue), BitConverterEx.SystemByteOrder, BitConverterEx.SystemByteOrder));
|
||||
}
|
||||
else
|
||||
throw new UnknownEnumTypeException();
|
||||
throw new InvalidOperationException($"An invalid enum type ({basetype.FullName}) was provided for type {type.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,99 +1,171 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Media.Exif
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the image file
|
||||
/// could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidImageFileException : Exception
|
||||
{
|
||||
public NotValidImageFileException()
|
||||
: base("Not a valid image file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
public NotValidImageFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the JPEG file
|
||||
/// could not be understood.
|
||||
/// The exception that is thrown when the format of the JPEG file could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidJPEGFileException : Exception
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class NotValidJPEGFileException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidJPEGFileException" /> class.
|
||||
/// </summary>
|
||||
public NotValidJPEGFileException()
|
||||
: base("Not a valid JPEG file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidJPEGFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public NotValidJPEGFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidJPEGFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public NotValidJPEGFileException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidJPEGFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected NotValidJPEGFileException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF file
|
||||
/// could not be understood.
|
||||
/// The exception that is thrown when the format of the TIFF file could not be understood.
|
||||
/// </summary>
|
||||
internal class NotValidTIFFileException : Exception
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class NotValidTIFFileException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFileException" /> class.
|
||||
/// </summary>
|
||||
public NotValidTIFFileException()
|
||||
: base("Not a valid TIFF file.")
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public NotValidTIFFileException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public NotValidTIFFileException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected NotValidTIFFileException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the format of the TIFF header
|
||||
/// could not be understood.
|
||||
/// The exception that is thrown when the format of the TIFF header could not be understood.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
internal class NotValidTIFFHeader : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFHeader" /> class.
|
||||
/// </summary>
|
||||
public NotValidTIFFHeader()
|
||||
: base("Not a valid TIFF header.")
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFHeader" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public NotValidTIFFHeader(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFHeader" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public NotValidTIFFHeader(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="NotValidTIFFHeader" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected NotValidTIFFHeader(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the length of a section exceeds 64 kB.
|
||||
/// </summary>
|
||||
internal class SectionExceeds64KBException : Exception
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class SectionExceeds64KBException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionExceeds64KBException" /> class.
|
||||
/// </summary>
|
||||
public SectionExceeds64KBException()
|
||||
: base("Section length exceeds 64 kB.")
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionExceeds64KBException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public SectionExceeds64KBException(string message)
|
||||
: base(message)
|
||||
{
|
||||
;
|
||||
}
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionExceeds64KBException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public SectionExceeds64KBException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SectionExceeds64KBException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected SectionExceeds64KBException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Umbraco.Web.Media.TypeDetector
|
||||
{
|
||||
document = XDocument.Load(fileStream);
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
catch (System.Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Umbraco.Web
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of cultures that have property validation errors errors
|
||||
/// Returns a list of cultures that have property validation errors
|
||||
/// </summary>
|
||||
/// <param name="modelState"></param>
|
||||
/// <param name="localizationService"></param>
|
||||
|
||||
@@ -16,20 +16,32 @@ namespace Umbraco.Web.Models
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "group")]
|
||||
public string Group { get; set; }
|
||||
|
||||
[DataMember(Name = "groupOrder")]
|
||||
public int GroupOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "hidden")]
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
[DataMember(Name = "allowDisable")]
|
||||
public bool AllowDisable { get; set; }
|
||||
|
||||
[DataMember(Name = "requiredSections")]
|
||||
public List<string> RequiredSections { get; set; }
|
||||
|
||||
[DataMember(Name = "steps")]
|
||||
public BackOfficeTourStep[] Steps { get; set; }
|
||||
|
||||
[DataMember(Name = "culture")]
|
||||
public string Culture { get; set; }
|
||||
|
||||
[DataMember(Name = "contentType")]
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,5 +49,11 @@ namespace Umbraco.Web.Models
|
||||
/// </summary>
|
||||
[DataMember(Name = "generatedPassword")]
|
||||
public string GeneratedPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The id of the user - required to allow changing password without the entire UserSave model
|
||||
/// </summary>
|
||||
[DataMember(Name = "id")]
|
||||
public int Id { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,11 +53,21 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[ReadOnly(true)]
|
||||
public string Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The segment of the property
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The segment value of a property can always be null but can only have a non-null value
|
||||
/// when the property can be varied by segment.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "segment")]
|
||||
[ReadOnly(true)]
|
||||
public string Segment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used internally during model mapping
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal IDataEditor PropertyEditor { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,17 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
internal class ContentPropertyDto : ContentPropertyBasic
|
||||
{
|
||||
public IDataType DataType { get; set; }
|
||||
|
||||
public string Label { get; set; }
|
||||
|
||||
public string Description { get; set; }
|
||||
|
||||
public bool IsRequired { get; set; }
|
||||
|
||||
public string IsRequiredMessage { get; set; }
|
||||
|
||||
public string ValidationRegExp { get; set; }
|
||||
|
||||
public string ValidationRegExpMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "culture")]
|
||||
public string Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The segment of this variant, if this is invariant than this is null or empty
|
||||
/// </summary>
|
||||
[DataMember(Name = "segment")]
|
||||
public string Segment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the variant should be updated
|
||||
/// </summary>
|
||||
|
||||
36
src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs
Normal file
36
src/Umbraco.Web/Models/ContentEditing/DataTypeReferences.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
[DataContract(Name = "dataTypeReferences", Namespace = "")]
|
||||
public class DataTypeReferences
|
||||
{
|
||||
[DataMember(Name = "documentTypes")]
|
||||
public IEnumerable<ContentTypeReferences> DocumentTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataMember(Name = "mediaTypes")]
|
||||
public IEnumerable<ContentTypeReferences> MediaTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataMember(Name = "memberTypes")]
|
||||
public IEnumerable<ContentTypeReferences> MemberTypes { get; set; } = Enumerable.Empty<ContentTypeReferences>();
|
||||
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class ContentTypeReferences : EntityBasic
|
||||
{
|
||||
[DataMember(Name = "properties")]
|
||||
public object Properties { get; set; }
|
||||
|
||||
[DataContract(Name = "property", Namespace = "")]
|
||||
public class PropertyTypeReferences
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,5 +16,10 @@
|
||||
/// along with any content types that have matching property types that are included in the filtered content types
|
||||
/// </summary>
|
||||
public string[] FilterContentTypes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Wether the content type is currently marked as an element type
|
||||
/// </summary>
|
||||
public bool IsElement { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataContract(Name = "contentType", Namespace = "")]
|
||||
public class MediaTypeDisplay : ContentTypeCompositionDisplay<PropertyTypeDisplay>
|
||||
{
|
||||
|
||||
[DataMember(Name = "isSystemMediaType")]
|
||||
public bool IsSystemMediaType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.ContentEditing;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
@@ -15,6 +16,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
public MemberDisplay()
|
||||
{
|
||||
MemberProviderFieldMapping = new Dictionary<string, string>();
|
||||
ContentApps = new List<ContentApp>();
|
||||
}
|
||||
|
||||
[DataMember(Name = "username")]
|
||||
@@ -34,5 +36,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "fieldConfig")]
|
||||
public IDictionary<string, string> MemberProviderFieldMapping { get; set; }
|
||||
|
||||
[DataMember(Name = "apps")]
|
||||
public IEnumerable<ContentApp> ContentApps { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,13 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "mandatory")]
|
||||
public bool Mandatory { get; set; }
|
||||
|
||||
[DataMember(Name = "mandatoryMessage")]
|
||||
public string MandatoryMessage { get; set; }
|
||||
|
||||
[DataMember(Name = "pattern")]
|
||||
public string Pattern { get; set; }
|
||||
|
||||
[DataMember(Name = "patternMessage")]
|
||||
public string PatternMessage { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember(Name = "parentObjectType", IsRequired = true)]
|
||||
public Guid ParentObjectType { get; set; }
|
||||
public Guid? ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Parent's object type name.
|
||||
@@ -38,7 +38,7 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to the NodeObjectType in the umbracoNode table</remarks>
|
||||
[DataMember(Name = "childObjectType", IsRequired = true)]
|
||||
public Guid ChildObjectType { get; set; }
|
||||
public Guid? ChildObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Child's object type name.
|
||||
@@ -47,13 +47,6 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[ReadOnly(true)]
|
||||
public string ChildObjectTypeName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the relations associated with this relation type.
|
||||
/// </summary>
|
||||
[DataMember(Name = "relations")]
|
||||
[ReadOnly(true)]
|
||||
public IEnumerable<RelationDisplay> Relations { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// This is used to add custom localized messages/strings to the response for the app to use for localized UI purposes.
|
||||
/// </summary>
|
||||
|
||||
@@ -16,12 +16,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// Gets or sets the parent object type ID.
|
||||
/// </summary>
|
||||
[DataMember(Name = "parentObjectType", IsRequired = false)]
|
||||
public Guid ParentObjectType { get; set; }
|
||||
public Guid? ParentObjectType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the child object type ID.
|
||||
/// </summary>
|
||||
[DataMember(Name = "childObjectType", IsRequired = false)]
|
||||
public Guid ChildObjectType { get; set; }
|
||||
public Guid? ChildObjectType { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,5 +33,11 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// </summary>
|
||||
[DataMember(Name = "userCount")]
|
||||
public int UserCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Is the user group a system group e.g. "Administrators", "Sensitive data" or "Translators"
|
||||
/// </summary>
|
||||
[DataMember(Name = "isSystemUserGroup")]
|
||||
public bool IsSystemUserGroup { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +70,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
dest.Culture = culture;
|
||||
|
||||
// Get the segment, which is always allowed to be null even if the propertyType *can* be varied by segment.
|
||||
// There is therefore no need to perform the null check like with culture above.
|
||||
var segment = !property.PropertyType.VariesBySegment() ? null : context.GetSegment();
|
||||
dest.Segment = segment;
|
||||
|
||||
// if no 'IncludeProperties' were specified or this property is set to be included - we will map the value and return.
|
||||
dest.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture);
|
||||
dest.Value = editor.GetValueEditor().ToEditor(property, DataTypeService, culture, segment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//add the validation information
|
||||
dest.Validation.Mandatory = originalProp.PropertyType.Mandatory;
|
||||
dest.Validation.MandatoryMessage = originalProp.PropertyType.MandatoryMessage;
|
||||
dest.Validation.Pattern = originalProp.PropertyType.ValidationRegExp;
|
||||
dest.Validation.PatternMessage = originalProp.PropertyType.ValidationRegExpMessage;
|
||||
|
||||
if (dest.PropertyEditor == null)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,9 @@ namespace Umbraco.Web.Models.Mapping
|
||||
base.Map(property, dest, context);
|
||||
|
||||
dest.IsRequired = property.PropertyType.Mandatory;
|
||||
dest.IsRequiredMessage = property.PropertyType.MandatoryMessage;
|
||||
dest.ValidationRegExp = property.PropertyType.ValidationRegExp;
|
||||
dest.ValidationRegExpMessage = property.PropertyType.ValidationRegExpMessage;
|
||||
dest.Description = property.PropertyType.Description;
|
||||
dest.Label = property.PropertyType.Name;
|
||||
dest.DataType = DataTypeService.GetDataType(property.PropertyType.DataTypeId);
|
||||
|
||||
@@ -8,6 +8,7 @@ using Umbraco.Core.Models;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
@@ -144,6 +145,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
//default listview
|
||||
target.ListViewEditorName = Constants.Conventions.DataTypes.ListViewPrefix + "Media";
|
||||
target.IsSystemMediaType = source.IsSystemMediaType();
|
||||
|
||||
if (string.IsNullOrEmpty(source.Name)) return;
|
||||
|
||||
@@ -214,16 +216,18 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -CreateDate -DeleteDate -UpdateDate
|
||||
// Umbraco.Code.MapAll -SupportsPublishing -Key -PropertyEditorAlias -ValueStorageType
|
||||
// Umbraco.Code.MapAll -SupportsPublishing -Key -PropertyEditorAlias -ValueStorageType -Variations
|
||||
private static void Map(PropertyTypeBasic source, PropertyType target, MapperContext context)
|
||||
{
|
||||
target.Name = source.Label;
|
||||
target.DataTypeId = source.DataTypeId;
|
||||
target.DataTypeKey = source.DataTypeKey;
|
||||
target.Mandatory = source.Validation.Mandatory;
|
||||
target.MandatoryMessage = source.Validation.MandatoryMessage;
|
||||
target.ValidationRegExp = source.Validation.Pattern;
|
||||
target.Variations = source.AllowCultureVariant ? ContentVariation.Culture : ContentVariation.Nothing;
|
||||
|
||||
target.ValidationRegExpMessage = source.Validation.PatternMessage;
|
||||
target.SetVariesBy(ContentVariation.Culture, source.AllowCultureVariant);
|
||||
|
||||
if (source.Id > 0)
|
||||
target.Id = source.Id;
|
||||
|
||||
@@ -364,7 +368,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Validation = source.Validation;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -CreatorId -Level -SortOrder
|
||||
// Umbraco.Code.MapAll -CreatorId -Level -SortOrder -Variations
|
||||
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate
|
||||
// Umbraco.Code.MapAll -ContentTypeComposition (done by AfterMapSaveToType)
|
||||
private static void MapSaveToTypeBase<TSource, TSourcePropertyType>(TSource source, IContentTypeComposition target, MapperContext context)
|
||||
@@ -394,9 +398,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
if (!(target is IMemberType))
|
||||
{
|
||||
target.Variations = ContentVariation.Nothing;
|
||||
if (source.AllowCultureVariant)
|
||||
target.Variations |= ContentVariation.Culture;
|
||||
target.SetVariesBy(ContentVariation.Culture, source.AllowCultureVariant);
|
||||
}
|
||||
|
||||
// handle property groups and property types
|
||||
@@ -487,7 +489,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Udi = MapContentTypeUdi(source);
|
||||
target.UpdateDate = source.UpdateDate;
|
||||
|
||||
target.AllowedContentTypes = source.AllowedContentTypes.Select(x => x.Id.Value);
|
||||
target.AllowedContentTypes = source.AllowedContentTypes.OrderBy(c => c.SortOrder).Select(x => x.Id.Value);
|
||||
target.CompositeContentTypes = source.ContentTypeComposition.Select(x => x.Alias);
|
||||
target.LockedCompositeContentTypes = MapLockedCompositions(source);
|
||||
}
|
||||
@@ -577,7 +579,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
udiType = Constants.UdiEntityType.DocumentType;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("panic");
|
||||
throw new PanicException($"Source is of type {source.GetType()} which isn't supported here");
|
||||
}
|
||||
|
||||
return Udi.Create(udiType, source.Key);
|
||||
|
||||
@@ -21,52 +21,117 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public IEnumerable<ContentVariantDisplay> Map(IContent source, MapperContext context)
|
||||
{
|
||||
var result = new List<ContentVariantDisplay>();
|
||||
if (!source.ContentType.VariesByCulture())
|
||||
var variesByCulture = source.ContentType.VariesByCulture();
|
||||
var variesBySegment = source.ContentType.VariesBySegment();
|
||||
|
||||
IList<ContentVariantDisplay> variants = new List<ContentVariantDisplay>();
|
||||
|
||||
if (!variesByCulture && !variesBySegment)
|
||||
{
|
||||
//this is invariant so just map the IContent instance to ContentVariationDisplay
|
||||
result.Add(context.Map<ContentVariantDisplay>(source));
|
||||
// this is invariant so just map the IContent instance to ContentVariationDisplay
|
||||
var variantDisplay = context.Map<ContentVariantDisplay>(source);
|
||||
variants.Add(variantDisplay);
|
||||
}
|
||||
else if (variesByCulture && !variesBySegment)
|
||||
{
|
||||
var languages = GetLanguages(context);
|
||||
variants = languages
|
||||
.Select(language => CreateVariantDisplay(context, source, language, null))
|
||||
.ToList();
|
||||
}
|
||||
else if (variesBySegment && !variesByCulture)
|
||||
{
|
||||
// Segment only
|
||||
var segments = GetSegments(source);
|
||||
variants = segments
|
||||
.Select(segment => CreateVariantDisplay(context, source, null, segment))
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariantDisplay>(); //this should never happen
|
||||
// Culture and segment
|
||||
var languages = GetLanguages(context).ToList();
|
||||
var segments = GetSegments(source).ToList();
|
||||
|
||||
var langs = context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
|
||||
|
||||
//create a variant for each language, then we'll populate the values
|
||||
var variants = langs.Select(x =>
|
||||
if (languages.Count == 0 || segments.Count == 0)
|
||||
{
|
||||
//We need to set the culture in the mapping context since this is needed to ensure that the correct property values
|
||||
//are resolved during the mapping
|
||||
context.SetCulture(x.IsoCode);
|
||||
return context.Map<ContentVariantDisplay>(source);
|
||||
}).ToList();
|
||||
|
||||
for (int i = 0; i < langs.Count; i++)
|
||||
{
|
||||
var x = langs[i];
|
||||
var variant = variants[i];
|
||||
|
||||
variant.Language = x;
|
||||
variant.Name = source.GetCultureName(x.IsoCode);
|
||||
// This should not happen
|
||||
throw new InvalidOperationException("No languages or segments available");
|
||||
}
|
||||
|
||||
//Put the default language first in the list & then sort rest by a-z
|
||||
var defaultLang = variants.SingleOrDefault(x => x.Language.IsDefault);
|
||||
variants = languages
|
||||
.SelectMany(language => segments
|
||||
.Select(segment => CreateVariantDisplay(context, source, language, segment)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
//Remove the default language from the list for now
|
||||
variants.Remove(defaultLang);
|
||||
|
||||
//Sort the remaining languages a-z
|
||||
variants = variants.OrderBy(x => x.Language.Name).ToList();
|
||||
|
||||
//Insert the default language as the first item
|
||||
variants.Insert(0, defaultLang);
|
||||
return SortVariants(variants);
|
||||
}
|
||||
|
||||
private IList<ContentVariantDisplay> SortVariants(IList<ContentVariantDisplay> variants)
|
||||
{
|
||||
if (variants == null || variants.Count <= 1)
|
||||
{
|
||||
return variants;
|
||||
}
|
||||
return result;
|
||||
|
||||
// Default variant first, then order by language, segment.
|
||||
return variants
|
||||
.OrderBy(v => IsDefaultLanguage(v) ? 0 : 1)
|
||||
.ThenBy(v => IsDefaultSegment(v) ? 0 : 1)
|
||||
.ThenBy(v => v?.Language?.Name)
|
||||
.ThenBy(v => v.Segment)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static bool IsDefaultSegment(ContentVariantDisplay variant)
|
||||
{
|
||||
return variant.Segment == null;
|
||||
}
|
||||
|
||||
private static bool IsDefaultLanguage(ContentVariantDisplay variant)
|
||||
{
|
||||
return variant.Language == null || variant.Language.IsDefault;
|
||||
}
|
||||
|
||||
private IEnumerable<Language> GetLanguages(MapperContext context)
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0)
|
||||
{
|
||||
// This should never happen
|
||||
return Enumerable.Empty<Language>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all segments assigned to the content
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns>
|
||||
/// Returns all segments assigned to the content including 'null' values
|
||||
/// </returns>
|
||||
private IEnumerable<string> GetSegments(IContent content)
|
||||
{
|
||||
return content.Properties.SelectMany(p => p.Values.Select(v => v.Segment)).Distinct();
|
||||
}
|
||||
|
||||
private ContentVariantDisplay CreateVariantDisplay(MapperContext context, IContent content, Language language, string segment)
|
||||
{
|
||||
context.SetCulture(language?.IsoCode);
|
||||
context.SetSegment(segment);
|
||||
|
||||
var variantDisplay = context.Map<ContentVariantDisplay>(content);
|
||||
|
||||
variantDisplay.Segment = segment;
|
||||
variantDisplay.Language = language;
|
||||
variantDisplay.Name = content.GetCultureName(language?.IsoCode);
|
||||
|
||||
return variantDisplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,14 +42,20 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Trashed = source.Trashed;
|
||||
target.Udi = Udi.Create(ObjectTypes.GetUdiType(source.NodeObjectType), source.Key);
|
||||
|
||||
if (source.NodeObjectType == Constants.ObjectTypes.Member && target.Icon.IsNullOrWhiteSpace())
|
||||
target.Icon = Constants.Icons.Member;
|
||||
|
||||
if (source is IContentEntitySlim contentSlim)
|
||||
{
|
||||
source.AdditionalData["ContentTypeAlias"] = contentSlim.ContentTypeAlias;
|
||||
}
|
||||
|
||||
if (source is IDocumentEntitySlim documentSlim)
|
||||
{
|
||||
source.AdditionalData["IsPublished"] = documentSlim.Published;
|
||||
}
|
||||
|
||||
if (source is IMediaEntitySlim mediaSlim)
|
||||
{
|
||||
source.AdditionalData["MediaPath"] = mediaSlim.MediaPath;
|
||||
}
|
||||
|
||||
// NOTE: we're mapping the objects in AdditionalData by object reference here.
|
||||
// it works fine for now, but it's something to keep in mind in the future
|
||||
@@ -171,6 +177,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
target.Name = source.Values.ContainsKey("nodeName") ? source.Values["nodeName"] : "[no name]";
|
||||
|
||||
if (source.Values.TryGetValue(UmbracoExamineIndex.UmbracoFileFieldName, out var umbracoFile))
|
||||
{
|
||||
if (umbracoFile != null)
|
||||
{
|
||||
target.Name = $"{target.Name} ({umbracoFile})";
|
||||
}
|
||||
}
|
||||
|
||||
if (source.Values.ContainsKey(UmbracoExamineIndex.NodeKeyFieldName))
|
||||
{
|
||||
if (Guid.TryParse(source.Values[UmbracoExamineIndex.NodeKeyFieldName], out var key))
|
||||
@@ -217,7 +231,18 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
|
||||
private static string MapContentTypeIcon(IEntitySlim entity)
|
||||
=> entity is ContentEntitySlim contentEntity ? contentEntity.ContentTypeIcon : null;
|
||||
{
|
||||
switch (entity)
|
||||
{
|
||||
case IMemberEntitySlim memberEntity:
|
||||
return memberEntity.ContentTypeIcon.IfNullOrWhiteSpace(Constants.Icons.Member);
|
||||
case IContentEntitySlim contentEntity:
|
||||
// NOTE: this case covers both content and media entities
|
||||
return contentEntity.ContentTypeIcon;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string MapName(IEntitySlim source, MapperContext context)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -23,6 +24,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<IMacro, EntityBasic>((source, context) => new EntityBasic(), Map);
|
||||
mapper.Define<IMacro, MacroDisplay>((source, context) => new MacroDisplay(), Map);
|
||||
mapper.Define<IMacro, IEnumerable<MacroParameter>>((source, context) => context.MapEnumerable<IMacroProperty, MacroParameter>(source.Properties.Values));
|
||||
mapper.Define<IMacroProperty, MacroParameter>((source, context) => new MacroParameter(), Map);
|
||||
}
|
||||
@@ -40,6 +42,23 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
|
||||
}
|
||||
|
||||
private void Map(IMacro source, MacroDisplay target, MapperContext context)
|
||||
{
|
||||
target.Alias = source.Alias;
|
||||
target.Icon = Constants.Icons.Macro;
|
||||
target.Id = source.Id;
|
||||
target.Key = source.Key;
|
||||
target.Name = source.Name;
|
||||
target.ParentId = -1;
|
||||
target.Path = "-1," + source.Id;
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
|
||||
target.CacheByPage = source.CacheByPage;
|
||||
target.CacheByUser = source.CacheByMember;
|
||||
target.CachePeriod = source.CacheDuration;
|
||||
target.UseInEditor = source.UseInEditor;
|
||||
target.RenderInEditor = !source.DontRender;
|
||||
target.View = source.MacroSource;
|
||||
}
|
||||
// Umbraco.Code.MapAll -Value
|
||||
private void Map(IMacroProperty source, MacroParameter target, MapperContext context)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
internal static class MapperContextExtensions
|
||||
{
|
||||
private const string CultureKey = "Map.Culture";
|
||||
private const string SegmentKey = "Map.Segment";
|
||||
private const string IncludedPropertiesKey = "Map.IncludedProperties";
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +19,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return context.HasItems && context.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context segment.
|
||||
/// </summary>
|
||||
public static string GetSegment(this MapperContext context)
|
||||
{
|
||||
return context.HasItems && context.Items.TryGetValue(SegmentKey, out var obj) && obj is string s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a context culture.
|
||||
/// </summary>
|
||||
@@ -26,6 +35,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
context.Items[CultureKey] = culture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a context segment.
|
||||
/// </summary>
|
||||
public static void SetSegment(this MapperContext context, string segment)
|
||||
{
|
||||
context.Items[SegmentKey] = segment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get included properties.
|
||||
/// </summary>
|
||||
@@ -42,4 +59,4 @@ namespace Umbraco.Web.Models.Mapping
|
||||
context.Items[IncludedPropertiesKey] = properties;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
// Umbraco.Code.MapAll -Trashed -IsContainer -VariesByCulture
|
||||
private void Map(IMember source, MemberDisplay target, MapperContext context)
|
||||
{
|
||||
target.ContentApps = _commonMapper.GetContentApps(source);
|
||||
target.ContentTypeId = source.ContentType.Id;
|
||||
target.ContentTypeAlias = source.ContentType.Alias;
|
||||
target.ContentTypeName = source.ContentType.Name;
|
||||
@@ -144,7 +145,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Id = source.Id;
|
||||
target.Key = source.Key;
|
||||
target.Name = source.Name;
|
||||
target.Path = "-1" + source.Id;
|
||||
target.Path = $"-1,{source.Id}";
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.MemberGroup, source.Key);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,13 @@ namespace Umbraco.Web.Models.Mapping
|
||||
Alias = p.Alias,
|
||||
Description = p.Description,
|
||||
Editor = p.PropertyEditorAlias,
|
||||
Validation = new PropertyTypeValidation {Mandatory = p.Mandatory, Pattern = p.ValidationRegExp},
|
||||
Validation = new PropertyTypeValidation
|
||||
{
|
||||
Mandatory = p.Mandatory,
|
||||
MandatoryMessage = p.MandatoryMessage,
|
||||
Pattern = p.ValidationRegExp,
|
||||
PatternMessage = p.ValidationRegExpMessage,
|
||||
},
|
||||
Label = p.Name,
|
||||
View = propertyEditor.GetValueEditor().View,
|
||||
Config = config,
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
using Umbraco.Core;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
|
||||
namespace Umbraco.Web.Models.Mapping
|
||||
{
|
||||
internal class RelationMapDefinition : IMapDefinition
|
||||
{
|
||||
private readonly IEntityService _entityService;
|
||||
private readonly IRelationService _relationService;
|
||||
|
||||
public RelationMapDefinition(IEntityService entityService, IRelationService relationService)
|
||||
{
|
||||
_entityService = entityService;
|
||||
_relationService = relationService;
|
||||
}
|
||||
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<IRelationType, RelationTypeDisplay>((source, context) => new RelationTypeDisplay(), Map);
|
||||
@@ -15,8 +26,8 @@ namespace Umbraco.Web.Models.Mapping
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -Icon -Trashed -AdditionalData
|
||||
// Umbraco.Code.MapAll -Relations -ParentId -Notifications
|
||||
private static void Map(IRelationType source, RelationTypeDisplay target, MapperContext context)
|
||||
// Umbraco.Code.MapAll -ParentId -Notifications
|
||||
private void Map(IRelationType source, RelationTypeDisplay target, MapperContext context)
|
||||
{
|
||||
target.ChildObjectType = source.ChildObjectType;
|
||||
target.Id = source.Id;
|
||||
@@ -28,18 +39,32 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.RelationType, source.Key);
|
||||
target.Path = "-1," + source.Id;
|
||||
|
||||
// Set the "friendly" names for the parent and child object types
|
||||
target.ParentObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType).GetFriendlyName();
|
||||
target.ChildObjectTypeName = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType).GetFriendlyName();
|
||||
// Set the "friendly" and entity names for the parent and child object types
|
||||
if (source.ParentObjectType.HasValue)
|
||||
{
|
||||
var objType = ObjectTypes.GetUmbracoObjectType(source.ParentObjectType.Value);
|
||||
target.ParentObjectTypeName = objType.GetFriendlyName();
|
||||
}
|
||||
|
||||
if (source.ChildObjectType.HasValue)
|
||||
{
|
||||
var objType = ObjectTypes.GetUmbracoObjectType(source.ChildObjectType.Value);
|
||||
target.ChildObjectTypeName = objType.GetFriendlyName();
|
||||
}
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -ParentName -ChildName
|
||||
private static void Map(IRelation source, RelationDisplay target, MapperContext context)
|
||||
private void Map(IRelation source, RelationDisplay target, MapperContext context)
|
||||
{
|
||||
target.ChildId = source.ChildId;
|
||||
target.Comment = source.Comment;
|
||||
target.CreateDate = source.CreateDate;
|
||||
target.ParentId = source.ParentId;
|
||||
|
||||
var entities = _relationService.GetEntitiesFromRelation(source);
|
||||
|
||||
target.ParentName = entities.Item1.Name;
|
||||
target.ChildName = entities.Item2.Name;
|
||||
}
|
||||
|
||||
// Umbraco.Code.MapAll -CreateDate -UpdateDate -DeleteDate
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user