Compare commits

..

1 Commits

Author SHA1 Message Date
Shannon 7c3a945d01 Fixes DI Lifetime.Request works per thread. #6044 2019-08-08 14:53:17 +10:00
230 changed files with 3496 additions and 3069 deletions
-35
View File
@@ -1,35 +0,0 @@
# Contributing to Umbraco CMS
When youre considering creating a pull request for Umbraco CMS, we will categorize them in two different sizes, small and large.
The process for both sizes is very similar, as [explained in the contribution document](CONTRIBUTING.md#how-do-i-begin).
## Small PRs
Bug fixes and small improvements - can be recognized by seeing a small number of changes and possibly a small number of new files.
Were usually able to handle small PRs pretty quickly. A community volunteer will do the initial review and flag it for Umbraco HQ as “community tested”. If everything looks good, it will be merged pretty quickly [as per the described process](REVIEW_PROCESS.md).
### Up for grabs
Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. We encourage anyone to pick them up and help out.
If you do start working on something, make sure leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
## Large PRs
New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGets packages.config, NPMs packages.json, etc.).
We would love to follow the same process for larger PRs but this is not always possible due to time limitations and priorities that need to be aligned. We dont want to put up any barriers, but this document should set the correct expectations.
Please make sure to describe your idea in an issue, it helps to put in mockup screenshots or videos.
If the change makes sense for HQ to include in Umbraco CMS we will leave you some feedback on how wed like to see it being implemented.
If a larger pull request is encouraged by Umbraco HQ, the process will be similar to what is described in the [small PRs process](#small-prs) above, we strive to feedback within 14 days. Finalizing and merging the PR might take longer though as it will likely need to be picked up by the development team to make sure everything is in order. Well keep you posted on the progress.
It is highly recommended that you speak to the HQ before making large, complex changes.
### Pull request or package?
If it doesnt fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and to fix bugs.
Eventually, a package could "graduate" to be included in the CMS.
+1 -1
View File
@@ -12,7 +12,7 @@ thoroughly. Then, proceed by filling out the rest of the details in the issue
template below. The more details you can give us, the easier it will be for us
to determine the cause of a problem.
See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md
See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md
-->
-25
View File
@@ -1,25 +0,0 @@
# Review process
You're an awesome person and have sent us your contribution in the form of a pull request! It's now time to relax for a bit and wait for our response.
In order to set some expectations, here's what happens next.
## Review process
You will get an initial reply within 48 hours (workdays) to acknowledge that weve seen your PR and well pick it up as soon as we can.
You will get feedback within at most 14 days after opening the PR. Youll most likely get feedback sooner though. Then there are a few possible outcomes:
- Your proposed change is awesome! We merge it in and it will be included in the next minor release of Umbraco
- If the change is a high priority bug fix, we will cherry-pick it into the next patch release as well so that we can release it as soon as possible
- Your proposed change is awesome but needs a bit more work, well give you feedback on the changes wed like to see
- Your proposed change is awesome but.. not something were looking to include at this point. Well close your PR and the related issue (well be nice about it!)
## Are you still available?
We understand you have other things to do and can't just drop everything to help us out.
So if were asking for your help to improve the PR well wait for two weeks to give you a fair chance to make changes. Well ask for an update if we dont hear back from you after that time.
If we dont hear back from you for 4 weeks, well close the PR so that it doesnt just hang around forever. Youre very welcome to re-open it once you have some more time to spend on it.
There will be times that we really like your proposed changes and well finish the final improvements wed like to see ourselves. You still get the credits and your commits will live on in the git repository.
+7 -7
View File
@@ -1,11 +1,11 @@
888
888
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
888 888 888 888 888 888 888 888 .d888888 888 888 888
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
888
888
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
888 888 888 888 888 888 888 888 .d888888 888 888 888
Y88b 888 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
"Y88888 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
------------------------------------------------------------------
+7 -8
View File
@@ -1,13 +1,12 @@
888
888
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
888 888 888 888 888 888 888 888 .d888888 888 888 888
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
------------------------------------------------------------------
----------------------------------------------------
Don't forget to build!
+2 -2
View File
@@ -18,5 +18,5 @@ using System.Resources;
[assembly: AssemblyVersion("8.0.0")]
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.2.0")]
[assembly: AssemblyInformationalVersion("8.2.0")]
[assembly: AssemblyFileVersion("8.1.1")]
[assembly: AssemblyInformationalVersion("8.1.1")]
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
// register the IFileSystem supporting the IMediaFileSystem
// THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM
// and, SupportingFileSystem.For<IMediaFileSystem>() returns the underlying filesystem
composition.SetMediaFileSystem(() => new PhysicalFileSystem(SystemDirectories.Media));
composition.SetMediaFileSystem(() => new PhysicalFileSystem("~/media"));
return composition;
}
@@ -96,7 +96,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
var pluginLangFolders = appPlugins.Exists == false
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
: appPlugins.GetDirectories()
.SelectMany(x => x.GetDirectories("Lang", SearchOption.AllDirectories))
.SelectMany(x => x.GetDirectories("Lang"))
.SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly))
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false));
+6 -9
View File
@@ -1,4 +1,6 @@
namespace Umbraco.Core.Composing
using System;
namespace Umbraco.Core.Composing
{
/// <summary>
/// Specifies the lifetime of a registered instance.
@@ -12,13 +14,6 @@
/// or MS.DI, PerDependency in Autofac.</remarks>
Transient,
// TODO: We need to fix this up, currently LightInject is the only one that behaves differently from all other containers.
// ... the simple fix would be to map this to PerScopeLifetime in LI but need to wait on a response here https://github.com/seesharper/LightInject/issues/494#issuecomment-518942625
//
// we use it for controllers, httpContextBase and other request scoped objects: MembershpHelper, TagQuery, UmbracoTreeSearcher and ISearchableTree
// - so that they are automatically disposed at the end of the scope (ie request)
// - not sure they should not be simply 'scoped'?
/// <summary>
/// One unique instance per request.
/// </summary>
@@ -28,7 +23,9 @@
/// </para>
/// Corresponds to
/// <para>
/// PerRequestLifeTime in LightInject - means transient but disposed at the end of the current web request.
/// PerScopeLifetime in LightInject.
/// Although it would seem that this should map to PerRequestLifeTime in LightInject,
/// that is actually misleading since PerRequestLifeTime in LightInject means transient but disposed at the end of the current web request.
/// see: https://github.com/seesharper/LightInject/issues/494#issuecomment-518493262
/// </para>
/// <para>
@@ -145,7 +145,7 @@ namespace Umbraco.Core.Composing.LightInject
/// <inheritdoc />
public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient)
=> Container.Register(serviceType, GetLifetime(lifetime));
=> Container.Register(serviceType, GetLifetime(lifetime, serviceType));
/// <inheritdoc />
public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient)
@@ -158,7 +158,7 @@ namespace Umbraco.Core.Composing.LightInject
case Lifetime.Request:
case Lifetime.Scope:
case Lifetime.Singleton:
Container.Register(serviceType, implementingType, GetLifetime(lifetime));
Container.Register(serviceType, implementingType, GetLifetime(lifetime, serviceType));
break;
default:
throw new NotSupportedException($"Lifetime {lifetime} is not supported.");
@@ -169,7 +169,7 @@ namespace Umbraco.Core.Composing.LightInject
public void Register<TService>(Func<IFactory, TService> factory, Lifetime lifetime = Lifetime.Transient)
where TService : class
{
Container.Register(f => factory(this), GetLifetime(lifetime));
Container.Register(f => factory(this), GetLifetime(lifetime, typeof(TService)));
}
/// <inheritdoc />
@@ -186,7 +186,7 @@ namespace Umbraco.Core.Composing.LightInject
where TService : class
{
// note that there can only be one implementation or instance registered "for" a service
Container.Register(typeof(TService), implementingType, GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
Container.Register(typeof(TService), implementingType, GetTargetedServiceName<TTarget>(), GetLifetime(lifetime, typeof(TService)));
}
/// <inheritdoc />
@@ -194,7 +194,7 @@ namespace Umbraco.Core.Composing.LightInject
where TService : class
{
// note that there can only be one implementation or instance registered "for" a service
Container.Register(f => factory(this), GetTargetedServiceName<TTarget>(), GetLifetime(lifetime));
Container.Register(f => factory(this), GetTargetedServiceName<TTarget>(), GetLifetime(lifetime, typeof(TService)));
}
/// <inheritdoc />
@@ -202,14 +202,20 @@ namespace Umbraco.Core.Composing.LightInject
where TService : class
=> Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName<TTarget>());
private ILifetime GetLifetime(Lifetime lifetime)
protected virtual ILifetime GetLifetime(Lifetime lifetime, Type type)
{
switch (lifetime)
{
case Lifetime.Transient:
return null;
case Lifetime.Request:
return new PerRequestLifeTime();
//LightInject behaves slightly differently than all containers and although it would seem that we should be using
//PerRequestLifeTime here, it actually does not mean "one per request", it
//just means transient per request and will be disposed at the end of the request.
//LightInject's PerScopeLifetime is equivalent to "one per request" which is what we'd expect when using the Lifetime.Request since
//that would be consistent with other containers.
//See: https://github.com/umbraco/Umbraco-CMS/issues/6044#issuecomment-518949758
return new PerScopeLifetime();
case Lifetime.Scope:
return new PerScopeLifetime();
case Lifetime.Singleton:
@@ -96,11 +96,6 @@ namespace Umbraco.Core
/// </summary>
public const string MediaPicker = "Umbraco.MediaPicker";
/// <summary>
/// Multiple Media Picker.
/// </summary>
public const string MultipleMediaPicker = "Umbraco.MultipleMediaPicker";
/// <summary>
/// Member Picker.
/// </summary>
@@ -208,24 +203,6 @@ namespace Umbraco.Core
/// <remarks>Must be a valid <see cref="ValueTypes"/> value.</remarks>
public const string DataValueType = "umbracoDataValueType";
}
/// <summary>
/// Defines Umbraco's built-in property editor groups.
/// </summary>
public static class Groups
{
public const string Common = "Common";
public const string Lists = "Lists";
public const string Media = "Media";
public const string People = "People";
public const string Pickers = "Pickers";
public const string RichContent = "Rich Content";
}
}
}
}
+1 -2
View File
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
@@ -16,7 +15,7 @@ namespace Umbraco.Core
/// <returns></returns>
public static string ToIsoString(this DateTime dt)
{
return dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
return dt.ToString("yyyy-MM-dd HH:mm:ss");
}
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
@@ -42,12 +42,6 @@ namespace Umbraco.Core.Logging.Viewer
bool CheckCanOpenLogs(LogTimePeriod logTimePeriod);
/// <summary>
/// Gets the current Serilog minimum log level
/// </summary>
/// <returns></returns>
string GetLogLevel();
/// <summary>
/// Returns the collection of logs
/// </summary>
@@ -1,14 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using Newtonsoft.Json;
using Serilog;
using Serilog.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Formatting = Newtonsoft.Json.Formatting;
namespace Umbraco.Core.Logging.Viewer
{
@@ -92,16 +89,6 @@ namespace Umbraco.Core.Logging.Viewer
return errorCounter.Count;
}
/// <summary>
/// Get the Serilog minimum-level value from the config file.
/// </summary>
/// <returns></returns>
public string GetLogLevel()
{
var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast<LogEventLevel>().Where(Log.Logger.IsEnabled)?.Min() ?? null;
return logLevel?.ToString() ?? "";
}
public LogLevelCounts GetLogLevelCounts(LogTimePeriod logTimePeriod)
{
var counter = new CountingFilter();
@@ -1,7 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using NPoco;
using Umbraco.Core;
using Umbraco.Core.Migrations.Expressions.Common;
using Umbraco.Core.Persistence.SqlSyntax;
@@ -29,57 +27,31 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.KeysAndIndexes
{
_context.BuildingExpression = false;
//get a list of all constraints - this will include all PK, FK and unique constraints
var tableConstraints = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList();
//get a list of defined indexes - this will include all indexes, unique indexes and unique constraint indexes
var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList();
var uniqueConstraintNames = tableConstraints.Where(x => !x.Item2.InvariantStartsWith("PK_") && !x.Item2.InvariantStartsWith("FK_")).Select(x => x.Item2);
var indexNames = indexes.Select(x => x.IndexName).ToList();
// drop keys
if (DeleteLocal || DeleteForeign)
{
// table, constraint
var tableKeys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList();
if (DeleteForeign)
{
//In some cases not all FK's are prefixed with "FK" :/ mostly with old upgraded databases so we need to check if it's either:
// * starts with FK OR
// * doesn't start with PK_ and doesn't exist in the list of indexes
foreach (var key in tableConstraints.Where(x => x.Item1 == TableName
&& (x.Item2.InvariantStartsWith("FK_") || (!x.Item2.InvariantStartsWith("PK_") && !indexNames.InvariantContains(x.Item2)))))
{
foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("FK_")))
Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do();
}
}
if (DeleteLocal)
{
foreach (var key in tableConstraints.Where(x => x.Item1 == TableName && x.Item2.InvariantStartsWith("PK_")))
foreach (var key in tableKeys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("PK_")))
Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do();
// note: we do *not* delete the DEFAULT constraints and if we wanted to we'd have to deal with that in interesting ways
// since SQL server has a specific way to handle that, see SqlServerSyntaxProvider.GetDefaultConstraintsPerColumn
// note: we do *not* delete the DEFAULT constraints
}
}
// drop indexes
if (DeleteLocal)
{
{
var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList();
foreach (var index in indexes.Where(x => x.TableName == TableName))
{
//if this is a unique constraint we need to drop the constraint, else drop the index
//to figure this out, the index must be tagged as unique and it must exist in the tableConstraints
if (index.IsUnique && uniqueConstraintNames.InvariantContains(index.IndexName))
Delete.UniqueConstraint(index.IndexName).FromTable(index.TableName).Do();
else
Delete.Index(index.IndexName).OnTable(index.TableName).Do();
}
Delete.Index(index.IndexName).OnTable(index.TableName).Do();
}
}
@@ -1,32 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
{
class DropDownFlexiblePreValueMigrator : IPreValueMigrator
{
public bool CanMigrate(string editorAlias)
=> editorAlias == "Umbraco.DropDown.Flexible";
public virtual string GetNewAlias(string editorAlias)
=> null;
public object GetConfiguration(int dataTypeId, string editorAlias, Dictionary<string, PreValueDto> preValues)
{
var config = new DropDownFlexibleConfiguration();
foreach (var preValue in preValues.Values)
{
if (preValue.Alias == "multiple")
{
config.Multiple = (preValue.Value == "1");
}
else
{
config.Items.Add(new ValueListConfiguration.ValueListItem { Id = preValue.Id, Value = preValue.Value });
}
}
return config;
}
}
}
@@ -1,19 +0,0 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
{
class MarkdownEditorPreValueMigrator : DefaultPreValueMigrator //PreValueMigratorBase
{
public override bool CanMigrate(string editorAlias)
=> editorAlias == Constants.PropertyEditors.Aliases.MarkdownEditor;
protected override object GetPreValueValue(PreValueDto preValue)
{
if (preValue.Alias == "preview")
return preValue.Value == "1";
return base.GetPreValueValue(preValue);
}
}
}
@@ -19,9 +19,7 @@ public class PreValueMigratorComposer : ICoreComposer
.Append<NestedContentPreValueMigrator>()
.Append<DecimalPreValueMigrator>()
.Append<ListViewPreValueMigrator>()
.Append<DropDownFlexiblePreValueMigrator>()
.Append<ValueListPreValueMigrator>()
.Append<MarkdownEditorPreValueMigrator>();
.Append<ValueListPreValueMigrator>();
}
}
}
@@ -15,17 +15,21 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0
{
//special trick to add the column without constraints and return the sql to add them later
AddColumn<MacroDto>("macroType", out var sqls1);
AddColumn<MacroDto>("macroSource", out var sqls2);
//populate the new columns with legacy data
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = '', macroType = {(int)MacroTypes.Unknown}").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroXSLT, macroType = {(int)MacroTypes.Unknown} WHERE macroXSLT != '' AND macroXSLT IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptAssembly, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptAssembly != '' AND macroScriptAssembly IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptType, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptType != '' AND macroScriptType IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroPython, macroType = {(int)MacroTypes.PartialView} WHERE macroPython != '' AND macroPython IS NOT NULL").Do();
//now we need to update the new column with some values because this column doesn't allow NULL values
Update.Table(Constants.DatabaseSchema.Tables.Macro).Set(new { macroType = (int)MacroTypes.Unknown}).AllRows().Do();
//now apply constraints (NOT NULL) to new table
foreach (var sql in sqls1) Execute.Sql(sql).Do();
//special trick to add the column without constraints and return the sql to add them later
AddColumn<MacroDto>("macroSource", out var sqls2);
//populate the new macroSource column with legacy data
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroXSLT, macroType = {(int)MacroTypes.Unknown} WHERE macroXSLT IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptAssembly, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptAssembly IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroScriptType, macroType = {(int)MacroTypes.Unknown} WHERE macroScriptType IS NOT NULL").Do();
Execute.Sql($"UPDATE {Constants.DatabaseSchema.Tables.Macro} SET macroSource = macroPython, macroType = {(int)MacroTypes.PartialView} WHERE macroPython IS NOT NULL").Do();
//now apply constraints (NOT NULL) to new table
foreach (var sql in sqls2) Execute.Sql(sql).Do();
//now remove these old columns
@@ -50,10 +50,10 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_1_0
var obj = JsonConvert.DeserializeObject<JObject>(value);
var allControls = obj.SelectTokens("$.sections..rows..areas..controls");
foreach (var control in allControls.SelectMany(c => c).OfType<JObject>())
foreach (var control in allControls.SelectMany(c => c))
{
var controlValue = control["value"];
if (controlValue?.Type == JTokenType.String)
if (controlValue.Type == JTokenType.String)
{
control["value"] = UpdateMediaUrls(mediaLinkPattern, controlValue.Value<string>());
}
+1 -1
View File
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Models
/// Gets of sets the alias of the property type.
/// </summary>
[DataMember]
public virtual string Alias
public string Alias
{
get => _alias;
set => SetPropertyValueAndDetectChanges(SanitizeAlias(value), ref _alias, nameof(Alias));
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web;
using System.Xml.Linq;
using System.Xml.XPath;
@@ -574,11 +575,12 @@ namespace Umbraco.Core.Packaging
contentType.Thumbnail = infoElement.Element("Thumbnail").Value;
contentType.Description = infoElement.Element("Description").Value;
//NOTE AllowAtRoot, IsListView, IsElement and Variations are new properties in the package xml so we need to verify it exists before using it.
//NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it.
var allowAtRoot = infoElement.Element("AllowAtRoot");
if (allowAtRoot != null)
contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true");
//NOTE IsListView is a new property in the package xml so we need to verify it exists before using it.
var isListView = infoElement.Element("IsListView");
if (isListView != null)
contentType.IsContainer = isListView.Value.InvariantEquals("true");
@@ -587,10 +589,6 @@ namespace Umbraco.Core.Packaging
if (isElement != null)
contentType.IsElement = isElement.Value.InvariantEquals("true");
var variationsElement = infoElement.Element("Variations");
if (variationsElement != null)
contentType.Variations = (ContentVariation)Enum.Parse(typeof(ContentVariation), variationsElement.Value);
//Name of the master corresponds to the parent and we need to ensure that the Parent Id is set
var masterElement = infoElement.Element("Master");
if (masterElement != null)
@@ -616,7 +614,7 @@ namespace Umbraco.Core.Packaging
var compositionContentType = importedContentTypes.ContainsKey(compositionAlias)
? importedContentTypes[compositionAlias]
: _contentTypeService.Get(compositionAlias);
contentType.AddContentType(compositionContentType);
var added = contentType.AddContentType(compositionContentType);
}
}
}
@@ -750,14 +748,9 @@ namespace Umbraco.Core.Packaging
{
Name = property.Element("Name").Value,
Description = (string)property.Element("Description"),
Mandatory = property.Element("Mandatory") != null
? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true")
: false,
Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false,
ValidationRegExp = (string)property.Element("Validation"),
SortOrder = sortOrder,
Variations = property.Element("Variations") != null
? (ContentVariation)Enum.Parse(typeof(ContentVariation), property.Element("Variations").Value)
: ContentVariation.Nothing
SortOrder = sortOrder
};
var tab = (string)property.Element("Tab");
@@ -174,7 +174,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
totalRecords = page.TotalItems;
var items = page.Items.Select(
dto => new AuditItem(dto.NodeId, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters)).ToList();
dto => new AuditItem(dto.Id, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId ?? Constants.Security.UnknownUserId, dto.EntityType, dto.Comment, dto.Parameters)).ToList();
// map the DateStamp
for (var i = 0; i < items.Count; i++)
@@ -404,7 +404,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (isMedia)
{
sql
.LeftJoin<MediaVersionDto>().On<ContentVersionDto, MediaVersionDto>((left, right) => left.Id == right.Id);
.InnerJoin<MediaVersionDto>().On<ContentVersionDto, MediaVersionDto>((left, right) => left.Id == right.Id);
}
//Any LeftJoin statements need to come last
@@ -250,6 +250,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
lock (_codeIdMap)
{
if (_codeIdMap.TryGetValue(isoCode, out var id)) return id;
if (isoCode.Contains('-') && _codeIdMap.TryGetValue(isoCode.Split('-').First(), out var invariantId)) return invariantId;
}
if (throwOnNotFound)
throw new ArgumentException($"Code {isoCode} does not correspond to an existing language.", nameof(isoCode));
@@ -30,7 +30,7 @@ namespace Umbraco.Core.PropertyEditors
// defaults
Type = type;
Icon = Constants.Icons.PropertyEditor;
Group = Constants.PropertyEditors.Groups.Common;
Group = "common";
// assign properties based on the attribute, if it is found
Attribute = GetType().GetCustomAttribute<DataEditorAttribute>(false);
@@ -121,7 +121,7 @@ namespace Umbraco.Core.PropertyEditors
/// Gets or sets an optional group.
/// </summary>
/// <remarks>The group can be used for example to group the editors by category.</remarks>
public string Group { get; set; } = Constants.PropertyEditors.Groups.Common;
public string Group { get; set; } = "common";
/// <summary>
/// Gets or sets a value indicating whether the value editor is deprecated.
@@ -5,11 +5,7 @@ namespace Umbraco.Core.PropertyEditors
/// <summary>
/// Represents a property editor for label properties.
/// </summary>
[DataEditor(
Constants.PropertyEditors.Aliases.Label,
"Label",
"readonlyvalue",
Icon = "icon-readonly")]
[DataEditor(Constants.PropertyEditors.Aliases.Label, "Label", "readonlyvalue", Icon = "icon-readonly")]
public class LabelPropertyEditor : DataEditor
{
/// <summary>
@@ -757,11 +757,11 @@ namespace Umbraco.Core.Services.Implement
{
var publishedState = content.PublishedState;
if (publishedState != PublishedState.Published && publishedState != PublishedState.Unpublished)
throw new InvalidOperationException($"Cannot save (un)publishing content with name: {content.Name} - and state: {content.PublishedState}, use the dedicated SavePublished method.");
throw new InvalidOperationException("Cannot save (un)publishing content, use the dedicated SavePublished method.");
if (content.Name != null && content.Name.Length > 255)
{
throw new InvalidOperationException($"Content with the name {content.Name} cannot be more than 255 characters in length.");
throw new InvalidOperationException("Name cannot be more than 255 characters in length.");
}
var evtMsgs = EventMessagesFactory.Get();
@@ -437,8 +437,7 @@ namespace Umbraco.Core.Services.Implement
new XElement("Description", contentType.Description),
new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()),
new XElement("IsListView", contentType.IsContainer.ToString()),
new XElement("IsElement", contentType.IsElement.ToString()),
new XElement("Variations", contentType.Variations.ToString()));
new XElement("IsElement", contentType.IsElement.ToString()));
var masterContentType = contentType.ContentTypeComposition.FirstOrDefault(x => x.Id == contentType.ParentId);
if(masterContentType != null)
@@ -488,8 +487,7 @@ namespace Umbraco.Core.Services.Implement
new XElement("SortOrder", propertyType.SortOrder),
new XElement("Mandatory", propertyType.Mandatory.ToString()),
propertyType.ValidationRegExp != null ? new XElement("Validation", propertyType.ValidationRegExp) : null,
propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null,
new XElement("Variations", propertyType.Variations.ToString()));
propertyType.Description != null ? new XElement("Description", new XCData(propertyType.Description)) : null);
genericProperties.Add(genericProperty);
}
@@ -97,7 +97,8 @@ namespace Umbraco.Core.Sync
? ":" + request.ServerVariables["SERVER_PORT"]
: "";
var ssl = globalSettings.UseHttps ? "s" : ""; // force, whatever the first request
var useSsl = globalSettings.UseHttps || port == "443";
var ssl = useSsl ? "s" : ""; // force, whatever the first request
var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + IOHelper.ResolveUrl(SystemDirectories.Umbraco);
return url.TrimEnd('/');
+1 -3
View File
@@ -71,7 +71,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="LightInject" Version="5.4.0" />
<PackageReference Include="LightInject" Version="5.5.0" />
<PackageReference Include="LightInject.Annotation" Version="1.1.0" />
<PackageReference Include="LightInject.Web" Version="2.0.0" />
<PackageReference Include="Microsoft.AspNet.Identity.Core">
@@ -235,7 +235,6 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\IPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\ListViewPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\MediaPickerPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\MarkdownEditorPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\NestedContentPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\PreValueMigratorCollection.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\PreValueMigratorCollectionBuilder.cs" />
@@ -243,7 +242,6 @@
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\DefaultPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\RenamingPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\RichTextPreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\DropDownFlexiblePreValueMigrator.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\MergeDateAndDateTimePropertyEditor.cs" />
<Compile Include="Models\Entities\EntityExtensions.cs" />
<Compile Include="Migrations\Upgrade\V_8_0_0\DataTypes\PreValueMigratorBase.cs" />
+18 -19
View File
@@ -12,7 +12,6 @@ using Lucene.Net.Store;
using Umbraco.Core;
using Version = Lucene.Net.Util.Version;
using Umbraco.Core.Logging;
using System.Threading;
namespace Umbraco.Examine
{
@@ -29,27 +28,29 @@ namespace Umbraco.Examine
/// </remarks>
internal static readonly Regex CultureIsoCodeFieldNameMatchExpression = new Regex("^([_\\w]+)_([a-z]{2}-[a-z0-9]{2,4})$", RegexOptions.Compiled);
private static bool _isConfigured = false;
private static object _configuredInit = null;
private static object _isConfiguredLocker = new object();
private static volatile bool _isUnlocked = false;
private static readonly object IsUnlockedLocker = new object();
/// <summary>
/// Called on startup to configure each index.
/// Unlocks all Lucene based indexes registered with the <see cref="IExamineManager"/>
/// </summary>
/// <remarks>
/// Configures and unlocks all Lucene based indexes registered with the <see cref="IExamineManager"/>.
/// Indexing rebuilding can occur on a normal boot if the indexes are empty or on a cold boot by the database server messenger. Before
/// either of these happens, we need to configure the indexes.
/// </remarks>
internal static void ConfigureIndexes(this IExamineManager examineManager, IMainDom mainDom, ILogger logger)
internal static void EnsureUnlocked(this IExamineManager examineManager, IMainDom mainDom, ILogger logger)
{
LazyInitializer.EnsureInitialized(
ref _configuredInit,
ref _isConfigured,
ref _isConfiguredLocker,
() =>
{
examineManager.ConfigureLuceneIndexes(logger, !mainDom.IsMainDom);
return null;
});
if (!mainDom.IsMainDom) return;
if (_isUnlocked) return;
lock (IsUnlockedLocker)
{
//double check
if (_isUnlocked) return;
_isUnlocked = true;
examineManager.UnlockLuceneIndexes(logger);
}
}
//TODO: We need a public method here to just match a field name against CultureIsoCodeFieldNameMatchExpression
@@ -126,7 +127,7 @@ namespace Umbraco.Examine
/// <remarks>
/// This is not thread safe, use with care
/// </remarks>
internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing)
internal static void UnlockLuceneIndexes(this IExamineManager examineManager, ILogger logger)
{
foreach (var luceneIndexer in examineManager.Indexes.OfType<LuceneIndex>())
{
@@ -135,8 +136,6 @@ namespace Umbraco.Examine
//that could end up halting shutdown for a very long time causing overlapping appdomains and many other problems.
luceneIndexer.WaitForIndexQueueOnShutdown = false;
if (disableExamineIndexing) continue; //exit if not enabled, we don't need to unlock them if we're not maindom
//we should check if the index is locked ... it shouldn't be! We are using simple fs lock now and we are also ensuring that
//the indexes are not operational unless MainDom is true
var dir = luceneIndexer.GetLuceneDirectory();
@@ -268,7 +268,7 @@ AnotherContentFinder
public void GetDataEditors()
{
var types = _typeLoader.GetDataEditors();
Assert.AreEqual(39, types.Count());
Assert.AreEqual(38, types.Count());
}
/// <summary>
@@ -79,6 +79,39 @@ namespace Umbraco.Tests.Persistence.Repositories
}
}
[Test]
public void Can_Perform_Get_By_Invariant_Code_On_LanguageRepository()
{
var provider = TestObjects.GetScopeProvider(Logger);
using (var scope = provider.CreateScope())
{
var repository = CreateRepository(provider);
var es = new CultureInfo("es");
var esSpecific = new CultureInfo("es-ES");
var language = (ILanguage)new Language(es.Name)
{
CultureName = es.DisplayName,
FallbackLanguageId = 1
};
repository.Save(language);
language = repository.GetByIsoCode(es.Name);
var languageSpecific = repository.GetByIsoCode(esSpecific.Name);
// Assert
Assert.That(language, Is.Not.Null);
Assert.That(language.HasIdentity, Is.True);
Assert.That(language.IsoCode, Is.EqualTo(es.Name));
Assert.That(languageSpecific, Is.Not.Null);
Assert.That(languageSpecific.HasIdentity, Is.True);
Assert.That(languageSpecific.Id, Is.EqualTo(language.Id));
Assert.That(language.IsoCode, Is.EqualTo(language.IsoCode));
}
}
[Test]
public void Get_When_Id_Doesnt_Exist_Returns_Null()
{
@@ -40,7 +40,7 @@ namespace Umbraco.Tests.Routing
{
base.SetUp();
WebInitialComponent.CreateRoutes(
WebFinalComponent.CreateRoutes(
new TestUmbracoContextAccessor(),
TestObjects.GetGlobalSettings(),
new SurfaceControllerTypeCollection(Enumerable.Empty<Type>()),
+1 -1
View File
@@ -83,7 +83,7 @@
<PackageReference Include="HtmlAgilityPack">
<Version>1.8.14</Version>
</PackageReference>
<PackageReference Include="LightInject" Version="5.4.0" />
<PackageReference Include="LightInject" Version="5.5.0" />
<PackageReference Include="LightInject.Annotation" Version="1.1.0" />
<PackageReference Include="Lucene.Net" Version="3.0.3" />
<PackageReference Include="Lucene.Net.Contrib" Version="3.0.3" />
+5 -23
View File
@@ -16,31 +16,13 @@ module.exports = {
//processed in the js task
js: {
preview: { files: ["./src/preview/**/*.js"], out: "umbraco.preview.js" },
installer: { files: ["./src/installer/**/*.js"], out: "umbraco.installer.js" },
installer: { files: ["./src/installer/**/*.js"], out: "umbraco.installer.js" },
controllers: { files: ["./src/{views,controllers}/**/*.controller.js"], out: "umbraco.controllers.js" },
directives: { files: ["./src/common/directives/**/*.js"], out: "umbraco.directives.js" },
filters: { files: ["./src/common/filters/**/*.js"], out: "umbraco.filters.js" },
resources: { files: ["./src/common/resources/**/*.js"], out: "umbraco.resources.js" },
services: { files: ["./src/common/services/**/*.js"], out: "umbraco.services.js" },
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" },
//the controllers for views
controllers: {
files: [
"./src/views/**/*.controller.js",
"./src/*.controller.js"
], out: "umbraco.controllers.js"
},
//directives/components
// - any JS file found in common / directives or common/ components
// - any JS file found inside views that has the suffix .directive.js or .component.js
directives: {
files: [
"./src/common/directives/_module.js",
"./src/{common/directives,common/components}/**/*.js",
"./src/views/**/*.{directive,component}.js"
],
out: "umbraco.directives.js"
},
security: { files: ["./src/common/interceptors/**/*.js"], out: "umbraco.interceptors.js" }
},
//selectors for copying all views into the build
@@ -52,7 +34,7 @@ module.exports = {
//globs for file-watching
globs:{
views: ["./src/views/**/*.html", "./src/common/directives/**/*.html", "./src/common/components/**/*.html" ],
views: "./src/views/**/*.html",
less: "./src/less/**/*.less",
js: "./src/*.js",
lib: "./lib/**/*",
+1 -4
View File
@@ -5,9 +5,6 @@ var gulp = require('gulp');
var runSequence = require('run-sequence');
// Dev - build the files ready for development and start watchers
gulp.task('dev', function (cb) {
global.isProd = false;
gulp.task('dev', function(cb) {
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});
@@ -0,0 +1,13 @@
'use strict';
var config = require('../config');
var gulp = require('gulp');
var runSequence = require('run-sequence');
// Dev - build the files ready for development and start watchers
gulp.task('fastdev', function(cb) {
global.isProd = false;
runSequence(["dependencies", "js", "less", "views"], "watch", cb);
});
@@ -9,28 +9,26 @@ var concat = require('gulp-concat');
var wrap = require("gulp-wrap-js");
var embedTemplates = require('gulp-angular-embed-templates');
module.exports = function (files, out) {
module.exports = function(files, out) {
var task = gulp.src(files);
// check for js errors
task = task.pipe(eslint());
// outputs the lint results to the console
task = task.pipe(eslint.format());
if (global.isProd === true) {
// check for js errors
task = task.pipe(eslint());
// outputs the lint results to the console
task = task.pipe(eslint.format());
}
// sort files in stream by path or any custom sort comparator
task = task.pipe(babel())
.pipe(sort());
if (global.isProd === true) {
//in production, embed the templates
task = task.pipe(embedTemplates({ basePath: "./src/", minimize: { loose: true } }))
}
task = task.pipe(concat(out))
.pipe(sort())
.pipe(embedTemplates({ basePath: "./src/", minimize:{ loose: true } }))
.pipe(concat(out))
.pipe(wrap('(function(){\n%= body %\n})();'))
.pipe(gulp.dest(config.root + config.targets.js));
return task;
};
@@ -17,12 +17,17 @@ module.exports = function(files, out) {
];
var task = gulp.src(files)
.pipe(less())
.pipe(cleanCss())
.pipe(postcss(processors))
.pipe(less());
if (global.isProd === true) {
task = task.pipe(cleanCss());
}
task = task.pipe(postcss(processors))
.pipe(rename(out))
.pipe(gulp.dest(config.root + config.targets.css));
return task;
};
};
@@ -1239,11 +1239,6 @@
// Perform the button's action.
function doClick(button) {
// don't do anything if the editor input or button bar isn't the currently active element
if (document.activeElement !== panels.input && !panels.buttonBar.contains(document.activeElement)) {
return;
}
inputBox.focus();
if (button.textOp) {
File diff suppressed because it is too large Load Diff
@@ -21,12 +21,6 @@
$scope.page.menu.currentSection = appState.getSectionState("currentSection");
$scope.page.listViewPath = null;
$scope.page.isNew = $scope.isNew ? true : false;
if (infiniteMode) {
$scope.page.allowInfinitePublishAndClose = $scope.infiniteModel.allowPublishAndClose;
$scope.page.allowInfiniteSaveAndClose = $scope.infiniteModel.allowSaveAndClose;
}
$scope.page.buttonGroupState = "init";
$scope.page.hideActionsMenu = infiniteMode ? true : false;
$scope.page.hideChangeVariant = false;
@@ -375,17 +369,12 @@
saveMethod: args.saveMethod,
scope: $scope,
content: $scope.content,
create: $scope.page.isNew,
action: args.action,
showNotifications: args.showNotifications,
softRedirect: true
}).then(function (data) {
//success
init();
//needs to be manually set for infinite editing mode
$scope.page.isNew = false;
syncTreeNode($scope.content, data.path);
eventsService.emit("content.saved", { content: $scope.content, action: args.action });
@@ -1,7 +1,7 @@
(function () {
'use strict';
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService, entityResource) {
function ContentNodeInfoDirective($timeout, logResource, eventsService, userService, localizationService, dateHelper, editorService, redirectUrlsResource, overlayService) {
function link(scope) {
@@ -16,12 +16,8 @@
scope.disableTemplates = Umbraco.Sys.ServerVariables.features.disabledFeatures.disableTemplates;
scope.allowChangeDocumentType = false;
scope.allowChangeTemplate = false;
scope.allTemplates = [];
function onInit() {
entityResource.getAll("Template").then(function (templates) {
scope.allTemplates = templates;
});
// set currentVariant
scope.currentVariant = _.find(scope.node.variants, (v) => v.active);
@@ -162,12 +158,8 @@
}
scope.openTemplate = function () {
var template = _.findWhere(scope.allTemplates, {alias: scope.node.template})
if (!template) {
return;
}
var templateEditor = {
id: template.id,
id: scope.node.templateId,
submit: function (model) {
editorService.close();
},
@@ -1,7 +1,7 @@
(function () {
'use strict';
function EditorContentHeader(serverValidationManager, localizationService, editorState) {
function EditorContentHeader(serverValidationManager) {
function link(scope, el, attr, ctrl) {
@@ -13,24 +13,7 @@
if (!scope.serverValidationAliasField) {
scope.serverValidationAliasField = "Alias";
}
scope.isNew = scope.content.state == "NotCreated";
localizationService.localizeMany([
scope.isNew ? "placeholders_a11yCreateItem" : "placeholders_a11yEdit",
"placeholders_a11yName"]
).then(function (data) {
scope.a11yMessage = data[0];
scope.a11yName = data[1];
if (!scope.isNew) {
scope.a11yMessage += " " + scope.content.name;
} else {
var name = editorState.current.contentTypeName;
scope.a11yMessage += " " + name;
scope.a11yName = name + " " + scope.a11yName;
}
});
scope.vm = {};
scope.vm.dropdownOpen = false;
scope.vm.currentVariant = "";
@@ -16,7 +16,9 @@
editor.moveRight = true;
editor.level = 0;
editor.styleIndex = 0;
editor.infinityMode = true;
// push the new editor to the dom
scope.editors.push(editor);
@@ -11,11 +11,6 @@ function hexBgColor() {
restrict: "A",
link: function (scope, element, attr, formCtrl) {
function setBackgroundColor(color) {
// note: can't use element.css(), it doesn't support hexa background colors
angular.element(element)[0].style.backgroundColor = "#" + color;
}
// Only add inline hex background color if defined and not "true".
if (attr.hexBgInline === undefined || (attr.hexBgInline !== undefined && attr.hexBgInline === "true")) {
@@ -31,21 +26,18 @@ function hexBgColor() {
// Get the orig color before changing it.
origColor = element.css("border-color");
}
// Is it a regular hex value - (#)AABBCC ?
var match = newVal.match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i);
if (match && match.length) {
setBackgroundColor(match[1]);
// Validate it - test with and without the leading hash.
if (/^([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", "#" + newVal);
return;
}
// Is it a hexa value - (#)AABBCCDD ?
match = newVal.match(/^#?([0-9a-f]{4}|[0-9a-f]{8})$/i);
if (match && match.length) {
setBackgroundColor(match[1]);
if (/^#([0-9a-f]{3}|[0-9a-f]{6})$/i.test(newVal)) {
element.css("background-color", newVal);
return;
}
}
setBackgroundColor(origColor);
element.css("background-color", origColor);
});
}
}
@@ -14,8 +14,6 @@ angular.module("umbraco.directives")
// TODO: A lot of the code below should be shared between the grid rte and the normal rte
scope.isLoading = true;
var promises = [];
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
@@ -93,10 +91,6 @@ angular.module("umbraco.directives")
//custom initialization for this editor within the grid
editor.on('init', function (e) {
// Used this init event - as opposed to property init_instance_callback
// to turn off the loader
scope.isLoading = false;
//force overflow to hidden to prevent no needed scroll
editor.getBody().style.overflow = "hidden";
@@ -1,7 +1,7 @@
(function () {
'use strict';
function MediaNodeInfoDirective($timeout, $location, eventsService, userService, dateHelper, editorService, mediaHelper) {
function MediaNodeInfoDirective($timeout, $location, eventsService, userService, dateHelper, editorService) {
function link(scope, element, attrs, ctrl) {
@@ -28,9 +28,6 @@
// make sure dates are formatted to the user's locale
formatDatesToLocal();
// set media file extension initially
setMediaExtension();
}
function formatDatesToLocal() {
@@ -52,10 +49,6 @@
}
}
function setMediaExtension() {
scope.node.extension = mediaHelper.getFileExtension(scope.nodeUrl);
}
scope.openMediaType = function (mediaType) {
var editor = {
id: mediaType.id,
@@ -69,16 +62,6 @@
editorService.mediaTypeEditor(editor);
};
scope.openSVG = function () {
var popup = window.open('', '_blank');
var html = '<!DOCTYPE html><body><img src="' + scope.nodeUrl + '"/>' +
'<script>history.pushState(null, null,"' + $location.$$absUrl + '");</script></body>';
popup.document.open();
popup.document.write(html);
popup.document.close();
}
// watch for content updates - reload content when node is saved, published etc.
scope.$watch('node.updateDate', function(newValue, oldValue){
if(!newValue) { return; }
@@ -89,9 +72,6 @@
// Update the create and update dates
formatDatesToLocal();
//Update the media file format
setMediaExtension();
});
//ensure to unregister from all events!
@@ -1,13 +1,13 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbDateTimePicker
@name umbraco.directives.directive:umbFlatpickr
@restrict E
@scope
@description
<b>Added in Umbraco version 8.0</b>
This directive is a wrapper of the flatpickr library. Use it to render a date time picker.
For extra details about options and events take a look here: https://flatpickr.js.org
For extra details about options and events take a look here: https://flatpickr.js.org/
Use this directive to render a date time picker
@@ -15,11 +15,11 @@ Use this directive to render a date time picker
<pre>
<div ng-controller="My.Controller as vm">
<umb-date-time-picker
<umb-flatpickr
ng-model="vm.date"
options="vm.config"
on-change="vm.datePickerChange(selectedDates, dateStr, instance)">
</umb-date-time-picker>
</umb-flatpickr>
</div>
</pre>
@@ -70,12 +70,12 @@ Use this directive to render a date time picker
(function() {
'use strict';
var umbDateTimePicker = {
var umbFlatpickr = {
template: '<ng-transclude>' +
'<input type="text" ng-if="!$ctrl.options.inline" ng-model="$ctrl.ngModel" placeholder="Select Date.."></input>' +
'<div ng-if="$ctrl.options.inline"></div>' +
'</ng-transclude>',
controller: umbDateTimePickerCtrl,
controller: umbFlatpickrCtrl,
transclude: true,
bindings: {
ngModel: '<',
@@ -92,9 +92,9 @@ Use this directive to render a date time picker
}
};
function umbDateTimePickerCtrl($element, $timeout, $scope, assetsService, userService) {
function umbFlatpickrCtrl($element, $timeout, $scope, assetsService, userService) {
var ctrl = this;
var loaded = false;
var userLocale = null;
ctrl.$onInit = function() {
@@ -102,14 +102,14 @@ Use this directive to render a date time picker
// load css file for the date picker
assetsService.loadCss('lib/flatpickr/flatpickr.css', $scope).then(function () {
userService.getCurrentUser().then(function (user) {
// init date picker
userLocale = user.locale;
if (userLocale.indexOf('-') > -1) {
userLocale = userLocale.split('-')[0];
}
loaded = true;
grabElementAndRunFlatpickr();
});
});
@@ -234,8 +234,7 @@ Use this directive to render a date time picker
}
}
// umbFlatpickr (umb-flatpickr) is deprecated, but we keep it for backwards compatibility
angular.module('umbraco.directives').component('umbFlatpickr', umbDateTimePicker);
angular.module('umbraco.directives').component('umbDateTimePicker', umbDateTimePicker);
angular.module('umbraco.directives').component('umbFlatpickr', umbFlatpickr);
})();
@@ -17,7 +17,6 @@
scope.sortableOptionsGroup = {};
scope.sortableOptionsProperty = {};
scope.sortingButtonKey = "general_reorder";
scope.compositionsButtonState = "init";
function activate() {
@@ -338,7 +337,6 @@
})), function(f) {
return f !== null && f !== undefined;
});
scope.compositionsButtonState = "busy";
$q.all([
//get available composite types
availableContentTypeResource(scope.model.id, [], propAliasesExisting).then(function (result) {
@@ -358,7 +356,6 @@
]).then(function() {
//resolves when both other promises are done, now show it
editorService.open(scope.compositionsDialogModel);
scope.compositionsButtonState = "init";
});
};
@@ -535,7 +532,6 @@
// push new init tab to the scope
addInitGroup(scope.model.groups);
notifyChanged();
},
close: function() {
if(_.isEqual(oldPropertyModel, propertyModel) === false) {
@@ -592,13 +588,8 @@
}
notifyChanged();
};
function notifyChanged() {
eventsService.emit("editors.groupsBuilder.changed");
}
function addInitProperty(group) {
var addInitPropertyBool = true;
@@ -1,75 +0,0 @@
/**
@ngdoc directive
@name umbraco.directives.directive:umbLoader
@restrict E
@description
Use this directive to generate a loading indicator.
<h3>Markup example</h3>
<pre>
<div ng-controller="My.Controller as vm">
<umb-loader
ng-if="vm.loading">
</umb-loader>
<div class="content" ng-if="!vm.loading">
<p>{{content}}</p>
</div>
</div>
</pre>
<h3>Controller example</h3>
<pre>
(function () {
"use strict";
function Controller(myService) {
var vm = this;
vm.content = "";
vm.loading = true;
myService.getContent().then(function(content){
vm.content = content;
vm.loading = false;
});
}
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@param {string=} position The loader position ("top", "bottom").
**/
(function() {
'use strict';
function UmbLoaderDirective() {
function link(scope, el, attr, ctrl) {
}
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/umb-loader.html',
scope: {
position: "@?"
},
link: link
};
return directive;
}
angular.module('umbraco.directives').directive('umbLoader', UmbLoaderDirective);
})();
@@ -39,7 +39,7 @@ Use this directive to generate a loading indicator.
});
}
½
angular.module("umbraco").controller("My.Controller", Controller);
})();
</pre>
@@ -4,7 +4,7 @@
@restrict A
@description
Use this directive make an element sticky and follow the page when scrolling. `umb-sticky-bar--active` class is applied when the element is stuck
Use this directive make an element sticky and follow the page when scrolling.
<h3>Markup example</h3>
<pre>
@@ -12,102 +12,140 @@ Use this directive make an element sticky and follow the page when scrolling. `u
<div
class="my-sticky-bar"
umb-sticky-bar>
umb-sticky-bar
scrollable-container=".container">
</div>
</div>
</pre>
<h3>CSS example</h3>
<pre>
.my-sticky-bar {
padding: 15px 0;
background: #000000;
position: relative;
top: 0;
}
.my-sticky-bar.-umb-sticky-bar {
top: 100px;
}
</pre>
@param {string} scrollableContainer Set the class (".element") or the id ("#element") of the scrollable container element.
**/
(function () {
'use strict';
function StickyBarDirective() {
/**
On initial load, the intersector fires if the grid editor is in the viewport
This flag is used to suppress the setClass behaviour on the initial load
**/
var initial = true;
/**
Toggle `umb-sticky-bar--active` class on the sticky-bar element
**/
function setClass(addClass, current) {
if (!initial) {
current.classList.toggle('umb-sticky-bar--active', addClass);
} else {
initial = false;
}
}
/**
Inserts two elements in the umbStickyBar parent element
These are used by the IntersectionObserve to calculate scroll position
**/
function addSentinels(current) {
['-top', '-bottom'].forEach(s => {
const sentinel = document.createElement('div');
sentinel.classList.add('umb-sticky-sentinel', s);
current.parentElement.appendChild(sentinel);
});
}
/**
Calls into setClass when the footer sentinel enters/exits the bottom of the container
Container is the parent element of the umbStickyBar element
**/
function observeFooter(current, container) {
const observer = new IntersectionObserver((records, observer) => {
let [target, rootBounds, intersected] = [records[0].boundingClientRect, records[0].rootBounds, records[0].intersectionRatio === 1];
if (target.bottom > rootBounds.top && intersected) {
setClass(true, current);
}
if (target.top < rootBounds.top && target.bottom < rootBounds.bottom) {
setClass(false, current);
}
}, {
threshold: [1],
root: container
});
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-bottom'));
}
/**
Calls into setClass when the header sentinel enters/exits the top of the container
Container is the parent element of the umbStickyBar element
**/
function observeHeader(current, container) {
const observer = new IntersectionObserver((records, observer) => {
let [target, rootBounds] = [records[0].boundingClientRect, records[0].rootBounds];
if (target.bottom < rootBounds.top) {
setClass(true, current);
}
if (target.bottom >= rootBounds.top && target.bottom < rootBounds.bottom) {
setClass(false, current);
}
}, {
threshold: [0],
root: container
});
observer.observe(current.parentElement.querySelector('.umb-sticky-sentinel.-top'));
}
function StickyBarDirective($rootScope) {
function link(scope, el, attr, ctrl) {
let current = el[0];
let container = current.closest('[data-element="editor-container"]');
var bar = $(el);
var scrollableContainer = null;
var clonedBar = null;
var cloneIsMade = false;
addSentinels(current);
function activate() {
if (bar.parents(".umb-property").length > 1) {
bar.addClass("nested");
return;
}
if (attr.scrollableContainer) {
scrollableContainer = bar.closest(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
scrollableContainer.on('scroll.umbStickyBar', determineVisibility).trigger("scroll");
$(window).on('resize.umbStickyBar', determineVisibility);
scope.$on('$destroy', function () {
scrollableContainer.off('.umbStickyBar');
$(window).off('.umbStickyBar');
});
}
function determineVisibility() {
var barTop = bar[0].offsetTop;
var scrollTop = scrollableContainer.scrollTop();
if (scrollTop > barTop) {
if (!cloneIsMade) {
createClone();
clonedBar.css({
'visibility': 'visible'
});
} else {
calculateSize();
}
} else {
if (cloneIsMade) {
//remove cloned element (switched places with original on creation)
bar.remove();
bar = clonedBar;
clonedBar = null;
bar.removeClass('-umb-sticky-bar');
bar.css({
position: 'relative',
'width': 'auto',
'height': 'auto',
'z-index': 'auto',
'visibility': 'visible'
});
cloneIsMade = false;
}
}
}
function calculateSize() {
var width = bar.innerWidth();
clonedBar.css({
width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS.
});
}
function createClone() {
//switch place with cloned element, to keep binding intact
clonedBar = bar;
bar = clonedBar.clone();
clonedBar.after(bar);
clonedBar.addClass('-umb-sticky-bar');
clonedBar.css({
'position': 'fixed',
// if you change this z-index value, make sure the sticky editor sub headers do not
// clash with umb-dropdown (e.g. the content actions dropdown in content list view)
'z-index': 99,
'visibility': 'hidden'
});
cloneIsMade = true;
calculateSize();
}
activate();
observeHeader(current, container);
observeFooter(current, container);
}
var directive = {
@@ -1,172 +1,167 @@
(function () {
'use strict';
'use strict';
function ChangePasswordController($scope) {
function ChangePasswordController($scope) {
var vm = this;
function resetModel(isNew) {
//the model config will contain an object, if it does not we'll create defaults
//NOTE: We will not support doing the password regex on the client side because the regex on the server side
//based on the membership provider cannot always be ported to js from .net directly.
/*
{
hasPassword: true/false,
requiresQuestionAnswer: true/false,
enableReset: true/false,
enablePasswordRetrieval: true/false,
minPasswordLength: 10
}
*/
vm.$onInit = onInit;
vm.$onDestroy = onDestroy;
vm.doChange = doChange;
vm.cancelChange = cancelChange;
vm.showOldPass = showOldPass;
vm.showCancelBtn = showCancelBtn;
$scope.showReset = false;
var unsubscribe = [];
function resetModel(isNew) {
//the model config will contain an object, if it does not we'll create defaults
//NOTE: We will not support doing the password regex on the client side because the regex on the server side
//based on the membership provider cannot always be ported to js from .net directly.
/*
{
hasPassword: true/false,
requiresQuestionAnswer: true/false,
enableReset: true/false,
enablePasswordRetrieval: true/false,
minPasswordLength: 10
}
*/
vm.showReset = false;
//set defaults if they are not available
if (vm.config.disableToggle === undefined) {
vm.config.disableToggle = false;
}
if (vm.config.hasPassword === undefined) {
vm.config.hasPassword = false;
}
if (vm.config.enablePasswordRetrieval === undefined) {
vm.config.enablePasswordRetrieval = true;
}
if (vm.config.requiresQuestionAnswer === undefined) {
vm.config.requiresQuestionAnswer = false;
}
//don't enable reset if it is new - that doesn't make sense
if (isNew === "true") {
vm.config.enableReset = false;
}
else if (vm.config.enableReset === undefined) {
vm.config.enableReset = true;
}
if (vm.config.minPasswordLength === undefined) {
vm.config.minPasswordLength = 0;
}
//set the model defaults
if (!angular.isObject(vm.passwordValues)) {
//if it's not an object then just create a new one
vm.passwordValues = {
newPassword: null,
oldPassword: null,
reset: null,
answer: null
};
}
else {
//just reset the values
if (!isNew) {
//if it is new, then leave the generated pass displayed
vm.passwordValues.newPassword = null;
vm.passwordValues.oldPassword = null;
}
vm.passwordValues.reset = null;
vm.passwordValues.answer = null;
}
//the value to compare to match passwords
if (!isNew) {
vm.passwordValues.confirm = "";
}
else if (vm.passwordValues.newPassword && vm.passwordValues.newPassword.length > 0) {
//if it is new and a new password has been set, then set the confirm password too
vm.passwordValues.confirm = vm.passwordValues.newPassword;
}
//set defaults if they are not available
if ($scope.config.disableToggle === undefined) {
$scope.config.disableToggle = false;
}
if ($scope.config.hasPassword === undefined) {
$scope.config.hasPassword = false;
}
if ($scope.config.enablePasswordRetrieval === undefined) {
$scope.config.enablePasswordRetrieval = true;
}
if ($scope.config.requiresQuestionAnswer === undefined) {
$scope.config.requiresQuestionAnswer = false;
}
//don't enable reset if it is new - that doesn't make sense
if (isNew === "true") {
$scope.config.enableReset = false;
}
else if ($scope.config.enableReset === undefined) {
$scope.config.enableReset = true;
}
if ($scope.config.minPasswordLength === undefined) {
$scope.config.minPasswordLength = 0;
}
//set the model defaults
if (!angular.isObject($scope.passwordValues)) {
//if it's not an object then just create a new one
$scope.passwordValues = {
newPassword: null,
oldPassword: null,
reset: null,
answer: null
};
}
else {
//just reset the values
if (!isNew) {
//if it is new, then leave the generated pass displayed
$scope.passwordValues.newPassword = null;
$scope.passwordValues.oldPassword = null;
}
$scope.passwordValues.reset = null;
$scope.passwordValues.answer = null;
}
//when the scope is destroyed we need to unsubscribe
function onDestroy() {
for (var u in unsubscribe) {
unsubscribe[u]();
}
}
function onInit() {
//listen for the saved event, when that occurs we'll
//change to changing = false;
unsubscribe.push($scope.$on("formSubmitted", function () {
if (vm.config.disableToggle === false) {
vm.changing = false;
}
}));
unsubscribe.push($scope.$on("formSubmitting", function () {
//if there was a previously generated password displaying, clear it
if (vm.changing && vm.passwordValues) {
vm.passwordValues.generatedPassword = null;
}
else if (!vm.changing) {
//we are not changing, so the model needs to be null
vm.passwordValues = null;
}
}));
resetModel(vm.isNew);
//if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there
//with validators turned on.
vm.changing = vm.config.disableToggle === true || !vm.config.hasPassword;
//we're not currently changing so set the model to null
if (!vm.changing) {
vm.passwordValues = null;
}
}
function doChange() {
resetModel();
vm.changing = true;
//if there was a previously generated password displaying, clear it
vm.passwordValues.generatedPassword = null;
vm.passwordValues.confirm = null;
};
function cancelChange() {
vm.changing = false;
//set model to null
vm.passwordValues = null;
};
function showOldPass() {
return vm.config.hasPassword &&
!vm.config.allowManuallyChangingPassword &&
!vm.config.enablePasswordRetrieval && !vm.showReset;
};
// TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this component
function showCancelBtn() {
return vm.config.disableToggle !== true && vm.config.hasPassword;
};
//the value to compare to match passwords
if (!isNew) {
$scope.passwordValues.confirm = "";
}
else if ($scope.passwordValues.newPassword && $scope.passwordValues.newPassword.length > 0) {
//if it is new and a new password has been set, then set the confirm password too
$scope.passwordValues.confirm = $scope.passwordValues.newPassword;
}
}
var component = {
templateUrl: 'views/components/users/change-password.html',
controller: ChangePasswordController,
controllerAs: 'vm',
bindings: {
isNew: "<",
passwordValues: "=", //TODO: Do we need bi-directional vals?
config: "=" //TODO: Do we need bi-directional vals?
//TODO: Do we need callbacks?
}
resetModel($scope.isNew);
//if there is no password saved for this entity , it must be new so we do not allow toggling of the change password, it is always there
//with validators turned on.
$scope.changing = $scope.config.disableToggle === true || !$scope.config.hasPassword;
//we're not currently changing so set the model to null
if (!$scope.changing) {
$scope.passwordValues = null;
}
$scope.doChange = function () {
resetModel();
$scope.changing = true;
//if there was a previously generated password displaying, clear it
$scope.passwordValues.generatedPassword = null;
$scope.passwordValues.confirm = null;
};
angular.module('umbraco.directives').component('changePassword', component);
$scope.cancelChange = function () {
$scope.changing = false;
//set model to null
$scope.passwordValues = null;
};
var unsubscribe = [];
//listen for the saved event, when that occurs we'll
//change to changing = false;
unsubscribe.push($scope.$on("formSubmitted", function () {
if ($scope.config.disableToggle === false) {
$scope.changing = false;
}
}));
unsubscribe.push($scope.$on("formSubmitting", function () {
//if there was a previously generated password displaying, clear it
if ($scope.changing && $scope.passwordValues) {
$scope.passwordValues.generatedPassword = null;
}
else if (!$scope.changing) {
//we are not changing, so the model needs to be null
$scope.passwordValues = null;
}
}));
//when the scope is destroyed we need to unsubscribe
$scope.$on('$destroy', function () {
for (var u in unsubscribe) {
unsubscribe[u]();
}
});
$scope.showOldPass = function () {
return $scope.config.hasPassword &&
!$scope.config.allowManuallyChangingPassword &&
!$scope.config.enablePasswordRetrieval && !$scope.showReset;
};
// TODO: I don't think we need this or the cancel button, this can be up to the editor rendering this directive
$scope.showCancelBtn = function () {
return $scope.config.disableToggle !== true && $scope.config.hasPassword;
};
}
function ChangePasswordDirective() {
var directive = {
restrict: 'E',
replace: true,
templateUrl: 'views/components/users/change-password.html',
controller: 'Umbraco.Editors.Users.ChangePasswordDirectiveController',
scope: {
isNew: "=?",
passwordValues: "=",
config: "="
}
};
return directive;
}
angular.module('umbraco.directives').controller('Umbraco.Editors.Users.ChangePasswordDirectiveController', ChangePasswordController);
angular.module('umbraco.directives').directive('changePassword', ChangePasswordDirective);
})();
@@ -104,26 +104,21 @@ function entityResource($q, $http, umbRequestHelper) {
*
* @param {Int} id Id of node to return the public url to
* @param {string} type Object type name
* @param {string} culture Culture
* @returns {Promise} resourcePromise object containing the url.
*
*/
getUrl: function (id, type, culture) {
getUrl: function (id, type) {
if (id === -1 || id === "-1") {
return "";
}
if (!culture) {
culture = "";
}
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetUrl",
[{ id: id }, {type: type }, {culture: culture }])),
[{ id: id }, {type: type }])),
'Failed to retrieve url for id:' + id);
},
@@ -138,7 +133,7 @@ function entityResource($q, $http, umbRequestHelper) {
* ##usage
* <pre>
* //get media by id
* entityResource.getById(0, "Media")
* entityResource.getEntityById(0, "Media")
* .then(function(ent) {
* var myDoc = ent;
* alert('its here!');
@@ -177,7 +172,7 @@ function entityResource($q, $http, umbRequestHelper) {
umbRequestHelper.getApiUrl(
"entityApiBaseUrl",
"GetUrlAndAnchors",
[{ id: id }])),
{ id: id })),
'Failed to retrieve url and anchors data for id ' + id);
},
@@ -209,7 +204,7 @@ function entityResource($q, $http, umbRequestHelper) {
* ##usage
* <pre>
* //Get templates for ids
* entityResource.getByIds( [1234,2526,28262], "Template")
* entityResource.getEntitiesByIds( [1234,2526,28262], "Template")
* .then(function(templateArray) {
* var myDoc = contentArray;
* alert('they are here!');
@@ -7,44 +7,64 @@
**/
function logViewerResource($q, $http, umbRequestHelper) {
/**
* verb => 'get', 'post',
* method => API method to call
* params => additional data to send
* error => error message when things go wrong...
*/
const request = (verb, method, params, error) =>
umbRequestHelper.resourcePromise(
(verb === 'GET' ?
$http.get(umbRequestHelper.getApiUrl("logViewerApiBaseUrl", method) + (params ? params : '')) :
$http.post(umbRequestHelper.getApiUrl("logViewerApiBaseUrl", method), params)),
error);
//the factory object returned
return {
getNumberOfErrors: (startDate, endDate) =>
request('GET', 'GetNumberOfErrors', '?startDate=' + startDate + '&endDate=' + endDate, 'Failed to retrieve number of errors in logs'),
getNumberOfErrors: function (startDate, endDate) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"GetNumberOfErrors")+ '?startDate='+startDate+ '&endDate='+ endDate ),
'Failed to retrieve number of errors in logs');
},
getLogLevel: () =>
request('GET', 'GetLogLevel', null, 'Failed to retrieve log level'),
getLogLevelCounts: function (startDate, endDate) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"GetLogLevelCounts")+ '?startDate='+startDate+ '&endDate='+ endDate ),
'Failed to retrieve log level counts');
},
getLogLevelCounts: (startDate, endDate) =>
request('GET', 'GetLogLevelCounts', '?startDate=' + startDate + '&endDate=' + endDate, 'Failed to retrieve log level counts'),
getMessageTemplates: function (startDate, endDate) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"GetMessageTemplates")+ '?startDate='+startDate+ '&endDate='+ endDate ),
'Failed to retrieve log templates');
},
getMessageTemplates: (startDate, endDate) =>
request('GET', 'GetMessageTemplates', '?startDate=' + startDate + '&endDate=' + endDate, 'Failed to retrieve log templates'),
getSavedSearches: function () {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"GetSavedSearches")),
'Failed to retrieve saved searches');
},
getSavedSearches: () =>
request('GET', 'GetSavedSearches', null, 'Failed to retrieve saved searches'),
postSavedSearch: function (name, query) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"PostSavedSearch"), { 'name': name, 'query': query }),
'Failed to add new saved search');
},
postSavedSearch: (name, query) =>
request('POST', 'PostSavedSearch', { 'name': name, 'query': query }, 'Failed to add new saved search'),
deleteSavedSearch: function (name, query) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"DeleteSavedSearch"), { 'name': name, 'query': query }),
'Failed to delete saved search');
},
deleteSavedSearch: (name, query) =>
request('POST', 'DeleteSavedSearch', { 'name': name, 'query': query }, 'Failed to delete saved search'),
getLogs: options => {
getLogs: function (options) {
var defaults = {
pageSize: 100,
@@ -63,6 +83,7 @@ function logViewerResource($q, $http, umbRequestHelper) {
//now copy back to the options we will use
options = defaults;
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -72,8 +93,15 @@ function logViewerResource($q, $http, umbRequestHelper) {
'Failed to retrieve common log messages');
},
canViewLogs: (startDate, endDate) =>
request('GET', 'GetCanViewLogs', '?startDate=' + startDate + '&endDate=' + endDate, 'Failed to retrieve state if logs can be viewed')
canViewLogs: function (startDate, endDate) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"logViewerApiBaseUrl",
"GetCanViewLogs") + '?startDate='+startDate+ '&endDate='+ endDate ),
'Failed to retrieve state if logs can be viewed');
}
};
}
@@ -1,4 +1,4 @@
/**
* @ngdoc service
* @name umbraco.services.contentEditingHelper
@@ -53,12 +53,6 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
}
if (args.showNotifications === undefined) {
args.showNotifications = true;
}
// needed for infinite editing to create new items
if (args.create === undefined) {
if ($routeParams.create) {
args.create = true;
}
}
if (args.softRedirect === undefined) {
//when true, the url will change but it won't actually re-route
@@ -67,7 +61,6 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
args.softRedirect = false;
}
var self = this;
//we will use the default one for content if not specified
@@ -75,7 +68,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
if (formHelper.submitForm({ scope: args.scope, action: args.action })) {
return args.saveMethod(args.content, args.create, fileManager.getFiles(), args.showNotifications)
return args.saveMethod(args.content, $routeParams.create, fileManager.getFiles(), args.showNotifications)
.then(function (data) {
formHelper.resetForm({ scope: args.scope });
@@ -491,7 +484,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
var savedVariants = [];
if (origContent.variants) {
isContent = true;
//it's content so assign the variants as they exist
//it's contnet so assign the variants as they exist
origVariants = origContent.variants;
savedVariants = savedContent.variants;
}
@@ -517,7 +510,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, editorSt
//special case for content, don't sync this variant if it wasn't tagged
//for saving in the first place
if (isContent && !origVariant.save) {
if (!origVariant.save) {
continue;
}
@@ -261,7 +261,7 @@ When building a custom infinite editor view you can use the same components as a
*/
unbindKeyboardShortcuts();
// set flag so we know when the editor is open in "infinite mode"
// set flag so we know when the editor is open in "infinie mode"
editor.infiniteMode = true;
editors.push(editor);
@@ -331,17 +331,13 @@ When building a custom infinite editor view you can use the same components as a
* @methodOf umbraco.services.editorService
*
* @description
* Opens a content editor in infinite editing, the submit callback returns the updated content item
* Opens a media editor in infinite editing, the submit callback returns the updated content item
* @param {Object} editor rendering options
* @param {String} editor.id The id of the content item
* @param {Boolean} editor.create Create new content item
* @param {Function} editor.submit Callback function when the publish and close button is clicked. Returns the editor model object
* @param {Function} editor.close Callback function when the close button is clicked.
* @param {String} editor.parentId If editor.create is true, provide parentId for the creation of the content item
* @param {String} editor.documentTypeAlias If editor.create is true, provide document type alias for the creation of the content item
* @param {Boolean} editor.allowSaveAndClose If editor is being used in infinite editing allows the editor to close when the save action is performed
* @param {Boolean} editor.allowPublishAndClose If editor is being used in infinite editing allows the editor to close when the publish action is performed
*
*
* @returns {Object} editor object
*/
function contentEditor(editor) {
@@ -10,7 +10,7 @@
*/
function MainController($scope, $location, appState, treeService, notificationsService,
userService, historyService, updateChecker, navigationService, eventsService,
tmhDynamicLocale, localStorageService, editorService, overlayService, assetsService, tinyMceAssets) {
tmhDynamicLocale, localStorageService, editorService, overlayService) {
//the null is important because we do an explicit bool check on this in the view
$scope.authenticated = null;
@@ -21,13 +21,7 @@ function MainController($scope, $location, appState, treeService, notificationsS
$scope.search = {};
$scope.login = {};
$scope.tabbingActive = false;
// Load TinyMCE assets ahead of time in the background for the user
// To help with first load of the RTE
tinyMceAssets.forEach(function (tinyJsAsset) {
assetsService.loadJs(tinyJsAsset, $scope);
});
// There are a number of ways to detect when a focus state should be shown when using the tab key and this seems to be the simplest solution.
// For more information about this approach, see https://hackernoon.com/removing-that-ugly-focus-ring-and-keeping-it-too-6c8727fefcd2
function handleFirstTab(evt) {
@@ -112,7 +112,7 @@
@import "components/umb-editor-navigation-item.less";
@import "components/umb-editor-sub-views.less";
@import "components/editor/subheader/umb-editor-sub-header.less";
@import "components/umb-date-time-picker.less";
@import "components/umb-flatpickr.less";
@import "components/umb-grid-selector.less";
@import "components/umb-child-selector.less";
@import "components/umb-group-builder.less";
@@ -124,7 +124,6 @@
@import "components/umb-form-check.less";
@import "components/umb-locked-field.less";
@import "components/umb-tabs.less";
@import "components/umb-loader.less";
@import "components/umb-load-indicator.less";
@import "components/umb-breadcrumbs.less";
@import "components/umb-media-grid.less";
@@ -1,12 +1,8 @@
.umb-loader-wrapper.umb-tour__loader {
margin: 0;
position: fixed;
.umb-tour__loader {
background: @white;
z-index: @zindexTourModal;
.umb-loader {
background-color: @white;
height: 5px;
}
position: fixed;
height: 5px;
}
.umb-tour__pulse {
@@ -74,11 +74,8 @@
}
.umb-editor-header__back {
background: transparent;
border: 0;
color: @gray-6;
margin: 0 0 1px 0;
padding: 0;
color: @gray-6;
margin-bottom: 1px;
transition: color 0.1s ease-in-out;
}
@@ -376,3 +373,14 @@ a.umb-variant-switcher__toggle {
margin-right: auto;
padding-right: 10px;
}
/* Confirm */
.umb-editor-confirm {
background-color: @white;
padding: 20px;
position: absolute;
left: 0;
bottom: 0;
z-index: 10;
box-shadow: 0 -3px 12px 0px rgba(0,0,0,0.16);
}
@@ -32,32 +32,15 @@
border-radius: 3px;
}
[umb-sticky-bar] {
.umb-editor-sub-header.-umb-sticky-bar {
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
transition: box-shadow 240ms;
margin-top: 0;
margin-top: 0;
margin-bottom: 0;
position:sticky;
z-index: 99;
top: calc(@appHeaderHeight + @editorHeaderHeight);
&.umb-sticky-bar--active {
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
}
}
.umb-sticky-sentinel {
position: absolute;
left: 0;
width: 100%;
pointer-events: none;
&.-top {
top:0px;
height:1px;
}
&.-bottom {
bottom:50px;
height:10px;
.umb-editor--infinityMode & {
top: calc(@editorHeaderHeight);
}
}
@@ -17,7 +17,7 @@
z-index: @zIndexEditor;
}
.umb-editor--infiniteMode {
.umb-editor--infinityMode {
transform: none;
will-change: transform;
transition: transform 400ms ease-in-out;
@@ -1,5 +1,5 @@
.umb-notifications {
z-index: 1100;
z-index: 1000;
position: absolute;
bottom: @editorFooterHeight;
left: 0;
@@ -39,10 +39,17 @@
// Loading Animation
// ------------------------
.umb-tree-item__loader {
.l {
width: 100%;
height: 2px;
overflow: hidden;
position: absolute;
margin: 0;
left: 0;
bottom: -1px;
div {
.umb-loader;
}
}
.umb-tree-item__label {
@@ -50,20 +57,19 @@
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1 1 auto;
flex: 1 0 auto;
}
}
// active is equivilant to selected, its the item that is begin affected by the actions performed in the right-click-dialog.
.umb-tree-item.active > .umb-tree-item__inner {
border-color: @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
color: @ui-selected-type;
a {
color: @ui-selected-type;
}
border-color: @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
&::before {
content: "";
position: absolute;
@@ -73,10 +79,8 @@
bottom: 0;
border: 2px solid fade(white, 80%);
}
&:hover {
color: @ui-selected-type-hover;
a {
color: @ui-selected-type-hover;
}
@@ -84,6 +88,7 @@
}
.umb-tree-item.current > .umb-tree-item__inner {
background: @ui-active;
color:@ui-active-type;
@@ -33,6 +33,12 @@
display: none;
}
}
//loader defaults
.umb-loader {
height: 10px;
margin: 10px 10px 10px 10px;
}
.search-subtitle {
color: @gray-7;
@@ -66,7 +72,8 @@ body.touch .umb-tree {
overflow: hidden;
display: flex;
flex-wrap: nowrap;
align-items: center;
align-items: center;
border:2px solid transparent;
color: @ui-option-type;
@@ -176,6 +183,7 @@ body.touch .umb-tree {
&:hover {
background: @btnBackgroundHighlight;
}
// 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 {
@@ -1,14 +1,12 @@
@boxUnit: 10px;
.umb-box {
background: @white;
border-radius: 3px;
margin-bottom: @boxUnit * 2;
margin-bottom: 20px;
box-shadow: 0 1px 1px 0 rgba(0,0,0,.16);
}
.umb-box-header {
padding: @boxUnit @boxUnit * 2;
padding: 10px 20px;
border-bottom: 1px solid @gray-9;
display: flex;
align-items: center;
@@ -29,19 +27,5 @@
}
.umb-box-content {
padding: @boxUnit * 2;
padding: 20px;
}
// allow side-by-side boxes
.umb-box-row {
margin-left:-@boxUnit;
margin-right:-@boxUnit;
display:flex;
justify-content: space-around;
.umb-box {
margin-left:@boxUnit;
margin-right:@boxUnit;
flex:1;
}
}
@@ -14,18 +14,15 @@
margin: 0 0 20px 0;
position: relative;
transition: height 0.8s;
.illustration {
width: 300px;
}
&.is-small {
height: 100px;
.illustration {
width: 200px;
}
}
&.drag-over {
border: 1px dashed @gray-1;
}
@@ -38,19 +35,15 @@
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
display: flex;
flex-direction: column;
}
// file select link
.file-select {
background: transparent;
border: 0;
padding: 0;
font-size: 15px;
color: @ui-action-discreet-type;
cursor: pointer;
margin-top: 10px;
&:hover {
@@ -1,4 +1,4 @@
.flatpickr-calendar {
.flatpickr-calendar.flatpickr-calendar {
border-radius: @baseBorderRadius;
box-shadow: 0 5px 10px 0 rgba(0,0,0,0.16);
}
@@ -1,42 +0,0 @@
// Loading Animation
// ------------------------
.umb-loader {
background-color: @blue;
margin-top: 0;
margin-left: -100%;
animation-name: bounce_loadingProgressG;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-timing-function: linear;
width: 100%;
height: 2px;
}
@keyframes bounce_loadingProgressG {
0% {
margin-left: -100%;
}
100% {
margin-left: 100%;
}
}
.umb-loader-wrapper {
position: absolute;
right: 0;
left: 0;
margin: 10px 0;
overflow: hidden;
}
.umb-loader-wrapper.-top {
top: 0;
bottom: auto;
}
.umb-loader-wrapper.-bottom {
top: auto;
bottom: 0;
}
@@ -264,16 +264,12 @@
flex-flow: row wrap;
}
.umb-package-details__back-action {
a.umb-package-details__back-link {
font-weight: bold;
color: @black;
padding: 0;
border: 0;
background-color: transparent;
}
.umb-package-details__back-action:focus,
.umb-package-details__back-action:hover {
.umb-package-details__back-link:hover {
color: @gray-4;
text-decoration: none;
}
@@ -295,11 +295,7 @@ input.umb-table__input {
.umb-table__row-expand {
font-size: 12px;
text-decoration: none;
color: @gray-4;
&:hover {
color: @black;
}
color: @black;
}
.umb-table__row-expand--hidden {
@@ -13,9 +13,6 @@
@import "../../lib/bootstrap/less/thumbnails.less";
@import "../../lib/bootstrap/less/media.less";
// Umbraco Components
@import "components/umb-loader.less";
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
@@ -168,17 +165,45 @@ input.ng-dirty.ng-invalid{border-color: #b94a48; color: #b94a48;}
opacity:1;
}
.umb-installer-loader {
margin: 0;
width: 0;
z-index: 777;
.umb-loader {
background-color: @white;
height: 5px;
.umb-loader{
background-color: @white;
margin-top:0;
margin-left:-100%;
animation-name:bounce_loadingProgressG;
animation-duration:1s;
animation-iteration-count:infinite;
animation-timing-function:linear;
width:100%;
height: 5px;
}
@keyframes bounce_loadingProgressG{
0%{
margin-left:-100%;
}
100%{
margin-left:100%;
}
}
//loader defaults
.umb-loader-container, .umb-loader-done{
height: 3px;
position: absolute;
bottom: 0;
left: 0;
overflow: hidden;
width: 0%;
z-index: 777;
}
.umb-loader-done{
right: 0%;
background: @white;
}
.permissions-report {
overflow:auto;
height:320px;
@@ -486,6 +486,42 @@ table thead a {
color:@green;
}
// Loading Animation
// ------------------------
.umb-loader{
background-color: @blue;
margin-top:0;
margin-left:-100%;
animation-name:bounce_loadingProgressG;
animation-duration:1s;
animation-iteration-count:infinite;
animation-timing-function:linear;
width:100%;
height:2px;
}
@keyframes bounce_loadingProgressG{
0%{
margin-left:-100%;
}
100%{
margin-left:100%;
}
}
.umb-loader-wrapper {
position: absolute;
right: 0;
left: 0;
margin: 10px 0;
overflow: hidden;
}
.umb-loader-wrapper.-bottom {
bottom: 0;
}
// Helpers
.strong {
@@ -556,3 +592,13 @@ input[type=checkbox]:checked + .input-label--small {
background-color: @green-l3;
text-decoration: none;
}
.visuallyhidden{
position: absolute !important;
clip: rect(1px, 1px, 1px, 1px);
padding:0 !important;
border:0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}
@@ -165,6 +165,8 @@
.sp-replacer {
display: inline-flex;
margin-right: 18px;
border: solid 1px @gray-8;
border-radius: 3px;
}
label {
@@ -1,7 +1,7 @@
(function () {
"use strict";
function CompositionsController($scope, $location, $filter, overlayService) {
function CompositionsController($scope, $location, $filter) {
var vm = this;
var oldModel = null;
@@ -56,35 +56,19 @@
if ($scope.model && $scope.model.submit) {
// check if any compositions has been removed
var compositionRemoved = false;
vm.compositionRemoved = false;
for (var i = 0; oldModel.compositeContentTypes.length > i; i++) {
var oldComposition = oldModel.compositeContentTypes[i];
if (_.contains($scope.model.compositeContentTypes, oldComposition) === false) {
compositionRemoved = true;
vm.compositionRemoved = true;
}
}
/* submit the form if there havne't been removed any composition
or the confirm checkbox has been checked */
if (compositionRemoved) {
vm.allowSubmit = false;
const dialog = {
view: "views/common/infiniteeditors/compositions/overlays/confirmremove.html",
submitButtonLabelKey: "general_ok",
closeButtonLabelKey: "general_cancel",
submit: function (model) {
$scope.model.submit($scope.model);
overlayService.close();
},
close: function () {
overlayService.close();
}
};
overlayService.open(dialog);
return;
if (!vm.compositionRemoved || vm.allowSubmit) {
$scope.model.submit($scope.model);
}
$scope.model.submit($scope.model);
}
}
@@ -92,6 +92,16 @@
</umb-box-content>
</umb-box>
<div ng-if="vm.compositionRemoved" class="umb-editor-confirm">
<h5 class="red"><i class="icon-alert"></i>Warning</h5>
<p>Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?</p>
<label class="checkbox no-indent">
<input type="checkbox" ng-model="vm.allowSubmit" />
<strong>I know what I'm doing</strong>
</label>
</div>
</umb-editor-container>
<umb-editor-footer>
@@ -1,6 +0,0 @@
<div>
<h5 class="red"><i class="icon-alert"></i>Warning</h5>
<p>Removing a composition will delete all the associated property data. Once you save the document type there's no way back, are you sure?</p>
</div>
@@ -59,10 +59,10 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
if (dialogOptions.currentTarget) {
// clone the current target so we don't accidentally update the caller's model while manipulating $scope.model.target
$scope.model.target = angular.copy(dialogOptions.currentTarget);
// if we have a node ID, we fetch the current node to build the form data
//if we have a node ID, we fetch the current node to build the form data
if ($scope.model.target.id || $scope.model.target.udi) {
// will be either a udi or an int
//will be either a udi or an int
var id = $scope.model.target.udi ? $scope.model.target.udi : $scope.model.target.id;
if ($scope.model.target.udi) {
@@ -93,7 +93,7 @@ angular.module("umbraco").controller("Umbraco.Editors.LinkPickerController",
});
}
} else if ($scope.model.target.url && $scope.model.target.url.length) {
} else if ($scope.model.target.url.length) {
// a url but no id/udi indicates an external link - trim the url to remove the anchor/qs
// only do the substring if there's a # or a ?
var indexOfAnchor = $scope.model.target.url.search(/(#|\?)/);
@@ -268,7 +268,7 @@ angular.module("umbraco")
// also make sure the node is not trashed
if (nodePath.indexOf($scope.startNodeId.toString()) !== -1 && node.trashed === false) {
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder", path: node.path });
$scope.gotoFolder({ id: $scope.lastOpenedNode, name: "Media", icon: "icon-folder" });
return true;
} else {
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
@@ -109,10 +109,10 @@
<input type="number" ng-switch-when="int" style="width:90px; margin-bottom: 0;" ng-model="filter.constraintValue" ng-change="vm.changeConstraintValue()" />
<span ng-switch-when="datetime">
<umb-date-time-picker
<umb-flatpickr
options="vm.datePickerConfig"
on-change="vm.datePickerChange(dateStr, filter)">
</umb-date-time-picker>
</umb-flatpickr>
</span>
</span>
@@ -18,11 +18,11 @@
</p>
<div class="control-group" ng-class="{error: vm.setPasswordForm.password.$invalid}">
<label for="umb-password">
<label>
<localize key="user_newPassword">New password</localize>
<small style="font-size: 13px;">{{vm.invitedUserPasswordModel.passwordPolicyText}}</small>
</label>
<input type="password" ng-model="vm.invitedUserPasswordModel.password" name="password" id="umb-password" class="-full-width-input" umb-auto-focus required val-server-field="value" ng-minlength="{{vm.invitedUserPasswordModel.passwordPolicies.minPasswordLength}}" />
<input type="password" ng-model="vm.invitedUserPasswordModel.password" name="password" class="-full-width-input" umb-auto-focus required val-server-field="value" ng-minlength="{{vm.invitedUserPasswordModel.passwordPolicies.minPasswordLength}}" />
<span ng-messages="inviteUserPasswordForm.password.$error" show-validation-on-submit >
<span class="help-inline" ng-message="required"><localize key="user_passwordIsBlank">Your new password cannot be blank!</localize></span>
<span class="help-inline" ng-message="minlength">Minimum {{vm.invitedUserPasswordModel.passwordPolicies.minPasswordLength}} characters</span>
@@ -31,8 +31,8 @@
</div>
<div class="control-group" ng-class="{error: vm.setPasswordForm.confirmPassword.$invalid}">
<label for="umb-confirmPassword"><localize key="user_confirmNewPassword">Confirm new password</localize></label>
<input type="password" ng-model="vm.invitedUserPasswordModel.confirmPassword" name="confirmPassword" id="umb-confirmPassword" class="-full-width-input" required val-compare="password" />
<label><localize key="user_confirmNewPassword">Confirm new password</localize></label>
<input type="password" ng-model="vm.invitedUserPasswordModel.confirmPassword" name="confirmPassword" class="-full-width-input" required val-compare="password" />
<span ng-messages="inviteUserPasswordForm.confirmPassword.$error" show-validation-on-submit >
<span class="help-inline" ng-message="required"><localize key="general_required">Required</localize></span>
<span class="help-inline" ng-message="valCompare"><localize key="user_passwordMismatch">The confirmed password doesn't match the new password!</localize></span>
@@ -152,13 +152,13 @@
<p ng-message="auth" class="text-error" role="alert">{{vm.errorMsg}}</p>
</div>
<div class="control-group" ng-class="{error: vm.loginForm.username.$invalid}">
<label for="umb-username">{{vm.labels.usernameLabel}}</label>
<input type="text" ng-model="vm.login" name="username" id="umb-username" class="-full-width-input" placeholder="{{vm.labels.usernamePlaceholder}}" focus-when="{{vm.view === 'login'}}" />
<label>{{vm.labels.usernameLabel}}</label>
<input type="text" ng-model="vm.login" name="username" class="-full-width-input" placeholder="{{vm.labels.usernamePlaceholder}}" focus-when="{{vm.view === 'login'}}" />
</div>
<div class="control-group" ng-class="{error: vm.loginForm.password.$invalid}">
<label for="umb-password"><localize key="general_password">Password</localize></label>
<input type="password" ng-model="vm.password" name="password" id="umb-password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" />
<label><localize key="general_password">Password</localize></label>
<input type="password" ng-model="vm.password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" />
<div class="password-toggle">
<a href="#" prevent-default ng-click="vm.togglePassword()">
<span class="password-text show"><localize key="login_showPassword">Show password</localize></span>
@@ -189,8 +189,8 @@
<form method="POST" name="vm.requestPasswordResetForm" ng-submit="vm.requestPasswordResetSubmit(email)">
<div class="control-group" ng-class="{error: requestPasswordResetForm.email.$invalid}">
<label for="umb-email"><localize key="general_email">Email</localize></label>
<input type="email" val-email ng-model="email" name="email" id="umb-email" class="-full-width-input" localize="placeholder" placeholder="@placeholders_email" focus-when="{{vm.view === 'request-password-reset'}}" />
<label><localize key="general_email">Email</localize></label>
<input type="email" val-email ng-model="email" name="email" class="-full-width-input" localize="placeholder" placeholder="@placeholders_email" focus-when="{{vm.view === 'request-password-reset'}}" />
</div>
<div class="control-group" ng-show="requestPasswordResetForm.$invalid">
@@ -220,13 +220,13 @@
<form method="POST" name="vm.setPasswordForm" ng-submit="vm.setPasswordSubmit(vm.password, vm.confirmPassword)">
<div ng-hide="vm.resetComplete" class="control-group" ng-class="{error: vm.setPasswordForm.password.$invalid}">
<label for="umb-password"><localize key="user_newPassword">New password</localize></label>
<input type="password" ng-model="vm.password" name="password" id="umb-password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" focus-when="{{vm.view === 'set-password'}}" />
<label><localize key="user_newPassword">New password</localize></label>
<input type="password" ng-model="vm.password" name="password" class="-full-width-input" localize="placeholder" placeholder="@placeholders_password" focus-when="{{vm.view === 'set-password'}}" />
</div>
<div ng-hide="vm.resetComplete" class="control-group" ng-class="{error: vm.setPasswordForm.confirmPassword.$invalid}">
<label for="umb-confirmPassword"><localize key="user_confirmNewPassword">Confirm new password</localize></label>
<input type="password" ng-model="vm.confirmPassword" name="confirmPassword" id="umb-confirmPassword" class="-full-width-input" localize="placeholder" placeholder="@placeholders_confirmPassword" />
<label><localize key="user_confirmNewPassword">Confirm new password</localize></label>
<input type="password" ng-model="vm.confirmPassword" name="confirmPassword" class="-full-width-input" localize="placeholder" placeholder="@placeholders_confirmPassword" />
</div>
<div ng-hide="vm.resetComplete" class="control-group" ng-show="vm.setPasswordForm.$invalid">
@@ -1,26 +1,25 @@
<div class="umb-tour">
<umb-loader class="umb-tour__loader" ng-if="loadingStep"></umb-loader>
<div class="umb-loader umb-tour__loader" ng-if="loadingStep"></div>
<div class="umb-tour__pulse"></div>
<div class="umb-tour__popover shadow-depth-2" ng-class="{'umb-tour__popover--l': model.currentStep.type === 'intro' || model.currentStepIndex === model.steps.length}">
<div ng-if="!configuredView && !elementNotFound">
<!-- Regular steps -->
<umb-tour-step ng-if="model.currentStepIndex < model.steps.length" on-close="model.endTour()">
<umb-tour-step-header
title="model.currentStep.title">
</umb-tour-step-header>
<umb-tour-step-content
content="model.currentStep.content">
</umb-tour-step-content>
<umb-tour-step-footer>
<div class="flex justify-between items-center">
<div>
@@ -28,11 +27,9 @@
current-step="model.currentStepIndex + 1"
total-steps="model.steps.length">
</umb-tour-step-counter>
<div ng-if="model.allowDisable && model.currentStep.type === 'intro'">
<button type="button" class="underline" ng-click="model.disableTour()" style="font-size: 13px; background: transparent; border: none; padding: 0;" prevent-default>Don't show this tour again</button>
</div>
<div ng-if="model.allowDisable && model.currentStep.type === 'intro'" style="font-size: 13px;"><a href="" class="underline" ng-click="model.disableTour()">Don't show this tour again</a></div>
</div>
<div ng-if="model.currentStep.type !== 'intro'">
<umb-button size="xs" ng-if="!model.currentStep.event" button-style="action" type="button" action="model.nextStep()" label="Next"></umb-button>
</div>
@@ -43,7 +40,7 @@
</div>
</umb-tour-step-footer>
</umb-tour-step>
<!-- Outro step -->
@@ -67,7 +64,7 @@
<!-- Custom step view -->
<div ng-if="configuredView && !loadingStep && !elementNotFound" ng-include="configuredView"></div>
<!-- Dom element not found error -->
<div ng-if="elementNotFound && !loadingStep">
<umb-tour-step class="tc">
@@ -86,4 +83,4 @@
</div>
</div>
</div>
@@ -2,13 +2,9 @@
<div ng-if="hideClose !== true">
<button class="icon-wrong umb-tour-step__close" ng-click="close()">
<span class="sr-only">
<localize key="general_close">Close</localize>
</span>
</button>
<button class="icon-wrong umb-tour-step__close" ng-click="close()"></button>
</div>
<div ng-transclude></div>
</div>
</div>
@@ -1,5 +1,6 @@
<div
class="umb-editor-sub-header umb-editor-sub-header--{{appearance}}"
umb-sticky-bar
scrollable-container=".umb-editor-container"
ng-transclude>
</div>
@@ -3,39 +3,32 @@
<div class="flex items-center" style="height: 100%;">
<div ng-if="showBackButton === true && splitViewOpen !== true" style="margin-right: 15px;">
<button type="button" class="umb-editor-header__back" ng-click="goBack()" prevent-default>
<a class="umb-editor-header__back" href="#" ng-click="goBack()" prevent-default>
<i class="fa fa-arrow-left" aria-hidden="true"></i>
<span class="sr-only"><localize key="visuallyHiddenTexts_goBack">Go back</localize></span>
</button>
</a>
</div>
<div class="flex items-center" style="flex: 1;">
<div id="nameField" class="umb-editor-header__name-and-description" style="flex: 1 1 auto;">
<div>
<p tabindex="0" class="sr-only">
{{a11yMessage}}
</p>
</div>
<div class="umb-editor-header__name-wrapper">
<label for="headerName" class="sr-only">{{a11yName}}</label>
<ng-form name="headerNameForm">
<input data-element="editor-name-field"
type="text"
class="umb-editor-header__name-input"
localize="placeholder"
placeholder="@placeholders_entername"
name="headerName"
id="headerName"
ng-model="name"
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
ng-disabled="nameDisabled"
umb-auto-focus
val-server-field="{{serverValidationNameField}}"
required
aria-required="true"
aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}"
autocomplete="off" maxlength="255" />
<input
data-element="editor-name-field"
type="text"
class="umb-editor-header__name-input"
localize="placeholder"
placeholder="@placeholders_entername"
name="headerName"
ng-model="name"
ng-class="{'name-is-empty': $parent.name===null || $parent.name===''}"
ng-disabled="nameDisabled"
umb-auto-focus
val-server-field="{{serverValidationNameField}}"
required
aria-required="true"
aria-invalid="{{contentForm.headerNameForm.headerName.$invalid ? true : false}}"
autocomplete="off" maxlength="255" />
</ng-form>
<a ng-if="content.variants.length > 0 && hideChangeVariant !== true" class="umb-variant-switcher__toggle" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen" ng-class="{'--error': vm.errorsOnOtherVariants}">
@@ -3,10 +3,9 @@
<div class="flex items-center" style="height: 100%;">
<div ng-if="showBackButton === true && splitViewOpen !== true" style="margin-right: 15px;">
<button type="button" class="umb-editor-header__back" ng-click="goBack()" prevent-default>
<a class="umb-editor-header__back" href="#" ng-click="goBack()" prevent-default>
<i class="fa fa-arrow-left" aria-hidden="true"></i>
<span class="sr-only"><localize key="visuallyHiddenTexts_goBack">Go back</localize></span>
</button>
</a>
</div>
<div class="flex items-center" style="flex: 1;">
@@ -5,7 +5,7 @@
ng-class="{'umb-editor--small': model.size === 'small',
'umb-editor--animating': model.animating,
'--notInFront': model.inFront !== true,
'umb-editor--infiniteMode': model.infiniteMode,
'umb-editor--infinityMode': model.infinityMode,
'moveRight': model.moveRight,
'umb-editor--n0': model.styleIndex === 0,
'umb-editor--n1': model.styleIndex === 1,
@@ -1,4 +1,3 @@
<div>
<umb-load-indicator ng-if="isLoading"></umb-load-indicator>
<div class="umb-rte" id="{{textAreaHtmlId}}" ng-style="{ visibility : isLoading ? 'hidden' : 'visible' }"></div>
<div class="umb-rte"
id="{{textAreaHtmlId}}">
</div>
@@ -12,10 +12,9 @@
<ul ng-if="nodeUrl" class="nav nav-stacked" style="margin-bottom: 0;">
<li>
<a ng-attr-href="{{node.extension !== 'svg' ? nodeUrl : undefined}}" ng-click="node.extension === 'svg' && openSVG()" target="_blank">
<i class="icon icon-out"></i>
<span>{{nodeFileName}}</span>
<a href="{{nodeUrl}}" target="_blank">
<i class="icon icon-out"></i>
<span>{{nodeFileName}}</span>
</a>
</li>
</ul>
@@ -15,8 +15,7 @@
<!-- NOTE: These are the 'option' elipses -->
<button data-element="tree-item-options" class="umb-options btn-reset sr-only sr-only--focusable sr-only--hoverable" ng-click="options(node, $event)" ng-if="::node.menuUrl"><i></i><i></i><i></i></button>
<umb-loader ng-show="node.loading" position="bottom" class="umb-tree-item__loader"></umb-loader>
<div ng-show="node.loading" class="l"><div></div></div>
</div>
<ul ng-class="{collapsed: !node.expanded}">
@@ -14,8 +14,7 @@
icon="icon-merge"
action="openCompositionsDialog()"
size="xs"
add-ellipsis="true"
state="compositionsButtonState">
add-ellipsis="true">
</umb-button>
<umb-button
@@ -1,3 +0,0 @@
<div class="umb-loader-wrapper" ng-class="{ '-top': position === 'top', '-bottom': position === 'bottom' }">
<div class="umb-loader"></div>
</div>
@@ -60,7 +60,7 @@
</div>
<!-- Items -->
<div class="umb-table-row cursor-pointer"
<div class="umb-table-row"
ng-repeat="child in miniListView.children"
ng-click="selectNode(child)"
ng-class="{'-selected':child.selected, 'not-allowed':!child.allowed}">
@@ -17,17 +17,13 @@
<div class="content" >
<p>
<localize key="media_dragAndDropYourFilesIntoTheArea">Drag and drop your file(s) into the area</localize>
</p>
<!-- Drag and drop illustration -->
<img class="illustration" src="assets/img/uploader/upload-illustration.svg" alt="" draggable="false" />
<!-- Select files -->
<button
type="button"
prevent-default
<div
data-element="button-uploadMedia"
class="file-select"
ngf-select
@@ -36,8 +32,8 @@
ngf-multiple="true"
ngf-pattern="{{ accept }}"
ngf-max-size="{{ maxFileSize }}">
-&nbsp;<localize key="media_orClickHereToUpload">or click here to choose files</localize>
</button>
- <localize key="media_orClickHereToUpload">or click here to choose files</localize>
</div>
</div>
</div>
@@ -108,7 +104,7 @@
ng-if="mediatypepickerOverlay.show"
model="mediatypepickerOverlay"
view="mediatypepickerOverlay.view"
position="right">
position="right">
</umb-overlay>
</div>

Some files were not shown because too many files have changed in this diff Show More