Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a60e15e771 | |||
| fec3d8b740 | |||
| aed223c16d | |||
| 3dd5224ea3 | |||
| e940f6dfdc | |||
| 47a47292e8 | |||
| 277d5968a6 | |||
| 22eef489e0 | |||
| 5386fabf3c | |||
| 1c141f1f02 | |||
| f2908211db | |||
| 11d18a6446 | |||
| cce8017155 | |||
| a6afa4bb36 | |||
| 3b6c99a05d | |||
| b27e10c207 | |||
| 0c83c3a9a9 | |||
| 79e033e194 | |||
| e422ac3d52 | |||
| 8b11792df4 | |||
| 57e58c6cf7 | |||
| 34e45abcc7 | |||
| b3497011e8 | |||
| a398881fa1 | |||
| 2cf3a7831c | |||
| 0138091a6c | |||
| 6c0fb2f849 | |||
| e495faf83c | |||
| 8721423f40 | |||
| 49b3351c72 | |||
| faef449750 | |||
| 4a24064783 | |||
| 0ab84fc443 | |||
| fca74ad8d7 | |||
| 7bc7dba86d | |||
| ca75a25802 | |||
| ece31733a9 | |||
| 28a8027179 | |||
| a97604d6c4 | |||
| 308f929f7b | |||
| c8d7df1515 | |||
| 2c795662d2 | |||
| 8a43e3a87e | |||
| 210e43fcb0 | |||
| beb8c7ac7c | |||
| 2f8979bbc3 | |||
| a8ed7f2c17 | |||
| 1c6bf55e5d | |||
| c246044ac7 | |||
| 5126521d37 | |||
| 27223738c6 | |||
| 968463912a | |||
| 05c6b856f9 | |||
| 6fe3089207 | |||
| 06b136fdf6 | |||
| c6246b5404 | |||
| 3062f40ef5 | |||
| dcd0d38419 | |||
| c01a8f8f13 | |||
| da71a94c39 | |||
| f6413af3f4 | |||
| 440e649d6d | |||
| 47dcd37d1d | |||
| d60fc63e78 | |||
| b0c4042e87 | |||
| da525513a3 | |||
| 2ac5e61e44 | |||
| 101a8b6c78 | |||
| f7a10e3a15 | |||
| 4e0dd728a8 | |||
| 320ccf8105 | |||
| 62f924a757 | |||
| c47a08b553 | |||
| de9241bcf5 | |||
| 7c03fe3c1f | |||
| 82eb241119 | |||
| 1073e6257c | |||
| bc3e4847fe | |||
| 1a7251909c | |||
| 61a18297d9 | |||
| bdd413f9ee | |||
| fd8ca35e63 | |||
| fdd9aee773 | |||
| 3c2a92d3b6 | |||
| cf49e6160a | |||
| ec93030e21 | |||
| 1d49c8626e | |||
| 89bb94aa1a | |||
| 03a4b75176 | |||
| 943b54dcf7 | |||
| 39bd18ec1a | |||
| e93ddff46b | |||
| 3144b51c91 | |||
| e92f68d040 | |||
| 469054e9b3 | |||
| d17022d87d | |||
| 6865629090 | |||
| a4749f201d | |||
| ad90264ef1 | |||
| 9ba7e7742a | |||
| 6473f600c7 | |||
| 6329dfa629 | |||
| 2cd0173534 | |||
| a0bcfeddcc | |||
| c4b5483d8c | |||
| 7557c584d8 | |||
| 97896e96af | |||
| 5c7a519372 | |||
| ae5d82da12 | |||
| 6529f472ab | |||
| bfd038f623 | |||
| e2b0053d31 | |||
| 6c2401fa27 | |||
| 4b36c72105 | |||
| 663228c33b | |||
| 36616fb9d1 | |||
| d71b2a07c4 | |||
| 7ce4fbe44c | |||
| f17cd844ff | |||
| f657c635ef | |||
| 22141af54c | |||
| 1eb1d4dc63 | |||
| 8279e6208b | |||
| c494ad0adc | |||
| 53007f4e8b | |||
| 8636b5124e | |||
| c2a57a1770 | |||
| 62e19279a4 | |||
| e41e458322 | |||
| acd329381c | |||
| b064566f9e | |||
| 89f93aa81d | |||
| 013bd2c424 | |||
| 5cb847930d | |||
| ab5e7aa1e8 | |||
| e7303602d2 | |||
| 202302e003 | |||
| 174af027e9 | |||
| 1a4c45dbdf | |||
| 2455e2bed9 |
@@ -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/dev-v7/.github/CONTRIBUTING.md
|
||||
See: https://github.com/umbraco/Umbraco-CMS/blob/v8/dev/.github/CONTRIBUTING.md
|
||||
|
||||
-->
|
||||
|
||||
|
||||
+2
-2
@@ -18,5 +18,5 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("8.0.0")]
|
||||
|
||||
// these are FYI and changed automatically
|
||||
[assembly: AssemblyFileVersion("8.1.1")]
|
||||
[assembly: AssemblyInformationalVersion("8.1.1")]
|
||||
[assembly: AssemblyFileVersion("8.2.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.2.0")]
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class Aliases
|
||||
{
|
||||
/// <summary>
|
||||
/// Block Editor.
|
||||
/// </summary>
|
||||
public const string BlockEditor = "Umbraco.BlockEditor";
|
||||
|
||||
/// <summary>
|
||||
/// CheckBox List.
|
||||
/// </summary>
|
||||
@@ -203,6 +208,24 @@ 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";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ public class PreValueMigratorComposer : ICoreComposer
|
||||
.Append<NestedContentPreValueMigrator>()
|
||||
.Append<DecimalPreValueMigrator>()
|
||||
.Append<ListViewPreValueMigrator>()
|
||||
.Append<DropDownFlexiblePreValueMigrator>()
|
||||
.Append<ValueListPreValueMigrator>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0.DataTypes
|
||||
private readonly string[] _editors =
|
||||
{
|
||||
"Umbraco.RadioButtonList",
|
||||
"Umbraco.CheckBoxList",
|
||||
"Umbraco.DropDown",
|
||||
"Umbraco.DropdownlistPublishingKeys",
|
||||
"Umbraco.DropDownMultiple",
|
||||
|
||||
@@ -114,7 +114,7 @@ namespace Umbraco.Core.Models
|
||||
/// Gets of sets the alias of the property type.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
public string Alias
|
||||
public virtual string Alias
|
||||
{
|
||||
get => _alias;
|
||||
set => SetPropertyValueAndDetectChanges(SanitizeAlias(value), ref _alias, nameof(Alias));
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace Umbraco.Core.PropertyEditors
|
||||
// defaults
|
||||
Type = type;
|
||||
Icon = Constants.Icons.PropertyEditor;
|
||||
Group = "common";
|
||||
Group = Constants.PropertyEditors.Groups.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; } = "common";
|
||||
public string Group { get; set; } = Constants.PropertyEditors.Groups.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the value editor is deprecated.
|
||||
|
||||
@@ -5,7 +5,11 @@ 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>
|
||||
|
||||
@@ -241,6 +241,7 @@
|
||||
<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" />
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace Umbraco.Examine
|
||||
continue;
|
||||
case string strVal:
|
||||
{
|
||||
if (strVal.IsNullOrWhiteSpace()) return;
|
||||
if (strVal.IsNullOrWhiteSpace()) continue;
|
||||
var key = $"{keyVal.Key}{cultureSuffix}";
|
||||
if (values.TryGetValue(key, out var v))
|
||||
values[key] = new List<object>(v) { val }.ToArray();
|
||||
|
||||
@@ -48,6 +48,31 @@ namespace Umbraco.Examine
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all index fields that are culture specific (suffixed) or invariant
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="culture"></param>
|
||||
/// <returns></returns>
|
||||
public static IEnumerable<string> GetCultureAndInvariantFields(this IUmbracoIndex index, string culture)
|
||||
{
|
||||
var allFields = index.GetFields();
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (var field in allFields)
|
||||
{
|
||||
var match = CultureIsoCodeFieldNameMatchExpression.Match(field);
|
||||
if (match.Success && match.Groups.Count == 3 && culture.InvariantEquals(match.Groups[2].Value))
|
||||
{
|
||||
yield return field; //matches this culture field
|
||||
}
|
||||
else if (!match.Success)
|
||||
{
|
||||
yield return field; //matches no culture field (invariant)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryParseLuceneQuery(string query)
|
||||
{
|
||||
// TODO: I'd assume there would be a more strict way to parse the query but not that i can find yet, for now we'll
|
||||
@@ -77,7 +102,7 @@ namespace Umbraco.Examine
|
||||
/// <remarks>
|
||||
/// This is not thread safe, use with care
|
||||
/// </remarks>
|
||||
internal static void UnlockLuceneIndexes(this IExamineManager examineManager, ILogger logger)
|
||||
internal static void ConfigureLuceneIndexes(this IExamineManager examineManager, ILogger logger, bool disableExamineIndexing)
|
||||
{
|
||||
foreach (var luceneIndexer in examineManager.Indexes.OfType<LuceneIndex>())
|
||||
{
|
||||
@@ -86,6 +111,8 @@ 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(38, types.Count());
|
||||
Assert.AreEqual(39, types.Count());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -81,6 +81,19 @@ namespace Umbraco.Tests.Routing
|
||||
Assert.AreEqual(expected, resolvedUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Media_Url_Returns_Absolute_Url_If_Stored_Url_Is_Absolute()
|
||||
{
|
||||
const string expected = "http://localhost/media/rfeiw584/test.jpg";
|
||||
|
||||
var umbracoContext = GetUmbracoContext("http://localhost", mediaUrlProviders: new[] { _mediaUrlProvider });
|
||||
var publishedContent = CreatePublishedContent(Constants.PropertyEditors.Aliases.UploadField, expected, null);
|
||||
|
||||
var resolvedUrl = umbracoContext.UrlProvider.GetMediaUrl(publishedContent, UrlMode.Relative);
|
||||
|
||||
Assert.AreEqual(expected, resolvedUrl);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Get_Media_Url_Returns_Empty_String_When_PropertyType_Is_Not_Supported()
|
||||
{
|
||||
|
||||
@@ -25,19 +25,23 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
public async Task<Tuple<HttpResponseMessage, string>> Execute(string controllerName, string actionName, HttpMethod method,
|
||||
HttpContent content = null,
|
||||
MediaTypeWithQualityHeaderValue mediaTypeHeader = null,
|
||||
bool assertOkResponse = true)
|
||||
bool assertOkResponse = true, object routeDefaults = null, string url = null)
|
||||
{
|
||||
if (mediaTypeHeader == null)
|
||||
{
|
||||
mediaTypeHeader = new MediaTypeWithQualityHeaderValue("application/json");
|
||||
}
|
||||
if (routeDefaults == null)
|
||||
{
|
||||
routeDefaults = new { controller = controllerName, action = actionName, id = RouteParameter.Optional };
|
||||
}
|
||||
|
||||
var startup = new TestStartup(
|
||||
configuration =>
|
||||
{
|
||||
configuration.Routes.MapHttpRoute("Default",
|
||||
routeTemplate: "{controller}/{action}/{id}",
|
||||
defaults: new { controller = controllerName, action = actionName, id = RouteParameter.Optional });
|
||||
defaults: routeDefaults);
|
||||
},
|
||||
_controllerFactory);
|
||||
|
||||
@@ -45,7 +49,7 @@ namespace Umbraco.Tests.TestHelpers.ControllerTesting
|
||||
{
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
RequestUri = new Uri("https://testserver/"),
|
||||
RequestUri = new Uri("https://testserver/" + (url ?? "")),
|
||||
Method = method
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
using Lucene.Net.Store;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Umbraco.Tests.TestHelpers
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Used for tests with Lucene so that each RAM directory is unique
|
||||
/// </summary>
|
||||
public class RandomIdRAMDirectory : RAMDirectory
|
||||
{
|
||||
private readonly string _lockId = Guid.NewGuid().ToString();
|
||||
public override string GetLockId()
|
||||
{
|
||||
return _lockId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,6 +157,7 @@
|
||||
<Compile Include="Services\MemberGroupServiceTests.cs" />
|
||||
<Compile Include="Services\MediaTypeServiceTests.cs" />
|
||||
<Compile Include="Services\PropertyValidationServiceTests.cs" />
|
||||
<Compile Include="TestHelpers\RandomIdRamDirectory.cs" />
|
||||
<Compile Include="Testing\Objects\TestDataSource.cs" />
|
||||
<Compile Include="Published\PublishedSnapshotTestObjects.cs" />
|
||||
<Compile Include="Published\ModelTypeTests.cs" />
|
||||
@@ -242,6 +243,7 @@
|
||||
<Compile Include="Cache\FullDataSetCachePolicyTests.cs" />
|
||||
<Compile Include="Cache\SingleItemsOnlyCachePolicyTests.cs" />
|
||||
<Compile Include="Collections\DeepCloneableListTests.cs" />
|
||||
<Compile Include="Web\Controllers\AuthenticationControllerTests.cs" />
|
||||
<Compile Include="Web\Controllers\BackOfficeControllerUnitTests.cs" />
|
||||
<Compile Include="CoreThings\DelegateExtensionsTests.cs" />
|
||||
<Compile Include="Web\Controllers\ContentControllerTests.cs" />
|
||||
@@ -268,6 +270,7 @@
|
||||
<Compile Include="Strings\StylesheetHelperTests.cs" />
|
||||
<Compile Include="Strings\StringValidationTests.cs" />
|
||||
<Compile Include="Web\Mvc\ValidateUmbracoFormRouteStringAttributeTests.cs" />
|
||||
<Compile Include="Web\PublishedContentQueryTests.cs" />
|
||||
<Compile Include="Web\UmbracoHelperTests.cs" />
|
||||
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
|
||||
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using System.Web.Http;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Persistence;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
using Umbraco.Core.Persistence.Querying;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Tests.TestHelpers.ControllerTesting;
|
||||
using Umbraco.Tests.Testing;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Features;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using IUser = Umbraco.Core.Models.Membership.IUser;
|
||||
|
||||
namespace Umbraco.Tests.Web.Controllers
|
||||
{
|
||||
[TestFixture]
|
||||
[UmbracoTest(Database = UmbracoTestOptions.Database.None)]
|
||||
public class AuthenticationControllerTests : TestWithDatabaseBase
|
||||
{
|
||||
protected override void ComposeApplication(bool withApplication)
|
||||
{
|
||||
base.ComposeApplication(withApplication);
|
||||
//if (!withApplication) return;
|
||||
|
||||
// replace the true IUserService implementation with a mock
|
||||
// so that each test can configure the service to their liking
|
||||
Composition.RegisterUnique(f => Mock.Of<IUserService>());
|
||||
|
||||
// kill the true IEntityService too
|
||||
Composition.RegisterUnique(f => Mock.Of<IEntityService>());
|
||||
|
||||
Composition.RegisterUnique<UmbracoFeatures>();
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async System.Threading.Tasks.Task GetCurrentUser_Fips()
|
||||
{
|
||||
ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper)
|
||||
{
|
||||
//setup some mocks
|
||||
var userServiceMock = Mock.Get(Current.Services.UserService);
|
||||
userServiceMock.Setup(service => service.GetUserById(It.IsAny<int>()))
|
||||
.Returns(() => null);
|
||||
|
||||
if (Thread.GetDomain().GetData(".appPath") != null)
|
||||
{
|
||||
HttpContext.Current = new HttpContext(new SimpleWorkerRequest("", "", new StringWriter()));
|
||||
}
|
||||
else
|
||||
{
|
||||
var baseDir = IOHelper.MapPath("", false).TrimEnd(IOHelper.DirSepChar);
|
||||
HttpContext.Current = new HttpContext(new SimpleWorkerRequest("/", baseDir, "", "", new StringWriter()));
|
||||
}
|
||||
IOHelper.ForceNotHosted = true;
|
||||
var usersController = new AuthenticationController(
|
||||
Factory.GetInstance<IGlobalSettings>(),
|
||||
umbracoContextAccessor,
|
||||
Factory.GetInstance<ISqlContext>(),
|
||||
Factory.GetInstance<ServiceContext>(),
|
||||
Factory.GetInstance<AppCaches>(),
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper);
|
||||
return usersController;
|
||||
}
|
||||
|
||||
Mock.Get(Current.SqlContext)
|
||||
.Setup(x => x.Query<IUser>())
|
||||
.Returns(new Query<IUser>(Current.SqlContext));
|
||||
|
||||
var syntax = new SqlCeSyntaxProvider();
|
||||
|
||||
Mock.Get(Current.SqlContext)
|
||||
.Setup(x => x.SqlSyntax)
|
||||
.Returns(syntax);
|
||||
|
||||
var mappers = new MapperCollection(new[]
|
||||
{
|
||||
new UserMapper(new Lazy<ISqlContext>(() => Current.SqlContext), new ConcurrentDictionary<Type, ConcurrentDictionary<string, string>>())
|
||||
});
|
||||
|
||||
Mock.Get(Current.SqlContext)
|
||||
.Setup(x => x.Mappers)
|
||||
.Returns(mappers);
|
||||
|
||||
// Testing what happens if the system were configured to only use FIPS-compliant algorithms
|
||||
var typ = typeof(CryptoConfig);
|
||||
var flds = typ.GetFields(BindingFlags.Static | BindingFlags.NonPublic);
|
||||
var haveFld = flds.FirstOrDefault(f => f.Name == "s_haveFipsAlgorithmPolicy");
|
||||
var isFld = flds.FirstOrDefault(f => f.Name == "s_fipsAlgorithmPolicy");
|
||||
var originalFipsValue = CryptoConfig.AllowOnlyFipsAlgorithms;
|
||||
|
||||
try
|
||||
{
|
||||
if (!originalFipsValue)
|
||||
{
|
||||
haveFld.SetValue(null, true);
|
||||
isFld.SetValue(null, true);
|
||||
}
|
||||
|
||||
var runner = new TestRunner(CtrlFactory);
|
||||
var response = await runner.Execute("Authentication", "GetCurrentUser", HttpMethod.Get);
|
||||
|
||||
var obj = JsonConvert.DeserializeObject<UserDetail>(response.Item2);
|
||||
Assert.AreEqual(-1, obj.UserId);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!originalFipsValue)
|
||||
{
|
||||
haveFld.SetValue(null, false);
|
||||
isFld.SetValue(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Formatting;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Web.Http;
|
||||
using Moq;
|
||||
using Newtonsoft.Json;
|
||||
@@ -155,7 +157,7 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
var runner = new TestRunner(CtrlFactory);
|
||||
var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get);
|
||||
|
||||
var obj = JsonConvert.DeserializeObject<PagedResult<UserDisplay>>(response.Item2);
|
||||
var obj = JsonConvert.DeserializeObject<PagedResult<UserBasic>>(response.Item2);
|
||||
Assert.AreEqual(0, obj.TotalItems);
|
||||
}
|
||||
|
||||
@@ -190,9 +192,100 @@ namespace Umbraco.Tests.Web.Controllers
|
||||
var runner = new TestRunner(CtrlFactory);
|
||||
var response = await runner.Execute("Users", "GetPagedUsers", HttpMethod.Get);
|
||||
|
||||
var obj = JsonConvert.DeserializeObject<PagedResult<UserDisplay>>(response.Item2);
|
||||
var obj = JsonConvert.DeserializeObject<PagedResult<UserBasic>>(response.Item2);
|
||||
Assert.AreEqual(10, obj.TotalItems);
|
||||
Assert.AreEqual(10, obj.Items.Count());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async System.Threading.Tasks.Task GetPagedUsers_Fips()
|
||||
{
|
||||
await RunFipsTest("GetPagedUsers", mock =>
|
||||
{
|
||||
var users = MockedUser.CreateMulipleUsers(10);
|
||||
long outVal = 10;
|
||||
mock.Setup(service => service.GetAll(
|
||||
It.IsAny<long>(), It.IsAny<int>(), out outVal, It.IsAny<string>(), It.IsAny<Direction>(),
|
||||
It.IsAny<UserState[]>(), It.IsAny<string[]>(), It.IsAny<string[]>(), It.IsAny<IQuery<IUser>>()))
|
||||
.Returns(() => users);
|
||||
}, response =>
|
||||
{
|
||||
var obj = JsonConvert.DeserializeObject<PagedResult<UserBasic>>(response.Item2);
|
||||
Assert.AreEqual(10, obj.TotalItems);
|
||||
Assert.AreEqual(10, obj.Items.Count());
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async System.Threading.Tasks.Task GetById_Fips()
|
||||
{
|
||||
const int mockUserId = 1234;
|
||||
var user = MockedUser.CreateUser();
|
||||
|
||||
await RunFipsTest("GetById", mock =>
|
||||
{
|
||||
mock.Setup(service => service.GetUserById(1234))
|
||||
.Returns((int i) => i == mockUserId ? user : null);
|
||||
}, response =>
|
||||
{
|
||||
var obj = JsonConvert.DeserializeObject<UserDisplay>(response.Item2);
|
||||
Assert.AreEqual(user.Username, obj.Username);
|
||||
Assert.AreEqual(user.Email, obj.Email);
|
||||
}, new { controller = "Users", action = "GetById" }, $"Users/GetById/{mockUserId}");
|
||||
}
|
||||
|
||||
|
||||
private async System.Threading.Tasks.Task RunFipsTest(string action, Action<Mock<IUserService>> userServiceSetup,
|
||||
Action<Tuple<HttpResponseMessage, string>> verification,
|
||||
object routeDefaults = null, string url = null)
|
||||
{
|
||||
ApiController CtrlFactory(HttpRequestMessage message, IUmbracoContextAccessor umbracoContextAccessor, UmbracoHelper helper)
|
||||
{
|
||||
//setup some mocks
|
||||
var userServiceMock = Mock.Get(Current.Services.UserService);
|
||||
userServiceSetup(userServiceMock);
|
||||
|
||||
var usersController = new UsersController(
|
||||
Factory.GetInstance<IGlobalSettings>(),
|
||||
umbracoContextAccessor,
|
||||
Factory.GetInstance<ISqlContext>(),
|
||||
Factory.GetInstance<ServiceContext>(),
|
||||
Factory.GetInstance<AppCaches>(),
|
||||
Factory.GetInstance<IProfilingLogger>(),
|
||||
Factory.GetInstance<IRuntimeState>(),
|
||||
helper);
|
||||
return usersController;
|
||||
}
|
||||
|
||||
// Testing what happens if the system were configured to only use FIPS-compliant algorithms
|
||||
var typ = typeof(CryptoConfig);
|
||||
var flds = typ.GetFields(BindingFlags.Static | BindingFlags.NonPublic);
|
||||
var haveFld = flds.FirstOrDefault(f => f.Name == "s_haveFipsAlgorithmPolicy");
|
||||
var isFld = flds.FirstOrDefault(f => f.Name == "s_fipsAlgorithmPolicy");
|
||||
var originalFipsValue = CryptoConfig.AllowOnlyFipsAlgorithms;
|
||||
|
||||
try
|
||||
{
|
||||
if (!originalFipsValue)
|
||||
{
|
||||
haveFld.SetValue(null, true);
|
||||
isFld.SetValue(null, true);
|
||||
}
|
||||
|
||||
MockForGetPagedUsers();
|
||||
|
||||
var runner = new TestRunner(CtrlFactory);
|
||||
var response = await runner.Execute("Users", action, HttpMethod.Get, routeDefaults: routeDefaults, url: url);
|
||||
verification(response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!originalFipsValue)
|
||||
{
|
||||
haveFld.SetValue(null, false);
|
||||
isFld.SetValue(null, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Examine;
|
||||
using Examine.LuceneEngine.Providers;
|
||||
using Lucene.Net.Store;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Examine;
|
||||
using Umbraco.Tests.TestHelpers;
|
||||
using Umbraco.Web;
|
||||
using Umbraco.Web.PublishedCache;
|
||||
|
||||
namespace Umbraco.Tests.Web
|
||||
{
|
||||
[TestFixture]
|
||||
public class PublishedContentQueryTests
|
||||
{
|
||||
|
||||
private class TestIndex : LuceneIndex, IUmbracoIndex
|
||||
{
|
||||
private readonly string[] _fieldNames;
|
||||
|
||||
public TestIndex(string name, Directory luceneDirectory, string[] fieldNames)
|
||||
: base(name, luceneDirectory, null, null, null, null)
|
||||
{
|
||||
_fieldNames = fieldNames;
|
||||
}
|
||||
public bool EnableDefaultEventHandler => throw new NotImplementedException();
|
||||
public bool PublishedValuesOnly => throw new NotImplementedException();
|
||||
public IEnumerable<string> GetFields() => _fieldNames;
|
||||
}
|
||||
|
||||
private TestIndex CreateTestIndex(Directory luceneDirectory, string[] fieldNames)
|
||||
{
|
||||
var indexer = new TestIndex("TestIndex", luceneDirectory, fieldNames);
|
||||
|
||||
using (indexer.ProcessNonAsync())
|
||||
{
|
||||
//populate with some test data
|
||||
indexer.IndexItem(new ValueSet("1", "content", new Dictionary<string, object>
|
||||
{
|
||||
[fieldNames[0]] = "Hello world, there are products here",
|
||||
[UmbracoContentIndex.VariesByCultureFieldName] = "n"
|
||||
}));
|
||||
indexer.IndexItem(new ValueSet("2", "content", new Dictionary<string, object>
|
||||
{
|
||||
[fieldNames[1]] = "Hello world, there are products here",
|
||||
[UmbracoContentIndex.VariesByCultureFieldName] = "y"
|
||||
}));
|
||||
indexer.IndexItem(new ValueSet("3", "content", new Dictionary<string, object>
|
||||
{
|
||||
[fieldNames[2]] = "Hello world, there are products here",
|
||||
[UmbracoContentIndex.VariesByCultureFieldName] = "y"
|
||||
}));
|
||||
}
|
||||
|
||||
return indexer;
|
||||
}
|
||||
|
||||
private PublishedContentQuery CreatePublishedContentQuery(IIndex indexer)
|
||||
{
|
||||
var examineManager = new Mock<IExamineManager>();
|
||||
IIndex outarg = indexer;
|
||||
examineManager.Setup(x => x.TryGetIndex("TestIndex", out outarg)).Returns(true);
|
||||
|
||||
var contentCache = new Mock<IPublishedContentCache>();
|
||||
contentCache.Setup(x => x.GetById(It.IsAny<int>())).Returns((int intId) => Mock.Of<IPublishedContent>(x => x.Id == intId));
|
||||
var snapshot = Mock.Of<IPublishedSnapshot>(x => x.Content == contentCache.Object);
|
||||
var variationContext = new VariationContext();
|
||||
var variationContextAccessor = Mock.Of<IVariationContextAccessor>(x => x.VariationContext == variationContext);
|
||||
|
||||
return new PublishedContentQuery(snapshot, variationContextAccessor, examineManager.Object);
|
||||
}
|
||||
|
||||
[TestCase("fr-fr", ExpectedResult = "1, 3", TestName = "Search Culture: fr-fr. Must return both fr-fr and invariant results")]
|
||||
[TestCase("en-us", ExpectedResult = "1, 2", TestName = "Search Culture: en-us. Must return both en-us and invariant results")]
|
||||
[TestCase("*", ExpectedResult = "1, 2, 3", TestName = "Search Culture: *. Must return all cultures and all invariant results")]
|
||||
[TestCase(null, ExpectedResult = "1", TestName = "Search Culture: null. Must return only invariant results")]
|
||||
public string Search(string culture)
|
||||
{
|
||||
using (var luceneDir = new RandomIdRAMDirectory())
|
||||
{
|
||||
var fieldNames = new[] { "title", "title_en-us", "title_fr-fr" };
|
||||
using (var indexer = CreateTestIndex(luceneDir, fieldNames))
|
||||
{
|
||||
var pcq = CreatePublishedContentQuery(indexer);
|
||||
|
||||
var results = pcq.Search("Products", culture, "TestIndex");
|
||||
|
||||
var ids = results.Select(x => x.Content.Id).ToArray();
|
||||
|
||||
return string.Join(", ", ids);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using Examine.LuceneEngine;
|
||||
using Lucene.Net.Analysis;
|
||||
using Lucene.Net.Analysis.Standard;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Umbraco.Core;
|
||||
@@ -13,18 +16,19 @@ using Umbraco.Web;
|
||||
|
||||
namespace Umbraco.Tests.Web
|
||||
{
|
||||
|
||||
[TestFixture]
|
||||
public class UmbracoHelperTests
|
||||
{
|
||||
{
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Current.Reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ------- Int32 conversion tests
|
||||
[Test]
|
||||
public static void Converting_Boxed_34_To_An_Int_Returns_34()
|
||||
|
||||
@@ -23,7 +23,7 @@ module.exports = function(files, out) {
|
||||
// sort files in stream by path or any custom sort comparator
|
||||
task = task.pipe(babel())
|
||||
.pipe(sort())
|
||||
.pipe(embedTemplates({ basePath: "./src/" }))
|
||||
.pipe(embedTemplates({ basePath: "./src/", minimize:{ loose: true } }))
|
||||
.pipe(concat(out))
|
||||
.pipe(wrap('(function(){\n%= body %\n})();'))
|
||||
.pipe(gulp.dest(config.root + config.targets.js));
|
||||
|
||||
+25
-44
@@ -1222,7 +1222,7 @@
|
||||
"arraybuffer.slice": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
|
||||
"integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
|
||||
"integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=",
|
||||
"dev": true
|
||||
},
|
||||
"asap": {
|
||||
@@ -1284,7 +1284,7 @@
|
||||
"async-limiter": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz",
|
||||
"integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==",
|
||||
"integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=",
|
||||
"dev": true
|
||||
},
|
||||
"asynckit": {
|
||||
@@ -1535,7 +1535,7 @@
|
||||
},
|
||||
"bl": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
|
||||
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
@@ -2516,7 +2516,7 @@
|
||||
"content-type": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
|
||||
"integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
|
||||
"integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=",
|
||||
"dev": true
|
||||
},
|
||||
"continuable-cache": {
|
||||
@@ -4045,7 +4045,7 @@
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
@@ -4081,7 +4081,7 @@
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
@@ -4476,7 +4476,7 @@
|
||||
"eventemitter3": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz",
|
||||
"integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==",
|
||||
"integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=",
|
||||
"dev": true
|
||||
},
|
||||
"exec-buffer": {
|
||||
@@ -5309,8 +5309,7 @@
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
@@ -5331,14 +5330,12 @@
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
@@ -5353,20 +5350,17 @@
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
@@ -5483,8 +5477,7 @@
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
@@ -5496,7 +5489,6 @@
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
@@ -5511,7 +5503,6 @@
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
@@ -5519,14 +5510,12 @@
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
@@ -5545,7 +5534,6 @@
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
@@ -5626,8 +5614,7 @@
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
@@ -5639,7 +5626,6 @@
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
@@ -5725,8 +5711,7 @@
|
||||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
@@ -5762,7 +5747,6 @@
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
@@ -5782,7 +5766,6 @@
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
@@ -5826,14 +5809,12 @@
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7575,7 +7556,7 @@
|
||||
"has-binary2": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
|
||||
"integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
|
||||
"integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"isarray": "2.0.1"
|
||||
@@ -7778,7 +7759,7 @@
|
||||
"http-proxy": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
|
||||
"integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
|
||||
"integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eventemitter3": "^3.0.0",
|
||||
@@ -13616,7 +13597,7 @@
|
||||
"qjobs": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
|
||||
"integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
|
||||
"integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
@@ -14787,7 +14768,7 @@
|
||||
"debug": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||
"integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
@@ -15860,7 +15841,7 @@
|
||||
"type-is": {
|
||||
"version": "1.6.16",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
|
||||
"integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==",
|
||||
"integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"media-typer": "0.3.0",
|
||||
@@ -15902,7 +15883,7 @@
|
||||
"ultron": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
|
||||
"integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==",
|
||||
"integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=",
|
||||
"dev": true
|
||||
},
|
||||
"unc-path-regex": {
|
||||
@@ -16505,7 +16486,7 @@
|
||||
"ws": {
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz",
|
||||
"integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==",
|
||||
"integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"async-limiter": "~1.0.0",
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* A component used to render a list of blocks
|
||||
*/
|
||||
var umbBlockList = {
|
||||
templateUrl: 'views/components/blockeditor/umb-block-list.html',
|
||||
bindings: {
|
||||
blocks: "<",
|
||||
config: "<",
|
||||
scaffolds: "<",
|
||||
onEdit: "&",
|
||||
onRemove: "&",
|
||||
onSettings: "&",
|
||||
onSort: "&?"
|
||||
},
|
||||
controllerAs: 'vm',
|
||||
controller: umbBlockListController
|
||||
};
|
||||
|
||||
function umbBlockListController() {
|
||||
var vm = this;
|
||||
|
||||
vm.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
handle: ".handle",
|
||||
tolerance: 'pointer',
|
||||
update: function (e, ui) {
|
||||
if (vm.onSort) {
|
||||
vm.onSort({ e: e, ui: ui });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
vm.editContent = function (block) {
|
||||
var scaffold = _.findWhere(vm.scaffolds, {
|
||||
udi: block.udi
|
||||
});
|
||||
var element = angular.copy(scaffold);
|
||||
_.each(element.variants[0].tabs, function (tab) {
|
||||
_.each(tab.properties, function (property) {
|
||||
if (block.content[property.alias]) {
|
||||
property.value = block.content[property.alias];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
vm.onEdit({ element: element, block: block });
|
||||
}
|
||||
|
||||
vm.remove = function (block) {
|
||||
// this should be replaced by a custom dialog (pending some PRs)
|
||||
if (confirm("TODO: Are you sure?")) {
|
||||
vm.onRemove({ block: block });
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
angular.module('umbraco').component('umbBlockList', umbBlockList);
|
||||
|
||||
|
||||
})();
|
||||
@@ -94,6 +94,10 @@
|
||||
content.apps[0].active = true;
|
||||
$scope.appChanged(content.apps[0]);
|
||||
}
|
||||
// otherwise make sure the save options are up to date with the current content state
|
||||
else {
|
||||
createButtons($scope.content);
|
||||
}
|
||||
|
||||
editorState.set(content);
|
||||
|
||||
|
||||
+6
-12
@@ -101,14 +101,14 @@ angular.module('umbraco.directives')
|
||||
var eventBindings = [];
|
||||
|
||||
function oneTimeClick(event) {
|
||||
// ignore clicks on button groups toggles (i.e. the save and publish button)
|
||||
var parents = $(event.target).closest("[data-element='button-group-toggle']");
|
||||
if (parents.length > 0) {
|
||||
return;
|
||||
}
|
||||
var el = event.target.nodeName;
|
||||
|
||||
//ignore link and button clicks
|
||||
var els = ["INPUT", "A", "BUTTON"];
|
||||
if (els.indexOf(el) >= 0) { return; }
|
||||
|
||||
// ignore clicks on new overlay
|
||||
parents = $(event.target).parents(".umb-overlay,.umb-tour");
|
||||
var parents = $(event.target).parents("a,button,.umb-overlay,.umb-tour");
|
||||
if (parents.length > 0) {
|
||||
return;
|
||||
}
|
||||
@@ -131,12 +131,6 @@ angular.module('umbraco.directives')
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore clicks on dialog actions
|
||||
var actions = $(event.target).parents(".umb-action");
|
||||
if (actions.length === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
//ignore clicks inside this element
|
||||
if ($(element).has($(event.target)).length > 0) {
|
||||
return;
|
||||
|
||||
+82
-120
@@ -4,7 +4,7 @@
|
||||
@restrict A
|
||||
|
||||
@description
|
||||
Use this directive make an element sticky and follow the page when scrolling.
|
||||
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
|
||||
|
||||
<h3>Markup example</h3>
|
||||
<pre>
|
||||
@@ -12,140 +12,102 @@ Use this directive make an element sticky and follow the page when scrolling.
|
||||
|
||||
<div
|
||||
class="my-sticky-bar"
|
||||
umb-sticky-bar
|
||||
scrollable-container=".container">
|
||||
umb-sticky-bar>
|
||||
</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($rootScope) {
|
||||
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 link(scope, el, attr, ctrl) {
|
||||
|
||||
var bar = $(el);
|
||||
var scrollableContainer = null;
|
||||
var clonedBar = null;
|
||||
var cloneIsMade = false;
|
||||
let current = el[0];
|
||||
let container = current.closest('[data-element="editor-container"]');
|
||||
|
||||
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();
|
||||
addSentinels(current);
|
||||
|
||||
observeHeader(current, container);
|
||||
observeFooter(current, container);
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
@@ -509,6 +509,20 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) {
|
||||
});
|
||||
},
|
||||
|
||||
getScaffoldByUdi: function (parentId, udi) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"contentApiBaseUrl",
|
||||
"GetEmpty",
|
||||
[{ udi: udi }, { parentId: parentId }])),
|
||||
'Failed to retrieve data for empty content item with udi ' + udi)
|
||||
.then(function (result) {
|
||||
return $q.when(umbDataFormatter.formatContentGetData(result));
|
||||
});
|
||||
},
|
||||
|
||||
getBlueprintScaffold: function (parentId, blueprintId) {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
|
||||
@@ -172,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);
|
||||
},
|
||||
|
||||
|
||||
@@ -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 "infinie mode"
|
||||
// set flag so we know when the editor is open in "infinite mode"
|
||||
editor.infiniteMode = true;
|
||||
|
||||
editors.push(editor);
|
||||
|
||||
+23
-6
@@ -32,15 +32,32 @@
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.umb-editor-sub-header.-umb-sticky-bar {
|
||||
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
|
||||
[umb-sticky-bar] {
|
||||
transition: box-shadow 240ms;
|
||||
margin-top: 0;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
top: calc(@appHeaderHeight + @editorHeaderHeight);
|
||||
position:sticky;
|
||||
z-index: 99;
|
||||
|
||||
.umb-editor--infinityMode & {
|
||||
top: calc(@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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,10 @@
|
||||
&.umb-overlay--medium {
|
||||
width: 480px;
|
||||
}
|
||||
|
||||
&.umb-overlay--large {
|
||||
width:640px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-overlay.umb-overlay-target .umb-overlay-header {
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
font-size: 14px;
|
||||
color: @black;
|
||||
margin-left: 10px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
small {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// --------------------------------------------------
|
||||
.umb-property-editor {
|
||||
width: 100%;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
.umb-property-editor-tiny {
|
||||
@@ -165,8 +166,6 @@
|
||||
.sp-replacer {
|
||||
display: inline-flex;
|
||||
margin-right: 18px;
|
||||
border: solid 1px @gray-8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
label {
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<ul class="unstyled" ui-sortable="vm.sortableOptions" ng-model="vm.blocks">
|
||||
<li ng-repeat="block in vm.blocks">
|
||||
<div class="handle">
|
||||
<div class="umb-node-preview umb-node-preview--sortable">
|
||||
<i ng-if="block.icon" class="umb-node-preview__icon {{ block.icon }}"></i>
|
||||
<div class="umb-node-preview__content">
|
||||
<div class="umb-node-preview__name" ng-attr-title="{{block.alias}}">TODO: Figure out how to name a block</div>
|
||||
<div class="umb-node-preview__description" ng-if="block.description">{{ block.description }}</div>
|
||||
</div>
|
||||
<div class="umb-node-preview__actions">
|
||||
<a class="umb-node-preview__action" title="Edit" href="" ng-click="vm.editContent(block)"><localize key="general_edit">Edit</localize></a>
|
||||
<a class="umb-node-preview__action" title="Settings" href="" ng-click="vm.onSettings({block: block})"><localize key="general_settings">Settings</localize></a>
|
||||
<a class="umb-node-preview__action umb-node-preview__action--red" title="Remove" ng-href="" ng-click="vm.remove(block)"><localize key="general_remove">Remove</localize></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
<div
|
||||
class="umb-editor-sub-header umb-editor-sub-header--{{appearance}}"
|
||||
umb-sticky-bar
|
||||
scrollable-container=".umb-editor-container"
|
||||
ng-transclude>
|
||||
</div>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
|
||||
<div class="text-center" ng-if="(availableItems | compareArrays:selectedItems:'alias').length === 0">
|
||||
<small><localize key="general_all">Akk</localize> {{itemLabel}}s <localize key="grid_areAdded">are added</localize></small>
|
||||
<small><localize key="general_all">All</localize> {{itemLabel}}s <localize key="grid_areAdded">are added</localize></small>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -94,6 +94,11 @@ function contentCreateController($scope,
|
||||
navigationService.hideDialog(showMenu);
|
||||
};
|
||||
|
||||
$scope.editContentType = function() {
|
||||
$location.path("/settings/documenttypes/edit/" + $scope.contentTypeId).search("view", "permissions");
|
||||
close();
|
||||
}
|
||||
|
||||
$scope.createBlank = createBlank;
|
||||
$scope.createOrSelectBlueprintIfAny = createOrSelectBlueprintIfAny;
|
||||
$scope.createFromBlueprint = createFromBlueprint;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<p class="abstract" ng-if="!hasSettingsAccess"><localize key="create_noDocumentTypesWithNoSettingsAccess"/></p>
|
||||
<div ng-if="hasSettingsAccess">
|
||||
<p class="abstract"><localize key="create_noDocumentTypes" /></p>
|
||||
<button class="btn umb-outline" href="#settings/documentTypes/edit/{{contentTypeId}}?view=permissions" ng-click="close()">
|
||||
<button class="btn umb-outline" ng-click="editContentType()">
|
||||
<localize key="create_noDocumentTypesEditPermissions"/>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="documentType in vm.documentTypes | orderBy:'name':false">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createBlueprint(documentType)" prevent-default>
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createBlueprint(documentType)" prevent-default>
|
||||
<i class="large icon {{documentType.icon}}"></i>
|
||||
<span class="menu-label">
|
||||
{{documentType.name}}
|
||||
@@ -19,7 +19,7 @@
|
||||
{{documentType.description}}
|
||||
</small>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -6,20 +6,20 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li data-element="action-data-type" class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="createDataType()" umb-auto-focus>
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="createDataType()" umb-auto-focus>
|
||||
<i class="large icon icon-autofill"></i>
|
||||
<span class="menu-label">
|
||||
<localize key="create_newDataType">New data type</localize>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li data-element="action-folder" class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label">
|
||||
<localize key="create_newFolder">New folder</localize>...
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -7,36 +7,36 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li data-element="action-documentType" class="umb-action" ng-hide="model.disableTemplates">
|
||||
<a href="" ng-click="createDocType()" class="umb-action-link" umb-auto-focus>
|
||||
<button href="" ng-click="createDocType()" class="umb-action-link umb-outline btn-reset" umb-auto-focus>
|
||||
<i class="large icon icon-item-arrangement"></i>
|
||||
<span class="menu-label">
|
||||
<localize key="content_documentType">Document type</localize>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li data-element="action-documentTypeWithoutTemplate" class="umb-action">
|
||||
<a href="" ng-click="createComponent()" class="umb-action-link">
|
||||
<button href="" ng-click="createComponent()" class="umb-action-link umb-outline btn-reset">
|
||||
<i class="large icon icon-item-arrangement"></i>
|
||||
<span class="menu-label">
|
||||
<localize ng-if="model.disableTemplates === false" key="create_documentTypeWithoutTemplate"></localize>
|
||||
<localize ng-if="model.disableTemplates === true" key="content_documentType">Document type></localize>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li data-element="action-documentTypeCollection" class="umb-action">
|
||||
<a href="" ng-click="showCreateDocTypeCollection()" class="umb-action-link">
|
||||
<button href="" ng-click="showCreateDocTypeCollection()" class="umb-action-link umb-outline btn-reset">
|
||||
<i class="large icon icon-thumbnail-list"></i>
|
||||
<span class="menu-label">
|
||||
Document Type Collection...
|
||||
<!-- <localize key="content_documentType_collection">Document Type Collection</localize>-->
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li data-element="action-folder" ng-if="model.allowCreateFolder" class="umb-action">
|
||||
<a href="" ng-click="showCreateFolder()" class="umb-action-link">
|
||||
<button href="" ng-click="showCreateFolder()" class="umb-action-link umb-outline btn-reset">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,11 +10,12 @@
|
||||
vm.numberOfErrors = 0;
|
||||
vm.commonLogMessages = [];
|
||||
vm.commonLogMessagesCount = 10;
|
||||
vm.dateRangeLabel = "";
|
||||
|
||||
// ChartJS Options - for count/overview of log distribution
|
||||
vm.logTypeLabels = ["Info", "Debug", "Warning", "Error", "Critical"];
|
||||
vm.logTypeLabels = ["Debug", "Info", "Warning", "Error", "Fatal"];
|
||||
vm.logTypeData = [0, 0, 0, 0, 0];
|
||||
vm.logTypeColors = [ '#dcdcdc', '#97bbcd', '#46bfbd', '#fdb45c', '#f7464a'];
|
||||
vm.logTypeColors = ['#eaddd5', '#2bc37c', '#3544b1', '#ff9412', '#d42054'];
|
||||
vm.chartOptions = {
|
||||
legend: {
|
||||
display: true,
|
||||
@@ -23,35 +24,42 @@
|
||||
};
|
||||
|
||||
let querystring = $location.search();
|
||||
if(querystring.startDate){
|
||||
if (querystring.startDate) {
|
||||
vm.startDate = querystring.startDate;
|
||||
}else{
|
||||
vm.dateRangeLabel = getDateRangeLabel("Selected Time Period");
|
||||
} else {
|
||||
vm.startDate = new Date(Date.now());
|
||||
vm.startDate.setDate(vm.startDate.getDate()-1);
|
||||
vm.startDate.setDate(vm.startDate.getDate() - 1);
|
||||
vm.startDate = vm.startDate.toIsoDateString();
|
||||
vm.dateRangeLabel = getDateRangeLabel("Today");
|
||||
}
|
||||
|
||||
if(querystring.endDate){
|
||||
if (querystring.endDate) {
|
||||
vm.endDate = querystring.endDate;
|
||||
}else{
|
||||
|
||||
if (querystring.endDate === querystring.startDate) {
|
||||
vm.dateRangeLabel = getDateRangeLabel("Selected Date");
|
||||
}
|
||||
} else {
|
||||
vm.endDate = new Date(Date.now()).toIsoDateString();
|
||||
}
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
|
||||
//functions
|
||||
vm.searchLogQuery = searchLogQuery;
|
||||
vm.findMessageTemplate = findMessageTemplate;
|
||||
|
||||
vm.searchErrors = searchErrors;
|
||||
|
||||
function preFlightCheck(){
|
||||
vm.loading = true;
|
||||
//Do our pre-flight check (to see if we can view logs)
|
||||
//IE the log file is NOT too big such as 1GB & crash the site
|
||||
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function(result){
|
||||
logViewerResource.canViewLogs(vm.startDate, vm.endDate).then(function (result) {
|
||||
vm.loading = false;
|
||||
vm.canLoadLogs = result;
|
||||
|
||||
if(result){
|
||||
if (result) {
|
||||
//Can view logs - so initalise
|
||||
init();
|
||||
}
|
||||
@@ -62,39 +70,39 @@
|
||||
function init() {
|
||||
|
||||
vm.loading = true;
|
||||
|
||||
|
||||
var savedSearches = logViewerResource.getSavedSearches().then(function (data) {
|
||||
vm.searches = data;
|
||||
},
|
||||
// fallback to some defaults if error from API response
|
||||
function () {
|
||||
vm.searches = [
|
||||
{
|
||||
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
vm.searches = data;
|
||||
},
|
||||
// fallback to some defaults if error from API response
|
||||
function () {
|
||||
vm.searches = [
|
||||
{
|
||||
"name": "Find all logs where the Level is NOT Verbose and NOT Debug",
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration'",
|
||||
"query": "Has(Duration)"
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration'",
|
||||
"query": "Has(Duration)"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
|
||||
"query": "Has(Duration) and Duration > 1000"
|
||||
{
|
||||
"name": "Find all logs that have the property 'Duration' and the duration is greater than 1000ms",
|
||||
"query": "Has(Duration) and Duration > 1000"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
|
||||
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
|
||||
{
|
||||
"name": "Find all logs that are from the namespace 'Umbraco.Core'",
|
||||
"query": "StartsWith(SourceContext, 'Umbraco.Core')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that use a specific log message template",
|
||||
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
|
||||
{
|
||||
"name": "Find all logs that use a specific log message template",
|
||||
"query": "@MessageTemplate = '[Timing {TimingId}] {EndMessage} ({TimingDuration}ms)'"
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
var numOfErrors = logViewerResource.getNumberOfErrors(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.numberOfErrors = data;
|
||||
@@ -102,41 +110,56 @@
|
||||
|
||||
var logCounts = logViewerResource.getLogLevelCounts(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.logTypeData = [];
|
||||
vm.logTypeData.push(data.Information);
|
||||
vm.logTypeData.push(data.Debug);
|
||||
vm.logTypeData.push(data.Warning);
|
||||
vm.logTypeData.push(data.Error);
|
||||
vm.logTypeData.push(data.Fatal);
|
||||
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
const index = vm.logTypeLabels.findIndex(x => key.startsWith(x));
|
||||
if (index > -1) {
|
||||
vm.logTypeData[index] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function(data){
|
||||
var commonMsgs = logViewerResource.getMessageTemplates(vm.startDate, vm.endDate).then(function (data) {
|
||||
vm.commonLogMessages = data;
|
||||
});
|
||||
|
||||
//Set loading indicatior to false when these 3 queries complete
|
||||
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function(data) {
|
||||
//Set loading indicator to false when these 3 queries complete
|
||||
$q.all([savedSearches, numOfErrors, logCounts, commonMsgs]).then(function () {
|
||||
vm.loading = false;
|
||||
});
|
||||
|
||||
$timeout(function () {
|
||||
navigationService.syncTree({ tree: "logViewer", path: "-1" });
|
||||
navigationService.syncTree({
|
||||
tree: "logViewer",
|
||||
path: "-1"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function searchLogQuery(logQuery){
|
||||
$location.path("/settings/logViewer/search").search({lq: logQuery, startDate: vm.startDate, endDate: vm.endDate});
|
||||
function searchLogQuery(logQuery) {
|
||||
$location.path("/settings/logViewer/search").search({
|
||||
lq: logQuery,
|
||||
startDate: vm.startDate,
|
||||
endDate: vm.endDate
|
||||
});
|
||||
}
|
||||
|
||||
function findMessageTemplate(template){
|
||||
function findMessageTemplate(template) {
|
||||
var logQuery = "@MessageTemplate='" + template.MessageTemplate + "'";
|
||||
searchLogQuery(logQuery);
|
||||
}
|
||||
|
||||
|
||||
|
||||
function getDateRangeLabel(suffix) {
|
||||
return "Log Overview for " + suffix;
|
||||
}
|
||||
|
||||
function searchErrors(){
|
||||
var logQuery = "@Level='Fatal' or @Level='Error' or Has(@Exception)";
|
||||
searchLogQuery(logQuery);
|
||||
}
|
||||
|
||||
preFlightCheck();
|
||||
|
||||
|
||||
/////////////////////
|
||||
|
||||
vm.config = {
|
||||
@@ -147,20 +170,21 @@
|
||||
maxDate: "today",
|
||||
conjunction: " to "
|
||||
};
|
||||
|
||||
vm.dateRangeChange = function(selectedDates, dateStr, instance) {
|
||||
|
||||
if(selectedDates.length > 0){
|
||||
vm.startDate = selectedDates[0].toIsoDateString();
|
||||
vm.endDate = selectedDates[selectedDates.length-1].toIsoDateString(); // Take the last date as end
|
||||
|
||||
if(vm.startDate === vm.endDate){
|
||||
vm.period = [vm.startDate];
|
||||
}else{
|
||||
vm.period = [vm.startDate, vm.endDate];
|
||||
}
|
||||
|
||||
preFlightCheck();
|
||||
vm.dateRangeChange = function (selectedDates, dateStr, instance) {
|
||||
|
||||
if (selectedDates.length > 0) {
|
||||
|
||||
// Update view by re-requesting route with updated querystring.
|
||||
// By doing this we make sure the URL matches the selected time period, aiding sharing the link.
|
||||
// Also resolves a minor layout issue where the " to " conjunction between the selected dates
|
||||
// is collapsed to a comma.
|
||||
const startDate = selectedDates[0].toIsoDateString();
|
||||
const endDate = selectedDates[selectedDates.length - 1].toIsoDateString(); // Take the last date as end
|
||||
$location.path("/settings/logViewer/overview").search({
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<umb-editor-view footer="false">
|
||||
|
||||
<umb-editor-header
|
||||
name="'Log Overview for Today'"
|
||||
name="vm.dateRangeLabel"
|
||||
name-locked="true"
|
||||
hide-icon="true"
|
||||
hide-description="true"
|
||||
@@ -74,17 +74,16 @@
|
||||
<umb-box>
|
||||
<umb-box-header title="Time Period"></umb-box-header>
|
||||
|
||||
<umb-flatpickr
|
||||
class="datepicker"
|
||||
ng-model="vm.period"
|
||||
options="vm.config"
|
||||
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
|
||||
<umb-flatpickr class="datepicker"
|
||||
ng-model="vm.period"
|
||||
options="vm.config"
|
||||
on-close="vm.dateRangeChange(selectedDates, dateStr, instance)">
|
||||
</umb-flatpickr>
|
||||
</umb-box>
|
||||
|
||||
<div ng-show=" vm.canLoadLogs">
|
||||
<!-- No of Errors -->
|
||||
<umb-box ng-click="vm.searchLogQuery('Has(@Exception)')" style="cursor:pointer;">
|
||||
<umb-box ng-click="vm.searchErrors()" style="cursor:pointer;">
|
||||
<umb-box-header title="Number of Errors"></umb-box-header>
|
||||
<umb-box-content class="block-form" style="font-size: 40px; font-weight:900; text-align:center; color:#fe6561;">
|
||||
{{ vm.numberOfErrors }}
|
||||
|
||||
@@ -11,26 +11,28 @@
|
||||
vm.showBackButton = true;
|
||||
vm.page = {};
|
||||
|
||||
// this array is also used to map the logTypeColor param onto the log items
|
||||
// in setLogTypeColors()
|
||||
vm.logLevels = [
|
||||
{
|
||||
name: 'Verbose',
|
||||
logTypeColor: 'gray'
|
||||
logTypeColor: ''
|
||||
},
|
||||
{
|
||||
name: 'Debug',
|
||||
logTypeColor: 'secondary'
|
||||
logTypeColor: 'gray'
|
||||
},
|
||||
{
|
||||
name: 'Information',
|
||||
logTypeColor: 'primary'
|
||||
logTypeColor: 'success'
|
||||
},
|
||||
{
|
||||
name: 'Warning',
|
||||
logTypeColor: 'warning'
|
||||
logTypeColor: 'primary'
|
||||
},
|
||||
{
|
||||
name: 'Error',
|
||||
logTypeColor: 'danger'
|
||||
logTypeColor: 'warning'
|
||||
},
|
||||
{
|
||||
name: 'Fatal',
|
||||
@@ -118,7 +120,7 @@
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
@@ -173,25 +175,8 @@
|
||||
}
|
||||
|
||||
function setLogTypeColor(logItems) {
|
||||
angular.forEach(logItems, function (log) {
|
||||
switch (log.Level) {
|
||||
case "Information":
|
||||
log.logTypeColor = "primary";
|
||||
break;
|
||||
case "Debug":
|
||||
log.logTypeColor = "secondary";
|
||||
break;
|
||||
case "Warning":
|
||||
log.logTypeColor = "warning";
|
||||
break;
|
||||
case "Fatal":
|
||||
case "Error":
|
||||
log.logTypeColor = "danger";
|
||||
break;
|
||||
default:
|
||||
log.logTypeColor = "gray";
|
||||
}
|
||||
});
|
||||
logItems.forEach(logItem =>
|
||||
logItem.logTypeColor = vm.logLevels.find(x => x.name === logItem.Level).logTypeColor);
|
||||
}
|
||||
|
||||
function getFilterName(array) {
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
|
||||
<li data-element="action-{{docType.alias}}" class="umb-action" ng-repeat="docType in allowedTypes">
|
||||
<a ng-href="" ng-click="createMediaItem(docType)" class="umb-action-link" prevent-default>
|
||||
<button ng-click="createMediaItem(docType)" class="umb-action-link umb-outline btn-reset" prevent-default>
|
||||
<i class="large icon {{docType.icon}}"></i>
|
||||
<span class="menu-label">
|
||||
{{docType.name}}
|
||||
<small>{{docType.description}}</small>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<!--
|
||||
<li class="add">
|
||||
|
||||
@@ -7,19 +7,19 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="createMediaType()" umb-auto-focus>
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="createMediaType()" umb-auto-focus>
|
||||
<i class="large icon icon-item-arrangement"></i>
|
||||
<span class="menu-label">
|
||||
<localize key="general_new">New</localize>
|
||||
<localize key="content_mediatype">Media type</localize>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
|
||||
<li class="umb-action" ng-repeat="docType in allowedTypes">
|
||||
<a class="umb-action-link" href="#member/member/edit/{{currentNode.id}}?doctype={{docType.alias}}&create=true" ng-click="hideActions()">
|
||||
<button class="umb-action-link umb-outline btn-reset" href="#member/member/edit/{{currentNode.id}}?doctype={{docType.alias}}&create=true" ng-click="hideActions()">
|
||||
<i class="large icon {{docType.icon}}"></i>
|
||||
<span class="menu-label">
|
||||
{{docType.name}}
|
||||
<small>{{docType.description}}</small>
|
||||
</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="createMemberType()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="createMemberType()">
|
||||
|
||||
<i class="large icon icon-item-arrangement"></i>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<localize key="content_memberType">Member type</localize>
|
||||
</span>
|
||||
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -11,28 +11,28 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createFile()" umb-auto-focus>
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFile()" umb-auto-focus>
|
||||
<i class="large icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacro">New partial view macro</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createFileWithoutMacro()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileWithoutMacro()">
|
||||
<i class="large icon icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroNoMacro">New partial view macro (without macro)</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFromSnippet()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()">
|
||||
<i class="large icon icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewMacroFromSnippet">>New partial view macro from snippet</localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -44,10 +44,10 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createFileFromSnippet(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createFileFromSnippet(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<i class="icon-article icon" style="font-size: 20px;"></i>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -10,22 +10,22 @@
|
||||
<div ng-if="!vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createPartialView()" umb-auto-focus>
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView()" umb-auto-focus>
|
||||
<i class="large icon icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newEmptyPartialView">New empty partial view</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFromSnippet()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFromSnippet()">
|
||||
<i class="large icon icon-article"></i>
|
||||
<span class="menu-label"><localize key="create_newPartialViewFromSnippet">New partial view from snippet</localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@@ -34,10 +34,10 @@
|
||||
<div ng-if="vm.showSnippets">
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action" ng-repeat="snippet in vm.snippets">
|
||||
<a href="" class="umb-action-link" ng-click="vm.createPartialView(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.createPartialView(snippet)" style="padding-top: 6px; padding-bottom: 6px;">
|
||||
<i class="icon-article icon" style="font-size: 20px;"></i>
|
||||
<span class="menu-label" style="margin-left: 0; padding-left: 5px;">{{ snippet.name }}</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<div>
|
||||
<umb-editor-view>
|
||||
|
||||
<umb-editor-header name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container class="block-form">
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
|
||||
<div class="content-type-editor-dialog edit-property-settings">
|
||||
|
||||
<div class="umb-control-group" ng-if="!model.property.locked">
|
||||
<div class="control-group">
|
||||
<textarea class="editor-label"
|
||||
data-element="property-name"
|
||||
name="propertyLabel"
|
||||
ng-model="model.property.label"
|
||||
localize="placeholder"
|
||||
placeholder="@placeholders_entername"
|
||||
umb-auto-focus
|
||||
focus-on-filled="true"
|
||||
umb-auto-resize
|
||||
required
|
||||
ng-keypress="submitOnEnter($event)">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<div class="control-group -no-margin">
|
||||
<umb-generate-alias enable-lock="true" alias-from="model.property.label" alias="model.property.alias"></umb-generate-alias>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="editor-wrapper umb-control-group control-group" ng-model="model.property.editor" val-require-component ng-if="!model.property.locked">
|
||||
|
||||
<a data-element="editor-add" href="" ng-if="!model.property.editor" class="editor-placeholder" hotkey="alt+shift+e" ng-click="openDataTypePicker(model.property)">
|
||||
<localize key="shortcuts_addEditor"></localize>
|
||||
</a>
|
||||
|
||||
<div class="editor clearfix" ng-if="model.property.editor">
|
||||
|
||||
<a href="" class="editor-icon-wrapper" ng-click="openDataTypePicker(model.property)">
|
||||
<i class="icon {{ model.property.dataTypeIcon }}" ng-class="{'icon-autofill': model.property.dataTypeIcon == null}"></i>
|
||||
</a>
|
||||
|
||||
<div class="editor-details">
|
||||
<a href="" class="editor-name" ng-click="openDataTypePicker(model.property)" ng-bind="model.property.dataTypeName"></a>
|
||||
<a href="" class="editor-editor" ng-click="openDataTypePicker(model.property)" ng-bind="model.property.editor"></a>
|
||||
</div>
|
||||
|
||||
<a href class="editor-settings-icon pull-right"
|
||||
ng-click="vm.openDataTypeSettings(model.property)"
|
||||
hotkey="alt+shift+d"
|
||||
ng-if="model.property.editor">
|
||||
<i class="icon icon-settings"></i>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
label-key="general_close"
|
||||
shortcut="esc"
|
||||
action="close()">
|
||||
</umb-button>
|
||||
<umb-button type="button"
|
||||
button-style="success"
|
||||
label-key="general_submit"
|
||||
state="saveButtonState"
|
||||
action="submit(model)">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
</div>
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function BlockEditorController($scope, contentResource, editorService, iconHelper, clipboardService) {
|
||||
var vm = this;
|
||||
|
||||
vm.scaffolds = [];
|
||||
vm.loading = true;
|
||||
|
||||
let allowedElements = [];
|
||||
|
||||
// it would be awesome if we could load all scaffolds in one go... however we need to have an eye out for performance,
|
||||
// oddly enough it's been shown to actually be slower to load them all at once instead of one at a time
|
||||
var scaffoldsLoaded = 0;
|
||||
|
||||
function init() {
|
||||
|
||||
_.each($scope.model.config.blocks, function (config) {
|
||||
contentResource.getScaffoldByUdi(-20, config.elementType).then(function (scaffold) {
|
||||
if (scaffold.isElement) {
|
||||
// the scaffold udi is not the same as the element type udi, but we need it to be for comparison
|
||||
scaffold.udi = config.elementType;
|
||||
vm.scaffolds.push(scaffold);
|
||||
}
|
||||
scaffoldsLoaded++;
|
||||
initIfAllScaffoldsHaveLoaded();
|
||||
}, function (error) {
|
||||
scaffoldsLoaded++;
|
||||
initIfAllScaffoldsHaveLoaded();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function initIfAllScaffoldsHaveLoaded() {
|
||||
// Initialize when all scaffolds have loaded
|
||||
if ($scope.model.config.blocks.length === scaffoldsLoaded) {
|
||||
vm.scaffolds = _.sortBy(vm.scaffolds, function (scaffold) {
|
||||
return _.findIndex($scope.model.config.blocks, function (blockConfig) {
|
||||
return blockConfig.elementType === scaffold.udi;
|
||||
});
|
||||
});
|
||||
|
||||
_.each($scope.model.value, function (block) {
|
||||
applyFakeSettings(block);
|
||||
});
|
||||
|
||||
allowedElements = vm.scaffolds.map(x => x.contentTypeAlias);
|
||||
|
||||
vm.loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
vm.openNodeTypePicker = function ($event) {
|
||||
|
||||
vm.overlayMenu = {
|
||||
show: false,
|
||||
style: {},
|
||||
filter: vm.scaffolds.length > 12 ? true : false,
|
||||
orderBy: "$index",
|
||||
view: "itempicker",
|
||||
event: $event,
|
||||
clickPasteItem: function (item) {
|
||||
vm.pasteFromClipboard(item.data);
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
},
|
||||
submit: function (model) {
|
||||
if (model && model.selectedItem) {
|
||||
vm.add(vm.scaffolds.filter(x => x.contentTypeAlias === model.selectedItem.alias)[0]);
|
||||
}
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
},
|
||||
close: function () {
|
||||
vm.overlayMenu.show = false;
|
||||
vm.overlayMenu = null;
|
||||
}
|
||||
};
|
||||
|
||||
// this could be used for future limiting on node types
|
||||
vm.overlayMenu.availableItems = [];
|
||||
vm.scaffolds.forEach(function (scaffold) {
|
||||
vm.overlayMenu.availableItems.push({
|
||||
alias: scaffold.contentTypeAlias,
|
||||
name: scaffold.contentTypeName,
|
||||
icon: iconHelper.convertFromLegacyIcon(scaffold.icon),
|
||||
description: scaffold.documentType.description
|
||||
});
|
||||
});
|
||||
|
||||
if (vm.overlayMenu.availableItems.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: If we support larger thumbnails of some sort one day, this could be large
|
||||
vm.overlayMenu.size = vm.overlayMenu.availableItems.length > 6 ? "medium" : "small";
|
||||
|
||||
vm.overlayMenu.pasteItems = [];
|
||||
var availableNodesForPaste = clipboardService.retriveDataOfType("elementType", allowedElements);
|
||||
availableNodesForPaste.forEach(function (node) {
|
||||
vm.overlayMenu.pasteItems.push({
|
||||
alias: node.contentTypeAlias,
|
||||
name: node.name, //contentTypeName
|
||||
data: node,
|
||||
icon: iconHelper.convertFromLegacyIcon(node.icon)
|
||||
});
|
||||
});
|
||||
|
||||
vm.overlayMenu.clickClearPaste = function ($event) {
|
||||
$event.stopPropagation();
|
||||
$event.preventDefault();
|
||||
clipboardService.clearEntriesOfType("elementType", allowedElements);
|
||||
vm.overlayMenu.pasteItems = []; // This dialog is not connected via the clipboardService events, so we need to update manually.
|
||||
};
|
||||
|
||||
if (vm.overlayMenu.availableItems.length === 1 && vm.overlayMenu.pasteItems.length === 0) {
|
||||
// only one scaffold type - no need to display the picker
|
||||
vm.add(vm.scaffolds[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
vm.overlayMenu.show = true;
|
||||
}
|
||||
|
||||
vm.add = function (scaffold) {
|
||||
var element = angular.copy(scaffold);
|
||||
var block = {
|
||||
udi: element.udi,
|
||||
icon: element.icon,
|
||||
description: element.documentType.description, // probably don't want to persist these.
|
||||
content: {},
|
||||
settings: {
|
||||
view: 'views/propertyeditors/blockeditor/blockeditor.block.html'
|
||||
}
|
||||
};
|
||||
openContent(element, block);
|
||||
}
|
||||
|
||||
vm.editSettings = function (block) {
|
||||
var options = {
|
||||
settings: block.settings,
|
||||
title: "Edit settings",
|
||||
view: "views/propertyeditors/blockeditor/blockeditor.editsettings.html",
|
||||
size: "small",
|
||||
submit: function (model) {
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
editorService.open(options);
|
||||
}
|
||||
|
||||
vm.onRemove = function (block) {
|
||||
$scope.model.value.splice($scope.model.value.indexOf(block), 1);
|
||||
}
|
||||
|
||||
vm.onEdit = function (element, block) {
|
||||
openContent(element, block);
|
||||
}
|
||||
|
||||
function openContent(element, block) {
|
||||
var options = {
|
||||
element: element,
|
||||
title: 'Edit block',
|
||||
//fixme: This isn't really a component if its strongly tied to views in the property editor :/
|
||||
view: "views/propertyeditors/blockeditor/blockeditor.editcontent.html",
|
||||
submit: function (model) {
|
||||
_.each(element.variants[0].tabs, function (tab) {
|
||||
_.each(tab.properties, function (property) {
|
||||
block.content[property.alias] = property.value;
|
||||
});
|
||||
});
|
||||
if ($scope.model.value.indexOf(block) < 0) {
|
||||
$scope.model.value.push(block);
|
||||
applyFakeSettings(block);
|
||||
}
|
||||
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
editorService.open(options);
|
||||
}
|
||||
|
||||
// TODO: remove this (only for testing)
|
||||
// currently pushes random values for setting a css grid location
|
||||
function applyFakeSettings(block) {
|
||||
block.settings["col"] = 1 + Math.floor(Math.random() * 7);
|
||||
block.settings["row"] = 1 + Math.floor(Math.random() * 7);
|
||||
block.settings["w"] = 1 + Math.floor(Math.random() * 3);
|
||||
block.settings["h"] = 1 + Math.floor(Math.random() * 3);
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditorController", BlockEditorController);
|
||||
|
||||
})();
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.PropertyEditors.BlockEditor.EditContentController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for editing content with the block editor
|
||||
*/
|
||||
|
||||
function BlockEditorEditContentController($scope) {
|
||||
var vm = this;
|
||||
vm.submit = submit;
|
||||
vm.close = close;
|
||||
vm.content = $scope.model.element.variants[0];
|
||||
|
||||
function init() {
|
||||
|
||||
}
|
||||
|
||||
function submit() {
|
||||
if($scope.model.submit) {
|
||||
$scope.model.submit($scope.model);
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
if($scope.model.close) {
|
||||
$scope.model.close();
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.EditContentController", BlockEditorEditContentController);
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.BlockEditor.EditContentController as vm">
|
||||
<umb-editor-view>
|
||||
<umb-editor-header name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
<umb-load-indicator ng-if="vm.loading" />
|
||||
|
||||
<div ng-if="!loading">
|
||||
<ng-form name="editBlockForm" val-form-manager>
|
||||
<umb-tabbed-content content="vm.content" />
|
||||
</ng-form>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
label-key="general_cancel"
|
||||
shortcut="esc"
|
||||
action="vm.close()">
|
||||
</umb-button>
|
||||
<umb-button type="button"
|
||||
button-style="success"
|
||||
label-key="general_ok"
|
||||
action="vm.submit()">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</div>
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.PropertyEditors.BlockEditor.EditSettingsController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for editing the configuration of an individual block
|
||||
*/
|
||||
|
||||
//fixme: Need to figure out the name of a block settings, is it block config or block settings?
|
||||
function BlockEditorEditSettingsController($scope) {
|
||||
|
||||
this.submit = () => {
|
||||
if($scope.model.submit) {
|
||||
$scope.model.submit($scope.model);
|
||||
}
|
||||
}
|
||||
|
||||
this.close = () => {
|
||||
if($scope.model.close) {
|
||||
$scope.model.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.BlockEditor.EditSettingsController", ['$scope', BlockEditorEditSettingsController]);
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.BlockEditor.EditSettingsController as vm">
|
||||
<umb-editor-view>
|
||||
<umb-editor-header name="model.title"
|
||||
name-locked="true"
|
||||
hide-alias="true"
|
||||
hide-icon="true"
|
||||
hide-description="true">
|
||||
</umb-editor-header>
|
||||
|
||||
<umb-editor-container>
|
||||
<umb-load-indicator ng-if="vm.loading" />
|
||||
|
||||
<div ng-if="!loading">
|
||||
<umb-box>
|
||||
<umb-box-content>
|
||||
<ng-form name="editBlockForm" class="form-horizontal" val-form-manager>
|
||||
<umb-control-group label="{{key}}" description="" ng-repeat="(key, value) in model.settings">
|
||||
<input type="text" ng-model="model.settings[key]" ng-model-options="{ debounce: 5000 }"/>
|
||||
</umb-control-group>
|
||||
</ng-form>
|
||||
</umb-box-content>
|
||||
</umb-box>
|
||||
</div>
|
||||
</umb-editor-container>
|
||||
|
||||
<umb-editor-footer>
|
||||
<umb-editor-footer-content-right>
|
||||
<umb-button type="button"
|
||||
button-style="link"
|
||||
label-key="general_cancel"
|
||||
shortcut="esc"
|
||||
action="vm.close()">
|
||||
</umb-button>
|
||||
<umb-button type="button"
|
||||
button-style="success"
|
||||
label-key="general_ok"
|
||||
action="vm.submit()">
|
||||
</umb-button>
|
||||
</umb-editor-footer-content-right>
|
||||
</umb-editor-footer>
|
||||
|
||||
</umb-editor-view>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.BlockEditorController as vm">
|
||||
<div style="position:relative" ng-if="vm.loading">
|
||||
<umb-load-indicator />
|
||||
</div>
|
||||
|
||||
<div ng-if="!vm.loading">
|
||||
<umb-block-list blocks="model.value"
|
||||
config="model.config.blocks"
|
||||
scaffolds="vm.scaffolds"
|
||||
on-edit="vm.onEdit(element, block)"
|
||||
on-remove="vm.onRemove(element, block)"
|
||||
on-settings="vm.editSettings(block)">
|
||||
</umb-block-list>
|
||||
</div>
|
||||
|
||||
<div ng-if="!vm.loading" class="umb-nested-content__footer-bar">
|
||||
<a href class="umb-nested-content__add-content" ng-class="{ '--disabled': !vm.scaffolds.length }" ng-click="vm.openNodeTypePicker($event)" prevent-default>
|
||||
<localize key="grid_addElement" ng-if="vm.scaffolds.length > 1"></localize>
|
||||
<span ng-if="vm.scaffolds.length === 1">
|
||||
<localize key="general_add"></localize> {{ vm.scaffolds[0].contentTypeName }}
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<umb-overlay ng-if="vm.overlayMenu.show"
|
||||
position="target"
|
||||
size="vm.overlayMenu.size"
|
||||
view="vm.overlayMenu.view"
|
||||
model="vm.overlayMenu">
|
||||
</umb-overlay>
|
||||
</div>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @ngdoc controller
|
||||
* @name Umbraco.PropertyEditors.BlockEditor.SettingsController
|
||||
* @function
|
||||
*
|
||||
* @description
|
||||
* The controller for the block editor's data type configuration
|
||||
*/
|
||||
|
||||
//fixme: Rename this! it shouldn't be called Settings, it should be called Configuration because that is what we call pre-values in v8 (data type config)
|
||||
angular.module("umbraco")
|
||||
.controller("Umbraco.PropertyEditors.BlockEditor.SettingsController", [
|
||||
"$scope",
|
||||
"contentTypeResource",
|
||||
"editorService",
|
||||
function ($scope, contentTypeResource, editorService) {
|
||||
|
||||
$scope.blocks = [];
|
||||
$scope.elementTypes = [];
|
||||
|
||||
if ($scope.model.value) {
|
||||
$scope.blocks = $scope.model.value;
|
||||
} else {
|
||||
$scope.model.value = [];
|
||||
}
|
||||
|
||||
contentTypeResource.getAll()
|
||||
.then(function (contentType) {
|
||||
$scope.elementTypes.push(contentType);
|
||||
});
|
||||
|
||||
$scope.$on("formSubmitting", function () {
|
||||
$scope.model.value = $scope.blocks;
|
||||
});
|
||||
|
||||
//fixme: This shouldn't be a method, using methods to return values in angular is a performance issue
|
||||
$scope.getBlockName = function(udi) {
|
||||
return 'TODO: Get block name from somewhere...';
|
||||
}
|
||||
|
||||
$scope.addBlock = function () {
|
||||
|
||||
var block = {};
|
||||
|
||||
var pickerOptions = {
|
||||
title: "Pick an element type",
|
||||
view: "views/common/infiniteeditors/treepicker/treepicker.html",
|
||||
size: "small",
|
||||
multiPicker: false,
|
||||
section: "settings",
|
||||
treeAlias: "documentTypes",
|
||||
entityType: "documentType",
|
||||
submit: function (model) {
|
||||
_.each(model.selection, function (elementType) {
|
||||
block.elementType = elementType.udi;
|
||||
block.settings = {
|
||||
view: 'views/propertyeditors/blockeditor/blockeditor.block.html'
|
||||
}
|
||||
$scope.model.value.push(block);
|
||||
});
|
||||
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
|
||||
editorService.open(pickerOptions);
|
||||
}
|
||||
|
||||
$scope.removeBlock = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
};
|
||||
|
||||
//fixme: This currently doesn't work or do anything
|
||||
$scope.editSettings = function (block) {
|
||||
|
||||
var pickerOptions = {
|
||||
title: "Block settings",
|
||||
//fixme: This file needs to be renamed since it's inconsistent with other blockeditor file names, probably blockeditor.blocksettings.html
|
||||
view: "views/propertyeditors/blockeditor/block.settings.html",
|
||||
size: "small",
|
||||
submit: function (model) {
|
||||
block.settings = model;
|
||||
editorService.close();
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
}
|
||||
};
|
||||
|
||||
editorService.open(pickerOptions);
|
||||
}
|
||||
}
|
||||
]);
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<div ng-controller="Umbraco.PropertyEditors.BlockEditor.SettingsController">
|
||||
|
||||
<umb-node-preview ng-repeat="block in blocks"
|
||||
name="getBlockName(block)"
|
||||
alias="block.elementType"
|
||||
icon="icon-folder"
|
||||
allow-edit="true"
|
||||
on-edit="editSettings(block)"
|
||||
allow-remove="true"
|
||||
on-remove="removeBlock($index)">
|
||||
</umb-node-preview>
|
||||
|
||||
<a href="" class="umb-node-preview-add" ng-click="addBlock()">
|
||||
<localize key="general_add">Add</localize>
|
||||
</a>
|
||||
|
||||
<hr />
|
||||
<pre>{{model.value | json}}</pre>
|
||||
|
||||
</div>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
function BlockEditorSimpleListController($scope) {
|
||||
$scope.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
handle: ".handle",
|
||||
tolerance: 'pointer'
|
||||
};
|
||||
}
|
||||
angular.module("umbraco").controller("Umbraco.PropertyEditors.BlockEditor.SimpleListController", ['$scope', BlockEditorSimpleListController]);
|
||||
@@ -307,22 +307,22 @@ angular.module("umbraco")
|
||||
$scope.openEditorOverlay = function (event, area, index, key) {
|
||||
var title = "";
|
||||
localizationService.localize("grid_insertControl").then(function (value) {
|
||||
title = value;
|
||||
$scope.editorOverlay = {
|
||||
view: "itempicker",
|
||||
filter: area.$allowedEditors.length > 15,
|
||||
title: title,
|
||||
availableItems: area.$allowedEditors,
|
||||
event: event,
|
||||
show: true,
|
||||
submit: function (model) {
|
||||
if (model.selectedItem) {
|
||||
$scope.addControl(model.selectedItem, area, index);
|
||||
$scope.editorOverlay.show = false;
|
||||
$scope.editorOverlay = null;
|
||||
title = value;
|
||||
$scope.editorOverlay = {
|
||||
view: "itempicker",
|
||||
filter: area.$allowedEditors.length > 15,
|
||||
title: title,
|
||||
availableItems: area.$allowedEditors,
|
||||
event: event,
|
||||
show: true,
|
||||
submit: function (model) {
|
||||
if (model.selectedItem) {
|
||||
$scope.addControl(model.selectedItem, area, index);
|
||||
$scope.editorOverlay.show = false;
|
||||
$scope.editorOverlay = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
+4
-3
@@ -18,7 +18,7 @@
|
||||
$scope.remove = function (index) {
|
||||
$scope.model.value.splice(index, 1);
|
||||
}
|
||||
|
||||
|
||||
$scope.sortableOptions = {
|
||||
axis: "y",
|
||||
cursor: "move",
|
||||
@@ -184,7 +184,8 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
$scope.overlayMenu.availableItems.push({
|
||||
alias: scaffold.contentTypeAlias,
|
||||
name: scaffold.contentTypeName,
|
||||
icon: iconHelper.convertFromLegacyIcon(scaffold.icon)
|
||||
icon: iconHelper.convertFromLegacyIcon(scaffold.icon),
|
||||
description: scaffold.documentType.description
|
||||
});
|
||||
});
|
||||
|
||||
@@ -192,7 +193,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small";
|
||||
$scope.overlayMenu.size = $scope.overlayMenu.availableItems.length > 6 ? "medium" : "small";
|
||||
|
||||
$scope.overlayMenu.pasteItems = [];
|
||||
var availableNodesForPaste = clipboardService.retriveDataOfType("elementType", contentTypeAliases);
|
||||
|
||||
+2
-1
@@ -40,7 +40,8 @@
|
||||
|
||||
<div class="umb-nested-content__footer-bar" ng-hide="hasContentTypes === false || nodes.length >= maxItems">
|
||||
<a href class="umb-nested-content__add-content" ng-class="{ '--disabled': !scaffolds.length }" ng-click="openNodeTypePicker($event)" prevent-default>
|
||||
<localize key="grid_addElement"></localize>
|
||||
<localize key="grid_addElement" ng-if="scaffolds.length > 1"></localize>
|
||||
<span ng-if="scaffolds.length === 1"><localize key="general_add"></localize> {{ scaffolds[0].contentTypeName }}</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -7,16 +7,16 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" umb-auto-focus ng-click="vm.createFile()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" umb-auto-focus ng-click="vm.createFile()">
|
||||
<i class="large icon icon-script"></i>
|
||||
<span class="menu-label"><localize key="create_newJavascriptFile">New javascript file</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -7,22 +7,22 @@
|
||||
|
||||
<ul class="umb-actions umb-actions-child">
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" umb-auto-focus ng-click="vm.createFile()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" umb-auto-focus ng-click="vm.createFile()">
|
||||
<i class="large icon icon-script"></i>
|
||||
<span class="menu-label"><localize key="create_newStyleSheetFile">New style sheet file</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" ng-click="vm.createRichtextStyle()" class="umb-action-link">
|
||||
<button href="" ng-click="vm.createRichtextStyle()" class="umb-action-link umb-outline btn-reset">
|
||||
<i class="large icon icon-script"></i>
|
||||
<span class="menu-label"><localize key="create_newRteStyleSheetFile">New richtext style sheet file</localize></span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li class="umb-action">
|
||||
<a href="" class="umb-action-link" ng-click="vm.showCreateFolder()">
|
||||
<button href="" class="umb-action-link umb-outline btn-reset" ng-click="vm.showCreateFolder()">
|
||||
<i class="large icon icon-folder"></i>
|
||||
<span class="menu-label"><localize key="general_folder"></localize>...</span>
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@
|
||||
ng-repeat="node in model.user.startContentIds"
|
||||
icon="node.icon"
|
||||
name="node.name"
|
||||
allow-remove="true"
|
||||
on-remove="model.removeSelectedItem($index, model.user.startContentIds)">
|
||||
</umb-node-preview>
|
||||
|
||||
@@ -114,6 +115,7 @@
|
||||
ng-repeat="node in model.user.startMediaIds"
|
||||
icon="node.icon"
|
||||
name="node.name"
|
||||
allow-remove="true"
|
||||
on-remove="model.removeSelectedItem($index, model.user.startMediaIds)">
|
||||
</umb-node-preview>
|
||||
|
||||
|
||||
@@ -345,9 +345,9 @@
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>8110</DevelopmentServerPort>
|
||||
<DevelopmentServerPort>8200</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>http://localhost:8110</IISUrl>
|
||||
<IISUrl>http://localhost:8200</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>False</UseCustomServer>
|
||||
<CustomServerUrl>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"query": "Not(@Level='Verbose') and Not(@Level='Debug')"
|
||||
},
|
||||
{
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Critical with Exceptions)",
|
||||
"name": "Find all logs that has an exception property (Warning, Error & Fatal with Exceptions)",
|
||||
"query": "Has(@Exception)"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,12 +26,13 @@
|
||||
<add key="serilog:write-to:File.restrictedToMinimumLevel" value="Debug" />
|
||||
<add key="serilog:write-to:File.retainedFileCountLimit" value="32" />--> <!-- Number of log files to keep (or remove value to keep all files) -->
|
||||
<!--<add key="serilog:write-to:File.rollingInterval" value="Day" />--> <!-- Create a new log file every Minute/Hour/Day/Month/Year/infinite -->
|
||||
<!--<add key="serilog:write-to:File.outputTemplate" value="{Timestamp:yyyy-MM-dd HH:mm:ss,fff} [P{ProcessId}/D{AppDomainId}/T{ThreadId}] {Log4NetLevel} {SourceContext} - {Message:lj}{NewLine}{Exception}" /> -->
|
||||
|
||||
<!-- Filters all above sink's to use this expression -->
|
||||
<!-- Common use case is to include SourceType starting with your own namespace -->
|
||||
<!--
|
||||
<add key="serilog:using:FilterExpressions" value="Serilog.Filters.Expressions" />
|
||||
<add key="serilog:filter:ByIncluding.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
|
||||
<add key="serilog:filter:ByIncludingOnly.expression" value="StartsWith(SourceContext, 'Umbraco.Core')" />
|
||||
-->
|
||||
|
||||
</appSettings>
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
|
||||
<security>
|
||||
<!-- set to true to auto update login interval (and there by disabling the lock screen -->
|
||||
<keepUserLoggedIn>false</keepUserLoggedIn>
|
||||
<keepUserLoggedIn>true</keepUserLoggedIn>
|
||||
<!-- by default this is true and if not specified in config will be true. set to false to always show a separate username field in the back office user editor -->
|
||||
<usernameIsEmail>true</usernameIsEmail>
|
||||
<!-- change in 4.8: Disabled users are now showed dimmed and last in the tree. If you prefer not to display them set this to true -->
|
||||
|
||||
@@ -354,12 +354,34 @@ namespace Umbraco.Web.Editors
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return EmptyContent(parentId, contentType);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an empty content item for the
|
||||
/// </summary>
|
||||
/// <param name="udi"></param>
|
||||
/// <param name="parentId"></param>
|
||||
[OutgoingEditorModelEvent]
|
||||
public ContentItemDisplay GetEmpty(GuidUdi udi, int parentId)
|
||||
{
|
||||
var contentType = Services.ContentTypeService.Get(udi.Guid);
|
||||
if(contentType == null)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
return EmptyContent(parentId, contentType);
|
||||
}
|
||||
|
||||
private ContentItemDisplay EmptyContent(int parentId, IContentType contentType)
|
||||
{
|
||||
var emptyContent = Services.ContentService.Create("", parentId, contentType.Alias, Security.GetUserId().ResultOr(0));
|
||||
var mapped = MapToDisplay(emptyContent);
|
||||
// translate the content type name if applicable
|
||||
mapped.ContentTypeName = Services.TextService.UmbracoDictionaryTranslate(mapped.ContentTypeName);
|
||||
// if your user type doesn't have access to the Settings section it would not get this property mapped
|
||||
if (mapped.DocumentType != null)
|
||||
if(mapped.DocumentType != null)
|
||||
mapped.DocumentType.Name = Services.TextService.UmbracoDictionaryTranslate(mapped.DocumentType.Name);
|
||||
|
||||
//remove the listview app if it exists
|
||||
|
||||
@@ -74,6 +74,7 @@ namespace Umbraco.Web.Editors
|
||||
//id is passed in eventually we'll probably want to support GUID + Udi too
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPagedChildren", "id", typeof(int), typeof(string)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetPath", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetUrlAndAnchors", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetById", "id", typeof(int), typeof(Guid), typeof(Udi)),
|
||||
new ParameterSwapControllerActionSelector.ParameterSwapInfo("GetByIds", "ids", typeof(int[]), typeof(Guid[]), typeof(Udi[]))));
|
||||
}
|
||||
@@ -288,7 +289,16 @@ namespace Umbraco.Web.Editors
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public UrlAndAnchors GetUrlAndAnchors([FromUri]int id, [FromUri]string culture = "*")
|
||||
public UrlAndAnchors GetUrlAndAnchors(Udi id, string culture = "*")
|
||||
{
|
||||
var intId = Services.EntityService.GetId(id);
|
||||
if (!intId.Success)
|
||||
throw new HttpResponseException(HttpStatusCode.NotFound);
|
||||
|
||||
return GetUrlAndAnchors(intId.Result, culture);
|
||||
}
|
||||
[HttpGet]
|
||||
public UrlAndAnchors GetUrlAndAnchors(int id, string culture = "*")
|
||||
{
|
||||
var url = UmbracoContext.UrlProvider.GetUrl(id);
|
||||
var anchorValues = Services.ContentService.GetAnchorValuesFromRTEs(id, culture);
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace Umbraco.Web.Editors
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check if the index has been rebuilt
|
||||
@@ -250,7 +250,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
private void Indexer_IndexOperationComplete(object sender, EventArgs e)
|
||||
{
|
||||
var indexer = (LuceneIndex)sender;
|
||||
var indexer = (IIndex)sender;
|
||||
|
||||
_logger.Debug<ExamineManagementController>("Logging operation completed for index {IndexName}", indexer.Name);
|
||||
|
||||
@@ -259,7 +259,7 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
_logger
|
||||
.Info<ExamineManagementController
|
||||
>($"Rebuilding index '{indexer.Name}' done, {indexer.CommitCount} items committed (can differ from the number of items in the index)");
|
||||
>($"Rebuilding index '{indexer.Name}' done.");
|
||||
|
||||
var cacheKey = "temp_indexing_op_" + indexer.Name;
|
||||
_runtimeCache.Clear(cacheKey);
|
||||
|
||||
@@ -15,11 +15,11 @@ namespace Umbraco.Web.Editors
|
||||
[PluginController("UmbracoApi")]
|
||||
public class LogViewerController : UmbracoAuthorizedJsonController
|
||||
{
|
||||
private ILogViewer _logViewer;
|
||||
private readonly ILogViewer _logViewer;
|
||||
|
||||
public LogViewerController(ILogViewer logViewer)
|
||||
{
|
||||
_logViewer = logViewer;
|
||||
_logViewer = logViewer ?? throw new ArgumentNullException(nameof(logViewer));
|
||||
}
|
||||
|
||||
private bool CanViewLogs(LogTimePeriod logTimePeriod)
|
||||
@@ -91,8 +91,6 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
var direction = orderDirection == "Descending" ? Direction.Descending : Direction.Ascending;
|
||||
|
||||
|
||||
|
||||
return _logViewer.GetLogs(logTimePeriod, filterExpression: filterExpression, pageNumber: pageNumber, orderDirection: direction, logLevels: logLevels);
|
||||
}
|
||||
|
||||
|
||||
@@ -223,7 +223,6 @@ namespace Umbraco.Web
|
||||
_method = method;
|
||||
_controllerName = controllerName;
|
||||
_encryptedString = UrlHelperRenderExtensions.CreateEncryptedRouteString(controllerName, controllerAction, area, additionalRouteVals);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -39,10 +39,14 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">Optional culture.</param>
|
||||
/// <param name="indexName">Optional index name.</param>
|
||||
/// <remarks>
|
||||
/// <para>When the <paramref name="culture"/> is not specified, all cultures are searched.</para>
|
||||
/// <para>
|
||||
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<PublishedSearchResult> Search(string term, string culture = null, string indexName = null);
|
||||
IEnumerable<PublishedSearchResult> Search(string term, string culture = "*", string indexName = null);
|
||||
|
||||
/// <summary>
|
||||
/// Searches content.
|
||||
@@ -54,10 +58,14 @@ namespace Umbraco.Web
|
||||
/// <param name="culture">Optional culture.</param>
|
||||
/// <param name="indexName">Optional index name.</param>
|
||||
/// <remarks>
|
||||
/// <para>When the <paramref name="culture"/> is not specified, all cultures are searched.</para>
|
||||
/// <para>
|
||||
/// When the <paramref name="culture"/> is not specified or is *, all cultures are searched.
|
||||
/// To search for only invariant documents and fields use null.
|
||||
/// When searching on a specific culture, all culture specific fields are searched for the provided culture and all invariant fields for all documents.
|
||||
/// </para>
|
||||
/// <para>While enumerating results, the ambient culture is changed to be the searched culture.</para>
|
||||
/// </remarks>
|
||||
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = null, string indexName = null);
|
||||
IEnumerable<PublishedSearchResult> Search(string term, int skip, int take, out long totalRecords, string culture = "*", string indexName = null);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the query and converts the results to PublishedSearchResult.
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
target.Avatars = source.GetUserAvatarUrls(_appCaches.RuntimeCache);
|
||||
target.Culture = source.GetUserCulture(_textService, _globalSettings).ToString();
|
||||
target.Email = source.Email;
|
||||
target.EmailHash = source.Email.ToLowerInvariant().Trim().ToMd5();
|
||||
target.EmailHash = source.Email.ToLowerInvariant().Trim().GenerateHash();
|
||||
target.Id = source.Id;
|
||||
target.Key = source.Key;
|
||||
target.LastLoginDate = source.LastLoginDate == default ? null : (DateTime?) source.LastLoginDate;
|
||||
|
||||
+1
-11
@@ -12,7 +12,7 @@ namespace Umbraco.Web.Properties {
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.2.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
@@ -50,15 +50,5 @@ namespace Umbraco.Web.Properties {
|
||||
return ((string)(this["test"]));
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.ApplicationScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.SpecialSettingAttribute(global::System.Configuration.SpecialSetting.WebServiceUrl)]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("https://our.umbraco.com/umbraco/webservices/api/repository.asmx")]
|
||||
public string umbraco_org_umbraco_our_Repository {
|
||||
get {
|
||||
return ((string)(this["umbraco_org_umbraco_our_Repository"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,5 @@
|
||||
<Setting Name="test" Type="System.String" Scope="Application">
|
||||
<Value Profile="(Default)">Something</Value>
|
||||
</Setting>
|
||||
<Setting Name="umbraco_org_umbraco_our_Repository" Type="(Web Service URL)" Scope="Application">
|
||||
<Value Profile="(Default)">https://our.umbraco.com/umbraco/webservices/api/repository.asmx</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
@@ -0,0 +1,36 @@
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
public class BlockEditorConfiguration
|
||||
{
|
||||
[ConfigurationField("blocks", "Blocks", "views/propertyeditors/blockeditor/blockeditor.settings.html")]
|
||||
public Block[] Blocks { get; set; }
|
||||
|
||||
[ConfigurationField("view", "View", "textstring", Description = "The path to a custom view for rendering the editor")]
|
||||
public string View { get; set; }
|
||||
|
||||
public class Block
|
||||
{
|
||||
[JsonProperty("elementType")]
|
||||
public Udi ElementType { get; set; }
|
||||
|
||||
[JsonProperty("settings")]
|
||||
public BlockSetting Settings { get; set; }
|
||||
}
|
||||
|
||||
public class BlockSetting
|
||||
{
|
||||
[JsonProperty("label")]
|
||||
public string Label { get; set; }
|
||||
|
||||
[JsonProperty("alias")]
|
||||
public string Alias { get; set; }
|
||||
|
||||
[JsonProperty("dataType")]
|
||||
public Udi DataType { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
public class BlockEditorConfigurationEditor : ConfigurationEditor<BlockEditorConfiguration>
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.BlockEditor, EditorType.PropertyValue , "Block Editor", "blockeditor", ValueType = ValueTypes.Json, Group="rich content", Icon="icon-application-window-alt")]
|
||||
public class BlockEditorPropertyEditor : DataEditor
|
||||
{
|
||||
public BlockEditorPropertyEditor(ILogger logger)
|
||||
: base(logger)
|
||||
{ }
|
||||
|
||||
protected override IConfigurationEditor CreateConfigurationEditor() => new BlockEditorConfigurationEditor();
|
||||
|
||||
protected override IDataValueEditor CreateValueEditor() => base.CreateValueEditor();
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// A property editor to allow multiple checkbox selection of pre-defined items.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.CheckBoxList, "Checkbox list", "checkboxlist", Icon="icon-bulleted-list", Group="lists")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.CheckBoxList,
|
||||
"Checkbox list",
|
||||
"checkboxlist",
|
||||
Icon = "icon-bulleted-list",
|
||||
Group = Constants.PropertyEditors.Groups.Lists)]
|
||||
public class CheckBoxListPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
@@ -4,7 +4,12 @@ using Umbraco.Core.PropertyEditors;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ColorPicker, "Color Picker", "colorpicker", Icon="icon-colorpicker", Group="Pickers")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ColorPicker,
|
||||
"Color Picker",
|
||||
"colorpicker",
|
||||
Icon = "icon-colorpicker",
|
||||
Group = Constants.PropertyEditors.Groups.Pickers)]
|
||||
public class ColorPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public ColorPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -8,7 +8,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Content property editor that stores UDI
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ContentPicker, EditorType.PropertyValue | EditorType.MacroParameter, "Content Picker", "contentpicker", ValueType = ValueTypes.String, Group = "Pickers")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ContentPicker,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Content Picker",
|
||||
"contentpicker",
|
||||
ValueType = ValueTypes.String,
|
||||
Group = Constants.PropertyEditors.Groups.Pickers)]
|
||||
public class ContentPickerPropertyEditor : DataEditor
|
||||
{
|
||||
public ContentPickerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -7,7 +7,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a date and time property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.DateTime, "Date/Time", "datepicker", ValueType = ValueTypes.DateTime, Icon="icon-time")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.DateTime,
|
||||
"Date/Time",
|
||||
"datepicker",
|
||||
ValueType = ValueTypes.DateTime,
|
||||
Icon = "icon-time")]
|
||||
public class DateTimePropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a decimal property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Decimal, EditorType.PropertyValue | EditorType.MacroParameter, "Decimal", "decimal", ValueType = ValueTypes.Decimal)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Decimal,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Decimal",
|
||||
"decimal",
|
||||
ValueType = ValueTypes.Decimal)]
|
||||
public class DecimalPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,12 @@ using Umbraco.Core.Services;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.DropDownListFlexible, "Dropdown", "dropdownFlexible", Group = "lists", Icon = "icon-indent")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.DropDownListFlexible,
|
||||
"Dropdown",
|
||||
"dropdownFlexible",
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = "icon-indent")]
|
||||
public class DropDownFlexiblePropertyEditor : DataEditor
|
||||
{
|
||||
private readonly ILocalizedTextService _textService;
|
||||
|
||||
@@ -5,7 +5,12 @@ using Umbraco.Core.PropertyEditors.Validators;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.EmailAddress, EditorType.PropertyValue | EditorType.MacroParameter, "Email address", "email", Icon="icon-message")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.EmailAddress,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Email address",
|
||||
"email",
|
||||
Icon = "icon-message")]
|
||||
public class EmailAddressPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -12,7 +12,12 @@ using Umbraco.Web.Media;
|
||||
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.UploadField, "File upload", "fileupload", Icon = "icon-download-alt", Group = "media")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.UploadField,
|
||||
"File upload",
|
||||
"fileupload",
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-download-alt")]
|
||||
public class FileUploadPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
|
||||
@@ -12,7 +12,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a grid property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Grid, "Grid layout", "grid", HideLabel = true, ValueType = ValueTypes.Json, Group="rich content", Icon="icon-layout")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Grid,
|
||||
"Grid layout",
|
||||
"grid",
|
||||
HideLabel = true,
|
||||
ValueType = ValueTypes.Json,
|
||||
Icon = "icon-layout",
|
||||
Group = Constants.PropertyEditors.Groups.RichContent)]
|
||||
public class GridPropertyEditor : DataEditor
|
||||
{
|
||||
public GridPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -19,7 +19,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents an image cropper property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ImageCropper, "Image Cropper", "imagecropper", ValueType = ValueTypes.Json, HideLabel = false, Group="media", Icon="icon-crop")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ImageCropper,
|
||||
"Image Cropper",
|
||||
"imagecropper",
|
||||
ValueType = ValueTypes.Json,
|
||||
HideLabel = false,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = "icon-crop")]
|
||||
public class ImageCropperPropertyEditor : DataEditor
|
||||
{
|
||||
private readonly IMediaFileSystem _mediaFileSystem;
|
||||
|
||||
@@ -8,7 +8,12 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents an integer property and parameter editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.Integer, EditorType.PropertyValue | EditorType.MacroParameter, "Numeric", "integer", ValueType = ValueTypes.Integer)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.Integer,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Numeric",
|
||||
"integer",
|
||||
ValueType = ValueTypes.Integer)]
|
||||
public class IntegerPropertyEditor : DataEditor
|
||||
{
|
||||
public IntegerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -8,7 +8,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a list-view editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.ListView, "List view", "listview", HideLabel = true, Group = "lists", Icon = Constants.Icons.ListView)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.ListView,
|
||||
"List view",
|
||||
"listview",
|
||||
HideLabel = true,
|
||||
Group = Constants.PropertyEditors.Groups.Lists,
|
||||
Icon = Constants.Icons.ListView)]
|
||||
public class ListViewPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,14 @@ using Umbraco.Core.PropertyEditors;
|
||||
namespace Umbraco.Web.PropertyEditors
|
||||
{
|
||||
// TODO: MacroContainerPropertyEditor is deprecated, but what's the alternative?
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MacroContainer, "(Obsolete) Macro Picker", "macrocontainer", ValueType = ValueTypes.Text, Group = "rich content", Icon = Constants.Icons.Macro, IsDeprecated = true)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MacroContainer,
|
||||
"(Obsolete) Macro Picker",
|
||||
"macrocontainer",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.RichContent,
|
||||
Icon = Constants.Icons.Macro,
|
||||
IsDeprecated = true)]
|
||||
public class MacroContainerPropertyEditor : DataEditor
|
||||
{
|
||||
public MacroContainerPropertyEditor(ILogger logger)
|
||||
|
||||
@@ -7,7 +7,13 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a markdown editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MarkdownEditor, "Markdown editor", "markdowneditor", ValueType = ValueTypes.Text, Icon="icon-code", Group="rich content")]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MarkdownEditor,
|
||||
"Markdown editor",
|
||||
"markdowneditor",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.RichContent,
|
||||
Icon = "icon-code")]
|
||||
public class MarkdownPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -7,8 +7,14 @@ namespace Umbraco.Web.PropertyEditors
|
||||
/// <summary>
|
||||
/// Represents a media picker property editor.
|
||||
/// </summary>
|
||||
[DataEditor(Constants.PropertyEditors.Aliases.MediaPicker, EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Media Picker", "mediapicker", ValueType = ValueTypes.Text, Group = "media", Icon = Constants.Icons.MediaImage)]
|
||||
[DataEditor(
|
||||
Constants.PropertyEditors.Aliases.MediaPicker,
|
||||
EditorType.PropertyValue | EditorType.MacroParameter,
|
||||
"Media Picker",
|
||||
"mediapicker",
|
||||
ValueType = ValueTypes.Text,
|
||||
Group = Constants.PropertyEditors.Groups.Media,
|
||||
Icon = Constants.Icons.MediaImage)]
|
||||
public class MediaPickerPropertyEditor : DataEditor
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user