Merge remote-tracking branch 'origin/netcore/dev' into netcore/feature/AB4549-Move-models-from-web
# Conflicts: # src/Umbraco.Web/Editors/ContentTypeController.cs # src/Umbraco.Web/Editors/PackageInstallController.cs
This commit is contained in:
+10
-6
@@ -13,7 +13,7 @@
|
||||
*.png binary
|
||||
*.gif binary
|
||||
|
||||
*.cs text=auto diff=csharp
|
||||
*.cs text=auto diff=csharp
|
||||
*.vb text=auto
|
||||
*.c text=auto
|
||||
*.cpp text=auto
|
||||
@@ -41,9 +41,13 @@
|
||||
*.fs text=auto
|
||||
*.fsx text=auto
|
||||
*.hs text=auto
|
||||
*.json text=auto
|
||||
*.xml text=auto
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
|
||||
*.gitattributes text=auto
|
||||
|
||||
@@ -55,6 +55,7 @@ App_Data/TEMP/*
|
||||
src/Umbraco.Web.UI/[Cc]ss/*
|
||||
src/Umbraco.Web.UI/App_Code/*
|
||||
src/Umbraco.Web.UI/App_Data/*
|
||||
src/Umbraco.Web.UI/data/*
|
||||
src/Umbraco.Tests/App_Data/*
|
||||
src/Umbraco.Web.UI/[Mm]edia/*
|
||||
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<dependency id="ClientDependency" version="[1.9.8,1.999999)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.9.3,1.999999)" />
|
||||
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
|
||||
<dependency id="Examine" version="[1.0.1,1.999999)" />
|
||||
<dependency id="Examine" version="[1.0.2,1.999999)" />
|
||||
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
|
||||
<dependency id="ImageProcessor" version="[2.7.0.100,2.999999)" />
|
||||
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Umbraco.Core.Composing
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error(componentType, ex, "Error while terminating component {ComponentType}.", componentType.FullName);
|
||||
_logger.Error<ComponentCollection>(ex, "Error while terminating component {ComponentType}.", componentType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,28 +22,6 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
private const int LogThresholdMilliseconds = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Composers" /> class.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="composerTypes">The <see cref="IComposer" /> types.</param>
|
||||
/// <param name="logger">The profiling logger.</param>
|
||||
[Obsolete("This overload only gets the EnableComposer/DisableComposer attributes from the composerTypes assemblies.")]
|
||||
public Composers(Composition composition, IEnumerable<Type> composerTypes, IProfilingLogger logger)
|
||||
: this(composition, composerTypes, Enumerable.Empty<Attribute>(), logger)
|
||||
{
|
||||
var enableDisableAttributes = new List<Attribute>();
|
||||
|
||||
var assemblies = composerTypes.Select(t => t.Assembly).Distinct();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(EnableComposerAttribute)));
|
||||
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(DisableComposerAttribute)));
|
||||
}
|
||||
|
||||
_enableDisableAttributes = enableDisableAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Composers" /> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,10 +9,6 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class AppSettings
|
||||
{
|
||||
// TODO: Kill me - still used in Umbraco.Core.IO.SystemFiles:27
|
||||
[Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")]
|
||||
public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML
|
||||
|
||||
/// <summary>
|
||||
/// TODO: FILL ME IN
|
||||
/// </summary>
|
||||
|
||||
@@ -19,6 +19,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
public static bool VariesBySegment(this ISimpleContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a null reference, or an empty argument, is passed to a method that does not accept it as a valid argument.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.ArgumentNullException" />
|
||||
[Obsolete("Throw an ArgumentNullException when the parameter is null or an ArgumentException when its empty instead.")]
|
||||
[Serializable]
|
||||
public class ArgumentNullOrEmptyException : ArgumentNullException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
public ArgumentNullOrEmptyException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with the name of the parameter that caused this exception.
|
||||
/// </summary>
|
||||
/// <param name="paramName">The named of the parameter that caused the exception.</param>
|
||||
public ArgumentNullOrEmptyException(string paramName)
|
||||
: base(paramName)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with a specified error message and the name of the parameter that caused this exception.
|
||||
/// </summary>
|
||||
/// <param name="paramName">The named of the parameter that caused the exception.</param>
|
||||
/// <param name="message">A message that describes the error.</param>
|
||||
public ArgumentNullOrEmptyException(string paramName, string message)
|
||||
: base(paramName, message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for this exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public ArgumentNullOrEmptyException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The object that holds the serialized object data.</param>
|
||||
/// <param name="context">An object that describes the source or destination of the serialized data.</param>
|
||||
protected ArgumentNullOrEmptyException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a requested method or operation is not, and will not be, implemented.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The <see cref="NotImplementedException" /> is to be used when some code is not implemented,
|
||||
/// but should eventually be implemented (i.e. work in progress) and is reported by tools such as ReSharper.
|
||||
/// This exception is to be used when some code is not implemented, and is not meant to be, for whatever
|
||||
/// reason.
|
||||
/// </remarks>
|
||||
/// <seealso cref="System.NotImplementedException" />
|
||||
[Serializable]
|
||||
[Obsolete("If a method or operation is not, and will not be, implemented, it is invalid or not supported, so we should throw either an InvalidOperationException or NotSupportedException instead.")]
|
||||
public class WontImplementException : NotImplementedException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
public WontImplementException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class with a specified reason message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
public WontImplementException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="inner">The exception that is the cause of the current exception. If the <paramref name="inner" /> parameter is not <see langword="null" />, the current exception is raised in a <see langword="catch" /> block that handles the inner exception.</param>
|
||||
public WontImplementException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected WontImplementException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the caller does not have the required permission to access a file.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Obsolete("Throw an UnauthorizedAccessException instead.")]
|
||||
[Serializable]
|
||||
public class FileSecurityException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
public FileSecurityException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public FileSecurityException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public FileSecurityException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected FileSecurityException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -53,11 +53,21 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[ReadOnly(true)]
|
||||
public string Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The segment of the property
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The segment value of a property can always be null but can only have a non-null value
|
||||
/// when the property can be varied by segment.
|
||||
/// </remarks>
|
||||
[DataMember(Name = "segment")]
|
||||
[ReadOnly(true)]
|
||||
public string Segment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Used internally during model mapping
|
||||
/// </summary>
|
||||
[IgnoreDataMember]
|
||||
internal IDataEditor PropertyEditor { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
[DataMember(Name = "culture")]
|
||||
public string Culture { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The segment of this variant, if this is invariant than this is null or empty
|
||||
/// </summary>
|
||||
[DataMember(Name = "segment")]
|
||||
public string Segment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the variant should be updated
|
||||
/// </summary>
|
||||
|
||||
@@ -17,25 +17,6 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
public static bool IsLiveFactory(this IPublishedModelFactory factory) => factory is ILivePublishedModelFactory;
|
||||
|
||||
[Obsolete("This method is no longer used or necessary and will be removed from future")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static void WithSafeLiveFactory(this IPublishedModelFactory factory, Action action)
|
||||
{
|
||||
if (factory is ILivePublishedModelFactory liveFactory)
|
||||
{
|
||||
lock (liveFactory.SyncRoot)
|
||||
{
|
||||
//Call refresh on the live factory to re-compile the models
|
||||
liveFactory.Refresh();
|
||||
action();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a flag to reset the ModelsBuilder models if the <see cref="IPublishedModelFactory"/> is <see cref="ILivePublishedModelFactory"/>
|
||||
/// </summary>
|
||||
|
||||
@@ -312,13 +312,6 @@ namespace Umbraco.Core.Services
|
||||
/// </summary>
|
||||
OperationResult MoveToRecycleBin(IContent content, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Empties the recycle bin.
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
|
||||
OperationResult EmptyRecycleBin();
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
|
||||
/// </summary>
|
||||
|
||||
@@ -156,13 +156,6 @@ namespace Umbraco.Core.Services
|
||||
/// <param name="userId">Id of the User deleting the Media</param>
|
||||
Attempt<OperationResult> MoveToRecycleBin(IMedia media, int userId = Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
|
||||
OperationResult EmptyRecycleBin();
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
|
||||
/// </summary>
|
||||
|
||||
@@ -185,23 +185,6 @@ namespace Umbraco.Core.Configuration
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of the content XML file.
|
||||
/// </summary>
|
||||
/// <value>The content XML.</value>
|
||||
/// <remarks>
|
||||
/// Defaults to ~/App_Data/umbraco.config
|
||||
/// </remarks>
|
||||
public string ContentXmlFile
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfigurationManager.AppSettings.ContainsKey(Constants.AppSettings.ContentXML)
|
||||
? ConfigurationManager.AppSettings[Constants.AppSettings.ContentXML]
|
||||
: "~/App_Data/umbraco.config";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to umbraco's root directory (/umbraco by default).
|
||||
/// </summary>
|
||||
|
||||
@@ -49,9 +49,9 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<!-- note: NuGet deals with transitive references now -->
|
||||
<PackageReference Include="Examine" Version="1.0.1" />
|
||||
<PackageReference Include="Examine" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub">
|
||||
<Version>1.0.0-beta2-19324-01</Version>
|
||||
<Version>1.0.0-beta2-19554-01</Version>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
+2
-20
@@ -5,7 +5,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
namespace Umbraco.Core.Migrations.Expressions.Delete.Index
|
||||
{
|
||||
public class DeleteIndexBuilder : ExpressionBuilderBase<DeleteIndexExpression>,
|
||||
IDeleteIndexForTableBuilder, IDeleteIndexOnColumnBuilder
|
||||
IDeleteIndexForTableBuilder, IExecutableBuilder
|
||||
{
|
||||
public DeleteIndexBuilder(DeleteIndexExpression expression)
|
||||
: base(expression)
|
||||
@@ -14,28 +14,10 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.Index
|
||||
/// <inheritdoc />
|
||||
public void Do() => Expression.Execute();
|
||||
|
||||
public IndexColumnDefinition CurrentColumn { get; set; }
|
||||
|
||||
public IDeleteIndexOnColumnBuilder OnTable(string tableName)
|
||||
public IExecutableBuilder OnTable(string tableName)
|
||||
{
|
||||
Expression.Index.TableName = tableName;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IExecutableBuilder OnColumn(string columnName)
|
||||
{
|
||||
var column = new IndexColumnDefinition { Name = columnName };
|
||||
Expression.Index.Columns.Add(column);
|
||||
return new ExecutableBuilder(Expression);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IExecutableBuilder OnColumns(params string[] columnNames)
|
||||
{
|
||||
foreach (string columnName in columnNames)
|
||||
Expression.Index.Columns.Add(new IndexColumnDefinition { Name = columnName });
|
||||
return new ExecutableBuilder(Expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,4 +1,6 @@
|
||||
namespace Umbraco.Core.Migrations.Expressions.Delete.Index
|
||||
using Umbraco.Core.Migrations.Expressions.Common;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Expressions.Delete.Index
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a Delete expression.
|
||||
@@ -8,6 +10,6 @@
|
||||
/// <summary>
|
||||
/// Specifies the table of the index to delete.
|
||||
/// </summary>
|
||||
IDeleteIndexOnColumnBuilder OnTable(string tableName);
|
||||
IExecutableBuilder OnTable(string tableName);
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
using System;
|
||||
using Umbraco.Core.Migrations.Expressions.Common;
|
||||
|
||||
namespace Umbraco.Core.Migrations.Expressions.Delete.Index
|
||||
{
|
||||
/// <summary>
|
||||
/// Builds a Delete expression.
|
||||
/// </summary>
|
||||
public interface IDeleteIndexOnColumnBuilder : IFluentBuilder, IExecutableBuilder
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the column of the index.
|
||||
/// </summary>
|
||||
[Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
|
||||
IExecutableBuilder OnColumn(string columnName);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the column of the index.
|
||||
/// </summary>
|
||||
[Obsolete("I don't think this would ever be used when dropping an index, see DeleteIndexExpression.ToString")]
|
||||
IExecutableBuilder OnColumns(params string[] columnNames);
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,25 @@ namespace Umbraco.Web.Models
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[DataMember(Name = "group")]
|
||||
public string Group { get; set; }
|
||||
|
||||
[DataMember(Name = "groupOrder")]
|
||||
public int GroupOrder { get; set; }
|
||||
|
||||
[DataMember(Name = "hidden")]
|
||||
public bool Hidden { get; set; }
|
||||
|
||||
[DataMember(Name = "allowDisable")]
|
||||
public bool AllowDisable { get; set; }
|
||||
|
||||
[DataMember(Name = "requiredSections")]
|
||||
public List<string> RequiredSections { get; set; }
|
||||
|
||||
[DataMember(Name = "steps")]
|
||||
public BackOfficeTourStep[] Steps { get; set; }
|
||||
|
||||
|
||||
@@ -21,52 +21,117 @@ namespace Umbraco.Web.Models.Mapping
|
||||
|
||||
public IEnumerable<ContentVariantDisplay> Map(IContent source, MapperContext context)
|
||||
{
|
||||
var result = new List<ContentVariantDisplay>();
|
||||
if (!source.ContentType.VariesByCulture())
|
||||
var variesByCulture = source.ContentType.VariesByCulture();
|
||||
var variesBySegment = source.ContentType.VariesBySegment();
|
||||
|
||||
IList<ContentVariantDisplay> variants = new List<ContentVariantDisplay>();
|
||||
|
||||
if (!variesByCulture && !variesBySegment)
|
||||
{
|
||||
//this is invariant so just map the IContent instance to ContentVariationDisplay
|
||||
result.Add(context.Map<ContentVariantDisplay>(source));
|
||||
// this is invariant so just map the IContent instance to ContentVariationDisplay
|
||||
var variantDisplay = context.Map<ContentVariantDisplay>(source);
|
||||
variants.Add(variantDisplay);
|
||||
}
|
||||
else if (variesByCulture && !variesBySegment)
|
||||
{
|
||||
var languages = GetLanguages(context);
|
||||
variants = languages
|
||||
.Select(language => CreateVariantDisplay(context, source, language, null))
|
||||
.ToList();
|
||||
}
|
||||
else if (variesBySegment && !variesByCulture)
|
||||
{
|
||||
// Segment only
|
||||
var segments = GetSegments(source);
|
||||
variants = segments
|
||||
.Select(segment => CreateVariantDisplay(context, source, null, segment))
|
||||
.ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0) return Enumerable.Empty<ContentVariantDisplay>(); //this should never happen
|
||||
// Culture and segment
|
||||
var languages = GetLanguages(context).ToList();
|
||||
var segments = GetSegments(source).ToList();
|
||||
|
||||
var langs = context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
|
||||
|
||||
//create a variant for each language, then we'll populate the values
|
||||
var variants = langs.Select(x =>
|
||||
if (languages.Count == 0 || segments.Count == 0)
|
||||
{
|
||||
//We need to set the culture in the mapping context since this is needed to ensure that the correct property values
|
||||
//are resolved during the mapping
|
||||
context.SetCulture(x.IsoCode);
|
||||
return context.Map<ContentVariantDisplay>(source);
|
||||
}).ToList();
|
||||
|
||||
for (int i = 0; i < langs.Count; i++)
|
||||
{
|
||||
var x = langs[i];
|
||||
var variant = variants[i];
|
||||
|
||||
variant.Language = x;
|
||||
variant.Name = source.GetCultureName(x.IsoCode);
|
||||
// This should not happen
|
||||
throw new InvalidOperationException("No languages or segments available");
|
||||
}
|
||||
|
||||
//Put the default language first in the list & then sort rest by a-z
|
||||
var defaultLang = variants.SingleOrDefault(x => x.Language.IsDefault);
|
||||
variants = languages
|
||||
.SelectMany(language => segments
|
||||
.Select(segment => CreateVariantDisplay(context, source, language, segment)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
//Remove the default language from the list for now
|
||||
variants.Remove(defaultLang);
|
||||
|
||||
//Sort the remaining languages a-z
|
||||
variants = variants.OrderBy(x => x.Language.Name).ToList();
|
||||
|
||||
//Insert the default language as the first item
|
||||
variants.Insert(0, defaultLang);
|
||||
return SortVariants(variants);
|
||||
}
|
||||
|
||||
private IList<ContentVariantDisplay> SortVariants(IList<ContentVariantDisplay> variants)
|
||||
{
|
||||
if (variants == null || variants.Count <= 1)
|
||||
{
|
||||
return variants;
|
||||
}
|
||||
return result;
|
||||
|
||||
// Default variant first, then order by language, segment.
|
||||
return variants
|
||||
.OrderBy(v => IsDefaultLanguage(v) ? 0 : 1)
|
||||
.ThenBy(v => IsDefaultSegment(v) ? 0 : 1)
|
||||
.ThenBy(v => v?.Language?.Name)
|
||||
.ThenBy(v => v.Segment)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private static bool IsDefaultSegment(ContentVariantDisplay variant)
|
||||
{
|
||||
return variant.Segment == null;
|
||||
}
|
||||
|
||||
private static bool IsDefaultLanguage(ContentVariantDisplay variant)
|
||||
{
|
||||
return variant.Language == null || variant.Language.IsDefault;
|
||||
}
|
||||
|
||||
private IEnumerable<Language> GetLanguages(MapperContext context)
|
||||
{
|
||||
var allLanguages = _localizationService.GetAllLanguages().OrderBy(x => x.Id).ToList();
|
||||
if (allLanguages.Count == 0)
|
||||
{
|
||||
// This should never happen
|
||||
return Enumerable.Empty<Language>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return context.MapEnumerable<ILanguage, Language>(allLanguages).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all segments assigned to the content
|
||||
/// </summary>
|
||||
/// <param name="content"></param>
|
||||
/// <returns>
|
||||
/// Returns all segments assigned to the content including 'null' values
|
||||
/// </returns>
|
||||
private IEnumerable<string> GetSegments(IContent content)
|
||||
{
|
||||
return content.Properties.SelectMany(p => p.Values.Select(v => v.Segment)).Distinct();
|
||||
}
|
||||
|
||||
private ContentVariantDisplay CreateVariantDisplay(MapperContext context, IContent content, Language language, string segment)
|
||||
{
|
||||
context.SetCulture(language?.IsoCode);
|
||||
context.SetSegment(segment);
|
||||
|
||||
var variantDisplay = context.Map<ContentVariantDisplay>(content);
|
||||
|
||||
variantDisplay.Segment = segment;
|
||||
variantDisplay.Language = language;
|
||||
variantDisplay.Name = content.GetCultureName(language?.IsoCode);
|
||||
|
||||
return variantDisplay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
@@ -23,6 +24,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public void DefineMaps(UmbracoMapper mapper)
|
||||
{
|
||||
mapper.Define<IMacro, EntityBasic>((source, context) => new EntityBasic(), Map);
|
||||
mapper.Define<IMacro, MacroDisplay>((source, context) => new MacroDisplay(), Map);
|
||||
mapper.Define<IMacro, IEnumerable<MacroParameter>>((source, context) => context.MapEnumerable<IMacroProperty, MacroParameter>(source.Properties.Values));
|
||||
mapper.Define<IMacroProperty, MacroParameter>((source, context) => new MacroParameter(), Map);
|
||||
}
|
||||
@@ -40,6 +42,23 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
|
||||
}
|
||||
|
||||
private void Map(IMacro source, MacroDisplay target, MapperContext context)
|
||||
{
|
||||
target.Alias = source.Alias;
|
||||
target.Icon = Constants.Icons.Macro;
|
||||
target.Id = source.Id;
|
||||
target.Key = source.Key;
|
||||
target.Name = source.Name;
|
||||
target.ParentId = -1;
|
||||
target.Path = "-1," + source.Id;
|
||||
target.Udi = Udi.Create(Constants.UdiEntityType.Macro, source.Key);
|
||||
target.CacheByPage = source.CacheByPage;
|
||||
target.CacheByUser = source.CacheByMember;
|
||||
target.CachePeriod = source.CacheDuration;
|
||||
target.UseInEditor = source.UseInEditor;
|
||||
target.RenderInEditor = !source.DontRender;
|
||||
target.View = source.MacroSource;
|
||||
}
|
||||
// Umbraco.Code.MapAll -Value
|
||||
private void Map(IMacroProperty source, MacroParameter target, MapperContext context)
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
public static class MapperContextExtensions
|
||||
{
|
||||
private const string CultureKey = "Map.Culture";
|
||||
private const string SegmentKey = "Map.Segment";
|
||||
private const string IncludedPropertiesKey = "Map.IncludedProperties";
|
||||
|
||||
/// <summary>
|
||||
@@ -18,6 +19,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
return context.HasItems && context.Items.TryGetValue(CultureKey, out var obj) && obj is string s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the context segment.
|
||||
/// </summary>
|
||||
public static string GetSegment(this MapperContext context)
|
||||
{
|
||||
return context.HasItems && context.Items.TryGetValue(SegmentKey, out var obj) && obj is string s ? s : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a context culture.
|
||||
/// </summary>
|
||||
@@ -26,6 +35,14 @@ namespace Umbraco.Web.Models.Mapping
|
||||
context.Items[CultureKey] = culture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a context segment.
|
||||
/// </summary>
|
||||
public static void SetSegment(this MapperContext context, string segment)
|
||||
{
|
||||
context.Items[SegmentKey] = segment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get included properties.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Persistence
|
||||
{
|
||||
/// <summary>
|
||||
/// An exception used to indicate that an Umbraco entity could not be found.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Obsolete("Instead of throwing an exception, return null or an HTTP 404 status code instead.")]
|
||||
[Serializable]
|
||||
public class EntityNotFoundException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the identifier.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The identifier.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This object should be serializable to prevent a <see cref="SerializationException" /> to be thrown.
|
||||
/// </remarks>
|
||||
public object Id { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
|
||||
/// </summary>
|
||||
public EntityNotFoundException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="id">The identifier.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
public EntityNotFoundException(object id, string message)
|
||||
: base(message)
|
||||
{
|
||||
Id = id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public EntityNotFoundException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public EntityNotFoundException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EntityNotFoundException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected EntityNotFoundException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
Id = info.GetValue(nameof(Id), typeof(object));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <exception cref="ArgumentNullException">info</exception>
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
info.AddValue(nameof(Id), Id);
|
||||
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="System.String" /> that represents this instance.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="System.String" /> that represents this instance.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var result = base.ToString();
|
||||
|
||||
if (Id != null)
|
||||
{
|
||||
return "Umbraco entity (id: " + Id + ") not found. " + result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2030,13 +2030,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
_documentRepository.Save(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
|
||||
public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IContent"/> that resides in the bin
|
||||
/// </summary>
|
||||
|
||||
@@ -1029,13 +1029,6 @@ namespace Umbraco.Core.Services.Implement
|
||||
_mediaRepository.Save(media);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
|
||||
/// </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use EmptyRecycleBin with explicit indication of user ID instead")]
|
||||
public OperationResult EmptyRecycleBin() => EmptyRecycleBin(Constants.Security.SuperUserId);
|
||||
|
||||
/// <summary>
|
||||
/// Empties the Recycle Bin by deleting all <see cref="IMedia"/> that resides in the bin
|
||||
/// </summary>
|
||||
|
||||
@@ -23,6 +23,9 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose
|
||||
{
|
||||
var isLegacyModelsBuilderInstalled = IsLegacyModelsBuilderInstalled();
|
||||
|
||||
|
||||
composition.Configs.Add<IModelsBuilderConfig>(() => new ModelsBuilderConfig(composition.IOHelper));
|
||||
|
||||
if (isLegacyModelsBuilderInstalled)
|
||||
{
|
||||
ComposeForLegacyModelsBuilder(composition);
|
||||
@@ -32,7 +35,6 @@ namespace Umbraco.ModelsBuilder.Embedded.Compose
|
||||
|
||||
composition.Components().Append<ModelsBuilderComponent>();
|
||||
composition.Register<UmbracoServices>(Lifetime.Singleton);
|
||||
composition.Configs.Add<IModelsBuilderConfig>(() => new ModelsBuilderConfig(composition.IOHelper));
|
||||
composition.RegisterUnique<ModelsGenerator>();
|
||||
composition.RegisterUnique<LiveModelsProvider>();
|
||||
composition.RegisterUnique<OutOfDateModelsStatus>();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Web.Configuration;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
@@ -14,6 +14,13 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
public class ModelsBuilderConfig : IModelsBuilderConfig
|
||||
{
|
||||
private readonly IIOHelper _ioHelper;
|
||||
private const string Prefix = "Umbraco.ModelsBuilder.";
|
||||
private object _modelsModelLock;
|
||||
private bool _modelsModelConfigured;
|
||||
private ModelsMode _modelsMode;
|
||||
private object _flagOutOfDateModelsLock;
|
||||
private bool _flagOutOfDateModelsConfigured;
|
||||
private bool _flagOutOfDateModels;
|
||||
public const string DefaultModelsNamespace = "Umbraco.Web.PublishedModels";
|
||||
|
||||
public string DefaultModelsDirectory => _ioHelper.MapPath("~/App_Data/Models");
|
||||
@@ -24,11 +31,10 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
public ModelsBuilderConfig(IIOHelper ioHelper)
|
||||
{
|
||||
_ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
|
||||
const string prefix = "Umbraco.ModelsBuilder.";
|
||||
|
||||
// giant kill switch, default: false
|
||||
// must be explicitely set to true for anything else to happen
|
||||
Enable = ConfigurationManager.AppSettings[prefix + "Enable"] == "true";
|
||||
Enable = ConfigurationManager.AppSettings[Prefix + "Enable"] == "true";
|
||||
|
||||
// ensure defaults are initialized for tests
|
||||
ModelsNamespace = DefaultModelsNamespace;
|
||||
@@ -38,44 +44,19 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
// stop here, everything is false
|
||||
if (!Enable) return;
|
||||
|
||||
// mode
|
||||
var modelsMode = ConfigurationManager.AppSettings[prefix + "ModelsMode"];
|
||||
if (!string.IsNullOrWhiteSpace(modelsMode))
|
||||
{
|
||||
switch (modelsMode)
|
||||
{
|
||||
case nameof(ModelsMode.Nothing):
|
||||
ModelsMode = ModelsMode.Nothing;
|
||||
break;
|
||||
case nameof(ModelsMode.PureLive):
|
||||
ModelsMode = ModelsMode.PureLive;
|
||||
break;
|
||||
case nameof(ModelsMode.AppData):
|
||||
ModelsMode = ModelsMode.AppData;
|
||||
break;
|
||||
case nameof(ModelsMode.LiveAppData):
|
||||
ModelsMode = ModelsMode.LiveAppData;
|
||||
break;
|
||||
default:
|
||||
throw new ConfigurationErrorsException($"ModelsMode \"{modelsMode}\" is not a valid mode."
|
||||
+ " Note that modes are case-sensitive. Possible values are: " + string.Join(", ", Enum.GetNames(typeof(ModelsMode))));
|
||||
}
|
||||
}
|
||||
|
||||
// default: false
|
||||
AcceptUnsafeModelsDirectory = ConfigurationManager.AppSettings[prefix + "AcceptUnsafeModelsDirectory"].InvariantEquals("true");
|
||||
AcceptUnsafeModelsDirectory = ConfigurationManager.AppSettings[Prefix + "AcceptUnsafeModelsDirectory"].InvariantEquals("true");
|
||||
|
||||
// default: true
|
||||
EnableFactory = !ConfigurationManager.AppSettings[prefix + "EnableFactory"].InvariantEquals("false");
|
||||
FlagOutOfDateModels = !ConfigurationManager.AppSettings[prefix + "FlagOutOfDateModels"].InvariantEquals("false");
|
||||
EnableFactory = !ConfigurationManager.AppSettings[Prefix + "EnableFactory"].InvariantEquals("false");
|
||||
|
||||
// default: initialized above with DefaultModelsNamespace const
|
||||
var value = ConfigurationManager.AppSettings[prefix + "ModelsNamespace"];
|
||||
var value = ConfigurationManager.AppSettings[Prefix + "ModelsNamespace"];
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
ModelsNamespace = value;
|
||||
|
||||
// default: initialized above with DefaultModelsDirectory const
|
||||
value = ConfigurationManager.AppSettings[prefix + "ModelsDirectory"];
|
||||
value = ConfigurationManager.AppSettings[Prefix + "ModelsDirectory"];
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
var root = _ioHelper.MapPath("~/");
|
||||
@@ -87,18 +68,14 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
}
|
||||
|
||||
// default: 0
|
||||
value = ConfigurationManager.AppSettings[prefix + "DebugLevel"];
|
||||
value = ConfigurationManager.AppSettings[Prefix + "DebugLevel"];
|
||||
if (!string.IsNullOrWhiteSpace(value))
|
||||
{
|
||||
int debugLevel;
|
||||
if (!int.TryParse(value, out debugLevel))
|
||||
if (!int.TryParse(value, out var debugLevel))
|
||||
throw new ConfigurationErrorsException($"Invalid debug level \"{value}\".");
|
||||
DebugLevel = debugLevel;
|
||||
}
|
||||
|
||||
// not flagging if not generating, or live (incl. pure)
|
||||
if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsLive())
|
||||
FlagOutOfDateModels = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -116,11 +93,11 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
{
|
||||
_ioHelper = ioHelper ?? throw new ArgumentNullException(nameof(ioHelper));
|
||||
Enable = enable;
|
||||
ModelsMode = modelsMode;
|
||||
_modelsMode = modelsMode;
|
||||
|
||||
ModelsNamespace = string.IsNullOrWhiteSpace(modelsNamespace) ? DefaultModelsNamespace : modelsNamespace;
|
||||
EnableFactory = enableFactory;
|
||||
FlagOutOfDateModels = flagOutOfDateModels;
|
||||
_flagOutOfDateModels = flagOutOfDateModels;
|
||||
ModelsDirectory = string.IsNullOrWhiteSpace(modelsDirectory) ? DefaultModelsDirectory : modelsDirectory;
|
||||
AcceptUnsafeModelsDirectory = acceptUnsafeModelsDirectory;
|
||||
DebugLevel = debugLevel;
|
||||
@@ -169,7 +146,26 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
/// <summary>
|
||||
/// Gets the models mode.
|
||||
/// </summary>
|
||||
public ModelsMode ModelsMode { get; }
|
||||
public ModelsMode ModelsMode =>
|
||||
LazyInitializer.EnsureInitialized(ref _modelsMode, ref _modelsModelConfigured, ref _modelsModelLock, () =>
|
||||
{
|
||||
// mode
|
||||
var modelsMode = ConfigurationManager.AppSettings[Prefix + "ModelsMode"];
|
||||
if (string.IsNullOrWhiteSpace(modelsMode)) return ModelsMode.Nothing; //default
|
||||
switch (modelsMode)
|
||||
{
|
||||
case nameof(ModelsMode.Nothing):
|
||||
return ModelsMode.Nothing;
|
||||
case nameof(ModelsMode.PureLive):
|
||||
return ModelsMode.PureLive;
|
||||
case nameof(ModelsMode.AppData):
|
||||
return ModelsMode.AppData;
|
||||
case nameof(ModelsMode.LiveAppData):
|
||||
return ModelsMode.LiveAppData;
|
||||
default:
|
||||
throw new ConfigurationErrorsException($"ModelsMode \"{modelsMode}\" is not a valid mode." + " Note that modes are case-sensitive. Possible values are: " + string.Join(", ", Enum.GetNames(typeof(ModelsMode))));
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether system.web/compilation/@debug is true.
|
||||
@@ -201,7 +197,17 @@ namespace Umbraco.ModelsBuilder.Embedded.Configuration
|
||||
/// <remarks>Models become out-of-date when data types or content types are updated. When this
|
||||
/// setting is activated the ~/App_Data/Models/ood.txt file is then created. When models are
|
||||
/// generated through the dashboard, the files is cleared. Default value is <c>false</c>.</remarks>
|
||||
public bool FlagOutOfDateModels { get; }
|
||||
public bool FlagOutOfDateModels
|
||||
=> LazyInitializer.EnsureInitialized(ref _flagOutOfDateModels, ref _flagOutOfDateModelsConfigured, ref _flagOutOfDateModelsLock, () =>
|
||||
{
|
||||
var flagOutOfDateModels = !ConfigurationManager.AppSettings[Prefix + "FlagOutOfDateModels"].InvariantEquals("false");
|
||||
if (ModelsMode == ModelsMode.Nothing || ModelsMode.IsLive())
|
||||
{
|
||||
flagOutOfDateModels = false;
|
||||
}
|
||||
|
||||
return flagOutOfDateModels;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Gets the models directory.
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<Version>2.10.0</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub">
|
||||
<Version>1.0.0-beta2-19324-01</Version>
|
||||
<Version>1.0.0-beta2-19554-01</Version>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Umbraco.TestData")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Umbraco.TestData")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2019")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("fb5676ed-7a69-492c-b802-e7b24144c0fc")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -0,0 +1,83 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Web.Mvc;
|
||||
|
||||
namespace Umbraco.TestData
|
||||
{
|
||||
public class SegmentTestController : SurfaceController
|
||||
{
|
||||
|
||||
public ActionResult EnableDocTypeSegments(string alias, string propertyTypeAlias)
|
||||
{
|
||||
if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true")
|
||||
return HttpNotFound();
|
||||
|
||||
var ct = Services.ContentTypeService.Get(alias);
|
||||
if (ct == null)
|
||||
return Content($"No document type found by alias {alias}");
|
||||
|
||||
var propType = ct.PropertyTypes.FirstOrDefault(x => x.Alias == propertyTypeAlias);
|
||||
if (propType == null)
|
||||
return Content($"The document type {alias} does not have a property type {propertyTypeAlias ?? "null"}");
|
||||
|
||||
if (ct.Variations.VariesBySegment())
|
||||
return Content($"The document type {alias} already allows segments, nothing has been changed");
|
||||
|
||||
ct.Variations = ct.Variations.SetFlag(ContentVariation.Segment);
|
||||
|
||||
propType.Variations = propType.Variations.SetFlag(ContentVariation.Segment);
|
||||
|
||||
Services.ContentTypeService.Save(ct);
|
||||
return Content($"The document type {alias} and property type {propertyTypeAlias} now allows segments");
|
||||
}
|
||||
|
||||
public ActionResult DisableDocTypeSegments(string alias)
|
||||
{
|
||||
if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true")
|
||||
return HttpNotFound();
|
||||
|
||||
var ct = Services.ContentTypeService.Get(alias);
|
||||
if (ct == null)
|
||||
return Content($"No document type found by alias {alias}");
|
||||
|
||||
if (!ct.VariesBySegment())
|
||||
return Content($"The document type {alias} does not allow segments, nothing has been changed");
|
||||
|
||||
ct.Variations = ct.Variations.UnsetFlag(ContentVariation.Segment);
|
||||
|
||||
Services.ContentTypeService.Save(ct);
|
||||
return Content($"The document type {alias} no longer allows segments");
|
||||
}
|
||||
|
||||
public ActionResult AddSegmentData(int contentId, string propertyAlias, string value, string segment, string culture = null)
|
||||
{
|
||||
var content = Services.ContentService.GetById(contentId);
|
||||
if (content == null)
|
||||
return Content($"No content found by id {contentId}");
|
||||
|
||||
if (propertyAlias.IsNullOrWhiteSpace() || !content.HasProperty(propertyAlias))
|
||||
return Content($"The content by id {contentId} does not contain a property with alias {propertyAlias ?? "null"}");
|
||||
|
||||
if (content.ContentType.VariesByCulture() && culture.IsNullOrWhiteSpace())
|
||||
return Content($"The content by id {contentId} varies by culture but no culture was specified");
|
||||
|
||||
if (value.IsNullOrWhiteSpace())
|
||||
return Content("'value' cannot be null");
|
||||
|
||||
if (segment.IsNullOrWhiteSpace())
|
||||
return Content("'segment' cannot be null");
|
||||
|
||||
content.SetValue(propertyAlias, value, culture, segment);
|
||||
Services.ContentService.Save(content);
|
||||
|
||||
return Content($"Segment value has been set on content {contentId} for property {propertyAlias}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{FB5676ED-7A69-492C-B802-E7B24144C0FC}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Umbraco.TestData</RootNamespace>
|
||||
<AssemblyName>Umbraco.TestData</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<Deterministic>true</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="SegmentTestController.cs" />
|
||||
<Compile Include="UmbracoTestDataController.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="readme.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Umbraco.Abstractions\Umbraco.Abstractions.csproj">
|
||||
<Project>{29aa69d9-b597-4395-8d42-43b1263c240a}</Project>
|
||||
<Name>Umbraco.Abstractions</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Umbraco.Infrastructure\Umbraco.Infrastructure.csproj">
|
||||
<Project>{3ae7bf57-966b-45a5-910a-954d7c554441}</Project>
|
||||
<Name>Umbraco.Infrastructure</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\Umbraco.Web\Umbraco.Web.csproj">
|
||||
<Project>{651e1350-91b6-44b7-bd60-7207006d7003}</Project>
|
||||
<Name>Umbraco.Web</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Bogus">
|
||||
<Version>28.4.4</Version>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNet.Mvc">
|
||||
<Version>5.2.7</Version>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core;
|
||||
using System.Web.Mvc;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Mvc;
|
||||
using System.Configuration;
|
||||
using Bogus;
|
||||
using Umbraco.Core.Scoping;
|
||||
using Umbraco.Core.Strings;
|
||||
|
||||
namespace Umbraco.TestData
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates test data
|
||||
/// </summary>
|
||||
public class UmbracoTestDataController : SurfaceController
|
||||
{
|
||||
private const string RichTextDataTypeName = "UmbracoTestDataContent.RTE";
|
||||
private const string MediaPickerDataTypeName = "UmbracoTestDataContent.MediaPicker";
|
||||
private const string TextDataTypeName = "UmbracoTestDataContent.Text";
|
||||
private const string TestDataContentTypeAlias = "umbTestDataContent";
|
||||
private readonly IScopeProvider _scopeProvider;
|
||||
private readonly PropertyEditorCollection _propertyEditors;
|
||||
private readonly IShortStringHelper _shortStringHelper;
|
||||
|
||||
public UmbracoTestDataController(IScopeProvider scopeProvider, PropertyEditorCollection propertyEditors, IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, ILogger logger, IProfilingLogger profilingLogger, UmbracoHelper umbracoHelper, IShortStringHelper shortStringHelper)
|
||||
: base(umbracoContextAccessor, databaseFactory, services, appCaches, logger, profilingLogger, umbracoHelper)
|
||||
{
|
||||
_scopeProvider = scopeProvider;
|
||||
_propertyEditors = propertyEditors;
|
||||
_shortStringHelper = shortStringHelper;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a content and associated media tree (hierarchy)
|
||||
/// </summary>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <param name="locale"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Each content item created is associated to a media item via a media picker and therefore a relation is created between the two
|
||||
/// </remarks>
|
||||
public ActionResult CreateTree(int count, int depth, string locale = "en")
|
||||
{
|
||||
if (ConfigurationManager.AppSettings["Umbraco.TestData.Enabled"] != "true")
|
||||
return HttpNotFound();
|
||||
|
||||
if (!Validate(count, depth, out var message, out var perLevel))
|
||||
throw new InvalidOperationException(message);
|
||||
|
||||
var faker = new Faker(locale);
|
||||
var company = faker.Company.CompanyName();
|
||||
|
||||
using (var scope = _scopeProvider.CreateScope())
|
||||
{
|
||||
var imageIds = CreateMediaTree(company, faker, count, depth).ToList();
|
||||
var contentIds = CreateContentTree(company, faker, count, depth, imageIds, out var root).ToList();
|
||||
|
||||
Services.ContentService.SaveAndPublishBranch(root, true);
|
||||
|
||||
scope.Complete();
|
||||
}
|
||||
|
||||
|
||||
return Content("Done");
|
||||
}
|
||||
|
||||
private bool Validate(int count, int depth, out string message, out int perLevel)
|
||||
{
|
||||
perLevel = 0;
|
||||
message = null;
|
||||
|
||||
if (count <= 0)
|
||||
{
|
||||
message = "Count must be more than 0";
|
||||
return false;
|
||||
}
|
||||
|
||||
perLevel = count / depth;
|
||||
if (perLevel < 1)
|
||||
{
|
||||
message = "Count not high enough for specified for number of levels required";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility to create a tree hierarchy
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <param name="create">
|
||||
/// A callback that returns a tuple of Content and another callback to produce a Container.
|
||||
/// For media, a container will be another folder, for content the container will be the Content itself.
|
||||
/// </param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<Udi> CreateHierarchy<T>(
|
||||
T parent, int count, int depth,
|
||||
Func<T, (T content, Func<T> container)> create)
|
||||
where T: class, IContentBase
|
||||
{
|
||||
yield return parent.GetUdi();
|
||||
|
||||
// This will not calculate a balanced tree but it will ensure that there will be enough nodes deep enough to not fill up the tree.
|
||||
var totalDescendants = count - 1;
|
||||
var perLevel = Math.Ceiling(totalDescendants / (double)depth);
|
||||
var perBranch = Math.Ceiling(perLevel / depth);
|
||||
|
||||
var tracked = new Stack<(T parent, int childCount)>();
|
||||
|
||||
var currChildCount = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var created = create(parent);
|
||||
var contentItem = created.content;
|
||||
|
||||
yield return contentItem.GetUdi();
|
||||
|
||||
currChildCount++;
|
||||
|
||||
if (currChildCount == perBranch)
|
||||
{
|
||||
// move back up...
|
||||
|
||||
var prev = tracked.Pop();
|
||||
|
||||
// restore child count
|
||||
currChildCount = prev.childCount;
|
||||
// restore the parent
|
||||
parent = prev.parent;
|
||||
|
||||
}
|
||||
else if (contentItem.Level < depth)
|
||||
{
|
||||
// track the current parent and it's current child count
|
||||
tracked.Push((parent, currChildCount));
|
||||
|
||||
// not at max depth, create below
|
||||
parent = created.container();
|
||||
|
||||
currChildCount = 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the media tree hiearachy
|
||||
/// </summary>
|
||||
/// <param name="company"></param>
|
||||
/// <param name="faker"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<Udi> CreateMediaTree(string company, Faker faker, int count, int depth)
|
||||
{
|
||||
var parent = Services.MediaService.CreateMediaWithIdentity(company, -1, Constants.Conventions.MediaTypes.Folder);
|
||||
|
||||
return CreateHierarchy(parent, count, depth, currParent =>
|
||||
{
|
||||
var imageUrl = faker.Image.PicsumUrl();
|
||||
|
||||
// we are appending a &ext=.jpg to the end of this for a reason. The result of this url will be something like:
|
||||
// https://picsum.photos/640/480/?image=106
|
||||
// and due to the way that we detect images there must be an extension so we'll change it to
|
||||
// https://picsum.photos/640/480/?image=106&ext=.jpg
|
||||
// which will trick our app into parsing this and thinking it's an image ... which it is so that's good.
|
||||
// if we don't do this we don't get thumbnails in the back office.
|
||||
imageUrl += "&ext=.jpg";
|
||||
|
||||
var media = Services.MediaService.CreateMedia(faker.Commerce.ProductName(), currParent, Constants.Conventions.MediaTypes.Image);
|
||||
media.SetValue(Constants.Conventions.Media.File, imageUrl);
|
||||
Services.MediaService.Save(media);
|
||||
return (media, () =>
|
||||
{
|
||||
// create a folder to contain child media
|
||||
var container = Services.MediaService.CreateMediaWithIdentity(faker.Commerce.Department(), currParent, Constants.Conventions.MediaTypes.Folder);
|
||||
return container;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the content tree hiearachy
|
||||
/// </summary>
|
||||
/// <param name="company"></param>
|
||||
/// <param name="faker"></param>
|
||||
/// <param name="count"></param>
|
||||
/// <param name="depth"></param>
|
||||
/// <param name="imageIds"></param>
|
||||
/// <returns></returns>
|
||||
private IEnumerable<Udi> CreateContentTree(string company, Faker faker, int count, int depth, List<Udi> imageIds, out IContent root)
|
||||
{
|
||||
var random = new Random(company.GetHashCode());
|
||||
|
||||
var docType = GetOrCreateContentType();
|
||||
|
||||
var parent = Services.ContentService.Create(company, -1, docType.Alias);
|
||||
parent.SetValue("review", faker.Rant.Review());
|
||||
parent.SetValue("desc", company);
|
||||
parent.SetValue("media", imageIds[random.Next(0, imageIds.Count - 1)]);
|
||||
Services.ContentService.Save(parent);
|
||||
|
||||
root = parent;
|
||||
|
||||
return CreateHierarchy(parent, count, depth, currParent =>
|
||||
{
|
||||
var content = Services.ContentService.Create(faker.Commerce.ProductName(), currParent, docType.Alias);
|
||||
content.SetValue("review", faker.Rant.Review());
|
||||
content.SetValue("desc", string.Join(", ", Enumerable.Range(0, 5).Select(x => faker.Commerce.ProductAdjective()))); ;
|
||||
content.SetValue("media", imageIds[random.Next(0, imageIds.Count - 1)]);
|
||||
|
||||
Services.ContentService.Save(content);
|
||||
return (content, () => content);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private IContentType GetOrCreateContentType()
|
||||
{
|
||||
var docType = Services.ContentTypeService.Get(TestDataContentTypeAlias);
|
||||
if (docType != null)
|
||||
return docType;
|
||||
|
||||
docType = new ContentType(_shortStringHelper, -1)
|
||||
{
|
||||
Alias = TestDataContentTypeAlias,
|
||||
Name = "Umbraco Test Data Content",
|
||||
Icon = "icon-science color-green"
|
||||
};
|
||||
docType.AddPropertyGroup("Content");
|
||||
docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateRichText(), "review")
|
||||
{
|
||||
Name = "Review"
|
||||
});
|
||||
docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateMediaPicker(), "media")
|
||||
{
|
||||
Name = "Media"
|
||||
});
|
||||
docType.AddPropertyType(new PropertyType(_shortStringHelper, GetOrCreateText(), "desc")
|
||||
{
|
||||
Name = "Description"
|
||||
});
|
||||
Services.ContentTypeService.Save(docType);
|
||||
docType.AllowedContentTypes = new[] { new ContentTypeSort(docType.Id, 0) };
|
||||
Services.ContentTypeService.Save(docType);
|
||||
return docType;
|
||||
}
|
||||
|
||||
private IDataType GetOrCreateRichText() => GetOrCreateDataType(RichTextDataTypeName, Constants.PropertyEditors.Aliases.TinyMce);
|
||||
|
||||
private IDataType GetOrCreateMediaPicker() => GetOrCreateDataType(MediaPickerDataTypeName, Constants.PropertyEditors.Aliases.MediaPicker);
|
||||
|
||||
private IDataType GetOrCreateText() => GetOrCreateDataType(TextDataTypeName, Constants.PropertyEditors.Aliases.TextBox);
|
||||
|
||||
private IDataType GetOrCreateDataType(string name, string editorAlias)
|
||||
{
|
||||
var dt = Services.DataTypeService.GetDataType(name);
|
||||
if (dt != null) return dt;
|
||||
|
||||
var editor = _propertyEditors.FirstOrDefault(x => x.Alias == editorAlias);
|
||||
if (editor == null)
|
||||
throw new InvalidOperationException($"No {editorAlias} editor found");
|
||||
|
||||
dt = new DataType(editor)
|
||||
{
|
||||
Name = name,
|
||||
Configuration = editor.GetConfigurationEditor().DefaultConfigurationObject,
|
||||
DatabaseType = ValueStorageType.Ntext
|
||||
};
|
||||
|
||||
Services.DataTypeService.Save(dt);
|
||||
return dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
## Umbraco Test Data
|
||||
|
||||
This project is a utility to be able to generate large amounts of content and media in an
|
||||
Umbraco installation for testing.
|
||||
|
||||
Currently this project is referenced in the Umbraco.Web.UI project but only when it's being built
|
||||
in Debug mode (i.e. when testing within Visual Studio).
|
||||
|
||||
## Usage
|
||||
|
||||
You must use SQL Server for this, using SQLCE will die if you try to bulk create huge amounts of data.
|
||||
|
||||
It has to be enabled by an appSetting:
|
||||
|
||||
```xml
|
||||
<add key="Umbraco.TestData.Enabled" value="true"/>
|
||||
```
|
||||
|
||||
Once this is enabled this endpoint can be executed:
|
||||
|
||||
`/umbraco/surface/umbracotestdata/CreateTree?count=100&depth=5`
|
||||
|
||||
The query string options are:
|
||||
|
||||
* `count` = the number of content and media nodes to create
|
||||
* `depth` = how deep the trees created will be
|
||||
* `locale` (optional, default = "en") = the language that the data will be generated in
|
||||
|
||||
This creates a content and associated media tree (hierarchy). Each content item created is associated
|
||||
to a media item via a media picker and therefore a relation is created between the two. Each content and
|
||||
media tree created have the same root node name so it's easy to know which content branch relates to
|
||||
which media branch.
|
||||
|
||||
All values are generated using the very handy `Bogus` package.
|
||||
|
||||
## Schema
|
||||
|
||||
This will install some schema items:
|
||||
|
||||
* `umbTestDataContent` Document Type. __TIP__: If you want to delete all of the content data generated with this tool, just delete this content type
|
||||
* `UmbracoTestDataContent.RTE` Data Type
|
||||
* `UmbracoTestDataContent.MediaPicker` Data Type
|
||||
* `UmbracoTestDataContent.Text` Data Type
|
||||
|
||||
For media, the normal folder and image is used
|
||||
|
||||
## Media
|
||||
|
||||
This does not upload physical files, it just uses a randomized online image as the `umbracoFile` value.
|
||||
This works when viewing the media item in the media section and the image will show up and with recent changes this will also work
|
||||
when editing content to view the thumbnail for the picked media.
|
||||
@@ -251,7 +251,7 @@ namespace Umbraco.Web.PublishedModels
|
||||
{
|
||||
Alias = "prop3",
|
||||
ClrName = "Prop3",
|
||||
ModelClrType = typeof(global::Umbraco.Core.IO.FileSecurityException),
|
||||
ModelClrType = typeof(global::Umbraco.Core.Exceptions.BootFailedException),
|
||||
});
|
||||
var types = new[] { type1 };
|
||||
|
||||
@@ -272,7 +272,7 @@ namespace Umbraco.Web.PublishedModels
|
||||
|
||||
Assert.IsTrue(gen.Contains(" global::Umbraco.Core.Models.PublishedContent.IPublishedContent Prop1"));
|
||||
Assert.IsTrue(gen.Contains(" global::System.Text.StringBuilder Prop2"));
|
||||
Assert.IsTrue(gen.Contains(" global::Umbraco.Core.IO.FileSecurityException Prop3"));
|
||||
Assert.IsTrue(gen.Contains(" global::Umbraco.Core.Exceptions.BootFailedException Prop3"));
|
||||
}
|
||||
|
||||
[TestCase("int", typeof(int))]
|
||||
|
||||
@@ -7,6 +7,8 @@ using Umbraco.Web;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web.Composing;
|
||||
using Moq;
|
||||
using Examine;
|
||||
|
||||
namespace Umbraco.Tests.PublishedContent
|
||||
{
|
||||
@@ -201,7 +203,8 @@ namespace Umbraco.Tests.PublishedContent
|
||||
[Test]
|
||||
public void PublishedContentQueryTypedContentList()
|
||||
{
|
||||
var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor);
|
||||
var examineManager = new Mock<IExamineManager>();
|
||||
var query = new PublishedContentQuery(Current.UmbracoContext.PublishedSnapshot, Current.UmbracoContext.VariationContextAccessor, examineManager.Object);
|
||||
var result = query.Content(new[] { 1, 2, 4 }).ToArray();
|
||||
Assert.AreEqual(2, result.Length);
|
||||
Assert.AreEqual(1, result[0].Id);
|
||||
|
||||
@@ -2372,8 +2372,6 @@ namespace Umbraco.Tests.Services
|
||||
//MCH: I'm guessing this is an issue because of the format the date is actually stored as, right? Cause we don't do any formatting when saving or loading
|
||||
Assert.That(sut.GetValue<DateTime>("dateTime").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("dateTime").ToString("G")));
|
||||
Assert.That(sut.GetValue<string>("colorPicker"), Is.EqualTo("black"));
|
||||
//that one is gone in 7.4
|
||||
//Assert.That(sut.GetValue<string>("folderBrowser"), Is.Null);
|
||||
Assert.That(sut.GetValue<string>("ddlMultiple"), Is.EqualTo("1234,1235"));
|
||||
Assert.That(sut.GetValue<string>("rbList"), Is.EqualTo("random"));
|
||||
Assert.That(sut.GetValue<DateTime>("date").ToString("G"), Is.EqualTo(content.GetValue<DateTime>("date").ToString("G")));
|
||||
|
||||
@@ -122,8 +122,6 @@ namespace Umbraco.Tests.TestHelpers.Entities
|
||||
content.SetValue("label", "Non-editable label");
|
||||
content.SetValue("dateTime", DateTime.Now.AddDays(-20));
|
||||
content.SetValue("colorPicker", "black");
|
||||
//that one is gone in 7.4
|
||||
//content.SetValue("folderBrowser", "");
|
||||
content.SetValue("ddlMultiple", "1234,1235");
|
||||
content.SetValue("rbList", "random");
|
||||
content.SetValue("date", DateTime.Now.AddDays(-10));
|
||||
|
||||
@@ -115,7 +115,6 @@ namespace Umbraco.Tests.Testing.TestingTests
|
||||
|
||||
internal class FakeUmbracoApiController : UmbracoApiController
|
||||
{
|
||||
public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper) { }
|
||||
public FakeUmbracoApiController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoHelper umbracoHelper, UmbracoMapper umbracoMapper) : base(globalSettings, umbracoContextAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoHelper, umbracoMapper) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Castle.Core" Version="4.3.1" />
|
||||
<PackageReference Include="Examine" Version="1.0.1" />
|
||||
<PackageReference Include="Examine" Version="1.0.2" />
|
||||
<PackageReference Include="HtmlAgilityPack">
|
||||
<Version>1.8.14</Version>
|
||||
</PackageReference>
|
||||
|
||||
@@ -34,6 +34,7 @@ using Umbraco.Web.Trees;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.Composing;
|
||||
using Task = System.Threading.Tasks.Task;
|
||||
using Umbraco.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Tests.Web.Controllers
|
||||
{
|
||||
@@ -268,7 +269,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -303,7 +305,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -346,7 +349,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -394,7 +398,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -434,7 +439,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
@@ -480,7 +486,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using IUser = Umbraco.Core.Models.Membership.IUser;
|
||||
using Umbraco.Core.Mapping;
|
||||
|
||||
namespace Umbraco.Tests.Web.Controllers
|
||||
{
|
||||
@@ -88,7 +89,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
Factory.GetInstance<IMediaFileSystem>(),
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
return usersController;
|
||||
}
|
||||
|
||||
@@ -154,7 +156,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
Factory.GetInstance<IMediaFileSystem>(),
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
return usersController;
|
||||
}
|
||||
|
||||
@@ -191,7 +194,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
Factory.GetInstance<IMediaFileSystem>(),
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
return usersController;
|
||||
}
|
||||
|
||||
@@ -263,7 +267,8 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper,
|
||||
Factory.GetInstance<IMediaFileSystem>(),
|
||||
ShortStringHelper);
|
||||
ShortStringHelper,
|
||||
Factory.GetInstance<UmbracoMapper>());
|
||||
return usersController;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,14 +15,6 @@ namespace Umbraco.Tests.Web.Mvc
|
||||
_htmlStringUtilities = new HtmlStringUtilities();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ReplaceLineBreaksWithHtmlBreak()
|
||||
{
|
||||
var output = _htmlStringUtilities.ReplaceLineBreaksForHtml("<div><h1>hello world</h1><p>hello world\r\nhello world\rhello world\nhello world</p></div>").ToString();
|
||||
var expected = "<div><h1>hello world</h1><p>hello world<br />hello world<br />hello world<br />hello world</p></div>";
|
||||
Assert.AreEqual(expected, output);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TruncateWithElipsis()
|
||||
{
|
||||
|
||||
@@ -9,14 +9,13 @@ pre.code {
|
||||
padding: 0 3px 2px;
|
||||
#font > #family > .monospace;
|
||||
font-size: @baseFontSize - 2;
|
||||
color: @grayDark;
|
||||
color: @blueExtraDark;
|
||||
.border-radius(3px);
|
||||
}
|
||||
|
||||
// Inline code
|
||||
code {
|
||||
padding: 2px 4px;
|
||||
color: #d14;
|
||||
background-color: #f7f7f9;
|
||||
border: 1px solid #e1e1e8;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -79,11 +79,9 @@
|
||||
// Hover/Focus state
|
||||
// -----------
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus,
|
||||
.dropdown-menu > li > button:hover,
|
||||
.dropdown-menu > li > button:focus,
|
||||
.dropdown-submenu:hover > a,
|
||||
.dropdown-submenu:focus > a {
|
||||
.dropdown-submenu:hover > button {
|
||||
text-decoration: none;
|
||||
color: @dropdownLinkColorHover;
|
||||
#gradient > .vertical(@dropdownLinkBackgroundHover, darken(@dropdownLinkBackgroundHover, 5%));
|
||||
@@ -92,8 +90,7 @@
|
||||
// Active state
|
||||
// ------------
|
||||
.dropdown-menu > .active > a,
|
||||
.dropdown-menu > .active > a:hover,
|
||||
.dropdown-menu > .active > a:focus {
|
||||
.dropdown-menu > .active > a:hover {
|
||||
color: @dropdownLinkColorActive;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
@@ -104,13 +101,11 @@
|
||||
// --------------
|
||||
// Gray out text and ensure the hover/focus state remains gray
|
||||
.dropdown-menu > .disabled > a,
|
||||
.dropdown-menu > .disabled > a:hover,
|
||||
.dropdown-menu > .disabled > a:focus {
|
||||
.dropdown-menu > .disabled > a:hover {
|
||||
color: @grayLight;
|
||||
}
|
||||
// Nuke hover/focus effects
|
||||
.dropdown-menu > .disabled > a:hover,
|
||||
.dropdown-menu > .disabled > a:focus {
|
||||
.dropdown-menu > .disabled > a:hover {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
background-image: none; // Remove CSS gradient
|
||||
|
||||
+2
@@ -75,6 +75,8 @@
|
||||
scope.displayLabelOff = "";
|
||||
|
||||
function onInit() {
|
||||
scope.inputId = scope.inputId || "umb-toggle_" + String.CreateGuid();
|
||||
|
||||
setLabelText();
|
||||
// must wait until the current digest cycle is finished before we emit this event on init,
|
||||
// otherwise other property editors might not yet be ready to receive the event
|
||||
|
||||
+2
@@ -46,6 +46,8 @@
|
||||
vm.change = change;
|
||||
|
||||
function onInit() {
|
||||
vm.inputId = vm.inputId || "umb-check_" + String.CreateGuid();
|
||||
|
||||
// If a labelKey is passed let's update the returned text if it's does not contain an opening square bracket [
|
||||
if (vm.labelKey) {
|
||||
localizationService.localize(vm.labelKey).then(function (data) {
|
||||
|
||||
+2
@@ -44,6 +44,8 @@
|
||||
vm.change = change;
|
||||
|
||||
function onInit() {
|
||||
vm.inputId = vm.inputId || "umb-radio_" + String.CreateGuid();
|
||||
|
||||
// If a labelKey is passed let's update the returned text if it's does not contain an opening square bracket [
|
||||
if (vm.labelKey) {
|
||||
localizationService.localize(vm.labelKey).then(function (data) {
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
@ngdoc directive
|
||||
@name umbraco.directives.directive:umbCodeSnippet
|
||||
@restrict E
|
||||
@scope
|
||||
|
||||
@description
|
||||
|
||||
<h3>Markup example</h3>
|
||||
<pre>
|
||||
<div ng-controller="My.Controller as vm">
|
||||
|
||||
<umb-code-snippet
|
||||
language="'csharp'">
|
||||
{{code}}
|
||||
</umb-code-snippet>
|
||||
|
||||
</div>
|
||||
</pre>
|
||||
|
||||
<h3>Controller example</h3>
|
||||
<pre>
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function Controller() {
|
||||
|
||||
var vm = this;
|
||||
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("My.Controller", Controller);
|
||||
|
||||
})();
|
||||
</pre>
|
||||
|
||||
@param {string=} language Language of the code snippet, e.g csharp, html, css.
|
||||
**/
|
||||
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
var umbCodeSnippet = {
|
||||
templateUrl: 'views/components/umb-code-snippet.html',
|
||||
controller: UmbCodeSnippetController,
|
||||
controllerAs: 'vm',
|
||||
transclude: true,
|
||||
bindings: {
|
||||
language: '<'
|
||||
}
|
||||
};
|
||||
|
||||
function UmbCodeSnippetController($timeout) {
|
||||
|
||||
const vm = this;
|
||||
|
||||
vm.page = {};
|
||||
|
||||
vm.$onInit = onInit;
|
||||
vm.copySuccess = copySuccess;
|
||||
vm.copyError = copyError;
|
||||
|
||||
function onInit() {
|
||||
vm.guid = String.CreateGuid();
|
||||
|
||||
if (vm.language)
|
||||
{
|
||||
switch (vm.language.toLowerCase()) {
|
||||
case "csharp":
|
||||
case "c#":
|
||||
vm.language = "C#";
|
||||
break;
|
||||
case "html":
|
||||
vm.language = "HTML";
|
||||
break;
|
||||
case "css":
|
||||
vm.language = "CSS";
|
||||
break;
|
||||
case "javascript":
|
||||
vm.language = "JavaScript";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// copy to clip board success
|
||||
function copySuccess() {
|
||||
if (vm.page.copyCodeButtonState !== "success") {
|
||||
$timeout(function () {
|
||||
vm.page.copyCodeButtonState = "success";
|
||||
});
|
||||
$timeout(function () {
|
||||
resetClipboardButtonState();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// copy to clip board error
|
||||
function copyError() {
|
||||
if (vm.page.copyCodeButtonState !== "error") {
|
||||
$timeout(function () {
|
||||
vm.page.copyCodeButtonState = "error";
|
||||
});
|
||||
$timeout(function () {
|
||||
resetClipboardButtonState();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function resetClipboardButtonState() {
|
||||
vm.page.copyCodeButtonState = "init";
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('umbraco.directives').component('umbCodeSnippet', umbCodeSnippet);
|
||||
|
||||
})();
|
||||
+3
-10
@@ -312,14 +312,7 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
scope.onDetailsHover(item, $event, hover);
|
||||
}
|
||||
};
|
||||
|
||||
scope.clickEdit = function(item, $event) {
|
||||
if (scope.onClickEdit) {
|
||||
scope.onClickEdit({"item": item})
|
||||
$event.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var unbindItemsWatcher = scope.$watch('items', function(newValue, oldValue) {
|
||||
if (angular.isArray(newValue)) {
|
||||
activate();
|
||||
@@ -341,8 +334,8 @@ Use this directive to generate a thumbnail grid of media items.
|
||||
onDetailsHover: "=",
|
||||
onClick: '=',
|
||||
onClickName: "=",
|
||||
onClickEdit: "&?",
|
||||
allowOnClickEdit: "@?",
|
||||
allowOpenFolder: "=",
|
||||
allowOpenFile: "=",
|
||||
filterBy: "=",
|
||||
itemMaxWidth: "@",
|
||||
itemMaxHeight: "@",
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @ngdoc service
|
||||
* @name umbraco.resources.emailMarketingResource
|
||||
* @description Used to add a backoffice user to Umbraco's email marketing system, if user opts in
|
||||
*
|
||||
*
|
||||
**/
|
||||
function emailMarketingResource($http, umbRequestHelper) {
|
||||
|
||||
// LOCAL
|
||||
// http://localhost:7071/api/EmailProxy
|
||||
|
||||
// LIVE
|
||||
// https://emailcollector.umbraco.io/api/EmailProxy
|
||||
|
||||
const emailApiUrl = 'https://emailcollector.umbraco.io/api/EmailProxy';
|
||||
|
||||
//the factory object returned
|
||||
return {
|
||||
|
||||
postAddUserToEmailMarketing: (user) => {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.post(emailApiUrl,
|
||||
{
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
usergroup: user.userGroups // [ "admin", "sensitiveData" ]
|
||||
}),
|
||||
'Failed to add user to email marketing list');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('umbraco.resources').factory('emailMarketingResource', emailMarketingResource);
|
||||
@@ -164,69 +164,6 @@ function logResource($q, $http, umbRequestHelper) {
|
||||
'Failed to retrieve log data for id');
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.logResource#getEntityLog
|
||||
* @methodOf umbraco.resources.logResource
|
||||
*
|
||||
* @description
|
||||
* <strong>[OBSOLETE] use getPagedEntityLog instead</strong><br />
|
||||
* Gets the log history for a give entity id
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* logResource.getEntityLog(1234)
|
||||
* .then(function(log) {
|
||||
* alert('its here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {Int} id id of entity to return log history
|
||||
* @returns {Promise} resourcePromise object containing the log.
|
||||
*
|
||||
*/
|
||||
getEntityLog: function(id) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"logApiBaseUrl",
|
||||
"GetEntityLog",
|
||||
[{ id: id }])),
|
||||
'Failed to retrieve user data for id ' + id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.logResource#getUserLog
|
||||
* @methodOf umbraco.resources.logResource
|
||||
*
|
||||
* @description
|
||||
* <strong>[OBSOLETE] use getPagedUserLog instead</strong><br />
|
||||
* Gets the current user's log history for a given type of log entry
|
||||
*
|
||||
* ##usage
|
||||
* <pre>
|
||||
* logResource.getUserLog("save", new Date())
|
||||
* .then(function(log) {
|
||||
* alert('its here!');
|
||||
* });
|
||||
* </pre>
|
||||
*
|
||||
* @param {String} type logtype to query for
|
||||
* @param {DateTime} since query the log back to this date, by defalt 7 days ago
|
||||
* @returns {Promise} resourcePromise object containing the log.
|
||||
*
|
||||
*/
|
||||
getUserLog: function(type, since) {
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"logApiBaseUrl",
|
||||
"GetCurrentUserLog",
|
||||
[{ logtype: type }, { sinceDate: dateToValidIsoString(since) }])),
|
||||
'Failed to retrieve log data for current user of type ' + type + ' since ' + since);
|
||||
},
|
||||
|
||||
/**
|
||||
* @ngdoc method
|
||||
* @name umbraco.resources.logResource#getLog
|
||||
|
||||
@@ -367,7 +367,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function contentPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.section = "content";
|
||||
editor.treeAlias = "content";
|
||||
open(editor);
|
||||
@@ -390,7 +390,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function contentTypePicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.section = "settings";
|
||||
editor.treeAlias = "documentTypes";
|
||||
open(editor);
|
||||
@@ -413,7 +413,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function mediaTypePicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.section = "settings";
|
||||
editor.treeAlias = "mediaTypes";
|
||||
open(editor);
|
||||
@@ -436,7 +436,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function memberTypePicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.section = "settings";
|
||||
editor.treeAlias = "memberTypes";
|
||||
open(editor);
|
||||
@@ -457,7 +457,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
|
||||
function copy(editor) {
|
||||
editor.view = "views/common/infiniteeditors/copy/copy.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -477,7 +477,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
|
||||
function move(editor) {
|
||||
editor.view = "views/common/infiniteeditors/move/move.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -495,7 +495,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
|
||||
function embed(editor) {
|
||||
editor.view = "views/common/infiniteeditors/embed/embed.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -514,7 +514,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
|
||||
function rollback(editor) {
|
||||
editor.view = "views/common/infiniteeditors/rollback/rollback.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -534,7 +534,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function linkPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/linkpicker/linkpicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -577,7 +577,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function mediaPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/mediapicker/mediapicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.updatedMediaNodes = [];
|
||||
open(editor);
|
||||
}
|
||||
@@ -598,7 +598,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function iconPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/iconpicker/iconpicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -692,7 +692,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function treePicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -710,7 +710,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function nodePermissions(editor) {
|
||||
editor.view = "views/common/infiniteeditors/nodepermissions/nodepermissions.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -728,7 +728,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function insertCodeSnippet(editor) {
|
||||
editor.view = "views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -746,7 +746,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function userGroupPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/usergrouppicker/usergrouppicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -782,7 +782,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function sectionPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/sectionpicker/sectionpicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -800,7 +800,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function insertField(editor) {
|
||||
editor.view = "views/common/infiniteeditors/insertfield/insertfield.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -818,7 +818,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function templateSections(editor) {
|
||||
editor.view = "views/common/infiniteeditors/templatesections/templatesections.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -836,7 +836,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function userPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/userpicker/userpicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -858,7 +858,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function itemPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/itempicker/itempicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -876,7 +876,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function macroPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/macropicker/macropicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -896,7 +896,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function memberGroupPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/membergrouppicker/membergrouppicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
open(editor);
|
||||
}
|
||||
|
||||
@@ -917,7 +917,7 @@ When building a custom infinite editor view you can use the same components as a
|
||||
*/
|
||||
function memberPicker(editor) {
|
||||
editor.view = "views/common/infiniteeditors/treepicker/treepicker.html";
|
||||
editor.size = "small";
|
||||
if (!editor.size) editor.size = "small";
|
||||
editor.section = "member";
|
||||
editor.treeAlias = "member";
|
||||
open(editor);
|
||||
|
||||
@@ -148,7 +148,7 @@ angular.module('umbraco.services')
|
||||
break;
|
||||
case 1:
|
||||
//info
|
||||
this.success(args.header, args.message);
|
||||
this.info(args.header, args.message);
|
||||
break;
|
||||
case 2:
|
||||
//error
|
||||
@@ -297,4 +297,4 @@ angular.module('umbraco.services')
|
||||
};
|
||||
|
||||
return service;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -488,7 +488,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
* @methodOf umbraco.services.tinyMceService
|
||||
*
|
||||
* @description
|
||||
* Creates the umbrco insert embedded media tinymce plugin
|
||||
* Creates the umbraco insert embedded media tinymce plugin
|
||||
*
|
||||
* @param {Object} editor the TinyMCE editor instance
|
||||
*/
|
||||
@@ -575,7 +575,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
* @methodOf umbraco.services.tinyMceService
|
||||
*
|
||||
* @description
|
||||
* Creates the umbrco insert media tinymce plugin
|
||||
* Creates the umbraco insert media tinymce plugin
|
||||
*
|
||||
* @param {Object} editor the TinyMCE editor instance
|
||||
*/
|
||||
@@ -705,7 +705,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
* @methodOf umbraco.services.tinyMceService
|
||||
*
|
||||
* @description
|
||||
* Creates the insert umbrco macro tinymce plugin
|
||||
* Creates the insert umbraco macro tinymce plugin
|
||||
*
|
||||
* @param {Object} editor the TinyMCE editor instance
|
||||
*/
|
||||
|
||||
@@ -147,7 +147,10 @@
|
||||
group.groupOrder = item.groupOrder
|
||||
}
|
||||
groupExists = true;
|
||||
group.tours.push(item)
|
||||
|
||||
if(item.hidden === false){
|
||||
group.tours.push(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -157,8 +160,11 @@
|
||||
if(item.groupOrder) {
|
||||
newGroup.groupOrder = item.groupOrder
|
||||
}
|
||||
newGroup.tours.push(item);
|
||||
groupedTours.push(newGroup);
|
||||
|
||||
if(item.hidden === false){
|
||||
newGroup.tours.push(item);
|
||||
groupedTours.push(newGroup);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module('umbraco.services')
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, requestRetryQueue, authResource, $timeout, angularHelper) {
|
||||
.factory('userService', function ($rootScope, eventsService, $q, $location, requestRetryQueue, authResource, emailMarketingResource, $timeout, angularHelper) {
|
||||
|
||||
var currentUser = null;
|
||||
var lastUserId = null;
|
||||
@@ -262,6 +262,11 @@ angular.module('umbraco.services')
|
||||
/** 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);
|
||||
},
|
||||
|
||||
/** Calls out to a Remote Azure Function to deal with email marketing service */
|
||||
addUserToEmailMarketing: (user) => {
|
||||
return emailMarketingResource.postAddUserToEmailMarketing(user);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** Executed when the application starts, binds to events and set global state */
|
||||
app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', 'appState', 'assetsService', 'eventsService', '$cookies', 'tourService',
|
||||
function ($rootScope, $route, $location, urlHelper, navigationService, appState, assetsService, eventsService, $cookies, tourService) {
|
||||
app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService', 'appState', 'assetsService', 'eventsService', '$cookies', 'tourService', 'localStorageService',
|
||||
function ($rootScope, $route, $location, urlHelper, navigationService, appState, assetsService, eventsService, $cookies, tourService, localStorageService) {
|
||||
|
||||
//This sets the default jquery ajax headers to include our csrf token, we
|
||||
// need to user the beforeSend method because our token changes per user/login so
|
||||
@@ -23,11 +23,35 @@ app.run(['$rootScope', '$route', '$location', 'urlHelper', 'navigationService',
|
||||
appReady(data);
|
||||
|
||||
tourService.registerAllTours().then(function () {
|
||||
// Auto start intro tour
|
||||
|
||||
// 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);
|
||||
localStorageService.set("introTourShown", true);
|
||||
}
|
||||
else {
|
||||
|
||||
const introTourShown = localStorageService.get("introTourShown");
|
||||
if(!introTourShown){
|
||||
// Go & show email marketing tour (ONLY when intro tour is completed or been dismissed)
|
||||
tourService.getTourByAlias("umbEmailMarketing").then(function (emailMarketingTour) {
|
||||
// Only show the email marketing tour one time - dismissing it or saying no will make sure it never appears again
|
||||
// Unless invoked from tourService JS Client code explicitly.
|
||||
// Accepted mails = Completed and Declicned mails = Disabled
|
||||
if (emailMarketingTour && emailMarketingTour.disabled !== true && emailMarketingTour.completed !== true) {
|
||||
|
||||
// Only show the email tour once per logged in session
|
||||
// The localstorage key is removed on logout or user session timeout
|
||||
const emailMarketingTourShown = localStorageService.get("emailMarketingTourShown");
|
||||
if(!emailMarketingTourShown){
|
||||
tourService.startTour(emailMarketingTour);
|
||||
localStorageService.set("emailMarketingTourShown", true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
*:focus {
|
||||
outline-color: @ui-outline;
|
||||
}
|
||||
|
||||
.umb-outline {
|
||||
&:focus {
|
||||
outline:none;
|
||||
@@ -10,7 +14,28 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid;
|
||||
box-shadow: 0 0 2px 0px @ui-outline, inset 0 0 2px 2px @ui-outline;
|
||||
}
|
||||
}
|
||||
|
||||
&.umb-outline--surrounding {
|
||||
&:focus {
|
||||
.tabbing-active &::after {
|
||||
top: -6px;
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
right: -6px;
|
||||
border-radius: 9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.umb-outline--thin {
|
||||
&:focus {
|
||||
.tabbing-active &::after {
|
||||
box-shadow: 0 0 2px @ui-outline, inset 0 0 2px 1px @ui-outline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -132,6 +132,7 @@
|
||||
@import "components/umb-content-grid.less";
|
||||
@import "components/umb-contextmenu.less";
|
||||
@import "components/umb-layout-selector.less";
|
||||
@import "components/umb-mini-search.less";
|
||||
@import "components/tooltip/umb-tooltip.less";
|
||||
@import "components/tooltip/umb-tooltip-list.less";
|
||||
@import "components/overlays/umb-overlay-backdrop.less";
|
||||
@@ -140,6 +141,7 @@
|
||||
@import "components/umb-empty-state.less";
|
||||
@import "components/umb-property-editor.less";
|
||||
@import "components/umb-property-actions.less";
|
||||
@import "components/umb-code-snippet.less";
|
||||
@import "components/umb-color-swatches.less";
|
||||
@import "components/check-circle.less";
|
||||
@import "components/umb-file-icon.less";
|
||||
@@ -192,6 +194,8 @@
|
||||
|
||||
@import "components/contextdialogs/umb-dialog-datatype-delete.less";
|
||||
|
||||
@import "components/umbemailmarketing.less";
|
||||
|
||||
|
||||
// Utilities
|
||||
@import "utilities/layout/_display.less";
|
||||
|
||||
@@ -23,8 +23,7 @@
|
||||
border-radius: 3px;
|
||||
|
||||
// Hover/focus state
|
||||
&:hover,
|
||||
&:focus {
|
||||
&:hover {
|
||||
background: @btnBackgroundHighlight;
|
||||
color: @gray-4;
|
||||
background-position: 0 -15px;
|
||||
@@ -35,11 +34,6 @@
|
||||
.transition(background-position .1s linear);
|
||||
}
|
||||
|
||||
// Focus state for keyboard and accessibility
|
||||
&:focus {
|
||||
.tab-focus();
|
||||
}
|
||||
|
||||
// Active state
|
||||
&.active,
|
||||
&:active {
|
||||
@@ -54,7 +48,7 @@
|
||||
&:disabled:hover {
|
||||
cursor: default;
|
||||
border-color: @btnBorder;
|
||||
.opacity(65);
|
||||
.opacity(80);
|
||||
.box-shadow(none);
|
||||
}
|
||||
|
||||
@@ -219,7 +213,7 @@ input[type="button"] {
|
||||
}
|
||||
// Made for Umbraco, 2019, used for buttons that has to stand back.
|
||||
.btn-white {
|
||||
.buttonBackground(@btnWhiteBackground, @btnWhiteBackgroundHighlight, @btnWhiteType, @btnWhiteTypeHover);
|
||||
.buttonBackground(@btnWhiteBackground, @btnWhiteBackgroundHighlight, @btnWhiteType, @btnWhiteTypeHover, @gray-10, @gray-7);
|
||||
}
|
||||
// Inverse appears as dark gray
|
||||
.btn-inverse {
|
||||
@@ -230,8 +224,7 @@ input[type="button"] {
|
||||
.buttonBackground(@btnNeutralBackground, @btnNeutralBackgroundHighlight);
|
||||
color: @gray-5;
|
||||
// Hover/focus state
|
||||
&:hover,
|
||||
&:focus {
|
||||
&:hover {
|
||||
color: @gray-5;
|
||||
}
|
||||
|
||||
@@ -261,18 +254,18 @@ input[type="button"] {
|
||||
.btn-outline {
|
||||
border: 1px solid;
|
||||
border-color: @gray-7;
|
||||
background: @white;
|
||||
background: transparent;
|
||||
color: @blueExtraDark;
|
||||
padding: 5px 13px;
|
||||
transition: all .2s linear;
|
||||
transition: border-color .12s linear, color .12s linear;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-outline:hover,
|
||||
.btn-outline:focus,
|
||||
.btn-outline:active {
|
||||
.btn-outline:hover {
|
||||
border-color: @ui-light-type-hover;
|
||||
color: @ui-light-type-hover;
|
||||
background: @white;
|
||||
background: transparent;
|
||||
transition: border-color .12s linear, color .12s linear;
|
||||
}
|
||||
|
||||
// Cross-browser Jank
|
||||
@@ -309,14 +302,12 @@ input[type="submit"].btn {
|
||||
color: @linkColor;
|
||||
.border-radius(0);
|
||||
}
|
||||
.btn-link:hover,
|
||||
.btn-link:focus {
|
||||
.btn-link:hover {
|
||||
color: @linkColorHover;
|
||||
text-decoration: underline;
|
||||
background-color: transparent;
|
||||
}
|
||||
.btn-link[disabled]:hover,
|
||||
.btn-link[disabled]:focus {
|
||||
.btn-link[disabled]:hover {
|
||||
color: @gray-4;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -324,8 +315,7 @@ input[type="submit"].btn {
|
||||
// Make a reverse type of a button link
|
||||
.btn-link-reverse{
|
||||
text-decoration:underline;
|
||||
&:hover,
|
||||
&:focus{
|
||||
&:hover {
|
||||
text-decoration:none;
|
||||
}
|
||||
}
|
||||
@@ -362,7 +352,7 @@ input[type="submit"].btn {
|
||||
outline: 0;
|
||||
-webkit-appearance: none;
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover {
|
||||
color: @ui-icon-hover;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
}
|
||||
|
||||
.umb-help-badge__title {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: @black;
|
||||
@@ -160,6 +161,9 @@
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid @gray-9;
|
||||
padding: 10px;
|
||||
background: transparent;
|
||||
width:100%;
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.umb-help-list-item:last-child {
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
box-sizing: border-box;
|
||||
color: @ui-option-type;
|
||||
width: 100%;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.umb-language-picker__expand {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.umb-language-picker__toggle:focus,
|
||||
.umb-language-picker__toggle:hover {
|
||||
background: @ui-option-hover;
|
||||
color:@ui-option-type-hover;
|
||||
@@ -54,10 +54,10 @@
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
outline-offset: -3px;
|
||||
}
|
||||
|
||||
.umb-language-picker__dropdown-item:hover,
|
||||
.umb-language-picker__dropdown-item:focus {
|
||||
.umb-language-picker__dropdown-item:hover {
|
||||
background: @ui-option-hover;
|
||||
text-decoration: none;
|
||||
color:@ui-option-type-hover;
|
||||
|
||||
@@ -112,3 +112,24 @@
|
||||
.umb-tour-is-visible .umb-backdrop {
|
||||
z-index: @zindexTourBackdrop;
|
||||
}
|
||||
|
||||
.umb-tour__popover .underline{
|
||||
font-size: 13px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.umb-tour__popover--promotion {
|
||||
width: 800px;
|
||||
min-height: 400px;
|
||||
padding: 40px;
|
||||
border-radius: @baseBorderRadius * 2;
|
||||
.umb-tour-step__close {
|
||||
top: 40px;
|
||||
right: 40px;
|
||||
}
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,21 +8,6 @@
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.umb-button__button:focus {
|
||||
outline: none;
|
||||
.tabbing-active &:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-button__content {
|
||||
opacity: 1;
|
||||
transition: opacity 0.25s ease;
|
||||
|
||||
@@ -164,6 +164,7 @@ a.umb-editor-header__close-split-view:hover {
|
||||
|
||||
/* variant switcher */
|
||||
.umb-variant-switcher__toggle {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
@@ -173,6 +174,8 @@ a.umb-editor-header__close-split-view:hover {
|
||||
text-decoration: none !important;
|
||||
font-size: 13px;
|
||||
color: @ui-action-discreet-type;
|
||||
background: transparent;
|
||||
border: none;
|
||||
|
||||
max-width: 50%;
|
||||
white-space: nowrap;
|
||||
@@ -185,7 +188,7 @@ a.umb-editor-header__close-split-view:hover {
|
||||
}
|
||||
}
|
||||
|
||||
a.umb-variant-switcher__toggle {
|
||||
button.umb-variant-switcher__toggle {
|
||||
transition: color 0.2s ease-in-out;
|
||||
&:hover {
|
||||
//background-color: @gray-10;
|
||||
@@ -242,8 +245,7 @@ a.umb-variant-switcher__toggle {
|
||||
border-left: 4px solid @ui-active;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__item:hover,
|
||||
.umb-variant-switcher__item:focus {
|
||||
.umb-variant-switcher__item:hover {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@@ -267,7 +269,7 @@ a.umb-variant-switcher__toggle {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5px;
|
||||
top: -6px;
|
||||
top: -3px;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 7px;
|
||||
@@ -285,8 +287,10 @@ a.umb-variant-switcher__toggle {
|
||||
flex: 1;
|
||||
cursor: pointer;
|
||||
padding-top: 6px !important;
|
||||
padding-bottom: 6px !important;
|
||||
border-left: 2px solid transparent;
|
||||
padding-bottom: 6px !important;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-left: 2px solid transparent;
|
||||
}
|
||||
|
||||
.umb-variant-switcher__name {
|
||||
|
||||
+3
-2
@@ -24,8 +24,9 @@
|
||||
.umb-editor-sub-header.--state-selection {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
background-color: @pinkLight;
|
||||
border-color: @pinkLight;
|
||||
background-color: @ui-selected-border;
|
||||
border-color: @ui-selected-border;
|
||||
color: @white;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
width: auto;
|
||||
margin-top:1px;
|
||||
|
||||
.umb-tree-item__label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
&:hover .umb-tree-item__arrow {
|
||||
visibility: visible;
|
||||
cursor: pointer
|
||||
}
|
||||
}
|
||||
|
||||
.umb-tree-item__label {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.umb-tree-item__arrow {
|
||||
position: relative;
|
||||
margin-left: -16px;
|
||||
@@ -92,18 +92,6 @@
|
||||
color: @blue;
|
||||
}
|
||||
|
||||
.umb-options {
|
||||
|
||||
&:hover i {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
i {
|
||||
background: @ui-active-type;
|
||||
transition: opacity 120ms ease;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
.umb-tree-icon,
|
||||
.umb-tree-item__arrow {
|
||||
|
||||
@@ -99,6 +99,7 @@ body.touch .umb-tree {
|
||||
|
||||
.umb-tree-item__inner {
|
||||
border: 2px solid transparent;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.umb-tree-header {
|
||||
@@ -176,9 +177,25 @@ body.touch .umb-tree {
|
||||
cursor: pointer;
|
||||
border-radius: @baseBorderRadius;
|
||||
|
||||
&:hover {
|
||||
background: @btnBackgroundHighlight;
|
||||
i {
|
||||
height: 5px !important;
|
||||
width: 5px !important;
|
||||
border-radius: 20px;
|
||||
display: inline-block;
|
||||
margin: 0 2px 0 0;
|
||||
background: @ui-active-type;
|
||||
|
||||
&:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, .5);
|
||||
i {
|
||||
background: @ui-active-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE - We're having to repeat ourselves here due to an .sr-only class appearing in umbraco/lib/font-awesome/css/font-awesome.min.css
|
||||
&.sr-only--hoverable:hover,
|
||||
&.sr-only--focusable:focus {
|
||||
@@ -193,19 +210,6 @@ body.touch .umb-tree {
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
i {
|
||||
height: 5px !important;
|
||||
width: 5px !important;
|
||||
border-radius: 20px;
|
||||
background: @black;
|
||||
display: inline-block;
|
||||
margin: 0 2px 0 0;
|
||||
|
||||
&:last-child {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.hide-options & {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -289,9 +293,8 @@ body.touch .umb-tree {
|
||||
}
|
||||
|
||||
.no-access {
|
||||
.umb-tree-icon,
|
||||
.root-link,
|
||||
.umb-tree-item__label {
|
||||
> .umb-tree-item__inner .umb-tree-icon,
|
||||
> .umb-tree-item__inner .umb-tree-item__label {
|
||||
color: @gray-7;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
margin-left: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.umb-breadcrumbs__ancestor {
|
||||
@@ -12,10 +13,23 @@
|
||||
}
|
||||
|
||||
.umb-breadcrumbs__action {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
padding: 0;
|
||||
margin-top: -4px;
|
||||
border-radius: 3px;
|
||||
padding: 0 4px;
|
||||
color: @ui-option-type;
|
||||
|
||||
&.--current {
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: @ui-option-type-hover;
|
||||
background-color: @white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.umb-breadcrumbs__ancestor-link,
|
||||
@@ -26,6 +40,7 @@
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.umb-breadcrumbs__ancestor-link {
|
||||
@@ -39,13 +54,13 @@
|
||||
.umb-breadcrumbs__separator {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
margin: 0 1px;
|
||||
margin-top: -3px;
|
||||
color: @gray-7;
|
||||
}
|
||||
|
||||
input.umb-breadcrumbs__add-ancestor {
|
||||
height: 25px;
|
||||
margin: 0 0 0 3px;
|
||||
height: 24px;
|
||||
margin: -2px 0 -2px 3px;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
@@ -2,21 +2,30 @@
|
||||
border: 2px solid @white;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border: 1px solid @gray-7;
|
||||
border: 1px solid @ui-action-discreet-border;
|
||||
border-radius: 3px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: @gray-7;
|
||||
color: @ui-selected-type;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
&:hover {
|
||||
border-color:@ui-action-discreet-border-hover;
|
||||
color: @ui-selected-type-hover;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-checkmark--checked {
|
||||
background: @ui-active;
|
||||
border-color: @ui-active;
|
||||
background: @ui-selected-border;
|
||||
border-color: @ui-selected-border;
|
||||
color: @white;
|
||||
&:hover {
|
||||
background: @ui-selected-border-hover;
|
||||
border-color: @ui-selected-border-hover;
|
||||
color: @white;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-checkmark--xs {
|
||||
@@ -45,4 +54,4 @@
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
.umb-code-snippet {
|
||||
|
||||
.umb-code-snippet__header {
|
||||
box-sizing: content-box;
|
||||
background-color: @gray-10;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: .8rem;
|
||||
border: 1px solid @gray-8;
|
||||
border-radius: 3px 3px 0 0;
|
||||
border-bottom: 0;
|
||||
margin-top: 16px;
|
||||
min-height: 30px;
|
||||
|
||||
.language {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-grow: 1;
|
||||
padding: 2px 10px;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
border-left: 1px solid @gray-8;
|
||||
border-radius: 0;
|
||||
color: #000;
|
||||
|
||||
&:hover {
|
||||
background-color: @grayLighter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-code-snippet__content {
|
||||
pre {
|
||||
border-radius: 0 0 3px 3px;
|
||||
overflow: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,15 +29,15 @@
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
|
||||
pointer-events: none;
|
||||
transition: opacity 100ms;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-content-grid__item:hover {
|
||||
&::before {
|
||||
opacity: .33;
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
.umb-content-grid__item.-selected:hover {
|
||||
@@ -46,6 +46,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-content-grid__icon-container {
|
||||
height: 75px;
|
||||
display: flex;
|
||||
@@ -66,8 +67,10 @@
|
||||
}
|
||||
|
||||
.umb-content-grid__item-name {
|
||||
position: relative;
|
||||
padding: 5px;
|
||||
margin: -5px -5px 15px -5px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
line-height: 1.4em;
|
||||
display: inline-flex;
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
&__action,
|
||||
> a {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
@@ -18,26 +19,20 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: calc(~'@{editorHeaderHeight}'- ~'1px'); // need to offset the 1px border-bottom on .umb-editor-header - avoids overflowing top of the container
|
||||
position: relative;
|
||||
color: @ui-active-type;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: @ui-active-type-hover !important;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
height: 0px;
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
background-color: @ui-light-active-border;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
opacity: 0;
|
||||
@@ -47,14 +42,13 @@
|
||||
&.is-active {
|
||||
color: @ui-light-active-type;
|
||||
|
||||
&::after {
|
||||
&::before {
|
||||
opacity: 1;
|
||||
height: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__action:focus,
|
||||
&__action:active,
|
||||
& > a:active {
|
||||
.box-shadow(~"inset 0 2px 4px rgba(0,0,0,.15), 0 1px 2px rgba(0,0,0,.05)");
|
||||
@@ -111,7 +105,6 @@
|
||||
&__anchor_dropdown {
|
||||
// inherits from .dropdown-menu
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
|
||||
// center align horizontal
|
||||
left: 50%;
|
||||
@@ -122,7 +115,7 @@
|
||||
|
||||
li {
|
||||
&.is-active a {
|
||||
border-left-color: @ui-selected-border;
|
||||
border-left-color: @ui-active;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -192,4 +185,4 @@
|
||||
&::after {
|
||||
background-color: @red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +41,17 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
button.umb-grid-selector__item {
|
||||
width: 169px;
|
||||
height: 194px;
|
||||
}
|
||||
|
||||
.umb-grid-selector__item-icon {
|
||||
font-size: 50px;
|
||||
color: @gray-8;
|
||||
display: block;
|
||||
line-height: 50px;
|
||||
margin-bottom: 15px;
|
||||
font-size: 50px;
|
||||
color: @gray-8;
|
||||
display: block;
|
||||
line-height: 50px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.umb-grid-selector__item-label {
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
.umb-layout-selector__active-layout {
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid @inputBorder;
|
||||
border: 1px solid @ui-action-discreet-border;
|
||||
color: @ui-action-discreet-type;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
@@ -17,7 +18,8 @@
|
||||
}
|
||||
|
||||
.umb-layout-selector__active-layout:hover {
|
||||
border-color: @inputBorderFocus;
|
||||
border-color: @ui-action-discreet-border-hover;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown {
|
||||
@@ -31,6 +33,7 @@
|
||||
flex-direction: column;
|
||||
transform: translate(-50%,0);
|
||||
left: 50%;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown-item {
|
||||
@@ -46,11 +49,11 @@
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown-item:hover {
|
||||
border: 1px solid @gray-8;
|
||||
border: 1px solid @ui-action-discreet-border;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown-item.-active {
|
||||
border: 1px solid @blue;
|
||||
border: 1px solid @ui-action-discreet-border-hover;
|
||||
}
|
||||
|
||||
.umb-layout-selector__dropdown-item-icon,
|
||||
|
||||
@@ -40,25 +40,41 @@
|
||||
}
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-selectable {
|
||||
.umb-media-grid__item.-selectable,
|
||||
.umb-media-grid__item.-folder {// If folders isnt selectable, they opens if clicked, therefor...
|
||||
cursor: pointer;
|
||||
|
||||
.tabbing-active &:focus {
|
||||
outline: 2px solid @inputBorderTabFocus;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-file {
|
||||
background-color: @white;
|
||||
}
|
||||
|
||||
.umb-media-grid__item.-folder {
|
||||
|
||||
&.-selectable {
|
||||
.media-grid-item-edit:hover .umb-media-grid__item-name,
|
||||
.media-grid-item-edit:focus .umb-media-grid__item-name {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
&.-unselectable {
|
||||
&:hover, &:focus {
|
||||
.umb-media-grid__item-name {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.umb-media-grid__item.-selected {
|
||||
color:@ui-selected-type;
|
||||
.umb-media-grid__item-overlay {
|
||||
color: @ui-selected-type;
|
||||
}
|
||||
}
|
||||
.umb-media-grid__item.-selected,
|
||||
.umb-media-grid__item.-selected,
|
||||
.umb-media-grid__item.-selectable:hover {
|
||||
&::before {
|
||||
content: "";
|
||||
@@ -139,10 +155,10 @@
|
||||
background: fade(@white, 92%);
|
||||
transition: opacity 150ms;
|
||||
|
||||
&:hover {
|
||||
&.-can-open:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
|
||||
.tabbing-active &:focus {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -190,7 +206,7 @@
|
||||
align-items: center;
|
||||
color: @black;
|
||||
transition: opacity 150ms;
|
||||
|
||||
|
||||
&:hover {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
.umb-mini-search {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
padding: 5px 8px;
|
||||
pointer-events: none;
|
||||
top: 2px;
|
||||
color: @ui-action-discreet-type;
|
||||
transition: color .1s linear;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 0px;
|
||||
padding-left:24px;
|
||||
margin-bottom: 0px;
|
||||
background-color: transparent;
|
||||
border-color: @ui-action-discreet-border;
|
||||
transition: background-color .1s linear, border-color .1s linear, color .1s linear, width .1s ease-in-out, padding-left .1s ease-in-out;
|
||||
}
|
||||
|
||||
&:focus-within, &:hover {
|
||||
.icon {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
input {
|
||||
color: @ui-action-discreet-border-hover;
|
||||
border-color: @ui-action-discreet-border-hover;
|
||||
}
|
||||
}
|
||||
|
||||
input:focus, &:focus-within input {
|
||||
background-color: white;
|
||||
color: @ui-action-discreet-border-hover;
|
||||
border-color: @ui-action-discreet-border-hover;
|
||||
}
|
||||
|
||||
input:focus, &:focus-within input, &.--has-value input {
|
||||
width: 190px;
|
||||
padding-left:30px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -41,11 +41,8 @@
|
||||
}
|
||||
|
||||
.umb-nested-content__item.ui-sortable-placeholder {
|
||||
background: @gray-10;
|
||||
border: 1px solid @gray-9;
|
||||
margin-top: 1px;
|
||||
visibility: visible !important;
|
||||
height: 55px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.umb-nested-content__item--single > .umb-nested-content__content {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
.umb-progress-circle__view-box {
|
||||
position: absolute;
|
||||
transform: rotate(-90deg);
|
||||
right: 0;
|
||||
}
|
||||
|
||||
// circle highlight on progressbar
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
&:focus {
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,12 @@
|
||||
text-decoration: none;
|
||||
padding: 0;
|
||||
margin-left: 1px;
|
||||
body:not(.tabbing-active) & {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
input.umb-table__input {
|
||||
margin: 0 auto;
|
||||
}
|
||||
@@ -47,6 +51,8 @@ input.umb-table__input {
|
||||
.umb-table-head {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
color: @ui-disabled-type;
|
||||
}
|
||||
|
||||
.umb-table-head__link {
|
||||
@@ -68,10 +74,12 @@ input.umb-table__input {
|
||||
|
||||
.umb-table-head__link.sortable {
|
||||
cursor: pointer;
|
||||
color: @ui-action-discreet-type;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
color: @black;
|
||||
color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
|
||||
outline-offset: 1px;
|
||||
}
|
||||
|
||||
.umb-table-thead__icon {
|
||||
@@ -129,6 +137,9 @@ input.umb-table__input {
|
||||
&::before {
|
||||
opacity:.66;
|
||||
}
|
||||
.umb-table-body__checkicon {
|
||||
color: @ui-selected-border;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,21 +152,19 @@ input.umb-table__input {
|
||||
}
|
||||
|
||||
.umb-table-body__link {
|
||||
position: relative;
|
||||
color: @ui-option-type;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover {
|
||||
color: @ui-option-type-hover;
|
||||
text-decoration: underline;
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-table-body__icon,
|
||||
.umb-table-body__icon[class^="icon-"],
|
||||
.umb-table-body__icon[class*=" icon-"] {
|
||||
.umb-table-body__icon {
|
||||
margin: 0 auto;
|
||||
font-size: 20px;
|
||||
line-height: 20px;
|
||||
@@ -164,13 +173,11 @@ input.umb-table__input {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.umb-table-body__checkicon,
|
||||
.umb-table-body__checkicon[class^="icon-"],
|
||||
.umb-table-body__checkicon[class*=" icon-"] {
|
||||
.umb-table-body__checkicon {
|
||||
display: none;
|
||||
font-size: 18px;
|
||||
line-height: 20px;
|
||||
color: @green;
|
||||
color: @ui-selected-border;
|
||||
}
|
||||
|
||||
.umb-table-body .umb-table__name {
|
||||
@@ -179,7 +186,8 @@ input.umb-table__input {
|
||||
font-weight: bold;
|
||||
a {
|
||||
color: @ui-option-type;
|
||||
&:hover, &:focus {
|
||||
outline-offset: 1px;
|
||||
&:hover {
|
||||
color: @ui-option-type-hover;
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -249,8 +257,8 @@ input.umb-table__input {
|
||||
flex-flow: row nowrap;
|
||||
flex: 1 1 5%;
|
||||
position: relative;
|
||||
margin: auto 14px;
|
||||
padding: 6px 2px;
|
||||
margin: auto 0;
|
||||
padding: 6px 16px;
|
||||
text-align: left;
|
||||
overflow:hidden;
|
||||
}
|
||||
@@ -268,8 +276,8 @@ input.umb-table__input {
|
||||
|
||||
.umb-table-cell:first-of-type:not(.not-fixed) {
|
||||
flex: 0 0 25px;
|
||||
margin: 0 0 0 15px;
|
||||
padding: 15px 0;
|
||||
margin: 0;
|
||||
padding: 15px 0 15px 15px;
|
||||
}
|
||||
|
||||
.umb-table-cell--auto-width {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
.umb-email-marketing {
|
||||
|
||||
h2 {
|
||||
font-weight: 800;
|
||||
max-width: 26ex;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
align-content: stretch;
|
||||
|
||||
.primary {
|
||||
flex-basis: 50%;
|
||||
padding-right: 40px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
.notice {
|
||||
color: @gray-5;
|
||||
font-style: italic;
|
||||
a {
|
||||
color: @gray-5;
|
||||
&:hover {
|
||||
color: @ui-action-type-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.secondary {
|
||||
flex-basis: 50%;
|
||||
svg {
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
margin-top: -60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cta {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
+10
-4
@@ -7,11 +7,17 @@
|
||||
display: flex;
|
||||
margin-bottom: 5px;
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.umb-user-group-picker-list-item:active,
|
||||
.umb-user-group-picker-list-item:focus {
|
||||
text-decoration: none;
|
||||
.umb-user-group-picker__action{
|
||||
background: transparent;
|
||||
border: 0 none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.umb-user-group-picker-list-item:hover {
|
||||
@@ -35,4 +41,4 @@
|
||||
.umb-user-group-picker-list-item__permission {
|
||||
font-size: 13px;
|
||||
color: @gray-4;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ input[type="color"],
|
||||
outline: 0;
|
||||
|
||||
.tabbing-active & {
|
||||
outline: 2px solid @inputBorderTabFocus;
|
||||
outline: 2px solid @ui-outline;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -297,11 +297,11 @@ select[size] {
|
||||
}
|
||||
|
||||
// Focus for select, file, radio, and checkbox
|
||||
select:focus,
|
||||
input[type="file"]:focus,
|
||||
input[type="radio"]:focus,
|
||||
input[type="checkbox"]:focus {
|
||||
.tab-focus();
|
||||
select,
|
||||
input[type="file"],
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
.umb-outline();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -185,40 +185,38 @@ iframe, .content-column-body {
|
||||
|
||||
// Inline code
|
||||
// 1: Revert border radius to match look and feel of 7.4+
|
||||
code{
|
||||
.border-radius(@baseBorderRadius); // 1
|
||||
code {
|
||||
.border-radius(@baseBorderRadius); // 1
|
||||
}
|
||||
|
||||
// Blocks of code
|
||||
// 1: Wrapping code is unreadable on small devices.
|
||||
pre {
|
||||
display: block;
|
||||
padding: (@baseLineHeight - 1) / 2;
|
||||
margin: 0 0 @baseLineHeight / 2;
|
||||
font-family: @sansFontFamily;
|
||||
//font-size: @baseFontSize - 1; // 14px to 13px
|
||||
color: @gray-2;
|
||||
line-height: @baseLineHeight;
|
||||
white-space: pre-wrap; // 1
|
||||
overflow-x: auto; // 1
|
||||
background-color: @gray-10;
|
||||
border: 1px solid @gray-8;
|
||||
.border-radius(@baseBorderRadius);
|
||||
display: block;
|
||||
padding: (@baseLineHeight - 1) / 2;
|
||||
margin: 0 0 @baseLineHeight / 2;
|
||||
font-family: @sansFontFamily;
|
||||
color: @gray-2;
|
||||
line-height: @baseLineHeight;
|
||||
white-space: pre-wrap; // 1
|
||||
overflow-x: auto; // 1
|
||||
background-color: @brownGrayLight;
|
||||
border: 1px solid @gray-8;
|
||||
.border-radius(@baseBorderRadius);
|
||||
|
||||
// Make prettyprint styles more spaced out for readability
|
||||
&.prettyprint {
|
||||
margin-bottom: @baseLineHeight;
|
||||
}
|
||||
|
||||
// Make prettyprint styles more spaced out for readability
|
||||
&.prettyprint {
|
||||
margin-bottom: @baseLineHeight;
|
||||
}
|
||||
|
||||
// Account for some code outputs that place code tags in pre tags
|
||||
code {
|
||||
padding: 0;
|
||||
white-space: pre; // 1
|
||||
word-wrap: normal; // 1
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
// Account for some code outputs that place code tags in pre tags
|
||||
code {
|
||||
padding: 0;
|
||||
white-space: pre; // 1
|
||||
word-wrap: normal; // 1
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Styling for content/media sort order dialog */
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
|
||||
@import "colors.less";
|
||||
@import "mixins.less";
|
||||
@import "application/umb-outline.less";
|
||||
@import "buttons.less";
|
||||
@import "forms.less";
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// Listview
|
||||
// -------------------------
|
||||
|
||||
.umb-listview {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.umb-listview table {
|
||||
border: 1px solid @gray-8;
|
||||
}
|
||||
@@ -43,6 +47,15 @@
|
||||
/* add padding */
|
||||
.left-addon input[type="text"] { padding-left: 30px !important; padding-right: 6px; }
|
||||
.right-addon input[type="text"] { padding-right: 30px; padding-left: 6px !important; }
|
||||
|
||||
&__label-icon{
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left:0;
|
||||
margin:0
|
||||
}
|
||||
}
|
||||
|
||||
.umb-listview table form {
|
||||
@@ -136,7 +149,36 @@
|
||||
/* TEMP */
|
||||
|
||||
.umb-minilistview {
|
||||
.umb-table-row.not-allowed { opacity: 0.6; cursor: not-allowed; }
|
||||
.umb-table-row.not-allowed {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
div.umb-mini-list-view__breadcrumb {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.no-display {
|
||||
display: none
|
||||
}
|
||||
|
||||
div.umb-table-cell-padding {
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
div.umb-table-cell .form-search {
|
||||
width: 100%;
|
||||
margin-right: 0;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon-search {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-listview .table-striped tbody td {
|
||||
|
||||
@@ -117,6 +117,12 @@ h5.-black {
|
||||
}
|
||||
.umb-control-group {
|
||||
position: relative;
|
||||
|
||||
&.umb-control-group__listview {
|
||||
// position: relative messes up the listview status messages (e.g. "no search results")
|
||||
position: unset;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display:block;
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
outline: thin dotted @gray-3;
|
||||
// Webkit
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
// Center-align a block level element
|
||||
@@ -435,7 +434,7 @@
|
||||
|
||||
// Button backgrounds
|
||||
// ------------------
|
||||
.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor) {
|
||||
.buttonBackground(@startColor, @hoverColor: @startColor, @textColor: @white, @textColorHover: @textColor, @disabledColor: @sand-1, @disabledTextColor: @white) {
|
||||
|
||||
color: @textColor;
|
||||
border-color: @startColor @startColor darken(@startColor, 15%);
|
||||
@@ -449,14 +448,14 @@
|
||||
}
|
||||
|
||||
// in these cases the gradient won't cover the background, so we override
|
||||
&:hover, &:focus, &:active, &.active {
|
||||
&:hover {
|
||||
color: @textColorHover;
|
||||
background-color: @hoverColor;
|
||||
}
|
||||
|
||||
&.disabled, &[disabled] {
|
||||
color: @white;
|
||||
background-color: @sand-1;
|
||||
background-color: @disabledColor;
|
||||
color: @disabledTextColor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,11 +233,14 @@
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a {
|
||||
position: relative;
|
||||
padding: 8px 20px;
|
||||
color: @ui-option-type;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.dropdown-menu > li > button {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 8px 20px;
|
||||
@@ -253,11 +256,9 @@
|
||||
}
|
||||
|
||||
.dropdown-menu > li > a:hover,
|
||||
.dropdown-menu > li > a:focus,
|
||||
.dropdown-menu > li > button:hover,
|
||||
.dropdown-menu > li > button:focus,
|
||||
.dropdown-submenu:hover > a,
|
||||
.dropdown-submenu:focus > a {
|
||||
.dropdown-submenu:hover > button {
|
||||
color: @ui-option-type-hover;
|
||||
background: @ui-option-hover;
|
||||
}
|
||||
@@ -300,8 +301,7 @@
|
||||
|
||||
// Active:hover/:focus dropdown links
|
||||
// -------------------------
|
||||
.nav > .dropdown.active > a:hover,
|
||||
.nav > .dropdown.active > a:focus {
|
||||
.nav > .dropdown.active > a:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -309,24 +309,21 @@
|
||||
// -------------------------
|
||||
.nav-tabs .open .dropdown-toggle,
|
||||
.nav-pills .open .dropdown-toggle,
|
||||
.nav > li.dropdown.open.active > a:hover,
|
||||
.nav > li.dropdown.open.active > a:focus {
|
||||
.nav > li.dropdown.open.active > a:hover {
|
||||
/*color: @white;*/
|
||||
background-color: @gray-8;
|
||||
border-color: @gray-8;
|
||||
}
|
||||
.nav li.dropdown.open .caret,
|
||||
.nav li.dropdown.open.active .caret,
|
||||
.nav li.dropdown.open a:hover .caret,
|
||||
.nav li.dropdown.open a:focus .caret {
|
||||
.nav li.dropdown.open a:hover .caret {
|
||||
border-top-color: @white;
|
||||
border-bottom-color: @white;
|
||||
.opacity(100);
|
||||
}
|
||||
|
||||
// Dropdowns in stacked tabs
|
||||
.tabs-stacked .open > a:hover,
|
||||
.tabs-stacked .open > a:focus {
|
||||
.tabs-stacked .open > a:hover {
|
||||
border-color: @gray-8;
|
||||
}
|
||||
|
||||
@@ -377,15 +374,13 @@
|
||||
}
|
||||
.tabs-below > .nav-tabs > li > a {
|
||||
.border-radius(0 0 4px 4px);
|
||||
&:hover,
|
||||
&:focus {
|
||||
&:hover {
|
||||
border-bottom-color: transparent;
|
||||
border-top-color: @gray-8;
|
||||
}
|
||||
}
|
||||
.tabs-below > .nav-tabs > .active > a,
|
||||
.tabs-below > .nav-tabs > .active > a:hover,
|
||||
.tabs-below > .nav-tabs > .active > a:focus {
|
||||
.tabs-below > .nav-tabs > .active > a:hover {
|
||||
border-color: transparent @gray-8 @gray-8 @gray-8;
|
||||
}
|
||||
|
||||
@@ -414,13 +409,11 @@
|
||||
margin-right: -1px;
|
||||
.border-radius(4px 0 0 4px);
|
||||
}
|
||||
.tabs-left > .nav-tabs > li > a:hover,
|
||||
.tabs-left > .nav-tabs > li > a:focus {
|
||||
.tabs-left > .nav-tabs > li > a:hover {
|
||||
border-color: @gray-10 @gray-8 @gray-10 @gray-10;
|
||||
}
|
||||
.tabs-left > .nav-tabs .active > a,
|
||||
.tabs-left > .nav-tabs .active > a:hover,
|
||||
.tabs-left > .nav-tabs .active > a:focus {
|
||||
.tabs-left > .nav-tabs .active > a:hover {
|
||||
border-color: @gray-8 transparent @gray-8 @gray-8;
|
||||
*border-right-color: @white;
|
||||
}
|
||||
@@ -435,13 +428,11 @@
|
||||
margin-left: -1px;
|
||||
.border-radius(0 4px 4px 0);
|
||||
}
|
||||
.tabs-right > .nav-tabs > li > a:hover,
|
||||
.tabs-right > .nav-tabs > li > a:focus {
|
||||
.tabs-right > .nav-tabs > li > a:hover {
|
||||
border-color: @gray-10 @gray-10 @gray-10 @gray-8;
|
||||
}
|
||||
.tabs-right > .nav-tabs .active > a,
|
||||
.tabs-right > .nav-tabs .active > a:hover,
|
||||
.tabs-right > .nav-tabs .active > a:focus {
|
||||
.tabs-right > .nav-tabs .active > a:hover {
|
||||
border-color: @gray-8 @gray-8 @gray-8 transparent;
|
||||
*border-left-color: @white;
|
||||
}
|
||||
@@ -456,8 +447,7 @@
|
||||
color: @gray-8;
|
||||
}
|
||||
// Nuke hover/focus effects
|
||||
.nav > .disabled > a:hover,
|
||||
.nav > .disabled > a:focus {
|
||||
.nav > .disabled > a:hover {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
cursor: default;
|
||||
|
||||
@@ -341,6 +341,7 @@
|
||||
.umb-panel-header-icon {
|
||||
cursor: pointer;
|
||||
margin-right: 5px;
|
||||
margin-top: -6px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -249,26 +249,11 @@
|
||||
|
||||
transition: all 150ms ease-in-out;
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
&:hover {
|
||||
color: @ui-action-discreet-type-hover;
|
||||
border-color: @ui-action-discreet-type-hover;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
.tabbing-active &:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
z-index: 10000;
|
||||
top: -6px;
|
||||
bottom: -6px;
|
||||
left: -6px;
|
||||
right: -6px;
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 2px @blueMid, inset 0 0 2px 1px @blueMid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.umb-mediapicker .label {
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
//@blueLight: #4f89de;
|
||||
@blue: #2E8AEA;
|
||||
@blueMid: #2152A3;// updated 2019
|
||||
@blueMidLight: rgb(99, 174, 236);
|
||||
@blueDark: #3544b1;// updated 2019
|
||||
@blueExtraDark: #1b264f;// added 2019
|
||||
@blueLight: #ADD8E6;
|
||||
@@ -139,6 +140,9 @@
|
||||
@ui-option-disabled-type-hover: @gray-5;
|
||||
@ui-option-disabled-hover: @sand-7;
|
||||
|
||||
@ui-disabled-type: @gray-6;
|
||||
@ui-disabled-border: @gray-6;
|
||||
|
||||
//@ui-active: #346ab3;
|
||||
@ui-active: @pinkLight;
|
||||
@ui-active-blur: @brownLight;
|
||||
@@ -149,8 +153,8 @@
|
||||
@ui-selected-hover: ligthen(@sand-5, 10);
|
||||
@ui-selected-type: @blueExtraDark;
|
||||
@ui-selected-type-hover: @blueMid;
|
||||
@ui-selected-border: @pinkLight;
|
||||
@ui-selected-border-hover: darken(@pinkLight, 10);
|
||||
@ui-selected-border: @blueDark;
|
||||
@ui-selected-border-hover: darken(@blueDark, 10);
|
||||
|
||||
@ui-light-border: @pinkLight;
|
||||
@ui-light-type: @gray-4;
|
||||
@@ -175,6 +179,8 @@
|
||||
@ui-action-discreet-border: @gray-7;
|
||||
@ui-action-discreet-border-hover: @blueMid;
|
||||
|
||||
@ui-outline: @blueMidLight;
|
||||
|
||||
@type-white: @white;
|
||||
@type-black: @blueNight;
|
||||
|
||||
@@ -255,7 +261,7 @@
|
||||
// Buttons
|
||||
// -------------------------
|
||||
@btnBackground: @gray-9;
|
||||
@btnBackgroundHighlight: @gray-9;
|
||||
@btnBackgroundHighlight: @gray-10;
|
||||
@btnBorder: @gray-9;
|
||||
|
||||
@btnPrimaryBackground: @ui-btn-positive;
|
||||
@@ -293,7 +299,7 @@
|
||||
@inputBackground: @white;
|
||||
@inputBorder: @gray-8;
|
||||
@inputBorderFocus: @gray-7;
|
||||
@inputBorderTabFocus: @blueExtraDark;
|
||||
@inputBorderTabFocus: @ui-outline;
|
||||
@inputBorderRadius: 0;
|
||||
@inputDisabledBackground: @gray-10;
|
||||
@formActionsBackground: @gray-9;
|
||||
@@ -448,7 +454,7 @@
|
||||
@successBorder: transparent;
|
||||
|
||||
@infoText: @white;
|
||||
@infoBackground: @turquoise-d1;
|
||||
@infoBackground: @blueDark;
|
||||
@infoBorder: transparent;
|
||||
|
||||
@alertBorderRadius: 0;
|
||||
|
||||
@@ -67,13 +67,18 @@ function MainController($scope, $location, appState, treeService, notificationsS
|
||||
};
|
||||
|
||||
var evts = [];
|
||||
|
||||
|
||||
//when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function (evt, data) {
|
||||
$scope.authenticated = null;
|
||||
$scope.user = null;
|
||||
const isTimedOut = data && data.isTimedOut ? true : false;
|
||||
$scope.showLoginScreen(isTimedOut);
|
||||
|
||||
// Remove the localstorage items for tours shown
|
||||
// Means that when next logged in they can be re-shown if not already dismissed etc
|
||||
localStorageService.remove("emailMarketingTourShown");
|
||||
localStorageService.remove("introTourShown");
|
||||
}));
|
||||
|
||||
evts.push(eventsService.on("app.userRefresh", function(evt) {
|
||||
|
||||
@@ -17,17 +17,21 @@
|
||||
|
||||
<div class="umb-help-list">
|
||||
|
||||
<a href="" class="umb-help-list-item umb-help-list-item__content flex items-center justify-between" ng-click="tourGroup.open = !tourGroup.open">
|
||||
<h5 class="umb-help-list-item__group-title">
|
||||
<i ng-class="{'icon-navigation-right': !tourGroup.open, 'icon-navigation-down': tourGroup.open}"></i>
|
||||
<button
|
||||
type="button"
|
||||
class="umb-help-list-item umb-help-list-item__content flex items-center justify-between"
|
||||
ng-click="tourGroup.open = !tourGroup.open"
|
||||
aria-expanded="{{tourGroup.open === undefined ? false : tourGroup.open }}">
|
||||
<span class="umb-help-list-item__group-title bold">
|
||||
<i ng-class="{'icon-navigation-right': !tourGroup.open, 'icon-navigation-down': tourGroup.open}" aria-hidden="true"></i>
|
||||
<span ng-if="tourGroup.group !== 'undefined'">{{tourGroup.group}}</span>
|
||||
<span ng-if="tourGroup.group === 'undefined'">Other</span>
|
||||
</h5>
|
||||
</span>
|
||||
<umb-progress-circle
|
||||
percentage="{{tourGroup.completedPercentage}}"
|
||||
size="40">
|
||||
</umb-progress-circle>
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<div ng-if="tourGroup.open">
|
||||
<div data-element="tour-{{tour.alias}}" class="umb-help-list-item" ng-repeat="tour in tourGroup.tours">
|
||||
@@ -85,9 +89,9 @@
|
||||
<ul class="umb-help-list">
|
||||
<li class="umb-help-list-item" ng-repeat="video in vm.videos track by $index">
|
||||
<a class="umb-help-list-item__content" data-element="help-article-{{video.title}}" target="_blank" ng-href="{{video.link}}?utm_source=core&utm_medium=help&utm_content=link&utm_campaign=tv">
|
||||
<i class="umb-help-list-item__icon icon-tv-old"></i>
|
||||
<i class="umb-help-list-item__icon icon-tv-old" aria-hidden="true"></i>
|
||||
<span class="umb-help-list-item__title">{{video.title}}</span>
|
||||
<i class="umb-help-list-item__open-icon icon-out"></i>
|
||||
<i class="umb-help-list-item__open-icon icon-out" aria-hidden="true"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -96,7 +100,7 @@
|
||||
<!-- Links -->
|
||||
<div class="umb-help-section" data-element="help-links" ng-if="vm.hasAccessToSettings">
|
||||
<a data-element="help-link-umbraco-tv" class="umb-help-badge" target="_blank" href="https://umbraco.tv?utm_source=core&utm_medium=help&utm_content=link&utm_campaign=tv">
|
||||
<i class="umb-help-badge__icon icon-tv-old"></i>
|
||||
<i class="umb-help-badge__icon icon-tv-old" aria-hidden="true"></i>
|
||||
<div class="umb-help-badge__title">Visit umbraco.tv</div>
|
||||
<small>
|
||||
<localize key="help_theBestUmbracoVideoTutorials">The best Umbraco video tutorials</localize>
|
||||
@@ -104,7 +108,7 @@
|
||||
</a>
|
||||
|
||||
<a data-element="help-link-our-umbraco" class="umb-help-badge" target="_blank" href="https://our.umbraco.com?utm_source=core&utm_medium=help&utm_content=link&utm_campaign=our">
|
||||
<i class="umb-help-badge__icon icon-favorite"></i>
|
||||
<i class="umb-help-badge__icon icon-favorite" aria-hidden="true"></i>
|
||||
|
||||
<div class="umb-help-badge__title">Visit our.umbraco.com</div>
|
||||
<small>
|
||||
|
||||
+9
-33
@@ -1,7 +1,7 @@
|
||||
//used for the media picker dialog
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.Editors.MediaPickerController",
|
||||
function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService, editorService) {
|
||||
function ($scope, $timeout, mediaResource, entityResource, userService, mediaHelper, mediaTypeHelper, eventsService, treeService, localStorageService, localizationService) {
|
||||
|
||||
var vm = this;
|
||||
|
||||
@@ -22,7 +22,6 @@ angular.module("umbraco")
|
||||
|
||||
vm.clickHandler = clickHandler;
|
||||
vm.clickItemName = clickItemName;
|
||||
vm.editMediaItem = editMediaItem;
|
||||
vm.gotoFolder = gotoFolder;
|
||||
|
||||
var dialogOptions = $scope.model;
|
||||
@@ -37,8 +36,7 @@ angular.module("umbraco")
|
||||
$scope.cropSize = dialogOptions.cropSize;
|
||||
$scope.lastOpenedNode = localStorageService.get("umbLastOpenedMediaNodeId");
|
||||
$scope.lockedFolder = true;
|
||||
$scope.allowMediaEdit = dialogOptions.allowMediaEdit ? dialogOptions.allowMediaEdit : false;
|
||||
|
||||
|
||||
var userStartNodes = [];
|
||||
|
||||
var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings;
|
||||
@@ -132,7 +130,7 @@ angular.module("umbraco")
|
||||
|
||||
// ID of a UDI or legacy int ID still could be null/undefinied here
|
||||
// As user may dragged in an image that has not been saved to media section yet
|
||||
if(id){
|
||||
if (id) {
|
||||
entityResource.getById(id, "Media")
|
||||
.then(function (node) {
|
||||
$scope.target = node;
|
||||
@@ -144,8 +142,7 @@ angular.module("umbraco")
|
||||
openDetailsDialog();
|
||||
}
|
||||
}, gotoStartNode);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// No ID set - then this is going to be a tmpimg that has not been uploaded
|
||||
// User editing this will want to be changing the ALT text
|
||||
openDetailsDialog();
|
||||
@@ -254,11 +251,14 @@ angular.module("umbraco")
|
||||
}
|
||||
}
|
||||
|
||||
function clickItemName(item) {
|
||||
function clickItemName(item, event, index) {
|
||||
if (item.isFolder) {
|
||||
gotoFolder(item);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$scope.clickHandler(item, event, index);
|
||||
}
|
||||
};
|
||||
|
||||
function selectMedia(media) {
|
||||
if (!media.selectable) {
|
||||
@@ -510,30 +510,6 @@ angular.module("umbraco")
|
||||
}
|
||||
}
|
||||
|
||||
function editMediaItem(item) {
|
||||
var mediaEditor = {
|
||||
id: item.id,
|
||||
submit: function (model) {
|
||||
editorService.close()
|
||||
// update the media picker item in the picker so it matched the saved media item
|
||||
// the media picker is using media entities so we get the
|
||||
// entity so we easily can format it for use in the media grid
|
||||
if (model && model.mediaNode) {
|
||||
entityResource.getById(model.mediaNode.id, "media")
|
||||
.then(function (mediaEntity) {
|
||||
angular.extend(item, mediaEntity);
|
||||
setMediaMetaData(item);
|
||||
setUpdatedMediaNodes(item);
|
||||
});
|
||||
}
|
||||
},
|
||||
close: function (model) {
|
||||
setUpdatedMediaNodes(item);
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
editorService.mediaEditor(mediaEditor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the umbImageGravity component updates the focal point value
|
||||
|
||||
+5
-6
@@ -56,19 +56,19 @@
|
||||
<div class="row umb-control-group" ng-show="!vm.searchOptions.filter">
|
||||
<ul class="umb-breadcrumbs">
|
||||
<li ng-hide="startNodeId != -1" class="umb-breadcrumbs__ancestor">
|
||||
<button type="button" class="umb-breadcrumbs__action" ng-click="vm.gotoFolder()">
|
||||
<button type="button" class="umb-breadcrumbs__action umb-outline umb-outline--surronding" ng-click="vm.gotoFolder()" ng-class="{'--current':path.length === 0}">
|
||||
<localize key="treeHeaders_media">Media</localize>
|
||||
</button>
|
||||
<span class="umb-breadcrumbs__separator" aria-hidden="true">/</span>
|
||||
</li>
|
||||
|
||||
<li ng-repeat="item in path" class="umb-breadcrumbs__ancestor">
|
||||
<button type="button" class="umb-breadcrumbs__action" ng-click="vm.gotoFolder(item)">{{item.name}}</button>
|
||||
<button type="button" class="umb-breadcrumbs__action umb-outline umb-outline--surronding" ng-click="vm.gotoFolder(item)" ng-class="{'--current':$last}">{{item.name}}</button>
|
||||
<span class="umb-breadcrumbs__separator" aria-hidden="true">/</span>
|
||||
</li>
|
||||
|
||||
<li class="umb-breadcrumbs__ancestor" ng-show="!lockedFolder">
|
||||
<button type="button" class="umb-breadcrumbs__action" ng-hide="model.showFolderInput" ng-click="model.showFolderInput = true">
|
||||
<button type="button" class="umb-breadcrumbs__action umb-outline umb-outline--surronding" ng-hide="model.showFolderInput" ng-click="model.showFolderInput = true">
|
||||
<i class="icon icon-add small" aria-hidden="true"></i>
|
||||
<span class="sr-only">
|
||||
<localize key="visuallyHiddenTexts_createNewFolder">Create new folder</localize>
|
||||
@@ -105,8 +105,6 @@
|
||||
items="images"
|
||||
on-click="vm.clickHandler"
|
||||
on-click-name="vm.clickItemName"
|
||||
on-click-edit="vm.editMediaItem(item)"
|
||||
allow-on-click-edit="{{allowMediaEdit}}"
|
||||
item-max-width="150"
|
||||
item-max-height="150"
|
||||
item-min-width="100"
|
||||
@@ -115,7 +113,8 @@
|
||||
only-images={{onlyImages}}
|
||||
only-folders={{onlyFolders}}
|
||||
include-sub-folders={{showChilds}}
|
||||
current-folder-id="{{currentFolder.id}}">
|
||||
current-folder-id="{{currentFolder.id}}"
|
||||
allow-open-folder="!disableFolderSelect">
|
||||
</umb-media-grid>
|
||||
|
||||
|
||||
|
||||
-6
@@ -31,7 +31,6 @@
|
||||
vm.datePickerChange = datePickerChange;
|
||||
vm.submit = submit;
|
||||
vm.close = close;
|
||||
vm.copyQuery = copyQuery;
|
||||
|
||||
function onInit() {
|
||||
|
||||
@@ -121,11 +120,6 @@
|
||||
query.filters.push({});
|
||||
}
|
||||
|
||||
function copyQuery() {
|
||||
var copyText = $scope.model.result.queryExpression;
|
||||
navigator.clipboard.writeText(copyText);
|
||||
}
|
||||
|
||||
function trashFilter(query, filter) {
|
||||
for (var i = 0; i < query.filters.length; i++) {
|
||||
if (query.filters[i] == filter) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user