Merge remote-tracking branch 'origin/dev-v7' into dev-v8
# Conflicts: # build/Modules/Umbraco.Build/Umbraco.Build.psm1 # src/SolutionInfo.cs # src/Umbraco.Core/Configuration/UmbracoVersion.cs # src/Umbraco.Core/DatabaseContext.cs # src/Umbraco.Core/IO/FileSystemProviderManager.cs # src/Umbraco.Core/ObjectExtensions.cs # src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenSevenZero/AddUserGroupTables.cs # src/Umbraco.Core/PropertyEditors/ValueConverters/IntegerValueConverter.cs # src/Umbraco.Core/Services/IdkMap.cs # src/Umbraco.Core/Services/UserService.cs # src/Umbraco.Core/packages.config # src/Umbraco.Tests.Benchmarks/app.config # src/Umbraco.Tests/UI/LegacyDialogTests.cs # src/Umbraco.Web/Cache/MediaCacheRefresher.cs # src/Umbraco.Web/Cache/UnpublishedPageCacheRefresher.cs # src/Umbraco.Web/Features/DisabledFeatures.cs # src/Umbraco.Web/Mvc/RenderRouteHandler.cs # src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs # src/Umbraco.Web/PublishedCache/ContextualPublishedCache.cs # src/Umbraco.Web/PublishedCache/ContextualPublishedContentCache.cs # src/Umbraco.Web/PublishedCache/ContextualPublishedMediaCache.cs # src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedContentCache.cs # src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs # src/Umbraco.Web/PublishedContentQuery.cs # src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs # src/Umbraco.Web/packages.config # src/Umbraco.Web/umbraco.presentation/umbraco/create/MemberGroupTasks.cs
This commit is contained in:
@@ -119,6 +119,7 @@ build/ApiDocs/*
|
||||
build/ApiDocs/Output/*
|
||||
src/Umbraco.Web.UI.Client/bower_components/*
|
||||
/src/Umbraco.Web.UI/Umbraco/preview
|
||||
/src/Umbraco.Web.UI/Umbraco/preview.old
|
||||
|
||||
#Ignore Rule for output of generated documentation files from Grunt docserve
|
||||
src/Umbraco.Web.UI.Client/docs/api
|
||||
|
||||
@@ -28,7 +28,7 @@ As an Open Source platform, Umbraco is more than just a CMS. We are transparent
|
||||
|
||||
[Umbraco Cloud](https://umbraco.com) is the easiest and fastest way to use Umbraco yet with full support for all your custom .NET code and intergrations. You're up and running in less than a minute and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14 day trial, no credit card needed.
|
||||
|
||||
If you want to DIY you can [download Umbraco](https://our.umbraco.org/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Xloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
|
||||
If you want to DIY you can [download Umbraco](https://our.umbraco.org/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
|
||||
|
||||
## Community
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ namespace Umbraco.Core.Configuration
|
||||
Current.Minor,
|
||||
Current.Build,
|
||||
CurrentComment.IsNullOrWhiteSpace() ? null : CurrentComment,
|
||||
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
|
||||
|
||||
Current.Revision > 0 ? Current.Revision.ToInvariantString() : null);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the "local" version of the site.
|
||||
/// </summary>
|
||||
@@ -51,8 +51,8 @@ namespace Umbraco.Core.Configuration
|
||||
/// and changes during an upgrade. The executing code version changes when new code is
|
||||
/// deployed. The site/files version changes during an upgrade.</para>
|
||||
/// </remarks>
|
||||
public static SemVersion Local
|
||||
{
|
||||
public static SemVersion Local
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
@@ -66,6 +66,6 @@ namespace Umbraco.Core.Configuration
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
public const string MasterpageFileSystem = "MasterpageFileSystem";
|
||||
public const string ViewFileSystem = "ViewFileSystem";
|
||||
public const string XsltFileSystem = "XsltFileSystem";
|
||||
public const string JavascriptLibraryFileSystem = "JavascriptLibraryFileSystem";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
@@ -28,7 +29,7 @@ namespace Umbraco.Core.IO
|
||||
private ShadowWrapper _xsltFileSystem;
|
||||
private ShadowWrapper _masterPagesFileSystem;
|
||||
private ShadowWrapper _mvcViewsFileSystem;
|
||||
|
||||
|
||||
// well-known file systems lazy initialization
|
||||
private object _wkfsLock = new object();
|
||||
private bool _wkfsInitialized;
|
||||
@@ -36,6 +37,10 @@ namespace Umbraco.Core.IO
|
||||
|
||||
private MediaFileSystem _mediaFileSystem;
|
||||
|
||||
|
||||
//fixme - is this needed to be a managed file system? seems irrelevant since it won't ever be moved and is only used in one place in code
|
||||
private IFileSystem _javascriptLibraryFileSystem;
|
||||
|
||||
#region Constructor
|
||||
|
||||
// DI wants a public ctor
|
||||
@@ -134,6 +139,16 @@ namespace Umbraco.Core.IO
|
||||
}
|
||||
}
|
||||
|
||||
//fixme - is this needed to be a managed file system? seems irrelevant since it won't ever be moved and is only used in one place in code
|
||||
internal IFileSystem JavascriptLibraryFileSystem
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Volatile.Read(ref _wkfsInitialized) == false) EnsureWellKnownFileSystems();
|
||||
return _javascriptLibraryFileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
private void EnsureWellKnownFileSystems()
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(ref _wkfsObject, ref _wkfsInitialized, ref _wkfsLock, CreateWellKnownFileSystems);
|
||||
@@ -150,6 +165,7 @@ namespace Umbraco.Core.IO
|
||||
var xsltFileSystem = new PhysicalFileSystem(SystemDirectories.Xslt);
|
||||
var masterPagesFileSystem = new PhysicalFileSystem(SystemDirectories.Masterpages);
|
||||
var mvcViewsFileSystem = new PhysicalFileSystem(SystemDirectories.MvcViews);
|
||||
|
||||
|
||||
_macroPartialFileSystem = new ShadowWrapper(macroPartialFileSystem, "Views/MacroPartials", () => IsScoped());
|
||||
_partialViewsFileSystem = new ShadowWrapper(partialViewsFileSystem, "Views/Partials", () => IsScoped());
|
||||
@@ -159,6 +175,8 @@ namespace Umbraco.Core.IO
|
||||
_masterPagesFileSystem = new ShadowWrapper(masterPagesFileSystem, "masterpages", () => IsScoped());
|
||||
_mvcViewsFileSystem = new ShadowWrapper(mvcViewsFileSystem, "Views", () => IsScoped());
|
||||
|
||||
_javascriptLibraryFileSystem = new PhysicalFileSystem(Path.Combine(SystemDirectories.Umbraco, "lib"));
|
||||
|
||||
// filesystems obtained from GetFileSystemProvider are already wrapped and do not need to be wrapped again
|
||||
_mediaFileSystem = GetFileSystemProvider<MediaFileSystem>();
|
||||
|
||||
|
||||
@@ -181,7 +181,6 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
get
|
||||
{
|
||||
//by default the packages folder should exist in the data folder
|
||||
return IOHelper.ReturnPath("umbracoPreviewPath", Data + IOHelper.DirSepChar + "preview");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,6 +302,22 @@ namespace Umbraco.Core.Migrations.Install
|
||||
var connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
|
||||
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
|
||||
|
||||
// honour configSource, if its set, change the xml file we are saving the configuration
|
||||
// to the one set in the configSource attribute
|
||||
if (connectionStrings.Attribute("configSource") != null)
|
||||
{
|
||||
var source = connectionStrings.Attribute("configSource").Value;
|
||||
var configFile = IOHelper.MapPath($"{SystemDirectories.Root}/{source}");
|
||||
logger.Info<DatabaseBuilder>("storing ConnectionString in {0}", () => configFile);
|
||||
if (File.Exists(configFile))
|
||||
{
|
||||
xml = XDocument.Load(fileName, LoadOptions.PreserveWhitespace);
|
||||
fileName = configFile;
|
||||
}
|
||||
connectionStrings = xml.Root.DescendantsAndSelf("connectionStrings").FirstOrDefault();
|
||||
if (connectionStrings == null) throw new Exception("Invalid web.config file.");
|
||||
}
|
||||
|
||||
// update connectionString if it exists, or else create a new connectionString
|
||||
var setting = connectionStrings.Descendants("add").FirstOrDefault(s => s.Attribute("name").Value == Constants.System.UmbracoConnectionName);
|
||||
if (setting == null)
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.IO;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using File = System.IO.File;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_7_10_0
|
||||
{
|
||||
/// <summary>
|
||||
/// Renames the preview folder containing static html files to ensure it does not interfere with the MVC route
|
||||
/// that is now supposed to render these views dynamically. We don't want to delete as people may have made
|
||||
/// customizations to these files that would need to be migrated to the new .cshtml view files.
|
||||
/// </summary>
|
||||
public class RenamePreviewFolder : MigrationBase
|
||||
{
|
||||
public RenamePreviewFolder(IMigrationContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Migrate()
|
||||
{
|
||||
var previewFolderPath = IOHelper.MapPath(SystemDirectories.Umbraco + "/preview");
|
||||
if (Directory.Exists(previewFolderPath))
|
||||
{
|
||||
var newPath = previewFolderPath.Replace("preview", "preview.old");
|
||||
if (Directory.Exists(newPath) == false)
|
||||
{
|
||||
Directory.Move(previewFolderPath, newPath);
|
||||
var readmeText =
|
||||
$"Static html files used for preview and canvas editing functionality no longer live in this directory.\r\n" +
|
||||
$"Instead they have been recreated as MVC views and can now be found in '~/Umbraco/Views/Preview'.\r\n" +
|
||||
$"See issue: http://issues.umbraco.org/issue/U4-11090";
|
||||
File.WriteAllText(Path.Combine(newPath, "readme.txt"), readmeText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Dtos;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using ColumnInfo = Umbraco.Core.Persistence.SqlSyntax.ColumnInfo;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Upgrade.V_7_7_0
|
||||
{
|
||||
@@ -339,14 +342,24 @@ namespace Umbraco.Core.Migrations.Upgrade.V_7_7_0
|
||||
|
||||
if (tables.InvariantContains("umbracoUserType") && tables.InvariantContains("umbracoUser"))
|
||||
{
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
|
||||
if (DatabaseType.IsMySql())
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser").Do();
|
||||
//In MySql, this will drop the FK according to it's special naming rules
|
||||
Delete.ForeignKey().FromTable("umbracoUser").ForeignColumn("userType").ToTable("umbracoUserType").PrimaryColumn("id");
|
||||
}
|
||||
//This is the super old constraint name of the FK for user type so check this one too
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_user_userType")))
|
||||
else
|
||||
{
|
||||
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser").Do();
|
||||
//Delete the FK if it exists before dropping the column
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_umbracoUser_umbracoUserType_id")))
|
||||
{
|
||||
Delete.ForeignKey("FK_umbracoUser_umbracoUserType_id").OnTable("umbracoUser").Do();
|
||||
}
|
||||
|
||||
//This is the super old constraint name of the FK for user type so check this one too
|
||||
if (constraints.Any(x => x.Item1.InvariantEquals("umbracoUser") && x.Item3.InvariantEquals("FK_user_userType")))
|
||||
{
|
||||
Delete.ForeignKey("FK_user_userType").OnTable("umbracoUser").Do();
|
||||
}
|
||||
}
|
||||
|
||||
Delete.Column("userType").FromTable("umbracoUser").Do();
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Umbraco.Core
|
||||
/// <param name="input"></param>
|
||||
public static void DisposeIfDisposable(this object input)
|
||||
{
|
||||
if (input is IDisposable disposable)
|
||||
if (input is IDisposable disposable)
|
||||
disposable.Dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,24 +17,12 @@ namespace Umbraco.Core.PropertyEditors.ValueConverters
|
||||
|
||||
public override object ConvertSourceToIntermediate(IPublishedElement owner, PublishedPropertyType propertyType, object source, bool preview)
|
||||
{
|
||||
if (source == null) return 0;
|
||||
|
||||
// in XML an integer is a string
|
||||
var sourceString = source as string;
|
||||
if (sourceString != null)
|
||||
{
|
||||
int i;
|
||||
return (int.TryParse(sourceString, out i)) ? i : 0;
|
||||
}
|
||||
return source.TryConvertTo<int>().Result;
|
||||
|
||||
// in json an integer comes back as Int64
|
||||
// ignore overflows ;(
|
||||
if (source is long)
|
||||
return Convert.ToInt32(source);
|
||||
|
||||
// in the database an integer is an integer
|
||||
// default value is zero
|
||||
return (source is int) ? source : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Models;
|
||||
@@ -23,12 +24,139 @@ namespace Umbraco.Core.Services
|
||||
|
||||
// note - for pure read-only we might want to *not* enforce a transaction?
|
||||
|
||||
// notes
|
||||
//
|
||||
// - this class assumes that the id/guid map is unique; that is, if an id and a guid map
|
||||
// to each other, then the id will never map to another guid, and the guid will never map
|
||||
// to another id
|
||||
//
|
||||
// - LeeK's solution in 7.7 was to look for the id/guid in the content cache "on demand" via
|
||||
// XPath, which is probably fast enough but cannot deal with media ids + it maintains a
|
||||
// separate, duplicate cache
|
||||
// see https://github.com/umbraco/Umbraco-CMS/pull/2398
|
||||
//
|
||||
// - Andy's solution in a package was to prefetch all by sql; it cannot prefecth reserved ids
|
||||
// as we don't know the corresponding object type, but that's not a big issue - but then we
|
||||
// have a full database query on startup
|
||||
// see https://github.com/AndyButland/UmbracoUdiToIdCache
|
||||
//
|
||||
// - the original IdkMap implementation that was used by services, did a database lookup on
|
||||
// each cache miss, which is fine enough for services, but would be really slow at content
|
||||
// cache level
|
||||
//
|
||||
// - cache is cleared by MediaCacheRefresher, UnpublishedPageCacheRefresher, and other
|
||||
// refreshers - because id/guid map is unique, we only clear to avoid leaking memory, 'cos
|
||||
// we don't risk caching obsolete values - and only when actually deleting
|
||||
//
|
||||
// so...
|
||||
//
|
||||
// - there's a single caching point, and it's idkMap
|
||||
// - there are no "helper methods" - the published content cache itself knows about Guids
|
||||
// - when the published content cache is instanciated, it populates the idkMap with what it knows
|
||||
// and it registers a way for the idkMap to look for id/keys in the published content cache
|
||||
// - we do NOT prefetch anything from database
|
||||
// - when a request comes in:
|
||||
// the published content cache uses the idkMap to map id/key
|
||||
// if the idkMap already knows about the map, it returns the value
|
||||
// else it tries the published cache via XPath
|
||||
// else it hits the database
|
||||
|
||||
|
||||
private readonly ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)> _dictionary
|
||||
= new ConcurrentDictionary<UmbracoObjectTypes, (Func<int, Guid> id2key, Func<Guid, int> key2id)>();
|
||||
|
||||
internal void SetMapper(UmbracoObjectTypes umbracoObjectType, Func<int, Guid> id2key, Func<Guid, int> key2id)
|
||||
{
|
||||
_dictionary[umbracoObjectType] = (id2key, key2id);
|
||||
}
|
||||
|
||||
internal void Populate(IEnumerable<(int id, Guid key)> pairs, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
foreach (var pair in pairs)
|
||||
{
|
||||
_id2Key.Add(pair.id, new TypedId<Guid>(pair.key, umbracoObjectType));
|
||||
_key2Id.Add(pair.key, new TypedId<int>(pair.id, umbracoObjectType));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsWriteLockHeld)
|
||||
_locker.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
#if POPULATE_FROM_DATABASE
|
||||
private void PopulateLocked()
|
||||
{
|
||||
// don't if not empty
|
||||
if (_key2Id.Count > 0) return;
|
||||
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
// populate content and media items
|
||||
var types = new[] { Constants.ObjectTypes.Document, Constants.ObjectTypes.Media };
|
||||
var values = uow.Database.Query<TypedIdDto>("SELECT id, uniqueId, nodeObjectType FROM umbracoNode WHERE nodeObjectType IN @types", new { types });
|
||||
foreach (var value in values)
|
||||
{
|
||||
var umbracoObjectType = UmbracoObjectTypesExtensions.GetUmbracoObjectType(value.NodeObjectType);
|
||||
_id2Key.Add(value.Id, new TypedId<Guid>(value.UniqueId, umbracoObjectType));
|
||||
_key2Id.Add(value.UniqueId, new TypedId<int>(value.Id, umbracoObjectType));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Attempt<int> PopulateAndGetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
|
||||
PopulateLocked();
|
||||
|
||||
return _key2Id.TryGetValue(key, out var id) && id.UmbracoObjectType == umbracoObjectType
|
||||
? Attempt.Succeed(id.Id)
|
||||
: Attempt<int>.Fail();
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
private Attempt<Guid> PopulateAndGetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
|
||||
PopulateLocked();
|
||||
|
||||
return _id2Key.TryGetValue(id, out var key) && key.UmbracoObjectType == umbracoObjectType
|
||||
? Attempt.Succeed(key.Id)
|
||||
: Attempt<Guid>.Fail();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_locker.IsReadLockHeld)
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public Attempt<int> GetIdForKey(Guid key, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
bool empty;
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
if (_key2Id.TryGetValue(key, out var id) && id.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(id.Id);
|
||||
empty = _key2Id.Count == 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -36,20 +164,37 @@ namespace Umbraco.Core.Services
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
|
||||
int? val;
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
var sql = scope.Database.SqlContext.Sql()
|
||||
.Select<NodeDto>(x => x.NodeId).From<NodeDto>().Where<NodeDto>(x => x.UniqueId == key);
|
||||
#if POPULATE_FROM_DATABASE
|
||||
// if cache is empty and looking for a document or a media,
|
||||
// populate the cache at once and return what we found
|
||||
if (empty && (umbracoObjectType == UmbracoObjectTypes.Document || umbracoObjectType == UmbracoObjectTypes.Media))
|
||||
return PopulateAndGetIdForKey(key, umbracoObjectType);
|
||||
#endif
|
||||
|
||||
if (umbracoObjectType != UmbracoObjectTypes.Unknown) // if unknow, don't include in query
|
||||
// optimize for read speed: reading database outside a lock means that we could read
|
||||
// multiple times, but we don't lock the cache while accessing the database = better
|
||||
|
||||
int? val = null;
|
||||
|
||||
if (_dictionary.TryGetValue(umbracoObjectType, out var mappers))
|
||||
if ((val = mappers.key2id(key)) == default(int)) val = null;
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
var objectType = GetNodeObjectTypeGuid(umbracoObjectType);
|
||||
sql = sql.Where<NodeDto>(x => x.NodeObjectType == objectType || x.NodeObjectType == Constants.ObjectTypes.IdReservation); // fixme TEST the OR here!
|
||||
//if it's unknown don't include the nodeObjectType in the query
|
||||
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id", new { id = key});
|
||||
}
|
||||
else
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
|
||||
new { id = key, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
val = scope.Database.ExecuteScalar<int?>(sql);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
if (val == null) return Attempt<int>.Fail();
|
||||
@@ -83,12 +228,23 @@ namespace Umbraco.Core.Services
|
||||
return GetIdForKey(guidUdi.Guid, umbracoType);
|
||||
}
|
||||
|
||||
public Attempt<Udi> GetUdiForId(int id, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
var keyAttempt = GetKeyForId(id, umbracoObjectType);
|
||||
return keyAttempt
|
||||
? Attempt.Succeed<Udi>(new GuidUdi(Constants.UdiEntityType.FromUmbracoObjectType(umbracoObjectType), keyAttempt.Result))
|
||||
: Attempt<Udi>.Fail();
|
||||
}
|
||||
|
||||
public Attempt<Guid> GetKeyForId(int id, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
bool empty;
|
||||
|
||||
try
|
||||
{
|
||||
_locker.EnterReadLock();
|
||||
if (_id2Key.TryGetValue(id, out var key) && key.UmbracoObjectType == umbracoObjectType) return Attempt.Succeed(key.Id);
|
||||
empty = _id2Key.Count == 0;
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -96,20 +252,37 @@ namespace Umbraco.Core.Services
|
||||
_locker.ExitReadLock();
|
||||
}
|
||||
|
||||
Guid? val;
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
var sql = scope.Database.SqlContext.Sql()
|
||||
.Select<NodeDto>(x => x.UniqueId).From<NodeDto>().Where<NodeDto>(x => x.NodeId == id);
|
||||
#if POPULATE_FROM_DATABASE
|
||||
// if cache is empty and looking for a document or a media,
|
||||
// populate the cache at once and return what we found
|
||||
if (empty && (umbracoObjectType == UmbracoObjectTypes.Document || umbracoObjectType == UmbracoObjectTypes.Media))
|
||||
return PopulateAndGetKeyForId(id, umbracoObjectType);
|
||||
#endif
|
||||
|
||||
if (umbracoObjectType != UmbracoObjectTypes.Unknown) // if unknow, don't include in query
|
||||
// optimize for read speed: reading database outside a lock means that we could read
|
||||
// multiple times, but we don't lock the cache while accessing the database = better
|
||||
|
||||
Guid? val = null;
|
||||
|
||||
if (_dictionary.TryGetValue(umbracoObjectType, out var mappers))
|
||||
if ((val = mappers.id2key(id)) == default(Guid)) val = null;
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
using (var uow = _uowProvider.GetUnitOfWork())
|
||||
{
|
||||
var objectType = GetNodeObjectTypeGuid(umbracoObjectType);
|
||||
sql = sql.Where<NodeDto>(x => x.NodeObjectType == objectType || x.NodeObjectType == Constants.ObjectTypes.IdReservation); // fixme TEST the OR here!
|
||||
//if it's unknown don't include the nodeObjectType in the query
|
||||
if (umbracoObjectType == UmbracoObjectTypes.Unknown)
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id", new { id });
|
||||
}
|
||||
else
|
||||
{
|
||||
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
|
||||
new { id, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
|
||||
}
|
||||
uow.Commit();
|
||||
}
|
||||
|
||||
val = scope.Database.ExecuteScalar<Guid?>(sql);
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
if (val == null) return Attempt<Guid>.Fail();
|
||||
@@ -141,6 +314,8 @@ namespace Umbraco.Core.Services
|
||||
return guid;
|
||||
}
|
||||
|
||||
// invoked on UnpublishedPageCacheRefresher.RefreshAll
|
||||
// anything else will use the id-specific overloads
|
||||
public void ClearCache()
|
||||
{
|
||||
try
|
||||
@@ -188,17 +363,28 @@ namespace Umbraco.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
// ReSharper disable ClassNeverInstantiated.Local
|
||||
// ReSharper disable UnusedAutoPropertyAccessor.Local
|
||||
private class TypedIdDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public Guid UniqueId { get; set; }
|
||||
public Guid NodeObjectType { get; set; }
|
||||
}
|
||||
// ReSharper restore ClassNeverInstantiated.Local
|
||||
// ReSharper restore UnusedAutoPropertyAccessor.Local
|
||||
|
||||
private struct TypedId<T>
|
||||
{
|
||||
public T Id { get; }
|
||||
|
||||
public UmbracoObjectTypes UmbracoObjectType { get; }
|
||||
|
||||
public TypedId(T id, UmbracoObjectTypes umbracoObjectType)
|
||||
{
|
||||
UmbracoObjectType = umbracoObjectType;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public UmbracoObjectTypes UmbracoObjectType { get; }
|
||||
|
||||
public T Id { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,28 +196,26 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
return _userRepository.GetByUsername(username, includeSecurityData: true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (DbException ex)
|
||||
{
|
||||
if (ex is SqlException || ex is SqlCeException)
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
if (_isUpgrading)
|
||||
{
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
if (_isUpgrading)
|
||||
{
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.GetByUsername(username, includeSecurityData: false);
|
||||
}
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.GetByUsername(username, includeSecurityData: false);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -735,22 +733,20 @@ namespace Umbraco.Core.Services.Implement
|
||||
{
|
||||
return _userRepository.Get(id);
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (DbException ex)
|
||||
{
|
||||
if (ex is SqlException || ex is SqlCeException)
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
if (_isUpgrading)
|
||||
{
|
||||
// fixme kill in v8
|
||||
//we need to handle this one specific case which is when we are upgrading to 7.7 since the user group
|
||||
//tables don't exist yet. This is the 'easiest' way to deal with this without having to create special
|
||||
//version checks in the BackOfficeSignInManager and calling into other special overloads that we'd need
|
||||
//like "GetUserById(int id, bool includeSecurityData)" which may cause confusion because the result of
|
||||
//that method would not be cached.
|
||||
if (_isUpgrading)
|
||||
{
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.Get(id, includeSecurityData: false);
|
||||
}
|
||||
//NOTE: this will not be cached
|
||||
return _userRepository.Get(id, includeSecurityData: false);
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,9 @@
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Transactions" />
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.Web.Extensions" />
|
||||
@@ -581,6 +584,103 @@
|
||||
<Compile Include="Media\Exif\ExifPropertyFactory.cs" />
|
||||
<Compile Include="Media\Exif\ExifTag.cs" />
|
||||
<Compile Include="Media\Exif\ExifTagFactory.cs" />
|
||||
<Compile Include="Models\Rdbms\AccessRuleDto.cs" />
|
||||
<Compile Include="Models\Rdbms\LockDto.cs" />
|
||||
<Compile Include="Models\Rdbms\RedirectUrlDto.cs" />
|
||||
<Compile Include="Models\Rdbms\DocumentPublishedReadOnlyDto.cs" />
|
||||
<Compile Include="Models\Rdbms\ExternalLoginDto.cs" />
|
||||
<Compile Include="Models\Rdbms\MigrationDto.cs" />
|
||||
<Compile Include="Models\XsltFile.cs" />
|
||||
<Compile Include="Models\TemplateOnDisk.cs" />
|
||||
<Compile Include="Models\Rdbms\User2UserGroupDto.cs" />
|
||||
<Compile Include="Models\Rdbms\UserGroup2AppDto.cs" />
|
||||
<Compile Include="Models\Rdbms\UserGroup2NodePermissionDto.cs" />
|
||||
<Compile Include="Models\Rdbms\UserGroupDto.cs" />
|
||||
<Compile Include="Models\Rdbms\UserTypeDto.cs" />
|
||||
<Compile Include="Models\UmbracoDomain.cs" />
|
||||
<Compile Include="Models\DoNotCloneAttribute.cs" />
|
||||
<Compile Include="Models\IDomain.cs" />
|
||||
<Compile Include="NamedUdiRange.cs" />
|
||||
<Compile Include="Packaging\Models\UninstallationSummary.cs" />
|
||||
<Compile Include="Persistence\BulkDataReader.cs" />
|
||||
<Compile Include="Persistence\Constants-Locks.cs" />
|
||||
<Compile Include="Persistence\DatabaseNodeLockExtensions.cs" />
|
||||
<Compile Include="Persistence\DatabaseDebugHelper.cs" />
|
||||
<Compile Include="Persistence\Factories\AuditEntryFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\ExternalLoginFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\MigrationEntryFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\PublicAccessEntryFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\TaskFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\TaskTypeFactory.cs" />
|
||||
<Compile Include="Persistence\Factories\UserGroupFactory.cs" />
|
||||
<Compile Include="Persistence\LocalDb.cs" />
|
||||
<Compile Include="Persistence\LockedRepository.cs" />
|
||||
<Compile Include="Persistence\LockingRepository.cs" />
|
||||
<Compile Include="Persistence\Mappers\AccessMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\AuditEntryMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\AuditMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\ConsentMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\DomainMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\ExternalLoginMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\MigrationEntryMapper.cs" />
|
||||
<Compile Include="Persistence\Mappers\UserGroupMapper.cs" />
|
||||
<Compile Include="Persistence\Migrations\LocalMigrationContext.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionFourOneZero\AddPreviewXmlTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddTourDataUserColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddIndexToPropertyTypeAliasColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddInstructionCountColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddCmsMediaTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenEightZero\AddUserLoginTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\AddUmbracoAuditTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\AddUmbracoConsentTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\AddIsSensitiveMemberTypeColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenNineZero\CreateSensitiveDataUserGroup.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\AddIndexToDictionaryKeyColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\EnsureContentTemplatePermissions.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\ReduceDictionaryKeyColumnsSize.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\AddUserStartNodeTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\UpdateUserTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddLockObjects.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddLockTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveFive\UpdateAllowedMediaTypesAtRoot.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\EnsureServersLockObject.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\AddRedirectUrlTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\RemoveStylesheetDataAndTablesAgain.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFiveZero\UpdateUniqueIndexOnCmsPropertyData.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\FixListViewMediaSortOrder.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddDataDecimalColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUmbracoDeployTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\AddUniqueIdPropertyTypeGroupColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\RemoveParentIdPropertyTypeGroupColumn.cs" />
|
||||
<Compile Include="Persistence\Mappers\TaskTypeMapper.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToCmsMemberLoginName.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexesToUmbracoRelationTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUmbracoNodePath.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddIndexToUser2NodePermission.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddRelationTypeUniqueIdColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\AddMacroUniqueIdColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSevenZero\AddUserGroupTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\ReduceLoginNameColumnsSize.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\NormalizeTemplateGuids.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemovePropertyDataIdIndex.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenSixZero\RemoveUmbracoDeployTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTenZero\RenamePreviewFolder.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeTwo\EnsureMigrationsTableIdentityIsCorrect.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenFourZero\EnsureContentTypeUniqueIdsAreConsistent.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddExternalLoginsTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddForeignKeysForLanguageAndDictionaryTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddServerRegistrationColumnsAndLock.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddMigrationTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddPublicAccessTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddUniqueIdPropertyTypeColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\AddUserColumns.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\CleanUpCorruptedPublishedFlags.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\MovePublicAccessXmlDataToDb.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\RemoveHelpTextColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\RemoveLanguageLocaleColumn.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\RemoveStylesheetDataAndTables.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\RemoveUmbracoLoginsTable.cs" />
|
||||
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\UpdateUniqueIdToHaveCorrectIndexType.cs" />
|
||||
<Compile Include="Media\Exif\IFD.cs" />
|
||||
<Compile Include="Media\Exif\ImageFile.cs" />
|
||||
<Compile Include="Media\Exif\ImageFileDirectory.cs" />
|
||||
|
||||
@@ -151,6 +151,7 @@
|
||||
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
||||
</startup>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Data.SqlServerCe" publicKeyToken="89845dcd8080cc91" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
|
||||
</dependentAssembly>
|
||||
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.1.0.0" newVersion="3.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="HtmlAgilityPack" publicKeyToken="bd319b19eaf3b43a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.5.1.0" newVersion="1.5.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.2.1.0" newVersion="1.2.1.0" />
|
||||
</dependentAssembly>
|
||||
<!--
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.InteropServices" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.4.1.0" newVersion="1.4.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.Extensions" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
-->
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IO.Compression" publicKeyToken="b77a5c561934e089" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.2.0" newVersion="4.1.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Cryptography.Algorithms" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IO.FileSystem" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.IO.FileSystem.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Cryptography.Primitives" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Xml.XPath.XDocument" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.FileVersionInfo" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.DotNet.PlatformAbstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-1.1.2.0" newVersion="1.1.2.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.CodeAnalysis" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.3.0.0" newVersion="2.3.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.CodeAnalysis.CSharp" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.3.0.0" newVersion="2.3.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
@@ -53,7 +53,7 @@
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.Threading.Tasks.Parallel" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.Threading.Thread" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.ValueTuple" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.ValueTuple" version="4.4.0" targetFramework="net46" />
|
||||
<package id="System.Xml.ReaderWriter" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.Xml.XDocument" version="4.3.0" targetFramework="net46" />
|
||||
<package id="System.Xml.XmlDocument" version="4.3.0" targetFramework="net46" />
|
||||
|
||||
@@ -4,6 +4,7 @@ using Umbraco.Core;
|
||||
using umbraco;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Web._Legacy.UI;
|
||||
using Umbraco.Web.umbraco.presentation.umbraco.create;
|
||||
|
||||
namespace Umbraco.Tests.UI
|
||||
{
|
||||
|
||||
@@ -43,7 +43,12 @@
|
||||
"codemirror"
|
||||
],
|
||||
"sources": {
|
||||
"moment": "bower_components/moment/min/moment-with-locales.js",
|
||||
"moment": [
|
||||
"bower_components/moment/min/moment.min.js",
|
||||
"bower_components/moment/min/moment-with-locales.js",
|
||||
"bower_components/moment/min/moment-with-locales.min.js",
|
||||
"bower_components/moment/locale/*.js"
|
||||
],
|
||||
"underscore": [
|
||||
"bower_components/underscore/underscore-min.js",
|
||||
"bower_components/underscore/underscore-min.map"
|
||||
|
||||
@@ -73,7 +73,6 @@ var sources = {
|
||||
js: {
|
||||
preview: { files: ["src/canvasdesigner/**/*.js"], out: "umbraco.canvasdesigner.js" },
|
||||
installer: { files: ["src/installer/**/*.js"], out: "umbraco.installer.js" },
|
||||
|
||||
controllers: { files: ["src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
|
||||
directives: { files: ["src/common/directives/**/*.js"], out: "umbraco.directives.js" },
|
||||
filters: { files: ["src/common/filters/**/*.js"], out: "umbraco.filters.js" },
|
||||
@@ -85,8 +84,7 @@ var sources = {
|
||||
//selectors for copying all views into the build
|
||||
//processed in the views task
|
||||
views:{
|
||||
umbraco: {files: ["src/views/**/*html"], folder: ""},
|
||||
preview: { files: ["src/canvasdesigner/**/*.html"], folder: "../preview"},
|
||||
umbraco: {files: ["src/views/**/*.html"], folder: ""},
|
||||
installer: {files: ["src/installer/steps/*.html"], folder: "install"}
|
||||
},
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@
|
||||
"gulp": "^3.9.1",
|
||||
"gulp-concat": "^2.6.0",
|
||||
"gulp-connect": "5.0.0",
|
||||
"gulp-less": "^3.1.0",
|
||||
"gulp-less": "^3.5.0",
|
||||
"gulp-ngdocs": "^0.3.0",
|
||||
"gulp-open": "^2.0.0",
|
||||
"gulp-open": "^2.1.0",
|
||||
"gulp-postcss": "^6.2.0",
|
||||
"gulp-rename": "^1.2.2",
|
||||
"gulp-sort": "^2.0.0",
|
||||
@@ -38,11 +38,11 @@
|
||||
"gulp-wrap-js": "^0.4.1",
|
||||
"jasmine-core": "2.5.2",
|
||||
"karma": "^1.7.0",
|
||||
"karma-jasmine": "^1.1.0",
|
||||
"karma-jasmine": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"less": "^2.6.1",
|
||||
"lodash": "^4.16.3",
|
||||
"less": "^2.7.3",
|
||||
"lodash": "^4.17.5",
|
||||
"merge-stream": "^1.0.1",
|
||||
"run-sequence": "^2.1.0"
|
||||
"run-sequence": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<div class="box-slider">
|
||||
<div colorpicker ng-model="item.values.color"></div>
|
||||
</div>
|
||||
+1
-3
@@ -255,11 +255,9 @@
|
||||
else {
|
||||
$scope.save().then(function (data) {
|
||||
previewWindow.location.href = redirect;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
$scope.restore = function (content) {
|
||||
|
||||
+2
@@ -8,6 +8,8 @@
|
||||
var evts = [];
|
||||
var isInfoTab = false;
|
||||
scope.publishStatus = {};
|
||||
|
||||
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
|
||||
function onInit() {
|
||||
|
||||
|
||||
@@ -145,6 +145,12 @@ angular.module("umbraco.directives")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (val === "true") {
|
||||
tinyMceConfig.customConfig[i] = true;
|
||||
}
|
||||
if (val === "false") {
|
||||
tinyMceConfig.customConfig[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
angular.extend(baseLineConfigObj, tinyMceConfig.customConfig);
|
||||
|
||||
+4
-1
@@ -495,6 +495,7 @@ Opens an overlay to show a custom YSOD. </br>
|
||||
var activeElementType = document.activeElement.tagName;
|
||||
var clickableElements = ["A", "BUTTON"];
|
||||
var submitOnEnter = document.activeElement.hasAttribute("overlay-submit-on-enter");
|
||||
var submitOnEnterValue = submitOnEnter ? document.activeElement.getAttribute("overlay-submit-on-enter") : "";
|
||||
|
||||
if(clickableElements.indexOf(activeElementType) === 0) {
|
||||
document.activeElement.click();
|
||||
@@ -502,7 +503,9 @@ Opens an overlay to show a custom YSOD. </br>
|
||||
} else if(activeElementType === "TEXTAREA" && !submitOnEnter) {
|
||||
|
||||
|
||||
} else {
|
||||
} else if (submitOnEnter && submitOnEnterValue === "false") {
|
||||
// don't do anything
|
||||
}else {
|
||||
scope.$apply(function () {
|
||||
scope.submitForm(scope.model);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function javascriptLibraryService($q, $http, umbRequestHelper) {
|
||||
|
||||
var existingLocales = [];
|
||||
|
||||
function getSupportedLocalesForMoment() {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (existingLocales.length === 0) {
|
||||
umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"backOfficeAssetsApiBaseUrl",
|
||||
"GetSupportedMomentLocales")),
|
||||
"Failed to get cultures").then(function(locales) {
|
||||
existingLocales = locales;
|
||||
deferred.resolve(existingLocales);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(existingLocales);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
var service = {
|
||||
getSupportedLocalesForMoment: getSupportedLocalesForMoment
|
||||
};
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
angular.module("umbraco.services").factory("javascriptLibraryService", javascriptLibraryService);
|
||||
|
||||
})();
|
||||
@@ -1,285 +1,324 @@
|
||||
angular.module('umbraco.services')
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout, angularHelper, $http) {
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, $log, securityRetryQueue, authResource, assetsService, dialogService, $timeout, angularHelper, $http, javascriptLibraryService) {
|
||||
|
||||
var currentUser = null;
|
||||
var lastUserId = null;
|
||||
var loginDialog = null;
|
||||
var currentUser = null;
|
||||
var lastUserId = null;
|
||||
var loginDialog = null;
|
||||
|
||||
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
|
||||
// this is used so that we know when to go and get the user's remaining seconds directly.
|
||||
var lastServerTimeoutSet = null;
|
||||
//this tracks the last date/time that the user's remainingAuthSeconds was updated from the server
|
||||
// this is used so that we know when to go and get the user's remaining seconds directly.
|
||||
var lastServerTimeoutSet = null;
|
||||
|
||||
function openLoginDialog(isTimedOut) {
|
||||
if (!loginDialog) {
|
||||
loginDialog = dialogService.open({
|
||||
function openLoginDialog(isTimedOut) {
|
||||
if (!loginDialog) {
|
||||
loginDialog = dialogService.open({
|
||||
|
||||
//very special flag which means that global events cannot close this dialog
|
||||
manualClose: true,
|
||||
//very special flag which means that global events cannot close this dialog
|
||||
manualClose: true,
|
||||
|
||||
template: 'views/common/dialogs/login.html',
|
||||
modalClass: "login-overlay",
|
||||
animation: "slide",
|
||||
show: true,
|
||||
callback: onLoginDialogClose,
|
||||
dialogData: {
|
||||
isTimedOut: isTimedOut
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onLoginDialogClose(success) {
|
||||
loginDialog = null;
|
||||
|
||||
if (success) {
|
||||
securityRetryQueue.retryAll(currentUser.name);
|
||||
}
|
||||
else {
|
||||
securityRetryQueue.cancelAll();
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This methods will set the current user when it is resolved and
|
||||
will then start the counter to count in-memory how many seconds they have
|
||||
remaining on the auth session
|
||||
*/
|
||||
function setCurrentUser(usr) {
|
||||
if (!usr.remainingAuthSeconds) {
|
||||
throw "The user object is invalid, the remainingAuthSeconds is required.";
|
||||
}
|
||||
currentUser = usr;
|
||||
lastServerTimeoutSet = new Date();
|
||||
//start the timer
|
||||
countdownUserTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
Method to count down the current user's timeout seconds,
|
||||
this will continually count down their current remaining seconds every 5 seconds until
|
||||
there are no more seconds remaining.
|
||||
*/
|
||||
function countdownUserTimeout() {
|
||||
|
||||
$timeout(function () {
|
||||
|
||||
if (currentUser) {
|
||||
//countdown by 5 seconds since that is how long our timer is for.
|
||||
currentUser.remainingAuthSeconds -= 5;
|
||||
|
||||
//if there are more than 30 remaining seconds, recurse!
|
||||
if (currentUser.remainingAuthSeconds > 30) {
|
||||
|
||||
//we need to check when the last time the timeout was set from the server, if
|
||||
// it has been more than 30 seconds then we'll manually go and retrieve it from the
|
||||
// server - this helps to keep our local countdown in check with the true timeout.
|
||||
if (lastServerTimeoutSet != null) {
|
||||
var now = new Date();
|
||||
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
|
||||
|
||||
if (seconds > 30) {
|
||||
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
//now go get it from the server
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
authResource.getRemainingTimeoutSeconds().then(function (result) {
|
||||
setUserTimeoutInternal(result);
|
||||
});
|
||||
template: 'views/common/dialogs/login.html',
|
||||
modalClass: "login-overlay",
|
||||
animation: "slide",
|
||||
show: true,
|
||||
callback: onLoginDialogClose,
|
||||
dialogData: {
|
||||
isTimedOut: isTimedOut
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
}
|
||||
else {
|
||||
function onLoginDialogClose(success) {
|
||||
loginDialog = null;
|
||||
|
||||
//we are either timed out or very close to timing out so we need to show the login dialog.
|
||||
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
try {
|
||||
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
|
||||
// don't actually care about this result.
|
||||
authResource.getRemainingTimeoutSeconds();
|
||||
}
|
||||
finally {
|
||||
userAuthExpired();
|
||||
}
|
||||
});
|
||||
if (success) {
|
||||
securityRetryQueue.retryAll(currentUser.name);
|
||||
}
|
||||
else {
|
||||
//we've got less than 30 seconds remaining so let's check the server
|
||||
|
||||
if (lastServerTimeoutSet != null) {
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
//now go get it from the server
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
authResource.getRemainingTimeoutSeconds().then(function (result) {
|
||||
setUserTimeoutInternal(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
|
||||
securityRetryQueue.cancelAll();
|
||||
$location.path('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5000, //every 5 seconds
|
||||
false); //false = do NOT execute a digest for every iteration
|
||||
}
|
||||
|
||||
/** Called to update the current user's timeout */
|
||||
function setUserTimeoutInternal(newTimeout) {
|
||||
|
||||
|
||||
var asNumber = parseFloat(newTimeout);
|
||||
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
|
||||
currentUser.remainingAuthSeconds = newTimeout;
|
||||
lastServerTimeoutSet = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
|
||||
function userAuthExpired(isLogout) {
|
||||
//store the last user id and clear the user
|
||||
if (currentUser && currentUser.id !== undefined) {
|
||||
lastUserId = currentUser.id;
|
||||
}
|
||||
|
||||
if (currentUser) {
|
||||
currentUser.remainingAuthSeconds = 0;
|
||||
}
|
||||
|
||||
lastServerTimeoutSet = null;
|
||||
currentUser = null;
|
||||
|
||||
//broadcast a global event that the user is no longer logged in
|
||||
eventsService.emit("app.notAuthenticated");
|
||||
|
||||
openLoginDialog(isLogout === undefined ? true : !isLogout);
|
||||
}
|
||||
|
||||
// Register a handler for when an item is added to the retry queue
|
||||
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
|
||||
if (securityRetryQueue.hasMore()) {
|
||||
userAuthExpired();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
/** Internal method to display the login dialog */
|
||||
_showLoginDialog: function () {
|
||||
openLoginDialog();
|
||||
},
|
||||
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
|
||||
isAuthenticated: function () {
|
||||
//if we've got a current user then just return true
|
||||
if (currentUser) {
|
||||
var deferred = $q.defer();
|
||||
deferred.resolve(true);
|
||||
return deferred.promise;
|
||||
/**
|
||||
This methods will set the current user when it is resolved and
|
||||
will then start the counter to count in-memory how many seconds they have
|
||||
remaining on the auth session
|
||||
*/
|
||||
function setCurrentUser(usr) {
|
||||
if (!usr.remainingAuthSeconds) {
|
||||
throw "The user object is invalid, the remainingAuthSeconds is required.";
|
||||
}
|
||||
currentUser = usr;
|
||||
lastServerTimeoutSet = new Date();
|
||||
//start the timer
|
||||
countdownUserTimeout();
|
||||
}
|
||||
return authResource.isAuthenticated();
|
||||
},
|
||||
|
||||
/** Returns a promise, sends a request to the server to validate the credentials */
|
||||
authenticate: function (login, password) {
|
||||
/**
|
||||
Method to count down the current user's timeout seconds,
|
||||
this will continually count down their current remaining seconds every 5 seconds until
|
||||
there are no more seconds remaining.
|
||||
*/
|
||||
function countdownUserTimeout() {
|
||||
|
||||
return authResource.performLogin(login, password)
|
||||
.then(this.setAuthenticationSuccessful);
|
||||
},
|
||||
setAuthenticationSuccessful: function (data) {
|
||||
$timeout(function () {
|
||||
|
||||
//when it's successful, return the user data
|
||||
setCurrentUser(data);
|
||||
if (currentUser) {
|
||||
//countdown by 5 seconds since that is how long our timer is for.
|
||||
currentUser.remainingAuthSeconds -= 5;
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
|
||||
//if there are more than 30 remaining seconds, recurse!
|
||||
if (currentUser.remainingAuthSeconds > 30) {
|
||||
|
||||
//broadcast a global event
|
||||
eventsService.emit("app.authenticated", result);
|
||||
return result;
|
||||
},
|
||||
//we need to check when the last time the timeout was set from the server, if
|
||||
// it has been more than 30 seconds then we'll manually go and retrieve it from the
|
||||
// server - this helps to keep our local countdown in check with the true timeout.
|
||||
if (lastServerTimeoutSet != null) {
|
||||
var now = new Date();
|
||||
var seconds = (now.getTime() - lastServerTimeoutSet.getTime()) / 1000;
|
||||
|
||||
/** Logs the user out
|
||||
*/
|
||||
logout: function () {
|
||||
if (seconds > 30) {
|
||||
|
||||
return authResource.performLogout()
|
||||
.then(function (data) {
|
||||
userAuthExpired();
|
||||
//done!
|
||||
return null;
|
||||
});
|
||||
},
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
/** Refreshes the current user data with the data stored for the user on the server and returns it */
|
||||
refreshCurrentUser: function() {
|
||||
var deferred = $q.defer();
|
||||
//now go get it from the server
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
authResource.getRemainingTimeoutSeconds().then(function (result) {
|
||||
setUserTimeoutInternal(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
}
|
||||
else {
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
|
||||
setCurrentUser(data);
|
||||
//we are either timed out or very close to timing out so we need to show the login dialog.
|
||||
if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) {
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
try {
|
||||
//NOTE: We are calling this again so that the server can create a log that the timeout has expired, we
|
||||
// don't actually care about this result.
|
||||
authResource.getRemainingTimeoutSeconds();
|
||||
}
|
||||
finally {
|
||||
userAuthExpired();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
//we've got less than 30 seconds remaining so let's check the server
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
if (lastServerTimeoutSet != null) {
|
||||
//first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we
|
||||
// wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait.
|
||||
lastServerTimeoutSet = null;
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
//now go get it from the server
|
||||
//NOTE: the safeApply because our timeout is set to not run digests (performance reasons)
|
||||
angularHelper.safeApply($rootScope, function () {
|
||||
authResource.getRemainingTimeoutSeconds().then(function (result) {
|
||||
setUserTimeoutInternal(result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the current user object in a promise */
|
||||
getCurrentUser: function (args) {
|
||||
var deferred = $q.defer();
|
||||
//recurse the countdown!
|
||||
countdownUserTimeout();
|
||||
|
||||
if (!currentUser) {
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 5000, //every 5 seconds
|
||||
false); //false = do NOT execute a digest for every iteration
|
||||
}
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
/** Called to update the current user's timeout */
|
||||
function setUserTimeoutInternal(newTimeout) {
|
||||
|
||||
if (args && args.broadcastEvent) {
|
||||
//broadcast a global event, will inform listening controllers to load in the user specific data
|
||||
|
||||
var asNumber = parseFloat(newTimeout);
|
||||
if (!isNaN(asNumber) && currentUser && angular.isNumber(asNumber)) {
|
||||
currentUser.remainingAuthSeconds = newTimeout;
|
||||
lastServerTimeoutSet = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
/** resets all user data, broadcasts the notAuthenticated event and shows the login dialog */
|
||||
function userAuthExpired(isLogout) {
|
||||
//store the last user id and clear the user
|
||||
if (currentUser && currentUser.id !== undefined) {
|
||||
lastUserId = currentUser.id;
|
||||
}
|
||||
|
||||
if (currentUser) {
|
||||
currentUser.remainingAuthSeconds = 0;
|
||||
}
|
||||
|
||||
lastServerTimeoutSet = null;
|
||||
currentUser = null;
|
||||
|
||||
//broadcast a global event that the user is no longer logged in
|
||||
eventsService.emit("app.notAuthenticated");
|
||||
|
||||
openLoginDialog(isLogout === undefined ? true : !isLogout);
|
||||
}
|
||||
|
||||
// Register a handler for when an item is added to the retry queue
|
||||
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
|
||||
if (securityRetryQueue.hasMore()) {
|
||||
userAuthExpired();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
/** Internal method to display the login dialog */
|
||||
_showLoginDialog: function () {
|
||||
openLoginDialog();
|
||||
},
|
||||
/** Returns a promise, sends a request to the server to check if the current cookie is authorized */
|
||||
isAuthenticated: function () {
|
||||
//if we've got a current user then just return true
|
||||
if (currentUser) {
|
||||
var deferred = $q.defer();
|
||||
deferred.resolve(true);
|
||||
return deferred.promise;
|
||||
}
|
||||
return authResource.isAuthenticated();
|
||||
},
|
||||
|
||||
/** Returns a promise, sends a request to the server to validate the credentials */
|
||||
authenticate: function (login, password) {
|
||||
|
||||
return authResource.performLogin(login, password)
|
||||
.then(this.setAuthenticationSuccessful);
|
||||
},
|
||||
setAuthenticationSuccessful: function (data) {
|
||||
|
||||
//when it's successful, return the user data
|
||||
setCurrentUser(data);
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" };
|
||||
|
||||
//broadcast a global event
|
||||
eventsService.emit("app.authenticated", result);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
setCurrentUser(data);
|
||||
/** Logs the user out
|
||||
*/
|
||||
logout: function () {
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
return authResource.performLogout()
|
||||
.then(function (data) {
|
||||
userAuthExpired();
|
||||
//done!
|
||||
return null;
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
else {
|
||||
deferred.resolve(currentUser);
|
||||
}
|
||||
/** Refreshes the current user data with the data stored for the user on the server and returns it */
|
||||
refreshCurrentUser: function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
|
||||
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
|
||||
setUserTimeout: function (newTimeout) {
|
||||
setUserTimeoutInternal(newTimeout);
|
||||
}
|
||||
};
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
|
||||
});
|
||||
setCurrentUser(data);
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/** Returns the current user object in a promise */
|
||||
getCurrentUser: function (args) {
|
||||
var deferred = $q.defer();
|
||||
|
||||
if (!currentUser) {
|
||||
authResource.getCurrentUser()
|
||||
.then(function (data) {
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" };
|
||||
|
||||
if (args && args.broadcastEvent) {
|
||||
//broadcast a global event, will inform listening controllers to load in the user specific data
|
||||
eventsService.emit("app.authenticated", result);
|
||||
}
|
||||
|
||||
setCurrentUser(data);
|
||||
|
||||
deferred.resolve(currentUser);
|
||||
}, function () {
|
||||
//it failed, so they are not logged in
|
||||
deferred.reject();
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
deferred.resolve(currentUser);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/** Loads the Moment.js Locale for the current user. */
|
||||
loadMomentLocaleForCurrentUser: function () {
|
||||
var deferred = $q.defer();
|
||||
|
||||
|
||||
function loadLocales(currentUser, supportedLocales) {
|
||||
var locale = currentUser.locale.toLowerCase();
|
||||
if (locale !== 'en-us') {
|
||||
var localeUrls = [];
|
||||
if (supportedLocales.indexOf(locale + '.js') > -1) {
|
||||
localeUrls.push('lib/moment/' + locale + '.js');
|
||||
}
|
||||
if (locale.indexOf('-') > -1) {
|
||||
var majorLocale = locale.split('-')[0] + '.js';
|
||||
if (supportedLocales.indexOf(majorLocale) > -1) {
|
||||
localeUrls.push('lib/moment/' + majorLocale);
|
||||
}
|
||||
}
|
||||
assetsService.load(localeUrls).then(function () {
|
||||
deferred.resolve(localeUrls);
|
||||
});
|
||||
} else {
|
||||
deferred.resolve(['']);
|
||||
}
|
||||
}
|
||||
|
||||
var promises = {
|
||||
currentUser: this.getCurrentUser(),
|
||||
supportedLocales: javascriptLibraryService.getSupportedLocalesForMoment()
|
||||
}
|
||||
|
||||
$q.all(promises).then(function (values) {
|
||||
loadLocales(values.currentUser, values.supportedLocales);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
|
||||
},
|
||||
|
||||
/** Called whenever a server request is made that contains a x-umb-user-seconds response header for which we can update the user's remaining timeout seconds */
|
||||
setUserTimeout: function (newTimeout) {
|
||||
setUserTimeoutInternal(newTimeout);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -18,23 +18,26 @@ app.run(['userService', '$log', '$rootScope', '$location', 'queryStrings', 'navi
|
||||
eventsService.on("app.authenticated", function(evt, data) {
|
||||
|
||||
assetsService._loadInitAssets().then(function() {
|
||||
|
||||
//Register all of the tours on the server
|
||||
tourService.registerAllTours().then(function () {
|
||||
appReady(data);
|
||||
|
||||
// Auto start intro tour
|
||||
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
|
||||
// start intro tour if it hasn't been completed or disabled
|
||||
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
|
||||
tourService.startTour(introTour);
|
||||
}
|
||||
|
||||
// Loads the user's locale settings for Moment.
|
||||
userService.loadMomentLocaleForCurrentUser().then(function() {
|
||||
|
||||
//Register all of the tours on the server
|
||||
tourService.registerAllTours().then(function () {
|
||||
appReady(data);
|
||||
|
||||
// Auto start intro tour
|
||||
tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) {
|
||||
// start intro tour if it hasn't been completed or disabled
|
||||
if (introTour && introTour.disabled !== true && introTour.completed !== true) {
|
||||
tourService.startTour(introTour);
|
||||
}
|
||||
});
|
||||
|
||||
}, function(){
|
||||
appReady(data);
|
||||
});
|
||||
|
||||
}, function(){
|
||||
appReady(data);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -168,6 +168,9 @@
|
||||
//used for property editors
|
||||
@import "property-editors.less";
|
||||
|
||||
//used for prevalue editors
|
||||
@import "components/prevalues/multivalues.less";
|
||||
|
||||
|
||||
@import "typeahead.less";
|
||||
@import "hacks.less";
|
||||
@@ -175,4 +178,4 @@
|
||||
@import "healthcheck.less";
|
||||
|
||||
// cleanup properties.less when it is done
|
||||
@import "properties.less";
|
||||
@import "properties.less";
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
.umb-prevalues-multivalues {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__left {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__right {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__add {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__add input {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__add input {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__add button {
|
||||
margin: 0 6px 0 0;
|
||||
float: right
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__listitem {
|
||||
display: flex;
|
||||
padding: 6px;
|
||||
margin: 10px 0px !important;
|
||||
background: #F3F3F5;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__listitem i {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 5px
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__listitem a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.umb-prevalues-multivalues__listitem input {
|
||||
width: 295px;
|
||||
}
|
||||
@@ -171,27 +171,25 @@
|
||||
{{publishStatus.label}}
|
||||
</umb-badge>
|
||||
</umb-control-group>
|
||||
|
||||
|
||||
<umb-control-group data-element="node-info-create-date" label="@template_createdDate">
|
||||
{{node.createDateFormatted}} <localize key="general_by">by</localize> {{ node.owner.name }}
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group data-element="node-info-document-type" label="@content_documentType">
|
||||
<umb-node-preview
|
||||
style="max-width: 100%; margin-bottom: 0;"
|
||||
icon="documentType.icon"
|
||||
name="documentType.name"
|
||||
allow-open="allowOpen"
|
||||
on-open="openDocumentType(documentType)">
|
||||
<umb-node-preview style="max-width: 100%; margin-bottom: 0;"
|
||||
icon="documentType.icon"
|
||||
name="documentType.name"
|
||||
allow-open="allowOpen"
|
||||
on-open="openDocumentType(documentType)">
|
||||
</umb-node-preview>
|
||||
</umb-control-group>
|
||||
|
||||
<umb-control-group data-element="node-info-template" label="@template_template">
|
||||
<select
|
||||
class="input-block-level"
|
||||
ng-model="node.template"
|
||||
ng-options="key as value for (key, value) in availableTemplates"
|
||||
ng-change="updateTemplate(node.template)">
|
||||
<umb-control-group ng-if="disableTemplates == false" data-element="node-info-template" label="@template_template">
|
||||
<select class="input-block-level"
|
||||
ng-model="node.template"
|
||||
ng-options="key as value for (key, value) in availableTemplates"
|
||||
ng-change="updateTemplate(node.template)">
|
||||
<option value=""><localize key="general_choose">Choose</localize>...</option>
|
||||
</select>
|
||||
</umb-control-group>
|
||||
@@ -200,7 +198,7 @@
|
||||
<div>{{ node.id }}</div>
|
||||
<small>{{ node.key }}</small>
|
||||
</umb-control-group>
|
||||
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</div>
|
||||
|
||||
@@ -14,18 +14,21 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
|
||||
creatingFolder: false,
|
||||
};
|
||||
|
||||
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
$scope.model.disableTemplates = disableTemplates;
|
||||
|
||||
var node = $scope.dialogOptions.currentNode,
|
||||
localizeCreateFolder = localizationService.localize("defaultdialog_createFolder");
|
||||
|
||||
$scope.showCreateFolder = function() {
|
||||
$scope.showCreateFolder = function () {
|
||||
$scope.model.creatingFolder = true;
|
||||
};
|
||||
|
||||
$scope.createContainer = function() {
|
||||
$scope.createContainer = function () {
|
||||
|
||||
if (formHelper.submitForm({scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder})) {
|
||||
if (formHelper.submitForm({ scope: $scope, formCtrl: this.createFolderForm, statusMessage: localizeCreateFolder })) {
|
||||
|
||||
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function(folderId) {
|
||||
contentTypeResource.createContainer(node.id, $scope.model.folderName).then(function (folderId) {
|
||||
|
||||
navigationService.hideMenu();
|
||||
|
||||
@@ -44,7 +47,7 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
|
||||
|
||||
var section = appState.getSectionState("currentSection");
|
||||
|
||||
}, function(err) {
|
||||
}, function (err) {
|
||||
|
||||
$scope.error = err;
|
||||
|
||||
@@ -58,14 +61,17 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
|
||||
}
|
||||
};
|
||||
|
||||
$scope.createDocType = function() {
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
|
||||
navigationService.hideMenu();
|
||||
};
|
||||
// Disabling logic for creating document type with template if disableTemplates is set to true
|
||||
if (!disableTemplates) {
|
||||
$scope.createDocType = function () {
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true");
|
||||
navigationService.hideMenu();
|
||||
};
|
||||
}
|
||||
|
||||
$scope.createComponent = function() {
|
||||
$scope.createComponent = function () {
|
||||
$location.search('create', null);
|
||||
$location.search('notemplate', null);
|
||||
$location.path("/settings/documenttypes/edit/" + node.id).search("create", "true").search("notemplate", "true");
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li data-element="action-documentType">
|
||||
<li data-element="action-documentType" ng-hide="model.disableTemplates">
|
||||
<a href="" ng-click="createDocType()" umb-auto-focus>
|
||||
|
||||
<i class="large icon-item-arrangement"></i>
|
||||
@@ -15,10 +15,14 @@
|
||||
</a>
|
||||
</li>
|
||||
<li data-element="action-documentTypeWithoutTemplate">
|
||||
<a href="" ng-click="createComponent()">
|
||||
<i class="large icon-item-arrangement"></i>
|
||||
<span class="menu-label"><localize key="create_documentTypeWithoutTemplate"></localize></span>
|
||||
</a>
|
||||
<a href="" ng-click="createComponent()">
|
||||
<i class="large icon-item-arrangement"></i>
|
||||
|
||||
<span class="menu-label">
|
||||
<localize ng-if="model.disableTemplates == false" key="create_documentTypeWithoutTemplate"></localize>
|
||||
<localize ng-if="model.disableTemplates == true" key="content_documentType">Document type></localize>
|
||||
</span>
|
||||
</a>
|
||||
</li>
|
||||
<li data-element="action-folder" ng-if="model.allowCreateFolder">
|
||||
<a href="" ng-click="showCreateFolder()">
|
||||
@@ -33,7 +37,7 @@
|
||||
<form novalidate name="createFolderForm"
|
||||
ng-submit="createContainer()"
|
||||
val-form-manager>
|
||||
|
||||
|
||||
<div ng-show="error">
|
||||
<h5 class="text-error">{{error.errorMsg}}</h5>
|
||||
<p class="text-error">{{error.data.message}}</p>
|
||||
|
||||
@@ -15,6 +15,36 @@
|
||||
var localizeSaving = localizationService.localize("general_saving");
|
||||
var evts = [];
|
||||
|
||||
var disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
|
||||
|
||||
var buttons = [
|
||||
{
|
||||
"name": localizationService.localize("general_design"),
|
||||
"alias": "design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/documenttypes/views/design/design.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_listView"),
|
||||
"alias": "listView",
|
||||
"icon": "icon-list",
|
||||
"view": "views/documenttypes/views/listview/listview.html"
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_rights"),
|
||||
"alias": "permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/documenttypes/views/permissions/permissions.html"
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("treeHeaders_templates"),
|
||||
"alias": "templates",
|
||||
"icon": "icon-layout",
|
||||
"view": "views/documenttypes/views/templates/templates.html"
|
||||
}
|
||||
];
|
||||
|
||||
vm.save = save;
|
||||
|
||||
vm.currentNode = null;
|
||||
@@ -23,97 +53,73 @@
|
||||
vm.page = {};
|
||||
vm.page.loading = false;
|
||||
vm.page.saveButtonState = "init";
|
||||
vm.page.navigation = [
|
||||
{
|
||||
"name": localizationService.localize("general_design"),
|
||||
"alias": "design",
|
||||
"icon": "icon-document-dashed-line",
|
||||
"view": "views/documenttypes/views/design/design.html",
|
||||
"active": true
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_listView"),
|
||||
"alias": "listView",
|
||||
"icon": "icon-list",
|
||||
"view": "views/documenttypes/views/listview/listview.html"
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_rights"),
|
||||
"alias": "permissions",
|
||||
"icon": "icon-keychain",
|
||||
"view": "views/documenttypes/views/permissions/permissions.html"
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("treeHeaders_templates"),
|
||||
"alias": "templates",
|
||||
"icon": "icon-layout",
|
||||
"view": "views/documenttypes/views/templates/templates.html"
|
||||
}
|
||||
];
|
||||
vm.page.navigation = [];
|
||||
|
||||
loadButtons();
|
||||
|
||||
vm.page.keyboardShortcutsOverview = [
|
||||
{
|
||||
"name": localizationService.localize("main_sections"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_navigateSections"),
|
||||
"keys": [{ "key": "1" }, { "key": "4" }],
|
||||
"keyRange": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_design"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addTab"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addProperty"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addEditor"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_editDataType"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_listView"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_toggleListView"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_rights"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_toggleAllowAsRoot"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addChildNode"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("treeHeaders_templates"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addTemplate"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
{
|
||||
"name": localizationService.localize("main_sections"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_navigateSections"),
|
||||
"keys": [{ "key": "1" }, { "key": "4" }],
|
||||
"keyRange": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_design"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addTab"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addProperty"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "p" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addEditor"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "e" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_editDataType"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "d" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_listView"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_toggleListView"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "l" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("general_rights"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_toggleAllowAsRoot"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "r" }]
|
||||
},
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addChildNode"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "c" }]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": localizationService.localize("treeHeaders_templates"),
|
||||
"shortcuts": [
|
||||
{
|
||||
"description": localizationService.localize("shortcuts_addTemplate"),
|
||||
"keys": [{ "key": "alt" }, { "key": "shift" }, { "key": "t" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
contentTypeHelper.checkModelsBuilderStatus().then(function (result) {
|
||||
@@ -144,7 +150,7 @@
|
||||
vm.page.saveButtonState = "busy";
|
||||
|
||||
localizationService.localize("modelsBuilder_buildingModels").then(function (headerValue) {
|
||||
localizationService.localize("modelsBuilder_waitingMessage").then(function(msgValue) {
|
||||
localizationService.localize("modelsBuilder_waitingMessage").then(function (msgValue) {
|
||||
notificationsService.info(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
@@ -155,26 +161,26 @@
|
||||
if (!result.lastError) {
|
||||
|
||||
//re-check model status
|
||||
contentTypeHelper.checkModelsBuilderStatus().then(function(statusResult) {
|
||||
contentTypeHelper.checkModelsBuilderStatus().then(function (statusResult) {
|
||||
vm.page.modelsBuilder = statusResult;
|
||||
});
|
||||
|
||||
//clear and add success
|
||||
vm.page.saveButtonState = "init";
|
||||
localizationService.localize("modelsBuilder_modelsGenerated").then(function(value) {
|
||||
localizationService.localize("modelsBuilder_modelsGenerated").then(function (value) {
|
||||
notificationsService.success(value);
|
||||
});
|
||||
|
||||
} else {
|
||||
vm.page.saveButtonState = "error";
|
||||
localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function(value) {
|
||||
localizationService.localize("modelsBuilder_modelsExceptionInUlog").then(function (value) {
|
||||
notificationsService.error(value);
|
||||
});
|
||||
}
|
||||
|
||||
}, function () {
|
||||
vm.page.saveButtonState = "error";
|
||||
localizationService.localize("modelsBuilder_modelsGeneratedError").then(function(value) {
|
||||
localizationService.localize("modelsBuilder_modelsGeneratedError").then(function (value) {
|
||||
notificationsService.error(value);
|
||||
});
|
||||
});
|
||||
@@ -189,7 +195,7 @@
|
||||
|
||||
//we are creating so get an empty data type item
|
||||
contentTypeResource.getScaffold($routeParams.id)
|
||||
.then(function(dt) {
|
||||
.then(function (dt) {
|
||||
init(dt);
|
||||
vm.page.loading = false;
|
||||
});
|
||||
@@ -207,12 +213,26 @@
|
||||
});
|
||||
}
|
||||
|
||||
function loadButtons() {
|
||||
|
||||
angular.forEach(buttons,
|
||||
function (val, index) {
|
||||
|
||||
if (disableTemplates === true && val.alias === "templates") {
|
||||
buttons.splice(index, 1);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
vm.page.navigation = buttons;
|
||||
}
|
||||
|
||||
/* ---------- SAVE ---------- */
|
||||
|
||||
function save() {
|
||||
|
||||
// only save if there is no overlays open
|
||||
if(overlayHelper.getNumberOfOverlays() === 0) {
|
||||
if (overlayHelper.getNumberOfOverlays() === 0) {
|
||||
|
||||
var deferred = $q.defer();
|
||||
|
||||
@@ -233,7 +253,7 @@
|
||||
// we need to rebind... the IDs that have been created!
|
||||
rebindCallback: function (origContentType, savedContentType) {
|
||||
vm.contentType.id = savedContentType.id;
|
||||
vm.contentType.groups.forEach(function(group) {
|
||||
vm.contentType.groups.forEach(function (group) {
|
||||
if (!group.name) return;
|
||||
var k = 0;
|
||||
while (k < savedContentType.groups.length && savedContentType.groups[k].name != group.name)
|
||||
@@ -273,7 +293,7 @@
|
||||
}
|
||||
else {
|
||||
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) {
|
||||
localizationService.localize("speechBubbles_validationFailedMessage").then(function (msgValue) {
|
||||
notificationsService.error(headerValue, msgValue);
|
||||
});
|
||||
});
|
||||
@@ -331,7 +351,7 @@
|
||||
function getDataTypeDetails(property) {
|
||||
if (property.propertyState !== "init") {
|
||||
dataTypeResource.getById(property.dataTypeId)
|
||||
.then(function(dataType) {
|
||||
.then(function (dataType) {
|
||||
property.dataTypeIcon = dataType.icon;
|
||||
property.dataTypeName = dataType.name;
|
||||
});
|
||||
@@ -345,7 +365,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
evts.push(eventsService.on("app.refreshEditor", function(name, error) {
|
||||
evts.push(eventsService.on("app.refreshEditor", function (name, error) {
|
||||
loadDocumentType();
|
||||
}));
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
angular.module("umbraco").controller("Umbraco.PrevalueEditors.BooleanController",
|
||||
function ($scope) {
|
||||
$scope.htmlId = "bool-" + String.CreateGuid();
|
||||
});
|
||||
@@ -1 +1,4 @@
|
||||
<input name="boolean" type="checkbox" ng-model="model.value" ng-true-value="1" ng-false-value="0" id="boolean" /> <label for="boolean">True</label>
|
||||
<label ng-controller="Umbraco.PrevalueEditors.BooleanController" for="{{htmlId}}" class="checkbox">
|
||||
<input name="boolean" type="checkbox" ng-model="model.value" ng-true-value="1" ng-false-value="0" id="{{htmlId}}" />
|
||||
<localize key="general_yes">Yes</localize>
|
||||
</label>
|
||||
|
||||
@@ -5,6 +5,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
|
||||
|
||||
$scope.newItem = "";
|
||||
$scope.hasError = false;
|
||||
$scope.focusOnNew = false;
|
||||
|
||||
if (!angular.isArray($scope.model.value)) {
|
||||
|
||||
@@ -43,6 +44,7 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
|
||||
$scope.model.value.push({ value: $scope.newItem });
|
||||
$scope.newItem = "";
|
||||
$scope.hasError = false;
|
||||
$scope.focusOnNew = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -73,6 +75,12 @@ angular.module("umbraco").controller("Umbraco.PrevalueEditors.MultiValuesControl
|
||||
}
|
||||
};
|
||||
|
||||
$scope.createNew = function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
$scope.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
function getElementIndexByPrevalueText(value) {
|
||||
for (var i = 0; i < $scope.model.value.length; i++) {
|
||||
if ($scope.model.value[i].value === value) {
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
<div class="umb-editor" ng-controller="Umbraco.PrevalueEditors.MultiValuesController">
|
||||
<div class="control-group">
|
||||
<input name="newItem" type="text" ng-model="newItem" val-highlight="{{hasError}}" />
|
||||
<button class="btn" ng-click="add($event)">Add</button>
|
||||
<div class="umb-editor umb-prevalues-multivalues" ng-controller="Umbraco.PrevalueEditors.MultiValuesController">
|
||||
<div class="control-group umb-prevalues-multivalues__add">
|
||||
<div class="umb-prevalues-multivalues__left">
|
||||
<input overlay-submit-on-enter="false" name="newItem" focus-when="{{focusOnNew}}" ng-keydown="createNew($event)" type="text" ng-model="newItem" val-highlight="{{hasError}}" />
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<button class="btn btn-info" ng-click="add($event)">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-sortable="sortableOptions">
|
||||
<div class="control-group" ng-repeat="item in model.value">
|
||||
<div class="control-group umb-prevalues-multivalues__listitem" ng-repeat="item in model.value">
|
||||
<i class="icon icon-navigation handle"></i>
|
||||
<input type="text" ng-model="item.value" val-server="item_{{$index}}" required />
|
||||
<button class="btn btn-danger" ng-click="remove(item, $event)">Remove</button>
|
||||
<div class="umb-prevalues-multivalues__left">
|
||||
<input type="text" ng-model="item.value" val-server="item_{{$index}}" required />
|
||||
</div>
|
||||
<div class="umb-prevalues-multivalues__right">
|
||||
<a class="umb-node-preview__action" ng-click="remove(item, $event)">Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+18
-20
@@ -157,26 +157,24 @@ angular.module("umbraco")
|
||||
.controller('Umbraco.PropertyEditors.FileUploadController', fileUploadController)
|
||||
.run(function(mediaHelper, umbRequestHelper, assetsService){
|
||||
if (mediaHelper && mediaHelper.registerFileResolver) {
|
||||
assetsService.load(["lib/moment/moment-with-locales.js"]).then(
|
||||
function () {
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
|
||||
if (thumbnail) {
|
||||
if (mediaHelper.detectIfImageByExtension(property.value)) {
|
||||
//get default big thumbnail from image processor
|
||||
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
|
||||
return thumbnailUrl;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return property.value;
|
||||
}
|
||||
});
|
||||
|
||||
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
|
||||
// they contain different data structures so if we need to query against it we need to be aware of this.
|
||||
mediaHelper.registerFileResolver("Umbraco.UploadField", function(property, entity, thumbnail){
|
||||
if (thumbnail) {
|
||||
if (mediaHelper.detectIfImageByExtension(property.value)) {
|
||||
//get default big thumbnail from image processor
|
||||
var thumbnailUrl = property.value + "?rnd=" + moment(entity.updateDate).format("YYYYMMDDHHmmss") + "&width=500&animationprocessmode=first";
|
||||
return thumbnailUrl;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
else {
|
||||
return property.value;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
@@ -185,6 +185,12 @@ angular.module("umbraco")
|
||||
//cannot parse, we'll just leave it
|
||||
}
|
||||
}
|
||||
if (val === "true") {
|
||||
tinyMceConfig.customConfig[i] = true;
|
||||
}
|
||||
if (val === "false") {
|
||||
tinyMceConfig.customConfig[i] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,4 +99,4 @@ module.exports = function (config) {
|
||||
'karma-phantomjs-launcher'
|
||||
]
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@@ -864,6 +864,19 @@
|
||||
<Content Include="Views\Partials\Grid\Editors\Base.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap3-Fluid.cshtml" />
|
||||
<Content Include="Views\Partials\Grid\Bootstrap2-Fluid.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Background.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Border.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Color.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Googlefontpicker.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\GridRow.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Index.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Layout.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Margin.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Padding.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Radius.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Shadow.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\Slider.cshtml" />
|
||||
<Content Include="Umbraco\Views\Preview\web.config" />
|
||||
<None Include="Web.Debug.config.transformed" />
|
||||
<None Include="web.Template.Debug.config">
|
||||
<DependentUpon>Web.Template.config</DependentUpon>
|
||||
@@ -973,6 +986,12 @@
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
|
||||
<Folder Include="App_Data\" />
|
||||
<Folder Include="Views\MacroPartials\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.0.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
|
||||
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.0.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
|
||||
@@ -999,6 +1018,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
|
||||
<DevelopmentServerPort>8000</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:8000</IISUrl>
|
||||
<DevelopmentServerPort>7110</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:7110</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.background">
|
||||
|
||||
<div class="box-slider">
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.border" class="bordereditor">
|
||||
|
||||
<div class="box-slider">
|
||||
@@ -17,4 +17,4 @@
|
||||
<div ui-slider min="0" max="40" step="1" ng-model="selectedBorder.size"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,4 @@
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div class="box-slider">
|
||||
<div colorpicker ng-model="item.values.color"></div>
|
||||
</div>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.googlefontpicker">
|
||||
|
||||
<div class="box-slider">
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.gridRow">
|
||||
|
||||
<div class="box-slider">
|
||||
<input type="checkbox" ng-model="item.values.fullsize" /><label>Full size</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+16
-31
@@ -1,4 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
@using System.Web.Mvc.Html
|
||||
@inherits System.Web.Mvc.WebViewPage<Umbraco.Web.Models.ContentEditing.BackOfficePreview>
|
||||
@{
|
||||
var disableDevicePreview = Model.DisableDevicePreview.ToString().ToLowerInvariant();
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Umbraco Canvas Designer</title>
|
||||
@@ -6,27 +11,25 @@
|
||||
<link href="../lib/spectrum/spectrum.css" type="text/css" rel="stylesheet" />
|
||||
<link href="../lib/jquery-ui/jquery-ui-1.10.4.custom.min.css" type="text/css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body id="canvasdesignerPanel" ng-mouseover="outlinePositionHide()" ng-class="{ leftOpen: (showStyleEditor || showPalettePicker) && !showDevicesPreview }" ng-controller="Umbraco.canvasdesignerController">
|
||||
|
||||
<div class="wait" ng-show="!frameLoaded"></div>
|
||||
|
||||
@if (string.IsNullOrWhiteSpace(Model.PreviewExtendedHeaderView) == false)
|
||||
{
|
||||
@Html.Partial(Model.PreviewExtendedHeaderView)
|
||||
}
|
||||
|
||||
<div id="demo-iframe-wrapper" ng-show="frameLoaded" class="{{previewDevice.css}}">
|
||||
<iframe id="resultFrame" ng-src="{{pageUrl}}" frameborder="0" iframe-is-loaded></iframe>
|
||||
</div>
|
||||
|
||||
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
|
||||
|
||||
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showDevices = !@(disableDevicePreview); showPalettePicker = true" ng-mouseenter="positionSelectedHide()">
|
||||
<div class="fix-left-menu selected">
|
||||
|
||||
<div class="avatar">
|
||||
<img
|
||||
ng-src="../assets/img/application/logo.png"
|
||||
ng-srcset="../assets/img/application/logo@2x.png 2x,
|
||||
<img ng-src="../assets/img/application/logo.png"
|
||||
ng-srcset="../assets/img/application/logo@2x.png 2x,
|
||||
../assets/img/application/logo@3x.png 3x" />
|
||||
</div>
|
||||
|
||||
<ul class="sections" ng-class="{selected: showDevicesPreview}">
|
||||
<ul class="sections" ng-class="{selected: showDevicesPreview && showDevices}">
|
||||
<li ng-repeat="device in devices" ng-class="{ current:previewDevice==device }" ng-click="updatePreviewDevice(device)">
|
||||
<a href="#"><i class="icon {{device.icon}}" title="{{device.title}}"></i><span></span></a>
|
||||
</li>
|
||||
@@ -39,9 +42,8 @@
|
||||
</li>
|
||||
<li ng-click="exitPreview()">
|
||||
<a href="#" title="Exit Preview"><i class="icon icon-wrong"></i><span> </span></a>
|
||||
</li>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul class="sections" ng-class="{selected: !showDevicesPreview}" ng-if="enableCanvasdesigner > 0">
|
||||
<li ng-click="openPreviewDevice()">
|
||||
<a href="#"><i class="icon {{previewDevice.icon}}"></i><span>Preview</span></a>
|
||||
@@ -53,15 +55,11 @@
|
||||
<a href="#"><i class="icon icon-paint-roller"></i><span>UI Designer</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="main-panel" ng-class="{selected: !showDevicesPreview && ( showPalettePicker || showStyleEditor )}">
|
||||
|
||||
<div class="header">
|
||||
<h3>Palette Style</h3>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<ul class="samples">
|
||||
<li ng-repeat="palette in canvasdesignerPalette">
|
||||
@@ -78,7 +76,6 @@
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
|
||||
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
|
||||
@@ -90,11 +87,8 @@
|
||||
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="main-panel" ng-class="{selected: !showDevicesPreview && showStyleEditor}">
|
||||
|
||||
<div ng-show="!currentSelected">
|
||||
<div class="header">
|
||||
<h3>Select</h3>
|
||||
@@ -112,7 +106,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-repeat="configItem in canvasdesignerModel.configs" ng-show="currentSelected && currentSelected.name.toLowerCase() == configItem.name.toLowerCase()
|
||||
&& currentSelected.schema.toLowerCase() == configItem.schema.toLowerCase()" on-finish-render-filters>
|
||||
<div class="header">
|
||||
@@ -134,7 +127,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-success" ng-click="saveStyle()">Save Style</a>
|
||||
<a class="btn btn-success dropdown-toggle" ng-click="opendropdown = !opendropdown">
|
||||
@@ -146,20 +138,13 @@
|
||||
<li><a ng-click="makePreset();opendropdown = false">Make preset</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="float-panel"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="speechbubble">
|
||||
<p>Styles saved and published</p>
|
||||
</div>
|
||||
|
||||
<script src="../lib/rgrove-lazyload/lazyload.js"></script>
|
||||
<script src="../js/canvasdesigner.loader.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.layout">
|
||||
|
||||
<div class="box-slider">
|
||||
@@ -7,4 +7,4 @@
|
||||
<input type="radio" ng-model="item.values.layout" value="full"> Full
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.margin">
|
||||
|
||||
<div class="box-slider">
|
||||
<div class="box-slider">
|
||||
<ul class="box-preview">
|
||||
<li ng-repeat="margin in marginList" class="border-{{margin}}" ng-class="{selected: selectedmargin.name == margin}" ng-click="setSelectedmargin(margin)"></li>
|
||||
</ul>
|
||||
@@ -11,4 +11,4 @@
|
||||
<div ui-slider min="0" max="400" step="1" ng-model="selectedmargin.value"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.padding">
|
||||
|
||||
<div class="box-slider">
|
||||
@@ -11,4 +11,4 @@
|
||||
<div ui-slider min="0" max="400" step="1" ng-model="selectedpadding.value"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.radius">
|
||||
|
||||
<div class="box-slider">
|
||||
<ul class="box-preview">
|
||||
|
||||
|
||||
<li ng-repeat="radius in radiusList" ng-class="{selected: selectedradius.name == radius}" ng-click="setSelectedradius(radius)">
|
||||
<span ng-show="radius == 'topleft' || radius == 'all'" class="radius-top-left"></span>
|
||||
<span ng-show="radius == 'topright' || radius == 'all'" class="radius-top-right"></span>
|
||||
@@ -18,4 +18,4 @@
|
||||
<div ui-slider min="0" max="40" step="1" ng-model="selectedradius.value"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.shadow">
|
||||
|
||||
<div class="box-slider">
|
||||
<div ui-slider min="0" max="100" step="1" ng-model="item.values.shadow"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
|
||||
@inherits System.Web.Mvc.WebViewPage
|
||||
<div ng-controller="Umbraco.canvasdesigner.slider">
|
||||
|
||||
<div class="box-slider">
|
||||
<div ui-slider min="{{item.min}}" max="{{item.max}}" step="1" ng-model="item.values.slider"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<configuration>
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
<add namespace="System.Web.Mvc.Ajax" />
|
||||
<add namespace="System.Web.Mvc.Html" />
|
||||
<add namespace="System.Web.Routing" />
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
|
||||
<system.webServer>
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
|
||||
<system.web>
|
||||
<compilation>
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
</configuration>
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.Repositories;
|
||||
using Umbraco.Core.Persistence.Repositories.Implement;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Changes;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
@@ -15,11 +16,13 @@ namespace Umbraco.Web.Cache
|
||||
public sealed class ContentCacheRefresher : PayloadCacheRefresherBase<ContentCacheRefresher, ContentCacheRefresher.JsonPayload>
|
||||
{
|
||||
private readonly IPublishedSnapshotService _publishedSnapshotService;
|
||||
private readonly IdkMap _idkMap;
|
||||
|
||||
public ContentCacheRefresher(CacheHelper cacheHelper, IPublishedSnapshotService publishedSnapshotService)
|
||||
public ContentCacheRefresher(CacheHelper cacheHelper, IPublishedSnapshotService publishedSnapshotService, IdkMap idkMap)
|
||||
: base(cacheHelper)
|
||||
{
|
||||
_publishedSnapshotService = publishedSnapshotService;
|
||||
_idkMap = idkMap;
|
||||
}
|
||||
|
||||
#region Define
|
||||
@@ -51,6 +54,8 @@ namespace Umbraco.Web.Cache
|
||||
// remove that one
|
||||
runtimeCache.ClearCacheItem(RepositoryCacheKeys.GetKey<IContent>(payload.Id));
|
||||
|
||||
_idkMap.ClearCache(payload.Id);
|
||||
|
||||
// remove those that are in the branch
|
||||
if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.RefreshBranch | TreeChangeTypes.Remove))
|
||||
{
|
||||
|
||||
@@ -41,6 +41,8 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
public override void Refresh(JsonPayload[] payloads)
|
||||
{
|
||||
if (payloads == null) return;
|
||||
|
||||
bool anythingChanged;
|
||||
_publishedSnapshotService.Notify(payloads, out anythingChanged);
|
||||
|
||||
@@ -52,7 +54,11 @@ namespace Umbraco.Web.Cache
|
||||
|
||||
foreach (var payload in payloads)
|
||||
{
|
||||
_idkMap.ClearCache(payload.Id);
|
||||
if (payload.Operation == OperationType.Deleted)
|
||||
_idkMap.ClearCache(payload.Id);
|
||||
|
||||
|
||||
|
||||
|
||||
// note: ClearCacheByKeySearch - does StartsWith(...)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Http;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
[PluginController("UmbracoApi")]
|
||||
public class BackOfficeAssetsController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
|
||||
[HttpGet]
|
||||
public IEnumerable<string> GetSupportedMomentLocales()
|
||||
{
|
||||
const string momentLocaleFolder = "moment";
|
||||
var fileSystem = FileSystemProviderManager.Current.JavaScriptLibraryFileSystem;
|
||||
var cultures = fileSystem.GetFiles(momentLocaleFolder, "*.js").ToList();
|
||||
for (var i = 0; i < cultures.Count; i++)
|
||||
{
|
||||
cultures[i] = cultures[i]
|
||||
.Substring(cultures[i].IndexOf(momentLocaleFolder, StringComparison.Ordinal) + momentLocaleFolder.Length + 1);
|
||||
}
|
||||
return cultures;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.HealthCheck;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
@@ -53,7 +54,8 @@ namespace Umbraco.Web.Editors
|
||||
{"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl"}},
|
||||
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage"}},
|
||||
{"application", new[] {"applicationPath", "cacheBuster"}},
|
||||
{"isDebuggingEnabled", new string[] { }}
|
||||
{"isDebuggingEnabled", new string[] { }},
|
||||
{"features", new [] {"disabledFeatures"}}
|
||||
};
|
||||
//now do the filtering...
|
||||
var defaults = GetServerVariables();
|
||||
@@ -82,7 +84,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
//TODO: This is ultra confusing! this same key is used for different things, when returning the full app when authenticated it is this URL but when not auth'd it's actually the ServerVariables address
|
||||
// so based on compat and how things are currently working we need to replace the serverVarsJs one
|
||||
((Dictionary<string, object>) defaults["umbracoUrls"])["serverVarsJs"] = _urlHelper.Action("ServerVariables", "BackOffice");
|
||||
((Dictionary<string, object>)defaults["umbracoUrls"])["serverVarsJs"] = _urlHelper.Action("ServerVariables", "BackOffice");
|
||||
|
||||
return defaults;
|
||||
}
|
||||
@@ -277,6 +279,10 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
"helpApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<HelpController>(
|
||||
controller => controller.GetContextHelpForPage("","",""))
|
||||
},
|
||||
{
|
||||
"backOfficeAssetsApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<BackOfficeAssetsController>(
|
||||
controller => controller.GetSupportedMomentLocales())
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -337,6 +343,18 @@ namespace Umbraco.Web.Editors
|
||||
.ToArray()
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"features", new Dictionary<string,object>
|
||||
{
|
||||
{
|
||||
"disabledFeatures", new Dictionary<string,object>
|
||||
{
|
||||
{ "disableTemplates", FeaturesResolver.Current.Features.Disabled.DisableTemplates}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
return defaultVals;
|
||||
@@ -361,9 +379,9 @@ namespace Umbraco.Web.Editors
|
||||
.ToArray();
|
||||
|
||||
return (from p in pluginTreesWithAttributes
|
||||
let treeAttr = p.attributes.OfType<TreeAttribute>().Single()
|
||||
let pluginAttr = p.attributes.OfType<PluginControllerAttribute>().Single()
|
||||
select new Dictionary<string, string>
|
||||
let treeAttr = p.attributes.OfType<TreeAttribute>().Single()
|
||||
let pluginAttr = p.attributes.OfType<PluginControllerAttribute>().Single()
|
||||
select new Dictionary<string, string>
|
||||
{
|
||||
{"alias", treeAttr.Alias}, {"packageFolder", pluginAttr.AreaName}
|
||||
}).ToArray();
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
[DisableBrowserCache]
|
||||
public class PreviewController : Controller
|
||||
{
|
||||
[UmbracoAuthorize(redirectToUmbracoLogin: true)]
|
||||
public ActionResult Index()
|
||||
{
|
||||
var model = new BackOfficePreview
|
||||
{
|
||||
DisableDevicePreview = FeaturesResolver.Current.Features.Disabled.DisableDevicePreview,
|
||||
PreviewExtendedHeaderView = FeaturesResolver.Current.Features.Enabled.PreviewExtendedView
|
||||
};
|
||||
|
||||
if (model.PreviewExtendedHeaderView.IsNullOrWhiteSpace() == false)
|
||||
{
|
||||
var viewEngineResult = ViewEngines.Engines.FindPartialView(ControllerContext, model.PreviewExtendedHeaderView);
|
||||
if (viewEngineResult.View == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find the view " + model.PreviewExtendedHeaderView + ", the following locations were searched: " + Environment.NewLine + string.Join(Environment.NewLine, viewEngineResult.SearchedLocations));
|
||||
}
|
||||
}
|
||||
|
||||
return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + "Index.cshtml", model);
|
||||
}
|
||||
|
||||
public ActionResult Editors(string editor)
|
||||
{
|
||||
if (string.IsNullOrEmpty(editor)) throw new ArgumentNullException("editor");
|
||||
return View(GlobalSettings.Path.EnsureEndsWith('/') + "Views/Preview/" + editor.Replace(".html", string.Empty) + ".cshtml");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,14 +73,20 @@ namespace Umbraco.Web.Editors
|
||||
//Checking to see if the user has access to the required tour sections, else we remove the tour
|
||||
foreach (var backOfficeTourFile in result)
|
||||
{
|
||||
foreach (var tour in backOfficeTourFile.Tours)
|
||||
if (backOfficeTourFile.Tours != null)
|
||||
{
|
||||
foreach (var toursRequiredSection in tour.RequiredSections)
|
||||
foreach (var tour in backOfficeTourFile.Tours)
|
||||
{
|
||||
if (allowedSections.Contains(toursRequiredSection) == false)
|
||||
if (tour.RequiredSections != null)
|
||||
{
|
||||
toursToBeRemoved.Add(backOfficeTourFile);
|
||||
break;
|
||||
foreach (var toursRequiredSection in tour.RequiredSections)
|
||||
{
|
||||
if (allowedSections.Contains(toursRequiredSection) == false)
|
||||
{
|
||||
toursToBeRemoved.Add(backOfficeTourFile);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,5 +20,16 @@ namespace Umbraco.Web.Features
|
||||
/// Gets the disabled controllers.
|
||||
/// </summary>
|
||||
public TypeList<UmbracoApiControllerBase> Controllers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Disables the device preview feature of previewing.
|
||||
/// </summary>
|
||||
public bool DisableDevicePreview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If true, all references to templates will be removed in the back office and routing
|
||||
/// </summary>
|
||||
public bool DisableTemplates { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ namespace Umbraco.Web.Features
|
||||
internal class EnabledFeatures
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies if rendering pipeline should ignore HasTemplate check when handling a request.
|
||||
/// <remarks>This is to allow JSON preview of content with no template set.</remarks>
|
||||
/// This allows us to inject a razor view into the Umbraco preview view to extend it
|
||||
/// </summary>
|
||||
public bool RenderNoTemplate { get; set; }
|
||||
/// <remarks>
|
||||
/// This is set to a virtual path of a razor view file
|
||||
/// </remarks>
|
||||
public string PreviewExtendedView { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,7 @@ namespace Umbraco.Web.Features
|
||||
Disabled = new DisabledFeatures();
|
||||
Enabled = new EnabledFeatures();
|
||||
}
|
||||
|
||||
// note
|
||||
// currently, the only thing a FeatureSet does is list disabled controllers,
|
||||
// but eventually we could enable and disable more parts of Umbraco. and then
|
||||
// we would need some logic to figure out what's enabled/disabled - hence it's
|
||||
// better to use IsEnabled, where the logic would go, rather than directly
|
||||
// accessing the Disabled collection.
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the disabled features.
|
||||
/// </summary>
|
||||
@@ -35,9 +28,9 @@ namespace Umbraco.Web.Features
|
||||
public EnabledFeatures Enabled { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a feature is enabled.
|
||||
/// Determines whether a controller is enabled.
|
||||
/// </summary>
|
||||
public bool IsEnabled(Type feature)
|
||||
public bool IsControllerEnabled(Type feature)
|
||||
{
|
||||
if (typeof(UmbracoApiControllerBase).IsAssignableFrom(feature))
|
||||
return Disabled.Controllers.Contains(feature) == false;
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace Umbraco.Web.Models
|
||||
[DataContract(Name = "tour", Namespace = "")]
|
||||
public class BackOfficeTour
|
||||
{
|
||||
public BackOfficeTour()
|
||||
{
|
||||
RequiredSections = new List<string>();
|
||||
}
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
[DataMember(Name = "alias")]
|
||||
|
||||
@@ -9,6 +9,11 @@ namespace Umbraco.Web.Models
|
||||
[DataContract(Name = "tourFile", Namespace = "")]
|
||||
public class BackOfficeTourFile
|
||||
{
|
||||
public BackOfficeTourFile()
|
||||
{
|
||||
Tours = new List<BackOfficeTour>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The file name for the tour
|
||||
/// </summary>
|
||||
@@ -27,4 +32,4 @@ namespace Umbraco.Web.Models
|
||||
[DataMember(Name = "tours")]
|
||||
public IEnumerable<BackOfficeTour> Tours { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
|
||||
[DataContract(Name = "auditLog", Namespace = "")]
|
||||
public class AuditLog
|
||||
{
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Umbraco.Web.Models.ContentEditing
|
||||
{
|
||||
/// <summary>
|
||||
/// The model representing Previewing of a content item from the back office
|
||||
/// </summary>
|
||||
public class BackOfficePreview
|
||||
{
|
||||
public string PreviewExtendedHeaderView { get; set; }
|
||||
//TODO: We could potentially have a 'footer' view
|
||||
public bool DisableDevicePreview { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Install;
|
||||
using Umbraco.Web.Install.Controllers;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
@@ -24,6 +20,12 @@ namespace Umbraco.Web.Mvc
|
||||
/// </remarks>
|
||||
public override void RegisterArea(AreaRegistrationContext context)
|
||||
{
|
||||
context.MapRoute(
|
||||
"Umbraco_preview",
|
||||
GlobalSettings.UmbracoMvcArea + "/preview/{action}/{editor}",
|
||||
new {controller = "Preview", action = "Index", editor = UrlParameter.Optional},
|
||||
new[] { "Umbraco.Web.Editors" });
|
||||
|
||||
context.MapRoute(
|
||||
"Umbraco_back_office",
|
||||
GlobalSettings.UmbracoMvcArea + "/{action}/{id}",
|
||||
|
||||
@@ -391,9 +391,12 @@ namespace Umbraco.Web.Mvc
|
||||
return GetWebFormsHandler();
|
||||
}
|
||||
|
||||
//here we need to check if there is no hijacked route and no template assigned, if this is the case
|
||||
//we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
|
||||
if (!request.HasTemplate && !routeDef.HasHijackedRoute && !Features.Enabled.RenderNoTemplate)
|
||||
//Here we need to check if there is no hijacked route and no template assigned,
|
||||
//if this is the case we want to return a blank page, but we'll leave that up to the NoTemplateHandler.
|
||||
//We also check if templates have been disabled since if they are then we're allowed to render even though there's no template,
|
||||
//for example for json rendering in headless.
|
||||
if ((request.HasTemplate == false && Features.Disabled.DisableTemplates == false)
|
||||
&& routeDef.HasHijackedRoute == false)
|
||||
{
|
||||
// fixme - better find a way to inject that engine? or at least Current.Engine of some sort!
|
||||
var engine = Core.Composing.Current.Container.GetInstance<PublishedRouter>();
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
|
||||
namespace Umbraco.Web.Mvc
|
||||
{
|
||||
@@ -14,6 +15,7 @@ namespace Umbraco.Web.Mvc
|
||||
// see note in HttpInstallAuthorizeAttribute
|
||||
private readonly UmbracoContext _umbracoContext;
|
||||
private readonly IRuntimeState _runtimeState;
|
||||
private readonly string _redirectUrl;
|
||||
|
||||
private IRuntimeState RuntimeState => _runtimeState ?? Current.RuntimeState;
|
||||
|
||||
@@ -32,9 +34,33 @@ namespace Umbraco.Web.Mvc
|
||||
_runtimeState = runtimeState;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public UmbracoAuthorizeAttribute()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor specifying to redirect to the specified location if not authorized
|
||||
/// </summary>
|
||||
/// <param name="redirectUrl"></param>
|
||||
public UmbracoAuthorizeAttribute(string redirectUrl)
|
||||
{
|
||||
_redirectUrl = redirectUrl ?? throw new ArgumentNullException(nameof(redirectUrl));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor specifying to redirect to the umbraco login page if not authorized
|
||||
/// </summary>
|
||||
/// <param name="redirectToUmbracoLogin"></param>
|
||||
public UmbracoAuthorizeAttribute(bool redirectToUmbracoLogin)
|
||||
{
|
||||
if (redirectToUmbracoLogin)
|
||||
{
|
||||
_redirectUrl = GlobalSettings.Path.EnsureStartsWith("~");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the user must be in the Administrator or the Install role
|
||||
/// </summary>
|
||||
@@ -64,7 +90,16 @@ namespace Umbraco.Web.Mvc
|
||||
/// <param name="filterContext"></param>
|
||||
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
|
||||
{
|
||||
filterContext.Result = new HttpUnauthorizedResult("You must login to view this resource.");
|
||||
if (_redirectUrl.IsNullOrWhiteSpace())
|
||||
{
|
||||
filterContext.Result = (ActionResult)new HttpUnauthorizedResult("You must login to view this resource.");
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
filterContext.Result = new RedirectResult(_redirectUrl);
|
||||
}
|
||||
|
||||
// DON'T do a FormsAuth redirect... argh!! thankfully we're running .Net 4.5 :)
|
||||
filterContext.RequestContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
|
||||
|
||||
@@ -8,6 +8,7 @@ using Examine.LuceneEngine.SearchCriteria;
|
||||
using Examine.SearchCriteria;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Xml;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
@@ -171,7 +172,6 @@ namespace Umbraco.Web
|
||||
private static IPublishedContent ItemById(Guid id, IPublishedCache cache)
|
||||
{
|
||||
var doc = cache.GetById(id);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static IPublishedContent ItemByXPath(string xpath, XPathVariable[] vars, IPublishedCache cache)
|
||||
@@ -243,13 +243,13 @@ namespace Umbraco.Web
|
||||
if (!(searcher is BaseLuceneSearcher luceneSearcher))
|
||||
{
|
||||
var results = searcher.Search(term, useWildCards);
|
||||
totalRecords = results.TotalItemCount;
|
||||
totalRecords = results.TotalItemCount;
|
||||
// Examine skip, Linq take
|
||||
return results.Skip(skip).ToPublishedSearchResults(_contentCache).Take(take);
|
||||
}
|
||||
|
||||
var criteria = SearchAllFields(term, useWildCards, luceneSearcher);
|
||||
return Search(skip, take, out totalRecords, criteria, searcher);
|
||||
return Search(skip, take, out totalRecords, criteria, searcher);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -287,7 +287,7 @@ namespace Umbraco.Web
|
||||
{
|
||||
//get the GetSearchFields method from BaseLuceneSearcher
|
||||
_examineGetSearchFields = typeof(BaseLuceneSearcher).GetMethod("GetSearchFields", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
|
||||
}
|
||||
}
|
||||
|
||||
//get the results of searcher.BaseLuceneSearcher() using ugly reflection since it's not public
|
||||
var searchFields = (IEnumerable<string>) _examineGetSearchFields.Invoke(searcher, null);
|
||||
@@ -314,8 +314,8 @@ namespace Umbraco.Web
|
||||
public Examineness Examineness { get; private set; }
|
||||
public string Value { get; private set; }
|
||||
public float Level { get; private set; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
'lib/angular/1.1.5/angular.min.js',
|
||||
'lib/underscore/underscore-min.js',
|
||||
|
||||
'lib/moment/moment-with-locales.js',
|
||||
'lib/moment/moment.min.js',
|
||||
|
||||
'lib/jquery-ui/jquery-ui.min.js',
|
||||
'lib/jquery-ui-touch-punch/jquery.ui.touch-punch.js',
|
||||
|
||||
@@ -49,6 +49,12 @@
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Threading.Tasks.Dataflow, Version=4.6.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Dataflow.4.7.0\lib\portable-net45+win8+wpa81\System.Threading.Tasks.Dataflow.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.4.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
@@ -128,6 +134,15 @@
|
||||
<Compile Include="Cache\UserGroupPermissionsCacheRefresher.cs" />
|
||||
<Compile Include="CompositionExtensions.cs" />
|
||||
<Compile Include="Composing\Current.cs" />
|
||||
<Compile Include="Cache\StylesheetCacheRefresher.cs" />
|
||||
<Compile Include="Cache\StylesheetPropertyCacheRefresher.cs" />
|
||||
<Compile Include="Cache\TemplateCacheRefresher.cs" />
|
||||
<Compile Include="Cache\UnpublishedPageCacheRefresher.cs" />
|
||||
<Compile Include="Cache\UserCacheRefresher.cs" />
|
||||
<Compile Include="Editors\PreviewController.cs" />
|
||||
<Compile Include="Editors\BackOfficeAssetsController.cs" />
|
||||
<Compile Include="Features\DisabledFeatures.cs" />
|
||||
<Compile Include="Editors\BackOfficeNotificationsController.cs" />
|
||||
<Compile Include="Editors\BackOfficeServerVariables.cs" />
|
||||
<Compile Include="Editors\CodeFileController.cs" />
|
||||
<Compile Include="Editors\DashboardHelper.cs" />
|
||||
@@ -143,6 +158,15 @@
|
||||
<Compile Include="Editors\PasswordChanger.cs" />
|
||||
<Compile Include="Editors\TemplateController.cs" />
|
||||
<Compile Include="Editors\TourController.cs" />
|
||||
<Compile Include="Features\EnabledFeatures.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFilter.cs" />
|
||||
<Compile Include="Models\ContentEditing\BackOfficePreview.cs" />
|
||||
<Compile Include="Models\Mapping\AutoMapperExtensions.cs" />
|
||||
<Compile Include="Models\Mapping\ContentTreeNodeUrlResolver.cs" />
|
||||
<Compile Include="Models\Mapping\MemberTreeNodeUrlResolver.cs" />
|
||||
<Compile Include="Models\Trees\ExportMember.cs" />
|
||||
<Compile Include="PropertyEditors\DropdownFlexiblePropertyEditor.cs" />
|
||||
<Compile Include="TourFilterResolver.cs" />
|
||||
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />
|
||||
<Compile Include="Editors\UserGroupAuthorizationAttribute.cs" />
|
||||
<Compile Include="Editors\UserGroupEditorAuthorizationHelper.cs" />
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Umbraco.Web.WebApi.Filters
|
||||
if (features == null) return true;
|
||||
|
||||
var controllerType = actionContext.ControllerContext.ControllerDescriptor.ControllerType;
|
||||
return features.IsEnabled(controllerType);
|
||||
return features.IsControllerEnabled(controllerType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
using System.Linq;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Web._Legacy.UI;
|
||||
|
||||
namespace umbraco
|
||||
namespace Umbraco.Web.umbraco.presentation.umbraco.create
|
||||
{
|
||||
public class MemberGroupTasks : LegacyDialogTask
|
||||
{
|
||||
public override bool PerformSave()
|
||||
{
|
||||
Roles.CreateRole(Alias);
|
||||
_returnUrl = string.Format("members/EditMemberGroup.aspx?id={0}", System.Web.HttpContext.Current.Server.UrlEncode(Alias));
|
||||
_returnUrl = $"members/EditMemberGroup.aspx?id={System.Web.HttpContext.Current.Server.UrlEncode(Alias)}";
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool PerformDelete()
|
||||
{
|
||||
var roleDeleted = false;
|
||||
|
||||
// only built-in roles can be deleted
|
||||
if (Roles.Provider.Name == Constants.Conventions.Member.UmbracoRoleProviderName)
|
||||
{
|
||||
@@ -24,21 +27,31 @@ namespace umbraco
|
||||
{
|
||||
Current.Services.MemberGroupService.Delete(group);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
// Need to delete the member group from any content item that has it assigned in public access settings
|
||||
var publicAccessService = UmbracoContext.Current.Application.Services.PublicAccessService;
|
||||
var allPublicAccessRules = publicAccessService.GetAll();
|
||||
|
||||
// Find only rules which have the current role name (alias) assigned to them
|
||||
var rulesWithDeletedRoles = allPublicAccessRules.Where(x => x.Rules.Any(r => r.RuleValue == Alias));
|
||||
|
||||
var contentService = UmbracoContext.Current.Application.Services.ContentService;
|
||||
foreach (var publicAccessEntry in rulesWithDeletedRoles)
|
||||
{
|
||||
var contentItem = contentService.GetById(publicAccessEntry.ProtectedNodeId);
|
||||
var rulesToDelete = publicAccessEntry.Rules.ToList();
|
||||
foreach (var rule in rulesToDelete)
|
||||
publicAccessService.RemoveRule(contentItem, rule.RuleType, rule.RuleValue);
|
||||
}
|
||||
|
||||
return roleDeleted;
|
||||
}
|
||||
|
||||
private string _returnUrl = "";
|
||||
|
||||
public override string ReturnUrl
|
||||
{
|
||||
get { return _returnUrl; }
|
||||
}
|
||||
public override string ReturnUrl => _returnUrl;
|
||||
|
||||
public override string AssignedApp
|
||||
{
|
||||
get { return Constants.Applications.Members.ToString(); }
|
||||
}
|
||||
public override string AssignedApp => Constants.Applications.Members.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user