From f04e27ca197ae99975ed90aedaa4e53f95a2aee7 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 22 Jun 2021 14:16:05 +0200 Subject: [PATCH 001/102] Bump version to 8.14.1 --- src/SolutionInfo.cs | 4 ++-- src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 3619dc1371..3a43aec402 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -18,5 +18,5 @@ using System.Resources; [assembly: AssemblyVersion("8.0.0")] // these are FYI and changed automatically -[assembly: AssemblyFileVersion("8.14.0")] -[assembly: AssemblyInformationalVersion("8.14.0")] +[assembly: AssemblyFileVersion("8.14.1")] +[assembly: AssemblyInformationalVersion("8.14.1")] diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index f3652a1273..f238984205 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -348,9 +348,9 @@ False True - 8140 + 8141 / - http://localhost:8140 + http://localhost:8141 8131 / http://localhost:8131 From 6816ae7ca41fb825b54dd23c9ffdfb1de32722a2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Fri, 2 Jul 2021 11:47:39 +0200 Subject: [PATCH 002/102] Fix dependencies to make 8.15 installable via NuGet (cherry picked from commit 3e208904356f1fe1262a7edee052dbf9663a6ad3) --- build/NuSpecs/UmbracoCms.Web.nuspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/NuSpecs/UmbracoCms.Web.nuspec b/build/NuSpecs/UmbracoCms.Web.nuspec index e96a217f4e..7aebfae108 100644 --- a/build/NuSpecs/UmbracoCms.Web.nuspec +++ b/build/NuSpecs/UmbracoCms.Web.nuspec @@ -43,7 +43,8 @@ - + + From 9b32c83182526b6532300fb6fbacea75a195c842 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 6 Jul 2021 13:03:44 +0200 Subject: [PATCH 003/102] Update PropertyTypeGroupDto IdentitySeed to no longer collide with new media types --- src/Umbraco.Core/Persistence/Dtos/PropertyTypeGroupDto.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeGroupDto.cs b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeGroupDto.cs index c48a6697ef..3de376ee0a 100644 --- a/src/Umbraco.Core/Persistence/Dtos/PropertyTypeGroupDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/PropertyTypeGroupDto.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Persistence.Dtos internal class PropertyTypeGroupDto { [Column("id")] - [PrimaryKeyColumn(IdentitySeed = 12)] + [PrimaryKeyColumn(IdentitySeed = 56)] public int Id { get; set; } [Column("contenttypeNodeId")] From dfa161adb485036236883bda0ba4b24ee7d1e7f3 Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 6 Jul 2021 13:08:20 +0200 Subject: [PATCH 004/102] Add migration --- .../Migrations/Upgrade/UmbracoPlan.cs | 1 + .../V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs | 21 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + 3 files changed, 23 insertions(+) create mode 100644 src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index d9a71a1cc2..15ae30d3b4 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -206,6 +206,7 @@ namespace Umbraco.Core.Migrations.Upgrade // to 8.15.0... To("{8DDDCD0B-D7D5-4C97-BD6A-6B38CA65752F}"); To("{4695D0C9-0729-4976-985B-048D503665D8}"); + To("{5C424554-A32D-4852-8ED1-A13508187901}"); //FINAL } diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs new file mode 100644 index 0000000000..908b5cda60 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/UpdateCmsPropertyGroupIdSeed.cs @@ -0,0 +1,21 @@ +using NPoco; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0 +{ + public class UpdateCmsPropertyGroupIdSeed : MigrationBase + { + public UpdateCmsPropertyGroupIdSeed(IMigrationContext context) : base(context) + { + } + + public override void Migrate() + { + if (DatabaseType.IsSqlCe()) + { + Database.Execute(Sql("ALTER TABLE [cmsPropertyTypeGroup] ALTER COLUMN [id] IDENTITY (56,1)")); + } + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 931cef070f..d37907ae0f 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -141,6 +141,7 @@ + From f7123b355d187eeb6a58e9563ecb27d2c54dc61f Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 6 Jul 2021 15:09:56 -0600 Subject: [PATCH 005/102] init commit for changing packages back to xml format so we can more easily deal with media. --- .../Extensions/ContentExtensions.cs | 7 ++ src/Umbraco.Core/IO/MediaFileManager.cs | 40 ++++++++- src/Umbraco.Core/Models/IMediaUrlGenerator.cs | 6 +- src/Umbraco.Core/Models/MediaExtensions.cs | 6 +- .../Packaging/PackagesRepository.cs | 89 ++++++++++++++----- .../MediaUrlGeneratorCollection.cs | 6 +- .../Services/IEntityXmlSerializer.cs | 6 +- .../UmbracoBuilder.Services.cs | 2 + .../Examine/MediaValueSetBuilder.cs | 5 +- .../Services/Implement/EntityXmlSerializer.cs | 19 ++-- .../CreatedPackagesRepositoryTests.cs | 4 + .../Umbraco.Core/Components/ComponentTests.cs | 15 +++- .../Scoping/ScopeEventDispatcherTests.cs | 10 ++- .../ScopedNotificationPublisherTests.cs | 12 ++- .../Scoping/ScopeUnitTests.cs | 12 ++- 15 files changed, 188 insertions(+), 51 deletions(-) diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index 5aaad5e303..8ea51fafca 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -224,6 +224,9 @@ namespace Umbraco.Extensions string oldpath = null; if (property.GetValue(culture, segment) is string svalue) { + // TODO: This is still horrible! We are not delagating to the actual property editors on how to handle this + // which we know how to do. + if (svalue.DetectIsJson()) { // the property value is a JSON serialized image crop data set - grab the "src" property as the file source @@ -233,6 +236,10 @@ namespace Umbraco.Extensions } var filepath = mediaFileManager.StoreFile(content, property.PropertyType, filename, filestream, oldpath); + + // TODO: And here we are just setting the value to a string which means that any file based editor + // will need to handle the raw string value and progressively save it to it's correct (i.e. JSON) + // format. property.SetValue(mediaFileManager.FileSystem.GetUrl(filepath), culture, segment); } diff --git a/src/Umbraco.Core/IO/MediaFileManager.cs b/src/Umbraco.Core/IO/MediaFileManager.cs index de89d7ca87..679eed9239 100644 --- a/src/Umbraco.Core/IO/MediaFileManager.cs +++ b/src/Umbraco.Core/IO/MediaFileManager.cs @@ -3,8 +3,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -15,6 +19,9 @@ namespace Umbraco.Cms.Core.IO private readonly IMediaPathScheme _mediaPathScheme; private readonly ILogger _logger; private readonly IShortStringHelper _shortStringHelper; + private readonly IServiceProvider _serviceProvider; + private MediaUrlGeneratorCollection _mediaUrlGenerators; + private readonly ContentSettings _contentSettings; /// /// Gets the media filesystem. @@ -25,11 +32,15 @@ namespace Umbraco.Cms.Core.IO IFileSystem fileSystem, IMediaPathScheme mediaPathScheme, ILogger logger, - IShortStringHelper shortStringHelper) + IShortStringHelper shortStringHelper, + IServiceProvider serviceProvider, + IOptions contentSettings) { _mediaPathScheme = mediaPathScheme; _logger = logger; _shortStringHelper = shortStringHelper; + _serviceProvider = serviceProvider; + _contentSettings = contentSettings.Value; FileSystem = fileSystem; } @@ -118,11 +129,36 @@ namespace Umbraco.Cms.Core.IO return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); } - + #endregion #region Associated Media Files + public Stream GetFile(IMedia media, out string mediaFilePath) + { + // TODO: If collections were lazy we could just inject them + if (_mediaUrlGenerators == null) + { + _mediaUrlGenerators = _serviceProvider.GetRequiredService(); + } + + // This is how we get all URLs based on the defined aliases, + // then we just try each to get a file until one works. + var urls = media.GetUrls(_contentSettings, _mediaUrlGenerators); + foreach(var url in urls) + { + Stream stream = FileSystem.OpenFile(url); + if (stream != null) + { + mediaFilePath = url; + return stream; + } + } + + mediaFilePath = null; + return null; + } + /// /// Stores a media file associated to a property of a content item. /// diff --git a/src/Umbraco.Core/Models/IMediaUrlGenerator.cs b/src/Umbraco.Core/Models/IMediaUrlGenerator.cs index 5d649ecd8a..0119f1bd24 100644 --- a/src/Umbraco.Core/Models/IMediaUrlGenerator.cs +++ b/src/Umbraco.Core/Models/IMediaUrlGenerator.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Cms.Core.Models +namespace Umbraco.Cms.Core.Models { /// /// Used to generate paths to media items for a specified property editor alias @@ -8,11 +8,11 @@ /// /// Tries to get a media path for a given property editor alias /// - /// The property editor alias + /// The property editor alias /// The value of the property /// /// True if a media path was returned /// - bool TryGetMediaPath(string alias, object value, out string mediaPath); + bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath); } } diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs index a7571d6317..137997c271 100644 --- a/src/Umbraco.Core/Models/MediaExtensions.cs +++ b/src/Umbraco.Core/Models/MediaExtensions.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; @@ -29,11 +29,9 @@ namespace Umbraco.Extensions /// Gets the URLs of a media item. /// public static string[] GetUrls(this IMedia media, ContentSettings contentSettings, MediaUrlGeneratorCollection mediaUrlGenerators) - { - return contentSettings.Imaging.AutoFillImageProperties + => contentSettings.Imaging.AutoFillImageProperties .Select(field => media.GetUrl(field.Alias, mediaUrlGenerators)) .Where(link => string.IsNullOrWhiteSpace(link) == false) .ToArray(); - } } } diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index b86f5d8695..ab86e338f0 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -2,12 +2,15 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.IO; +using System.IO.Compression; using System.Linq; using System.Xml.Linq; +using System.Xml.XPath; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Services; using Umbraco.Extensions; @@ -35,6 +38,7 @@ namespace Umbraco.Cms.Core.Packaging private readonly PackageDefinitionXmlParser _parser; private readonly IMediaService _mediaService; private readonly IMediaTypeService _mediaTypeService; + private readonly MediaFileManager _mediaFileManager; /// /// Constructor @@ -66,6 +70,7 @@ namespace Umbraco.Cms.Core.Packaging IOptions globalSettings, IMediaService mediaService, IMediaTypeService mediaTypeService, + MediaFileManager mediaFileManager, string packageRepositoryFileName, string tempFolderPath = null, string packagesFolderPath = null, @@ -90,6 +95,7 @@ namespace Umbraco.Cms.Core.Packaging _parser = new PackageDefinitionXmlParser(); _mediaService = mediaService; _mediaTypeService = mediaTypeService; + _mediaFileManager = mediaFileManager; } private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + _packageRepositoryFileName; @@ -197,19 +203,32 @@ namespace Umbraco.Cms.Core.Packaging PackageDictionaryItems(definition, root); PackageLanguages(definition, root); PackageDataTypes(definition, root); - PackageMedia(definition, root); + Dictionary mediaFiles = PackageMedia(definition, root); - var packageXmlFileName = temporaryPath + "/package.xml"; + var tempPackagePath = temporaryPath + "/package.zip"; - if (File.Exists(packageXmlFileName)) + using (FileStream fileStream = File.OpenWrite(tempPackagePath)) + using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true)) { - File.Delete(packageXmlFileName); + ZipArchiveEntry packageXmlEntry = archive.CreateEntry("package.xml"); + using (Stream entryStream = packageXmlEntry.Open()) + { + compiledPackageXml.Save(entryStream); + } + + foreach (KeyValuePair mediaFile in mediaFiles) + { + // must ensure the path is relative, doesn't start with / + ZipArchiveEntry mediaEntry = archive.CreateEntry(mediaFile.Key.TrimStart('/')); + using (Stream entryStream = mediaEntry.Open()) + using (mediaFile.Value) + { + mediaFile.Value.Seek(0, SeekOrigin.Begin); + mediaFile.Value.CopyTo(entryStream); + } + } } - compiledPackageXml.Save(packageXmlFileName); - - // check if there's a packages directory below media - var directoryName = _hostingEnvironment.MapPathWebRoot(Path.Combine(_mediaFolderPath, definition.Name.Replace(' ', '_'))); @@ -218,18 +237,19 @@ namespace Umbraco.Cms.Core.Packaging Directory.CreateDirectory(directoryName); } - var packPath = Path.Combine(directoryName, "package.xml"); + var finalPackagePath = Path.Combine(directoryName, "package.zip"); - if (File.Exists(packPath)) + if (File.Exists(finalPackagePath)) { - File.Delete(packPath); + File.Delete(finalPackagePath); } - File.Move(packageXmlFileName, packPath); - definition.PackagePath = packPath; + File.Move(tempPackagePath, finalPackagePath); + + definition.PackagePath = finalPackagePath; SavePackage(definition); - return packPath; + return finalPackagePath; } finally { @@ -305,7 +325,7 @@ namespace Umbraco.Cms.Core.Packaging var processed = new Dictionary(); while (processed.Count < itemCount) { - foreach(Guid key in items.Keys.ToList()) + foreach (Guid key in items.Keys.ToList()) { (IDictionaryItem dictionaryItem, XElement serializedDictionaryValue) = items[key]; @@ -518,14 +538,43 @@ namespace Umbraco.Cms.Core.Packaging } - private void PackageMedia(PackageDefinition definition, XElement root) + private Dictionary PackageMedia(PackageDefinition definition, XElement root) { + var mediaStreams = new Dictionary(); + + // callback that occurs on each serialized media item + void OnSerializedMedia(IMedia media, XElement xmlMedia) + { + // get the media file path and store that separately in the XML. + // the media file path is different from the URL and is specifically + // extracted using the property editor for this media file and the current media file system. + Stream mediaStream = _mediaFileManager.GetFile(media, out var mediaFilePath); + if (mediaStream != null) + { + xmlMedia.Add(new XAttribute("mediaFilePath", mediaFilePath)); + + // add the stream to our outgoing stream + mediaStreams.Add(mediaFilePath, mediaStream); + } + } + IEnumerable medias = _mediaService.GetByIds(definition.MediaUdis); - root.Add( - new XElement( - "MediaItems", - medias.Select(x => new XElement("MediaSet", _serializer.Serialize(x, definition.MediaLoadChildNodes))))); + var mediaXml = new XElement( + "MediaItems", + medias.Select(media => + { + XElement serializedMedia = _serializer.Serialize( + media, + definition.MediaLoadChildNodes, + OnSerializedMedia); + + return new XElement("MediaSet", serializedMedia); + })); + + root.Add(mediaXml); + + return mediaStreams; } // TODO: Delete this diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs index c3c168ed1f..cd3a36c607 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs @@ -10,11 +10,11 @@ namespace Umbraco.Cms.Core.PropertyEditors { } - public bool TryGetMediaPath(string alias, object value, out string mediaPath) + public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath) { - foreach(var generator in this) + foreach(IMediaUrlGenerator generator in this) { - if (generator.TryGetMediaPath(alias, value, out var mp)) + if (generator.TryGetMediaPath(propertyEditorAlias, value, out var mp)) { mediaPath = mp; return true; diff --git a/src/Umbraco.Core/Services/IEntityXmlSerializer.cs b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs index 20de556395..97838b1a69 100644 --- a/src/Umbraco.Core/Services/IEntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Xml.Linq; using Umbraco.Cms.Core.Models; @@ -22,7 +23,8 @@ namespace Umbraco.Cms.Core.Services /// XElement Serialize( IMedia media, - bool withDescendants = false); + bool withDescendants = false, + Action onMediaItemSerialized = null); /// /// Exports an IMember item as an XElement. diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index 134ffc070c..14da8f7aa6 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -10,6 +10,7 @@ using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Routing; using Umbraco.Cms.Core.Services; @@ -93,6 +94,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection factory.GetRequiredService>(), factory.GetRequiredService(), factory.GetRequiredService(), + factory.GetRequiredService(), packageRepoFileName); private static LocalizedTextServiceFileSources SourcesFactory(IServiceProvider container) diff --git a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs index 997cbfe19f..58c1f73bf2 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Examine; @@ -44,6 +44,9 @@ namespace Umbraco.Cms.Infrastructure.Examine var umbracoFilePath = string.Empty; var umbracoFile = string.Empty; + // TODO: To get the file path we should/could use the MediaUrlGeneratorCollection instead of this + // just like we are doing in the MediaFileManager? + var umbracoFileSource = m.GetValue(Constants.Conventions.Media.File); if (umbracoFileSource.DetectIsJson()) diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index 88b19e6270..2e46b0b975 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Xml.Linq; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; @@ -98,7 +100,8 @@ namespace Umbraco.Cms.Core.Services.Implement /// public XElement Serialize( IMedia media, - bool withDescendants = false) + bool withDescendants = false, + Action onMediaItemSerialized = null) { if (_mediaService == null) throw new ArgumentNullException(nameof(_mediaService)); if (_dataTypeService == null) throw new ArgumentNullException(nameof(_dataTypeService)); @@ -110,7 +113,9 @@ namespace Umbraco.Cms.Core.Services.Implement var nodeName = media.ContentType.Alias.ToSafeAlias(_shortStringHelper); const bool published = false; // always false for media - var xml = SerializeContentBase(media, media.GetUrlSegment(_shortStringHelper, _urlSegmentProviders), nodeName, published); + string urlValue = media.GetUrlSegment(_shortStringHelper, _urlSegmentProviders); + XElement xml = SerializeContentBase(media, urlValue, nodeName, published); + xml.Add(new XAttribute("nodeType", media.ContentType.Id)); xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias)); @@ -123,6 +128,8 @@ namespace Umbraco.Cms.Core.Services.Implement //xml.Add(new XAttribute("template", 0)); // no template for media + onMediaItemSerialized?.Invoke(media, xml); + if (withDescendants) { const int pageSize = 500; @@ -131,7 +138,7 @@ namespace Umbraco.Cms.Core.Services.Implement while (page * pageSize < total) { var children = _mediaService.GetPagedChildren(media.Id, page++, pageSize, out total); - SerializeChildren(children, xml); + SerializeChildren(children, xml, onMediaItemSerialized); } } @@ -619,12 +626,12 @@ namespace Umbraco.Cms.Core.Services.Implement } // exports an IMedia item descendants. - private void SerializeChildren(IEnumerable children, XElement xml) + private void SerializeChildren(IEnumerable children, XElement xml, Action onMediaItemSerialized) { foreach (var child in children) { // add the child xml - var childXml = Serialize(child); + var childXml = Serialize(child, onMediaItemSerialized: onMediaItemSerialized); xml.Add(childXml); const int pageSize = 500; @@ -634,7 +641,7 @@ namespace Umbraco.Cms.Core.Services.Implement { var grandChildren = _mediaService.GetPagedChildren(child.Id, page++, pageSize, out total); // recurse - SerializeChildren(grandChildren, childXml); + SerializeChildren(grandChildren, childXml, onMediaItemSerialized); } } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs index 9565c77258..892c7595d5 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs @@ -11,6 +11,7 @@ using NUnit.Framework; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Packaging; using Umbraco.Cms.Core.Services; @@ -56,6 +57,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging private IMediaTypeService MediaTypeService => GetRequiredService(); + private MediaFileManager MediaFileManager => GetRequiredService(); + public ICreatedPackagesRepository PackageBuilder => new PackagesRepository( ContentService, ContentTypeService, @@ -68,6 +71,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging Microsoft.Extensions.Options.Options.Create(new GlobalSettings()), MediaService, MediaTypeService, + MediaFileManager, "createdPackages.config", // temp paths diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index 11fee778e2..71498bfba8 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -22,6 +22,8 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Logging; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations.Install; @@ -60,17 +62,22 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components var fs = new FileSystems(loggerFactory, IOHelper, Options.Create(globalSettings), Mock.Of()); var coreDebug = new CoreDebugSettings(); - MediaFileManager mediaFileManager = new MediaFileManager(Mock.Of(), - Mock.Of(), Mock.Of>(), Mock.Of()); + var mediaFileManager = new MediaFileManager( + Mock.Of(), + Mock.Of(), + Mock.Of>(), + Mock.Of(), + Mock.Of(), + Options.Create(new ContentSettings())); IEventAggregator eventAggregator = Mock.Of(); - var p = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); + var scopeProvider = new ScopeProvider(f, fs, Options.Create(coreDebug), mediaFileManager, loggerFactory.CreateLogger(), loggerFactory, NoAppCache.Instance, eventAggregator); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(logger); mock.Setup(x => x.GetService(typeof(ILogger))).Returns(loggerFactory.CreateLogger); mock.Setup(x => x.GetService(typeof(ILoggerFactory))).Returns(loggerFactory); mock.Setup(x => x.GetService(typeof(IProfilingLogger))).Returns(new ProfilingLogger(loggerFactory.CreateLogger(), Mock.Of())); mock.Setup(x => x.GetService(typeof(IUmbracoDatabaseFactory))).Returns(f); - mock.Setup(x => x.GetService(typeof(IScopeProvider))).Returns(p); + mock.Setup(x => x.GetService(typeof(IScopeProvider))).Returns(scopeProvider); setup?.Invoke(mock); return mock.Object; diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs index 9820bef8c2..59eba97c59 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopeEventDispatcherTests.cs @@ -11,6 +11,7 @@ using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -79,8 +80,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping Options.Create(new GlobalSettings()), Mock.Of()); - var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), - instance.CreateLogger(), Mock.Of()); + var mediaFileManager = new MediaFileManager( + Mock.Of(), + Mock.Of(), + instance.CreateLogger(), + Mock.Of(), + Mock.Of(), + Options.Create(new ContentSettings())); return new ScopeProvider( Mock.Of(), diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs index e8b72bdd5e..db25b946b4 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Scoping/ScopedNotificationPublisherTests.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; @@ -9,7 +10,9 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; @@ -76,8 +79,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Scoping Options.Create(new GlobalSettings()), Mock.Of()); - var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), - loggerFactory.CreateLogger(), Mock.Of()); + var mediaFileManager = new MediaFileManager( + Mock.Of(), + Mock.Of(), + loggerFactory.CreateLogger(), + Mock.Of(), + Mock.Of(), + Options.Create(new ContentSettings())); eventAggregatorMock = new Mock(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs index 6995be88b7..5beb89846c 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Scoping/ScopeUnitTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using CSharpTest.Net.Interfaces; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -13,6 +14,8 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Persistence; @@ -33,8 +36,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Scoping var loggerFactory = NullLoggerFactory.Instance; var fileSystems = new FileSystems(loggerFactory, Mock.Of(), Mock.Of>(), Mock.Of()); - var mediaFileManager = new MediaFileManager(Mock.Of(), Mock.Of(), - loggerFactory.CreateLogger(), Mock.Of()); + var mediaFileManager = new MediaFileManager( + Mock.Of(), + Mock.Of(), + loggerFactory.CreateLogger(), + Mock.Of(), + Mock.Of(), + Options.Create(new ContentSettings())); var databaseFactory = new Mock(); var database = new Mock(); var sqlContext = new Mock(); From 4ac29927deb0183e9e27b42db32eaad661e1536d Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Wed, 7 Jul 2021 11:22:50 +0200 Subject: [PATCH 006/102] Bump version to 8.15.0 --- src/SolutionInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 6ef4d6ce85..201213cffa 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -19,4 +19,4 @@ using System.Resources; // these are FYI and changed automatically [assembly: AssemblyFileVersion("8.15.0")] -[assembly: AssemblyInformationalVersion("8.15.0-rc")] +[assembly: AssemblyInformationalVersion("8.15.0")] From 9101cfdc09141fb7e00a82d096900fca3e510d73 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 7 Jul 2021 14:23:24 +0200 Subject: [PATCH 007/102] Make GetLocalCropUrl use GetCropUrl on local crops instead of calling itself --- .../Extensions/FriendlyImageCropperTemplateExtensions.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs index e8d580968e..2fe6353429 100644 --- a/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/FriendlyImageCropperTemplateExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System; +using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Media; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; @@ -337,10 +338,14 @@ namespace Umbraco.Extensions ); + [Obsolete("Use GetCrop to merge local and media crops, get automatic cache buster value and have more parameters.")] public static string GetLocalCropUrl( this MediaWithCrops mediaWithCrops, string alias, string cacheBusterValue = null) - => mediaWithCrops.GetLocalCropUrl(alias, cacheBusterValue); + { + return mediaWithCrops.LocalCrops.Src + + mediaWithCrops.LocalCrops.GetCropUrl(alias, ImageUrlGenerator, cacheBusterValue: cacheBusterValue); + } } } From 58a4e600db0be15202def6162c7c42a27b15e4d3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jul 2021 09:41:11 -0600 Subject: [PATCH 008/102] Pretty much gets zip package format working with media install --- .../Packaging/PackageMigrationResource.cs | 50 ++++++++++-- .../AutomaticPackageMigrationPlan.cs | 50 +++++------- .../Packaging/ImportPackageBuilder.cs | 19 ++++- .../ImportPackageBuilderExpression.cs | 81 +++++++++++++++++-- .../Packaging/PackageMigrationBase.cs | 33 +++++++- .../Umbraco.Core/RuntimeStateTests.cs | 6 +- .../Controllers/MediaController.cs | 25 +++--- 7 files changed, 199 insertions(+), 65 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs index 82cdb31427..74ec9ac240 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs @@ -1,28 +1,62 @@ -using System; +using System; using System.IO; +using System.IO.Compression; using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Xml; using System.Xml.Linq; namespace Umbraco.Cms.Core.Packaging { public static class PackageMigrationResource { - public static XDocument GetEmbeddedPackageDataManifest(Type planType) + private static Stream GetEmbeddedPackageStream(Type planType) { - // lookup the embedded resource by convention + // lookup the embedded resource by convention Assembly currentAssembly = planType.Assembly; - var fileName = $"{planType.Namespace}.package.xml"; + var fileName = $"{planType.Namespace}.package.zip"; Stream stream = currentAssembly.GetManifestResourceStream(fileName); if (stream == null) { throw new FileNotFoundException("Cannot find the embedded file.", fileName); } - XDocument xml; - using (stream) + return stream; + } + + public static string GetEmbeddedPackageDataManifestHash(Type planType) + { + using Stream stream = GetEmbeddedPackageStream(planType); + using HashAlgorithm alg = SHA1.Create(); + + // create a string output for the hash + var stringBuilder = new StringBuilder(); + var hashedByteArray = alg.ComputeHash(stream); + foreach (var b in hashedByteArray) { - xml = XDocument.Load(stream); + stringBuilder.Append(b.ToString("x2")); } - return xml; + return stringBuilder.ToString(); + } + + public static ZipArchive GetEmbeddedPackageDataManifest(Type planType, out XDocument packageXml) + { + Stream stream = GetEmbeddedPackageStream(planType); + + var zip = new ZipArchive(stream, ZipArchiveMode.Read); + ZipArchiveEntry packageXmlEntry = zip.GetEntry("package.xml"); + if (packageXmlEntry == null) + { + throw new InvalidOperationException("Zip package does not contain the required package.xml file"); + } + + using (Stream packageXmlStream = packageXmlEntry.Open()) + using (var xmlReader = XmlReader.Create(packageXmlStream)) + { + packageXml = XDocument.Load(xmlReader); + } + + return zip; } } } diff --git a/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs b/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs index 304e650bb6..8188545f6b 100644 --- a/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs +++ b/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs @@ -1,7 +1,11 @@ using System; +using System.IO.Compression; using System.Xml.Linq; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; using Umbraco.Extensions; @@ -12,54 +16,36 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// public abstract class AutomaticPackageMigrationPlan : PackageMigrationPlan { - private XDocument _xdoc; + protected AutomaticPackageMigrationPlan(string packageName) + : this(packageName, packageName) + { } - protected AutomaticPackageMigrationPlan(string name) - : base(name) - { - } - - protected AutomaticPackageMigrationPlan(string packageName, string planName) : base(packageName, planName) - { - } + protected AutomaticPackageMigrationPlan(string packageName, string planName) + : base(packageName, planName) + { } protected sealed override void DefinePlan() { // calculate the final state based on the hash value of the embedded resource - var finalId = PackageDataManifest.ToString(SaveOptions.DisableFormatting).ToGuid(); + Type planType = GetType(); + var hash = PackageMigrationResource.GetEmbeddedPackageDataManifestHash(planType); + + var finalId = hash.ToGuid(); To(finalId); } - /// - /// Get the extracted package data xml manifest - /// - private XDocument PackageDataManifest - { - get - { - if (_xdoc != null) - { - return _xdoc; - } - - Type planType = GetType(); - _xdoc = PackageMigrationResource.GetEmbeddedPackageDataManifest(planType); - return _xdoc; - } - } - private class MigrateToPackageData : PackageMigrationBase { - public MigrateToPackageData(IPackagingService packagingService, IMigrationContext context) - : base(packagingService, context) + public MigrateToPackageData(IPackagingService packagingService, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer jsonSerializer, IMigrationContext context) + : base(packagingService, mediaFileManager, shortStringHelper, contentTypeBaseServiceProvider, jsonSerializer, context) { } protected override void Migrate() { var plan = (AutomaticPackageMigrationPlan)Context.Plan; - XDocument xml = plan.PackageDataManifest; - ImportPackage.FromXmlDataManifest(xml).Do(); + + ImportPackage.FromEmbeddedResource(plan.GetType()).Do(); } } } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs index 09c4ec74a5..0416cea8ae 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs @@ -1,6 +1,9 @@ using System; using System.Xml.Linq; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; using Umbraco.Cms.Infrastructure.Migrations.Expressions; using Umbraco.Cms.Infrastructure.Migrations.Expressions.Common; @@ -9,8 +12,20 @@ namespace Umbraco.Cms.Infrastructure.Packaging { internal class ImportPackageBuilder : ExpressionBuilderBase, IImportPackageBuilder, IExecutableBuilder { - public ImportPackageBuilder(IPackagingService packagingService, IMigrationContext context) - : base(new ImportPackageBuilderExpression(packagingService, context)) + public ImportPackageBuilder( + IPackagingService packagingService, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IJsonSerializer jsonSerializer, + IMigrationContext context) + : base(new ImportPackageBuilderExpression( + packagingService, + mediaFileManager, + shortStringHelper, + contentTypeBaseServiceProvider, + jsonSerializer, + context)) { } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs index 441109f2ca..4c57ff856a 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs @@ -1,19 +1,45 @@ using System; +using System.IO; +using System.IO.Compression; +using System.Linq; using System.Xml.Linq; +using System.Xml.XPath; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core; +using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; +using Umbraco.Extensions; namespace Umbraco.Cms.Infrastructure.Packaging { internal class ImportPackageBuilderExpression : MigrationExpressionBase { private readonly IPackagingService _packagingService; + private readonly MediaFileManager _mediaFileManager; + private readonly IShortStringHelper _shortStringHelper; + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IJsonSerializer _jsonSerializer; private bool _executed; - public ImportPackageBuilderExpression(IPackagingService packagingService, IMigrationContext context) : base(context) - => _packagingService = packagingService; + public ImportPackageBuilderExpression( + IPackagingService packagingService, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IJsonSerializer jsonSerializer, + IMigrationContext context) : base(context) + { + _packagingService = packagingService; + _mediaFileManager = mediaFileManager; + _shortStringHelper = shortStringHelper; + _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _jsonSerializer = jsonSerializer; + } /// /// The type of the migration which dictates the namespace of the embedded resource @@ -37,19 +63,60 @@ namespace Umbraco.Cms.Infrastructure.Packaging throw new InvalidOperationException($"Nothing to execute, neither {nameof(EmbeddedResourceMigrationType)} or {nameof(PackageDataManifest)} has been set."); } - XDocument xml; + InstallationSummary installationSummary; if (EmbeddedResourceMigrationType != null) { // get the embedded resource - xml = PackageMigrationResource.GetEmbeddedPackageDataManifest(EmbeddedResourceMigrationType); + using (ZipArchive zipPackage = PackageMigrationResource.GetEmbeddedPackageDataManifest( + EmbeddedResourceMigrationType, + out XDocument xml)) + { + // first install the package + installationSummary = _packagingService.InstallCompiledPackageData(xml); + + // then we need to save each file to the saved media items + var mediaWithFiles = xml.XPathSelectElements( + "./umbPackage/MediaItems/MediaSet//*[@id][@mediaFilePath]") + .ToDictionary( + x => x.AttributeValue("id"), + x => x.AttributeValue("mediaFilePath")); + + // TODO: Almost works! Just wonder if the media installed list is empty because nothing changed? + + foreach(IMedia media in installationSummary.MediaInstalled) + { + if (mediaWithFiles.TryGetValue(media.Id, out var mediaFilePath)) + { + // this is a media item that has a file, so find that file in the zip + string entryName = mediaFilePath.TrimStart('/'); + ZipArchiveEntry mediaEntry = zipPackage.GetEntry(entryName); + if (mediaEntry == null) + { + throw new InvalidOperationException("No media file found in package zip for path " + entryName); + } + + // read the media file and save it to the media item + // using the current file system provider. + using (Stream mediaStream = mediaEntry.Open()) + { + media.SetValue( + _mediaFileManager, + _shortStringHelper, + _contentTypeBaseServiceProvider, + _jsonSerializer, + Constants.Conventions.Media.File, + Path.GetFileName(mediaFilePath), + mediaStream); + } + } + } + } } else { - xml = PackageDataManifest; + installationSummary = _packagingService.InstallCompiledPackageData(PackageDataManifest); } - InstallationSummary installationSummary = _packagingService.InstallCompiledPackageData(xml); - Logger.LogInformation($"Package migration executed. Summary: {installationSummary}"); } } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs index ebe6f82a8f..a2fa7040ea 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs @@ -1,5 +1,8 @@ +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute; @@ -9,11 +12,35 @@ namespace Umbraco.Cms.Infrastructure.Packaging public abstract class PackageMigrationBase : MigrationBase { private readonly IPackagingService _packagingService; + private readonly MediaFileManager _mediaFileManager; + private readonly IShortStringHelper _shortStringHelper; + private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; + private readonly IJsonSerializer _jsonSerializer; - public PackageMigrationBase(IPackagingService packagingService, IMigrationContext context) : base(context) - => _packagingService = packagingService; + public PackageMigrationBase( + IPackagingService packagingService, + MediaFileManager mediaFileManager, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + IJsonSerializer jsonSerializer, + IMigrationContext context) + : base(context) + { + _packagingService = packagingService; + _mediaFileManager = mediaFileManager; + _shortStringHelper = shortStringHelper; + _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; + _jsonSerializer = jsonSerializer; + } - public IImportPackageBuilder ImportPackage => BeginBuild(new ImportPackageBuilder(_packagingService, Context)); + public IImportPackageBuilder ImportPackage => BeginBuild( + new ImportPackageBuilder( + _packagingService, + _mediaFileManager, + _shortStringHelper, + _contentTypeBaseServiceProvider, + _jsonSerializer, + Context)); } } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs index 0e3a3b43a1..d4db4a76c7 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs @@ -6,8 +6,11 @@ using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; using Umbraco.Cms.Infrastructure.Packaging; using Umbraco.Cms.Tests.Common.Testing; @@ -89,8 +92,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core private class TestMigration : PackageMigrationBase { - public TestMigration(IPackagingService packagingService, IMigrationContext context) - : base(packagingService, context) + public TestMigration(IPackagingService packagingService, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer jsonSerializer, IMigrationContext context) : base(packagingService, mediaFileManager, shortStringHelper, contentTypeBaseServiceProvider, jsonSerializer, context) { } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index 51a7c082e9..c8c33c510e 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -232,7 +232,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// [FilterAllowedOutgoingMedia(typeof(IEnumerable))] - public IEnumerable GetByIds([FromQuery]int[] ids) + public IEnumerable GetByIds([FromQuery] int[] ids) { var foundMedia = _mediaService.GetByIds(ids); return foundMedia.Select(media => _umbracoMapper.Map(media)); @@ -318,7 +318,8 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var nodes = _mediaService.GetByIds(UserStartNodes).ToArray(); if (nodes.Length == 0) return new PagedResult>(0, 0, 0); - if (pageSize < nodes.Length) pageSize = nodes.Length; // bah + if (pageSize < nodes.Length) + pageSize = nodes.Length; // bah var pr = new PagedResult>(nodes.Length, pageNumber, pageSize) { Items = nodes.Select(_umbracoMapper.Map>) @@ -350,7 +351,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers else { //better to not use this without paging where possible, currently only the sort dialog does - children = _mediaService.GetPagedChildren(id,0, int.MaxValue, out var total).ToList(); + children = _mediaService.GetPagedChildren(id, 0, int.MaxValue, out var total).ToList(); totalChildren = children.Count; } @@ -496,7 +497,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers if (sourceParentID == destinationParentID) { - return ValidationProblem(new SimpleNotificationModel(new BackOfficeNotification("",_localizedTextService.Localize("media", "moveToSameFolderFailed"),NotificationStyle.Error))); + return ValidationProblem(new SimpleNotificationModel(new BackOfficeNotification("", _localizedTextService.Localize("media", "moveToSameFolderFailed"), NotificationStyle.Error))); } if (moveResult == false) { @@ -668,7 +669,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public async Task> PostAddFolder(PostedFolder folder) { - var parentIdResult = await GetParentIdAsIntAsync(folder.ParentId, validatePermissions:true); + var parentIdResult = await GetParentIdAsIntAsync(folder.ParentId, validatePermissions: true); if (!(parentIdResult.Result is null)) { return new ActionResult(parentIdResult.Result); @@ -692,7 +693,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// We cannot validate this request with attributes (nicely) due to the nature of the multi-part for data. /// - public async Task PostAddFile([FromForm]string path, [FromForm]string currentFolder, [FromForm]string contentTypeAlias, List file) + public async Task PostAddFile([FromForm] string path, [FromForm] string currentFolder, [FromForm] string contentTypeAlias, List file) { var root = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.TempFileUploads); //ensure it exists @@ -705,7 +706,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } //get the string json from the request - var parentIdResult = await GetParentIdAsIntAsync(currentFolder, validatePermissions:true); + var parentIdResult = await GetParentIdAsIntAsync(currentFolder, validatePermissions: true); if (!(parentIdResult.Result is null)) { return parentIdResult.Result; @@ -773,7 +774,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //get the files foreach (var formFile in file) { - var fileName = formFile.FileName.Trim(Constants.CharArrays.DoubleQuote).TrimEnd(); + var fileName = formFile.FileName.Trim(Constants.CharArrays.DoubleQuote).TrimEnd(); var safeFileName = fileName.ToSafeFileName(ShortStringHelper); var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower(); @@ -788,11 +789,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers foreach (var mediaTypeItem in mediaTypes) { var fileProperty = mediaTypeItem.CompositionPropertyTypes.FirstOrDefault(x => x.Alias == "umbracoFile"); - if (fileProperty != null) { + if (fileProperty != null) + { var dataTypeKey = fileProperty.DataTypeKey; var dataType = _dataTypeService.GetDataType(dataTypeKey); - if (dataType != null && dataType.Configuration is IFileExtensionsConfig fileExtensionsConfig) { + if (dataType != null && dataType.Configuration is IFileExtensionsConfig fileExtensionsConfig) + { var fileExtensions = fileExtensionsConfig.FileExtensions; if (fileExtensions != null) { @@ -824,7 +827,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers await using (var stream = formFile.OpenReadStream()) { - f.SetValue(_mediaFileManager,_shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File,fileName, stream); + f.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, fileName, stream); } From 3eb54831cb297ea6b2127438969a89a415c000fb Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jul 2021 14:36:52 -0600 Subject: [PATCH 009/102] Remove OriginalMediaPathScheme, changes how we extract the media file path and use that correctly instead of hacks. --- .../Extensions/ContentExtensions.cs | 93 +++++++++++++++---- src/Umbraco.Core/IO/IMediaPathScheme.cs | 6 +- src/Umbraco.Core/IO/MediaFileManager.cs | 62 +++++-------- .../CombinedGuidsMediaPathScheme.cs | 4 +- .../OriginalMediaPathScheme.cs | 82 ---------------- .../TwoGuidsMediaPathScheme.cs | 4 +- .../MediaPathSchemes/UniqueMediaPathScheme.cs | 4 +- src/Umbraco.Core/IO/PhysicalFileSystem.cs | 12 ++- .../Media/UploadAutoFillProperties.cs | 28 +++--- .../Models/ContentBaseExtensions.cs | 15 ++- src/Umbraco.Core/Models/MediaExtensions.cs | 9 +- .../Packaging/PackagesRepository.cs | 4 +- .../Examine/MediaValueSetBuilder.cs | 62 ++++--------- .../AutomaticPackageMigrationPlan.cs | 7 +- .../Packaging/ImportPackageBuilder.cs | 7 +- .../ImportPackageBuilderExpression.cs | 35 ++++--- .../Packaging/PackageDataInstallation.cs | 29 +++--- .../Packaging/PackageMigrationBase.cs | 16 ++-- .../FileUploadPropertyEditor.cs | 4 +- .../FileUploadPropertyValueEditor.cs | 14 ++- .../ImageCropperPropertyEditor.cs | 4 +- .../ImageCropperPropertyValueEditor.cs | 16 +++- .../RichTextEditorPastedImages.cs | 15 ++- .../Umbraco.Core/RuntimeStateTests.cs | 3 +- .../UmbracoExamine/IndexInitializer.cs | 21 +++-- .../Controllers/MediaController.cs | 7 +- 26 files changed, 274 insertions(+), 289 deletions(-) delete mode 100644 src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs diff --git a/src/Umbraco.Core/Extensions/ContentExtensions.cs b/src/Umbraco.Core/Extensions/ContentExtensions.cs index 8ea51fafca..8385de5e70 100644 --- a/src/Umbraco.Core/Extensions/ContentExtensions.cs +++ b/src/Umbraco.Core/Extensions/ContentExtensions.cs @@ -9,6 +9,8 @@ using System.Xml.Linq; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Models.PublishedContent; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -17,6 +19,41 @@ namespace Umbraco.Extensions { public static class ContentExtensions { + /// + /// Returns the path to a media item stored in a property if the property editor is + /// + /// + /// + /// + /// + /// + /// + /// True if the file path can be resolved and the property is + public static bool TryGetMediaPath( + this IContentBase content, + string propertyTypeAlias, + MediaUrlGeneratorCollection mediaUrlGenerators, + out string mediaFilePath, + string culture = null, + string segment = null) + { + if (!content.Properties.TryGetValue(propertyTypeAlias, out IProperty property)) + { + mediaFilePath = null; + return false; + } + + if (!mediaUrlGenerators.TryGetMediaPath( + property.PropertyType.PropertyEditorAlias, + property.GetValue(culture, segment), + out mediaFilePath)) + { + return false; + } + + return true; + } + public static bool IsAnyUserPropertyDirty(this IContentBase entity) { return entity.Properties.Any(x => x.IsDirty()); @@ -204,42 +241,56 @@ namespace Umbraco.Extensions /// /// Sets the posted file value of a property. /// - public static void SetValue(this IContentBase content, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + public static void SetValue( + this IContentBase content, + MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, + IShortStringHelper shortStringHelper, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + string propertyTypeAlias, + string filename, + Stream filestream, + string culture = null, + string segment = null) { - if (filename == null || filestream == null) return; + if (filename == null || filestream == null) + return; filename = shortStringHelper.CleanStringForSafeFileName(filename); - if (string.IsNullOrWhiteSpace(filename)) return; + if (string.IsNullOrWhiteSpace(filename)) + return; filename = filename.ToLower(); - SetUploadFile(content, mediaFileManager, contentTypeBaseServiceProvider, serializer, propertyTypeAlias, filename, filestream, culture, segment); + SetUploadFile(content, mediaFileManager, mediaUrlGenerators, contentTypeBaseServiceProvider, propertyTypeAlias, filename, filestream, culture, segment); } - private static void SetUploadFile(this IContentBase content, MediaFileManager mediaFileManager, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer serializer, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null) + private static void SetUploadFile( + this IContentBase content, + MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, + IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, + string propertyTypeAlias, + string filename, + Stream filestream, + string culture = null, + string segment = null) { var property = GetProperty(content, contentTypeBaseServiceProvider, propertyTypeAlias); // Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an // existing IMedia with extension SetValue causes exception 'Illegal characters in path' string oldpath = null; - if (property.GetValue(culture, segment) is string svalue) - { - // TODO: This is still horrible! We are not delagating to the actual property editors on how to handle this - // which we know how to do. - if (svalue.DetectIsJson()) - { - // the property value is a JSON serialized image crop data set - grab the "src" property as the file source - svalue = serializer.DeserializeSubset(svalue, "src"); - } - oldpath = mediaFileManager.FileSystem.GetRelativePath(svalue); + if (content.TryGetMediaPath(property.Alias, mediaUrlGenerators, out string mediaFilePath, culture, segment)) + { + oldpath = mediaFileManager.FileSystem.GetRelativePath(mediaFilePath); } var filepath = mediaFileManager.StoreFile(content, property.PropertyType, filename, filestream, oldpath); - // TODO: And here we are just setting the value to a string which means that any file based editor - // will need to handle the raw string value and progressively save it to it's correct (i.e. JSON) - // format. + // NOTE: Here we are just setting the value to a string which means that any file based editor + // will need to handle the raw string value and save it to it's correct (i.e. JSON) + // format. I'm unsure how this works today with image cropper but it does (maybe events?) property.SetValue(mediaFileManager.FileSystem.GetUrl(filepath), culture, segment); } @@ -247,7 +298,8 @@ namespace Umbraco.Extensions private static IProperty GetProperty(IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias) { var property = content.Properties.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); - if (property != null) return property; + if (property != null) + return property; var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(content); var propertyType = contentType.CompositionPropertyTypes @@ -281,7 +333,8 @@ namespace Umbraco.Extensions var contentType = contentTypeBaseServiceProvider.GetContentTypeOf(content); var propertyType = contentType .CompositionPropertyTypes.FirstOrDefault(x => x.Alias.InvariantEquals(propertyTypeAlias)); - if (propertyType == null) throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); + if (propertyType == null) + throw new ArgumentException("Invalid property type alias " + propertyTypeAlias + "."); return mediaFileManager.StoreFile(content, propertyType, filename, filestream, filepath); } diff --git a/src/Umbraco.Core/IO/IMediaPathScheme.cs b/src/Umbraco.Core/IO/IMediaPathScheme.cs index 2b2696ec5d..5daf5490a8 100644 --- a/src/Umbraco.Core/IO/IMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/IMediaPathScheme.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Umbraco.Cms.Core.IO { @@ -14,9 +14,9 @@ namespace Umbraco.Cms.Core.IO /// The (content, media) item unique identifier. /// The property type unique identifier. /// The file name. - /// A previous filename. + /// /// The filesystem-relative complete file path. - string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null); + string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename); /// /// Gets the directory that can be deleted when the file is deleted. diff --git a/src/Umbraco.Core/IO/MediaFileManager.cs b/src/Umbraco.Core/IO/MediaFileManager.cs index 679eed9239..96680d3f84 100644 --- a/src/Umbraco.Core/IO/MediaFileManager.cs +++ b/src/Umbraco.Core/IO/MediaFileManager.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Strings; using Umbraco.Extensions; @@ -107,52 +108,40 @@ namespace Umbraco.Cms.Core.IO return _mediaPathScheme.GetFilePath(this, cuid, puid, filename); } - /// - /// Gets the file path of a media file. - /// - /// The file name. - /// A previous file path. - /// The unique identifier of the content/media owning the file. - /// The unique identifier of the property type owning the file. - /// The filesystem-relative path to the media file. - /// In the old, legacy, number-based scheme, we try to re-use the media folder - /// specified by . Else, we CREATE a new one. Each time we are invoked. - public string GetMediaPath(string filename, string prevpath, Guid cuid, Guid puid) - { - filename = Path.GetFileName(filename); - if (filename == null) - { - throw new ArgumentException("Cannot become a safe filename.", nameof(filename)); - } - - filename = _shortStringHelper.CleanStringForSafeFileName(filename.ToLowerInvariant()); - - return _mediaPathScheme.GetFilePath(this, cuid, puid, filename, prevpath); - } - #endregion #region Associated Media Files - public Stream GetFile(IMedia media, out string mediaFilePath) + /// + /// Returns a stream (file) for a content item or null if there is no file. + /// + /// + /// The file path if a file was found + /// + /// + /// + public Stream GetFile( + IContentBase content, + out string mediaFilePath, + string propertyTypeAlias = Constants.Conventions.Media.File, + string culture = null, + string segment = null) { // TODO: If collections were lazy we could just inject them if (_mediaUrlGenerators == null) { _mediaUrlGenerators = _serviceProvider.GetRequiredService(); - } + } - // This is how we get all URLs based on the defined aliases, - // then we just try each to get a file until one works. - var urls = media.GetUrls(_contentSettings, _mediaUrlGenerators); - foreach(var url in urls) + if (!content.TryGetMediaPath(propertyTypeAlias, _mediaUrlGenerators, out mediaFilePath, culture, segment)) { - Stream stream = FileSystem.OpenFile(url); - if (stream != null) - { - mediaFilePath = url; - return stream; - } + return null; + } + + Stream stream = FileSystem.OpenFile(mediaFilePath); + if (stream != null) + { + return stream; } mediaFilePath = null; @@ -207,8 +196,7 @@ namespace Umbraco.Cms.Core.IO } // get the filepath, store the data - // use oldpath as "prevpath" to try and reuse the folder, in original number-based scheme - var filepath = GetMediaPath(filename, oldpath, content.Key, propertyType.Key); + var filepath = GetMediaPath(filename, content.Key, propertyType.Key); FileSystem.AddFile(filepath, filestream); return filepath; } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs index f3c15a13d6..b4127e080d 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/CombinedGuidsMediaPathScheme.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Umbraco.Cms.Core.IO.MediaPathSchemes @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class CombinedGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename) { // assumes that cuid and puid keys can be trusted - and that a single property type // for a single content cannot store two different files with the same name diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs deleted file mode 100644 index d1cea0c46b..0000000000 --- a/src/Umbraco.Core/IO/MediaPathSchemes/OriginalMediaPathScheme.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Globalization; -using System.IO; -using System.Threading; - -namespace Umbraco.Cms.Core.IO.MediaPathSchemes -{ - /// - /// Implements the original media path scheme. - /// - /// - /// Path is "{number}/{filename}" or "{number}-{filename}" where number is an incremented counter. - /// Use '/' or '-' depending on UploadAllowDirectories setting. - /// - // scheme: path is "/" where number is an incremented counter - public class OriginalMediaPathScheme : IMediaPathScheme - { - private readonly object _folderCounterLock = new object(); - private long _folderCounter; - private bool _folderCounterInitialized; - - /// - public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) - { - string directory; - if (previous != null) - { - // old scheme, with a previous path - // prevpath should be "/" OR "-" - // and we want to reuse the "" part, so try to find it - - const string sep = "/"; - var pos = previous.IndexOf(sep, StringComparison.Ordinal); - var s = pos > 0 ? previous.Substring(0, pos) : null; - - directory = pos > 0 && int.TryParse(s, out _) ? s : GetNextDirectory(fileManager.FileSystem); - } - else - { - directory = GetNextDirectory(fileManager.FileSystem); - } - - if (directory == null) - throw new InvalidOperationException("Cannot use a null directory."); - - return Path.Combine(directory, filename).Replace('\\', '/'); - } - - /// - public string GetDeleteDirectory(MediaFileManager fileSystem, string filepath) - { - return Path.GetDirectoryName(filepath); - } - - private string GetNextDirectory(IFileSystem fileSystem) - { - EnsureFolderCounterIsInitialized(fileSystem); - return Interlocked.Increment(ref _folderCounter).ToString(CultureInfo.InvariantCulture); - } - - private void EnsureFolderCounterIsInitialized(IFileSystem fileSystem) - { - lock (_folderCounterLock) - { - if (_folderCounterInitialized) return; - - _folderCounter = 1000; // seed - var directories = fileSystem.GetDirectories(""); - foreach (var directory in directories) - { - if (long.TryParse(directory, out var folderNumber) && folderNumber > _folderCounter) - _folderCounter = folderNumber; - } - - // note: not multi-domains ie LB safe as another domain could create directories - // while we read and parse them - don't fix, move to new scheme eventually - - _folderCounterInitialized = true; - } - } - } -} diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs index 203c8aaed8..ebce899697 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/TwoGuidsMediaPathScheme.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Umbraco.Cms.Core.IO.MediaPathSchemes @@ -12,7 +12,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes public class TwoGuidsMediaPathScheme : IMediaPathScheme { /// - public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename) { return Path.Combine(itemGuid.ToString("N"), propertyGuid.ToString("N"), filename).Replace('\\', '/'); } diff --git a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs index 55a9cd4574..c247c37032 100644 --- a/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs +++ b/src/Umbraco.Core/IO/MediaPathSchemes/UniqueMediaPathScheme.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; namespace Umbraco.Cms.Core.IO.MediaPathSchemes @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Core.IO.MediaPathSchemes private const int DirectoryLength = 8; /// - public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename, string previous = null) + public string GetFilePath(MediaFileManager fileManager, Guid itemGuid, Guid propertyGuid, string filename) { var combinedGuid = GuidUtils.Combine(itemGuid, propertyGuid); var directory = GuidUtils.ToBase32String(combinedGuid, DirectoryLength); diff --git a/src/Umbraco.Core/IO/PhysicalFileSystem.cs b/src/Umbraco.Core/IO/PhysicalFileSystem.cs index e517f0be63..8c35e80988 100644 --- a/src/Umbraco.Core/IO/PhysicalFileSystem.cs +++ b/src/Umbraco.Core/IO/PhysicalFileSystem.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -146,17 +146,21 @@ namespace Umbraco.Cms.Core.IO var fullPath = GetFullPath(path); var exists = File.Exists(fullPath); if (exists && overrideExisting == false) + { throw new InvalidOperationException(string.Format("A file at path '{0}' already exists", path)); + } var directory = Path.GetDirectoryName(fullPath); if (directory == null) throw new InvalidOperationException("Could not get directory."); Directory.CreateDirectory(directory); // ensure it exists - if (stream.CanSeek) // TODO: what if we cannot? + if (stream.CanSeek) + { stream.Seek(0, 0); + } - using (var destination = (Stream) File.Create(fullPath)) - stream.CopyTo(destination); + using var destination = (Stream)File.Create(fullPath); + stream.CopyTo(destination); } /// diff --git a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs index 32c8ddd98d..10a372ce9f 100644 --- a/src/Umbraco.Core/Media/UploadAutoFillProperties.cs +++ b/src/Umbraco.Core/Media/UploadAutoFillProperties.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Configuration.Models; @@ -66,20 +66,24 @@ namespace Umbraco.Cms.Core.Media } else { - // if anything goes wrong, just reset the properties - try + // it might not exist if the media item has been created programatically but doesn't have a file persisted yet. + if (_mediaFileManager.FileSystem.FileExists(filepath)) { - using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath)) + // if anything goes wrong, just reset the properties + try { - var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period); - var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null; - SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); + using (var filestream = _mediaFileManager.FileSystem.OpenFile(filepath)) + { + var extension = (Path.GetExtension(filepath) ?? "").TrimStart(Constants.CharArrays.Period); + var size = _imageUrlGenerator.IsSupportedImageFormat(extension) ? (ImageSize?)_imageDimensionExtractor.GetDimensions(filestream) : null; + SetProperties(content, autoFillConfig, size, filestream.Length, extension, culture, segment); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not populate upload auto-fill properties for file '{File}'.", filepath); + ResetProperties(content, autoFillConfig, culture, segment); } - } - catch (Exception ex) - { - _logger.LogError(ex, "Could not populate upload auto-fill properties for file '{File}'.", filepath); - ResetProperties(content, autoFillConfig, culture, segment); } } } diff --git a/src/Umbraco.Core/Models/ContentBaseExtensions.cs b/src/Umbraco.Core/Models/ContentBaseExtensions.cs index 45fe9e41d9..9aae08c74b 100644 --- a/src/Umbraco.Core/Models/ContentBaseExtensions.cs +++ b/src/Umbraco.Core/Models/ContentBaseExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Models; @@ -25,8 +25,19 @@ namespace Umbraco.Extensions if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders)); var url = urlSegmentProviders.Select(p => p.GetUrlSegment(content, culture)).FirstOrDefault(u => u != null); - url ??= new DefaultUrlSegmentProvider(shortStringHelper).GetUrlSegment(content, culture); // be safe + if (url == null) + { + if (s_defaultUrlSegmentProvider == null) + { + s_defaultUrlSegmentProvider = new DefaultUrlSegmentProvider(shortStringHelper); + } + + url = s_defaultUrlSegmentProvider.GetUrlSegment(content, culture); // be safe + } + return url; } + + private static DefaultUrlSegmentProvider s_defaultUrlSegmentProvider; } } diff --git a/src/Umbraco.Core/Models/MediaExtensions.cs b/src/Umbraco.Core/Models/MediaExtensions.cs index 137997c271..616dbd1d12 100644 --- a/src/Umbraco.Core/Models/MediaExtensions.cs +++ b/src/Umbraco.Core/Models/MediaExtensions.cs @@ -12,16 +12,11 @@ namespace Umbraco.Extensions /// public static string GetUrl(this IMedia media, string propertyAlias, MediaUrlGeneratorCollection mediaUrlGenerators) { - if (!media.Properties.TryGetValue(propertyAlias, out var property)) - return string.Empty; - - // TODO: would need to be adjusted to variations, when media become variants - if (mediaUrlGenerators.TryGetMediaPath(property.PropertyType.PropertyEditorAlias, property.GetValue(), out var mediaUrl)) + if (media.TryGetMediaPath(propertyAlias, mediaUrlGenerators, out var mediaPath)) { - return mediaUrl; + return mediaPath; } - // Without knowing what it is, just adding a string here might not be very nice return string.Empty; } diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index ab86e338f0..ae19da3036 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -218,8 +218,8 @@ namespace Umbraco.Cms.Core.Packaging foreach (KeyValuePair mediaFile in mediaFiles) { - // must ensure the path is relative, doesn't start with / - ZipArchiveEntry mediaEntry = archive.CreateEntry(mediaFile.Key.TrimStart('/')); + var entryPath = $"media{mediaFile.Key.EnsureStartsWith('/')}"; + ZipArchiveEntry mediaEntry = archive.CreateEntry(entryPath); using (Stream entryStream = mediaEntry.Open()) using (mediaFile.Value) { diff --git a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs index 58c1f73bf2..446290f83f 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs @@ -1,8 +1,11 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using Examine; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.PropertyEditors.ValueConverters; @@ -17,67 +20,40 @@ namespace Umbraco.Cms.Infrastructure.Examine public class MediaValueSetBuilder : BaseValueSetBuilder { private readonly UrlSegmentProviderCollection _urlSegmentProviders; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IUserService _userService; - private readonly ILogger _logger; private readonly IShortStringHelper _shortStringHelper; - private readonly IJsonSerializer _serializer; + private readonly ContentSettings _contentSettings; - public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, + public MediaValueSetBuilder( + PropertyEditorCollection propertyEditors, UrlSegmentProviderCollection urlSegmentProviders, - IUserService userService, ILogger logger, IShortStringHelper shortStringHelper, IJsonSerializer serializer) + MediaUrlGeneratorCollection mediaUrlGenerators, + IUserService userService, + IShortStringHelper shortStringHelper, + IOptions contentSettings) : base(propertyEditors, false) { _urlSegmentProviders = urlSegmentProviders; + _mediaUrlGenerators = mediaUrlGenerators; _userService = userService; - _logger = logger; _shortStringHelper = shortStringHelper; - _serializer = serializer; + _contentSettings = contentSettings.Value; } /// public override IEnumerable GetValueSets(params IMedia[] media) { - foreach (var m in media) + foreach (IMedia m in media) { - var urlValue = m.GetUrlSegment(_shortStringHelper, _urlSegmentProviders); - var umbracoFilePath = string.Empty; var umbracoFile = string.Empty; - // TODO: To get the file path we should/could use the MediaUrlGeneratorCollection instead of this - // just like we are doing in the MediaFileManager? + var urlValue = m.GetUrlSegment(_shortStringHelper, _urlSegmentProviders); - var umbracoFileSource = m.GetValue(Constants.Conventions.Media.File); - - if (umbracoFileSource.DetectIsJson()) - { - ImageCropperValue cropper = null; - try - { - cropper = _serializer.Deserialize( - m.GetValue(Constants.Conventions.Media.File)); - } - catch (Exception ex) - { - _logger.LogError(ex, $"Could not Deserialize ImageCropperValue for item with key {m.Key} "); - } - - if (cropper != null) - { - umbracoFilePath = cropper.Src; - } - } - else - { - umbracoFilePath = umbracoFileSource; - } - - if (!string.IsNullOrEmpty(umbracoFilePath)) - { - // intentional dummy Uri - var uri = new Uri("https://localhost/" + umbracoFilePath); - umbracoFile = uri.Segments.Last(); - } + IEnumerable mediaFiles = m.GetUrls(_contentSettings, _mediaUrlGenerators) + .Select(x => Path.GetFileName(x)) + .Distinct(); var values = new Dictionary> { @@ -95,7 +71,7 @@ namespace Umbraco.Cms.Infrastructure.Examine {"path", m.Path?.Yield() ?? Enumerable.Empty()}, {"nodeType", m.ContentType.Id.ToString().Yield() }, {"creatorName", (m.GetCreatorProfile(_userService)?.Name ?? "??").Yield()}, - {UmbracoExamineFieldNames.UmbracoFileFieldName, umbracoFile.Yield()} + {UmbracoExamineFieldNames.UmbracoFileFieldName, mediaFiles} }; foreach (var property in m.Properties) diff --git a/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs b/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs index 8188545f6b..92e1086fbd 100644 --- a/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs +++ b/src/Umbraco.Infrastructure/Packaging/AutomaticPackageMigrationPlan.cs @@ -1,9 +1,7 @@ using System; -using System.IO.Compression; -using System.Xml.Linq; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; @@ -36,8 +34,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging private class MigrateToPackageData : PackageMigrationBase { - public MigrateToPackageData(IPackagingService packagingService, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer jsonSerializer, IMigrationContext context) - : base(packagingService, mediaFileManager, shortStringHelper, contentTypeBaseServiceProvider, jsonSerializer, context) + public MigrateToPackageData(IPackagingService packagingService, IMediaService mediaService, MediaFileManager mediaFileManager, MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMigrationContext context) : base(packagingService, mediaService, mediaFileManager, mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, context) { } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs index 0416cea8ae..c14d3e5119 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Xml.Linq; using Umbraco.Cms.Core.IO; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -14,17 +15,19 @@ namespace Umbraco.Cms.Infrastructure.Packaging { public ImportPackageBuilder( IPackagingService packagingService, + IMediaService mediaService, MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IJsonSerializer jsonSerializer, IMigrationContext context) : base(new ImportPackageBuilderExpression( packagingService, + mediaService, mediaFileManager, + mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, - jsonSerializer, context)) { } diff --git a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs index 4c57ff856a..b16326ea56 100644 --- a/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs +++ b/src/Umbraco.Infrastructure/Packaging/ImportPackageBuilderExpression.cs @@ -9,7 +9,7 @@ using Umbraco.Cms.Core; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Packaging; -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; @@ -20,25 +20,28 @@ namespace Umbraco.Cms.Infrastructure.Packaging internal class ImportPackageBuilderExpression : MigrationExpressionBase { private readonly IPackagingService _packagingService; + private readonly IMediaService _mediaService; private readonly MediaFileManager _mediaFileManager; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IJsonSerializer _jsonSerializer; private bool _executed; public ImportPackageBuilderExpression( IPackagingService packagingService, + IMediaService mediaService, MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IJsonSerializer jsonSerializer, IMigrationContext context) : base(context) { _packagingService = packagingService; + _mediaService = mediaService; _mediaFileManager = mediaFileManager; + _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _jsonSerializer = jsonSerializer; } /// @@ -78,21 +81,23 @@ namespace Umbraco.Cms.Infrastructure.Packaging var mediaWithFiles = xml.XPathSelectElements( "./umbPackage/MediaItems/MediaSet//*[@id][@mediaFilePath]") .ToDictionary( - x => x.AttributeValue("id"), + x => x.AttributeValue("key"), x => x.AttributeValue("mediaFilePath")); - // TODO: Almost works! Just wonder if the media installed list is empty because nothing changed? - - foreach(IMedia media in installationSummary.MediaInstalled) + // Any existing media by GUID will not be installed by the package service, it will just be skipped + // so you cannot 'update' media (or content) using a package since those are not schema type items. + // This means you cannot 'update' the media file either. The installationSummary.MediaInstalled + // will be empty for any existing media which means that the files will also not be updated. + foreach (IMedia media in installationSummary.MediaInstalled) { - if (mediaWithFiles.TryGetValue(media.Id, out var mediaFilePath)) + if (mediaWithFiles.TryGetValue(media.Key, out var mediaFilePath)) { - // this is a media item that has a file, so find that file in the zip - string entryName = mediaFilePath.TrimStart('/'); - ZipArchiveEntry mediaEntry = zipPackage.GetEntry(entryName); + // this is a media item that has a file, so find that file in the zip + var entryPath = $"media{mediaFilePath.EnsureStartsWith('/')}"; + ZipArchiveEntry mediaEntry = zipPackage.GetEntry(entryPath); if (mediaEntry == null) { - throw new InvalidOperationException("No media file found in package zip for path " + entryName); + throw new InvalidOperationException("No media file found in package zip for path " + entryPath); } // read the media file and save it to the media item @@ -101,13 +106,15 @@ namespace Umbraco.Cms.Infrastructure.Packaging { media.SetValue( _mediaFileManager, + _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, - _jsonSerializer, Constants.Conventions.Media.File, Path.GetFileName(mediaFilePath), mediaStream); } + + _mediaService.Save(media); } } } diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index e680471486..79cbf00de0 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -168,7 +168,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging var contents = ParseContentBaseRootXml(roots, parentId, importedDocumentTypes, typeService, service).ToList(); if (contents.Any()) + { service.Save(contents, userId); + } return contents; @@ -189,29 +191,35 @@ namespace Umbraco.Cms.Infrastructure.Packaging // "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); } - private IEnumerable ParseContentBaseRootXml( + private IEnumerable ParseContentBaseRootXml( IEnumerable roots, int parentId, - IDictionary importedContentTypes, - IContentTypeBaseService typeService, - IContentServiceBase service) - where T : class, IContentBase - where S : IContentTypeComposition + IDictionary importedContentTypes, + IContentTypeBaseService typeService, + IContentServiceBase service) + where TContentBase : class, IContentBase + where TContentTypeComposition : IContentTypeComposition { - var contents = new List(); - foreach (var root in roots) + var contents = new List(); + foreach (XElement root in roots) { var contentTypeAlias = root.Name.LocalName; if (!importedContentTypes.ContainsKey(contentTypeAlias)) { - var contentType = FindContentTypeByAlias(contentTypeAlias, typeService); + TContentTypeComposition contentType = FindContentTypeByAlias(contentTypeAlias, typeService); + if (contentType == null) + { + throw new InvalidOperationException("Could not find content type with alias " + contentTypeAlias); + } importedContentTypes.Add(contentTypeAlias, contentType); } - var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], default, parentId, service); + TContentBase content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], default, parentId, service); if (content == null) + { continue; + } contents.Add(content); @@ -280,7 +288,6 @@ namespace Umbraco.Cms.Infrastructure.Packaging return null; } - var id = element.Attribute("id").Value; var level = element.Attribute("level").Value; var sortOrder = element.Attribute("sortOrder").Value; var nodeName = element.Attribute("nodeName").Value; diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs index a2fa7040ea..3166cdbd4f 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationBase.cs @@ -1,10 +1,8 @@ using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Packaging; -using Umbraco.Cms.Core.Serialization; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Migrations; -using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute; namespace Umbraco.Cms.Infrastructure.Packaging { @@ -12,34 +10,38 @@ namespace Umbraco.Cms.Infrastructure.Packaging public abstract class PackageMigrationBase : MigrationBase { private readonly IPackagingService _packagingService; + private readonly IMediaService _mediaService; private readonly MediaFileManager _mediaFileManager; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; - private readonly IJsonSerializer _jsonSerializer; public PackageMigrationBase( IPackagingService packagingService, + IMediaService mediaService, MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, - IJsonSerializer jsonSerializer, IMigrationContext context) : base(context) { _packagingService = packagingService; + _mediaService = mediaService; _mediaFileManager = mediaFileManager; + _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider; - _jsonSerializer = jsonSerializer; } public IImportPackageBuilder ImportPackage => BeginBuild( new ImportPackageBuilder( _packagingService, + _mediaService, _mediaFileManager, + _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, - _jsonSerializer, Context)); } diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs index 3511a4ce0e..6cfc248827 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyEditor.cs @@ -69,9 +69,9 @@ namespace Umbraco.Cms.Core.PropertyEditors return editor; } - public bool TryGetMediaPath(string alias, object value, out string mediaPath) + public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath) { - if (alias == Alias) + if (propertyEditorAlias == Alias) { mediaPath = value?.ToString(); return true; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs index 3892071e57..805d92a267 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/FileUploadPropertyValueEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -92,11 +92,13 @@ namespace Umbraco.Cms.Core.PropertyEditors } // process the file - var filepath = editorFile == null ? null : ProcessFile(editorValue, file, currentPath, cuid, puid); + var filepath = editorFile == null ? null : ProcessFile(file, cuid, puid); // remove all temp files - foreach (var f in uploads) + foreach (ContentPropertyFile f in uploads) + { File.Delete(f.TempFilePath); + } // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) @@ -109,16 +111,18 @@ namespace Umbraco.Cms.Core.PropertyEditors } - private string ProcessFile(ContentPropertyData editorValue, ContentPropertyFile file, string currentPath, Guid cuid, Guid puid) + private string ProcessFile(ContentPropertyFile file, Guid cuid, Guid puid) { // process the file // no file, invalid file, reject change if (UploadFileTypeValidator.IsValidFileExtension(file.FileName, _contentSettings) == false) + { return null; + } // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs index f9425cbade..f21a18401c 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyEditor.cs @@ -68,9 +68,9 @@ namespace Umbraco.Cms.Core.PropertyEditors _logger = loggerFactory.CreateLogger(); } - public bool TryGetMediaPath(string alias, object value, out string mediaPath) + public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath) { - if (alias == Alias) + if (propertyEditorAlias == Alias) { mediaPath = GetFileSrcFromPropertyValue(value, out _, false); return true; diff --git a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs index c375b6f576..10a99ccd99 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/ImageCropperPropertyValueEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -142,15 +142,19 @@ namespace Umbraco.Cms.Core.PropertyEditors } // process the file - var filepath = editorJson == null ? null : ProcessFile(editorValue, file, currentPath, cuid, puid); + var filepath = editorJson == null ? null : ProcessFile(file, cuid, puid); // remove all temp files - foreach (var f in uploads) + foreach (ContentPropertyFile f in uploads) + { File.Delete(f.TempFilePath); + } // remove current file if replaced if (currentPath != filepath && string.IsNullOrWhiteSpace(currentPath) == false) + { _mediaFileManager.FileSystem.DeleteFile(currentPath); + } // update json and return if (editorJson == null) return null; @@ -158,16 +162,18 @@ namespace Umbraco.Cms.Core.PropertyEditors return editorJson.ToString(); } - private string ProcessFile(ContentPropertyData editorValue, ContentPropertyFile file, string currentPath, Guid cuid, Guid puid) + private string ProcessFile(ContentPropertyFile file, Guid cuid, Guid puid) { // process the file // no file, invalid file, reject change if (UploadFileTypeValidator.IsValidFileExtension(file.FileName, _contentSettings) == false) + { return null; + } // get the filepath // in case we are using the old path scheme, try to re-use numbers (bah...) - var filepath = _mediaFileManager.GetMediaPath(file.FileName, currentPath, cuid, puid); // fs-relative path + var filepath = _mediaFileManager.GetMediaPath(file.FileName, cuid, puid); // fs-relative path using (var filestream = File.OpenRead(file.TempFilePath)) { diff --git a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs index f92b2911dd..80769663c6 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/RichTextEditorPastedImages.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -28,11 +28,10 @@ namespace Umbraco.Cms.Core.PropertyEditors private readonly IMediaService _mediaService; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly MediaFileManager _mediaFileManager; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IShortStringHelper _shortStringHelper; private readonly IPublishedUrlProvider _publishedUrlProvider; - private readonly IJsonSerializer _serializer; - - const string TemporaryImageDataAttribute = "data-tmpimg"; + private const string TemporaryImageDataAttribute = "data-tmpimg"; public RichTextEditorPastedImages( IUmbracoContextAccessor umbracoContextAccessor, @@ -41,9 +40,9 @@ namespace Umbraco.Cms.Core.PropertyEditors IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, - IPublishedUrlProvider publishedUrlProvider, - IJsonSerializer serializer) + IPublishedUrlProvider publishedUrlProvider) { _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -51,9 +50,9 @@ namespace Umbraco.Cms.Core.PropertyEditors _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _contentTypeBaseServiceProvider = contentTypeBaseServiceProvider ?? throw new ArgumentNullException(nameof(contentTypeBaseServiceProvider)); _mediaFileManager = mediaFileManager; + _mediaUrlGenerators = mediaUrlGenerators; _shortStringHelper = shortStringHelper; _publishedUrlProvider = publishedUrlProvider; - _serializer = serializer; } /// @@ -107,7 +106,7 @@ namespace Umbraco.Cms.Core.PropertyEditors if (fileStream == null) throw new InvalidOperationException("Could not acquire file stream"); using (fileStream) { - mediaFile.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, safeFileName, fileStream); + mediaFile.SetValue(_mediaFileManager, _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File, safeFileName, fileStream); } _mediaService.Save(mediaFile, userId); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs index d4db4a76c7..52fade8dc2 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/RuntimeStateTests.cs @@ -8,6 +8,7 @@ using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.DependencyInjection; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Packaging; +using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; @@ -92,7 +93,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core private class TestMigration : PackageMigrationBase { - public TestMigration(IPackagingService packagingService, MediaFileManager mediaFileManager, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IJsonSerializer jsonSerializer, IMigrationContext context) : base(packagingService, mediaFileManager, shortStringHelper, contentTypeBaseServiceProvider, jsonSerializer, context) + public TestMigration(IPackagingService packagingService, IMediaService mediaService, MediaFileManager mediaFileManager, MediaUrlGeneratorCollection mediaUrlGenerators, IShortStringHelper shortStringHelper, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, IMigrationContext context) : base(packagingService, mediaService, mediaFileManager, mediaUrlGenerators, shortStringHelper, contentTypeBaseServiceProvider, context) { } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index b7aa9fafe1..c0a1b1700c 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -10,6 +10,7 @@ using Lucene.Net.Store; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Moq; +using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Logging; using Umbraco.Cms.Core.Models; @@ -17,7 +18,6 @@ using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Core.PropertyEditors; using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Serialization; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Core.Strings; using Umbraco.Cms.Infrastructure.Examine; @@ -33,23 +33,26 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine public class IndexInitializer { private readonly IShortStringHelper _shortStringHelper; - private readonly IJsonSerializer _jsonSerializer; private readonly PropertyEditorCollection _propertyEditors; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IScopeProvider _scopeProvider; private readonly ILoggerFactory _loggerFactory; + private readonly IOptions _contentSettings; public IndexInitializer( IShortStringHelper shortStringHelper, - IJsonSerializer jsonSerializer, PropertyEditorCollection propertyEditors, + MediaUrlGeneratorCollection mediaUrlGenerators, IScopeProvider scopeProvider, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IOptions contentSettings) { _shortStringHelper = shortStringHelper; - _jsonSerializer = jsonSerializer; _propertyEditors = propertyEditors; + _mediaUrlGenerators = mediaUrlGenerators; _scopeProvider = scopeProvider; _loggerFactory = loggerFactory; + _contentSettings = contentSettings; } public ContentValueSetBuilder GetContentValueSetBuilder(bool publishedValuesOnly) @@ -80,7 +83,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine public MediaIndexPopulator GetMediaIndexRebuilder(IMediaService mediaService) { - var mediaValueSetBuilder = new MediaValueSetBuilder(_propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), Mock.Of>(), _shortStringHelper, _jsonSerializer); + var mediaValueSetBuilder = new MediaValueSetBuilder( + _propertyEditors, + new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), + _mediaUrlGenerators, + GetMockUserService(), + _shortStringHelper, + _contentSettings); var mediaIndexDataSource = new MediaIndexPopulator(null, mediaService, mediaValueSetBuilder); return mediaIndexDataSource; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs index c8c33c510e..e8947dbf17 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MediaController.cs @@ -67,7 +67,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly IRelationService _relationService; private readonly IImageUrlGenerator _imageUrlGenerator; - private readonly IJsonSerializer _serializer; private readonly IAuthorizationService _authorizationService; private readonly AppCaches _appCaches; private readonly ILogger _logger; @@ -90,6 +89,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IRelationService relationService, PropertyEditorCollection propertyEditors, MediaFileManager mediaFileManager, + MediaUrlGeneratorCollection mediaUrlGenerators, IHostingEnvironment hostingEnvironment, IImageUrlGenerator imageUrlGenerator, IJsonSerializer serializer, @@ -111,10 +111,10 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _relationService = relationService; _propertyEditors = propertyEditors; _mediaFileManager = mediaFileManager; + _mediaUrlGenerators = mediaUrlGenerators; _hostingEnvironment = hostingEnvironment; _logger = loggerFactory.CreateLogger(); _imageUrlGenerator = imageUrlGenerator; - _serializer = serializer; _authorizationService = authorizationService; _appCaches = appCaches; } @@ -289,6 +289,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private int[] _userStartNodes; private readonly PropertyEditorCollection _propertyEditors; private readonly MediaFileManager _mediaFileManager; + private readonly MediaUrlGeneratorCollection _mediaUrlGenerators; private readonly IHostingEnvironment _hostingEnvironment; @@ -827,7 +828,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers await using (var stream = formFile.OpenReadStream()) { - f.SetValue(_mediaFileManager, _shortStringHelper, _contentTypeBaseServiceProvider, _serializer, Constants.Conventions.Media.File, fileName, stream); + f.SetValue(_mediaFileManager, _mediaUrlGenerators, _shortStringHelper, _contentTypeBaseServiceProvider, Constants.Conventions.Media.File, fileName, stream); } From 2897ef96955cfed8851fb6f9d3a21d257c36b589 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jul 2021 15:35:35 -0600 Subject: [PATCH 010/102] adds hash benchmark --- .../Packaging/PackageMigrationResource.cs | 7 +- .../CombineGuidBenchmarks.cs | 1 + .../HashFromStreams.cs | 132 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Tests.Benchmarks/HashFromStreams.cs diff --git a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs index 74ec9ac240..14cffc041f 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs @@ -26,11 +26,16 @@ namespace Umbraco.Cms.Core.Packaging public static string GetEmbeddedPackageDataManifestHash(Type planType) { + // SEE: HashFromStreams in the benchmarks project for how fast this is. It will run + // on every startup for every embedded package.zip. The bigger the zip, the more time it takes. + // But it is still very fast ~303ms for a 100MB file. This will only be an issue if there are + // several very large package.zips. + using Stream stream = GetEmbeddedPackageStream(planType); using HashAlgorithm alg = SHA1.Create(); // create a string output for the hash - var stringBuilder = new StringBuilder(); + var stringBuilder = new StringBuilder(); var hashedByteArray = alg.ComputeHash(stream); foreach (var b in hashedByteArray) { diff --git a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs index a27437c6f8..6f7cea07ca 100644 --- a/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/CombineGuidBenchmarks.cs @@ -5,6 +5,7 @@ using Umbraco.Tests.Benchmarks.Config; namespace Umbraco.Tests.Benchmarks { + [QuickRunWithMemoryDiagnoserConfig] public class CombineGuidBenchmarks { diff --git a/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs b/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs new file mode 100644 index 0000000000..9d2d483c69 --- /dev/null +++ b/src/Umbraco.Tests.Benchmarks/HashFromStreams.cs @@ -0,0 +1,132 @@ +using System.IO; +using System.Security.Cryptography; +using System.Text; +using BenchmarkDotNet.Attributes; +using Umbraco.Tests.Benchmarks.Config; + +namespace Umbraco.Tests.Benchmarks +{ + [MemoryDiagnoser] + public class HashFromStreams + { + // according to this post: https://stackoverflow.com/a/1051777/694494 + // the SHA1CryptoServiceProvider is faster, but that is not reflected in these benchmarks. + + /* + + BenchmarkDotNet=v0.13.0, OS=Windows 10.0.19042.1052 (20H2/October2020Update) + Intel Core i7-7700HQ CPU 2.80GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores + .NET SDK=5.0.300-preview.21258.4 + [Host] : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT + DefaultJob : .NET 5.0.7 (5.0.721.25508), X64 RyuJIT + + + ``` + | Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated | + |------------------------ |-------------:|------------:|------------:|------:|------:|------:|----------:| + | NormalSmall100KBFile | 279.6 μs | 4.97 μs | 4.65 μs | - | - | - | 1 KB | + | NormalMedium5MBFile | 15,051.0 μs | 242.68 μs | 215.13 μs | - | - | - | 1 KB | + | NormalLarge100MBFile | 303,356.2 μs | 2,520.40 μs | 2,234.27 μs | - | - | - | 1 KB | + | ManagedSmall100KBFile | 281.2 μs | 5.54 μs | 5.69 μs | - | - | - | 1 KB | + | ManagedMedium5MBFile | 14,895.4 μs | 95.67 μs | 74.69 μs | - | - | - | 1 KB | + | ManagedLarge100MBFile | 302,513.9 μs | 3,681.02 μs | 3,073.82 μs | - | - | - | 2 KB | + | UnmanagedSmall100KBFile | 279.1 μs | 2.91 μs | 2.43 μs | - | - | - | 1 KB | + | UnmanagedMedium5MBFile | 14,969.7 μs | 169.22 μs | 150.01 μs | - | - | - | 1 KB | + | UnmanagedLarge100MBFile | 306,127.9 μs | 3,203.10 μs | 2,839.46 μs | - | - | - | 2 KB | + + + */ + + private readonly SHA1CryptoServiceProvider _unmanaged; + private readonly SHA1Managed _managed; + private readonly SHA1 _normal; + private readonly Stream _smallFile; + private readonly Stream _medFile; + private readonly Stream _largeFile; + + public HashFromStreams() + { + _unmanaged = new SHA1CryptoServiceProvider(); + _managed = new SHA1Managed(); + _normal = SHA1.Create(); + _smallFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\small-file.bin"); + _medFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\med-file.bin"); + _largeFile = File.OpenRead(@"C:\YOUR_PATH_GOES_HERE\large-file.bin"); + } + + private string DoHash(HashAlgorithm alg, Stream stream) + { + var stringBuilder = new StringBuilder(); + var hashedByteArray = alg.ComputeHash(stream); + foreach (var b in hashedByteArray) + { + stringBuilder.Append(b.ToString("x2")); + } + return stringBuilder.ToString(); + } + + [Benchmark] + public string NormalSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _smallFile); + } + + [Benchmark] + public string NormalMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _medFile); + } + + [Benchmark] + public string NormalLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_normal, _largeFile); + } + + [Benchmark] + public string ManagedSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _smallFile); + } + + [Benchmark] + public string ManagedMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _medFile); + } + + [Benchmark] + public string ManagedLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_managed, _largeFile); + } + + [Benchmark] + public string UnmanagedSmall100KBFile() + { + _smallFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _smallFile); + } + + [Benchmark] + public string UnmanagedMedium5MBFile() + { + _medFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _medFile); + } + + [Benchmark] + public string UnmanagedLarge100MBFile() + { + _largeFile.Seek(0, SeekOrigin.Begin); + return DoHash(_unmanaged, _largeFile); + } + } +} + From f7d0e417d6228ecfcff34d1a62bc5d96b625d29d Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 7 Jul 2021 16:16:14 -0600 Subject: [PATCH 011/102] packages partial views associated with macros. --- .../Models/Packaging/CompiledPackage.cs | 1 + .../Packaging/CompiledPackageXmlParser.cs | 1 + .../Packaging/PackagesRepository.cs | 44 ++++++++++++++++++- .../Packaging/PackageDataInstallation.cs | 41 +++++++++++++++-- .../Controllers/MacrosController.cs | 9 +++- 5 files changed, 88 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index 5f6c3991a0..e3596a268e 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -14,6 +14,7 @@ namespace Umbraco.Cms.Core.Models.Packaging public string Name { get; set; } public InstallWarnings Warnings { get; set; } = new InstallWarnings(); public IEnumerable Macros { get; set; } // TODO: make strongly typed + public IEnumerable MacroPartialViews { get; set; } // TODO: make strongly typed public IEnumerable Templates { get; set; } // TODO: make strongly typed public IEnumerable Stylesheets { get; set; } // TODO: make strongly typed public IEnumerable DataTypes { get; set; } // TODO: make strongly typed diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index 2a4929930e..f4ee9738ab 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -38,6 +38,7 @@ namespace Umbraco.Cms.Core.Packaging PackageFile = null, Name = package.Element("name")?.Value, Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), + MacroPartialViews = xml.Root.Element("MacroPartialViews")?.Elements("view") ?? Enumerable.Empty(), Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), DataTypes = xml.Root.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index ae19da3036..f8e651c7fa 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.IO; using System.IO.Compression; using System.Linq; +using System.Text; using System.Xml.Linq; using System.Xml.XPath; using Microsoft.Extensions.Options; @@ -372,18 +373,30 @@ namespace Umbraco.Cms.Core.Packaging private void PackageMacros(PackageDefinition definition, XContainer root) { + var packagedMacros = new List(); var macros = new XElement("Macros"); foreach (var macroId in definition.Macros) { - if (!int.TryParse(macroId, out var outInt)) + if (!int.TryParse(macroId, out int outInt)) + { continue; + } - var macroXml = GetMacroXml(outInt, out var macro); + XElement macroXml = GetMacroXml(outInt, out IMacro macro); if (macroXml == null) + { continue; + } + macros.Add(macroXml); + packagedMacros.Add(macro); } + root.Add(macros); + + // get the partial views for macros and package those + IEnumerable views = packagedMacros.Select(x => x.MacroSource).Where(x => x.EndsWith(".cshtml")); + PackagePartialViews(views, root, "MacroPartialViews"); } private void PackageStylesheets(PackageDefinition definition, XContainer root) @@ -415,6 +428,33 @@ namespace Umbraco.Cms.Core.Packaging root.Add(templatesXml); } + private void PackagePartialViews(IEnumerable viewPaths, XContainer root, string elementName) + { + var viewsXml = new XElement(elementName); + foreach (var viewPath in viewPaths) + { + // TODO: See TODO note in MacrosController about the inconsistencies of usages of partial views + // and how paths are saved. We have no choice currently but to assume that all views are 100% always + // on the content path. + + var physicalPath = _hostingEnvironment.MapPathContentRoot(viewPath); + if (!File.Exists(physicalPath)) + { + throw new InvalidOperationException("Could not find partial view at path " + viewPath); + } + + var fileContents = File.ReadAllText(physicalPath, Encoding.UTF8); + + viewsXml.Add( + new XElement( + "view", + new XAttribute("path", viewPath), + new XCData(fileContents))); + } + + root.Add(viewsXml); + } + private void PackageDocumentTypes(PackageDefinition definition, XContainer root) { var contentTypes = new HashSet(); diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 79cbf00de0..8d87570229 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; +using System.Text; using System.Xml.Linq; using System.Xml.XPath; using Microsoft.Extensions.Logging; @@ -39,6 +40,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging private readonly IConfigurationEditorJsonSerializer _serializer; private readonly IMediaService _mediaService; private readonly IMediaTypeService _mediaTypeService; + private readonly IHostingEnvironment _hostingEnvironment; private readonly IEntityService _entityService; private readonly IContentTypeService _contentTypeService; private readonly IContentService _contentService; @@ -59,7 +61,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging IOptions globalSettings, IConfigurationEditorJsonSerializer serializer, IMediaService mediaService, - IMediaTypeService mediaTypeService) + IMediaTypeService mediaTypeService, + IHostingEnvironment hostingEnvironment) { _dataValueEditorFactory = dataValueEditorFactory; _logger = logger; @@ -74,6 +77,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging _serializer = serializer; _mediaService = mediaService; _mediaTypeService = mediaTypeService; + _hostingEnvironment = hostingEnvironment; _entityService = entityService; _contentTypeService = contentTypeService; _contentService = contentService; @@ -91,7 +95,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging DataTypesInstalled = ImportDataTypes(compiledPackage.DataTypes.ToList(), userId), LanguagesInstalled = ImportLanguages(compiledPackage.Languages, userId), DictionaryItemsInstalled = ImportDictionaryItems(compiledPackage.DictionaryItems, userId), - MacrosInstalled = ImportMacros(compiledPackage.Macros, userId), + MacrosInstalled = ImportMacros(compiledPackage.Macros, compiledPackage.MacroPartialViews, userId), TemplatesInstalled = ImportTemplates(compiledPackage.Templates.ToList(), userId), DocumentTypesInstalled = ImportDocumentTypes(compiledPackage.DocumentTypes, userId), MediaTypesInstalled = ImportMediaTypes(compiledPackage.MediaTypes, userId), @@ -1235,6 +1239,30 @@ namespace Umbraco.Cms.Infrastructure.Packaging #endregion + private void ImportPartialViews(IEnumerable viewElements) + { + foreach(XElement element in viewElements) + { + var path = element.AttributeValue("path"); + if (path == null) + { + throw new InvalidOperationException("No path attribute found"); + } + var contents = element.Value; + if (contents.IsNullOrWhiteSpace()) + { + throw new InvalidOperationException("No content found for partial view"); + } + + var physicalPath = _hostingEnvironment.MapPathContentRoot(path); + // TODO: Do we overwrite? IMO I don't think so since these will be views a user will change. + if (!System.IO.File.Exists(physicalPath)) + { + System.IO.File.WriteAllText(physicalPath, contents, Encoding.UTF8); + } + } + } + #region Macros /// @@ -1243,15 +1271,20 @@ namespace Umbraco.Cms.Infrastructure.Packaging /// Xml to import /// Optional id of the User performing the operation /// - public IReadOnlyList ImportMacros(IEnumerable macroElements, int userId) + public IReadOnlyList ImportMacros( + IEnumerable macroElements, + IEnumerable macroPartialViewsElements, + int userId) { var macros = macroElements.Select(ParseMacroElement).ToList(); - foreach (var macro in macros) + foreach (IMacro macro in macros) { _macroService.Save(macro, userId); } + ImportPartialViews(macroPartialViewsElements); + return macros; } diff --git a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs index ec91d76c8e..84ff7565cc 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/MacrosController.cs @@ -46,8 +46,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IBackOfficeSecurityAccessor backofficeSecurityAccessor, ILogger logger, IHostingEnvironment hostingEnvironment, - IUmbracoMapper umbracoMapper - ) + IUmbracoMapper umbracoMapper) { _parameterEditorCollection = parameterEditorCollection ?? throw new ArgumentNullException(nameof(parameterEditorCollection)); _macroService = macroService ?? throw new ArgumentNullException(nameof(macroService)); @@ -315,6 +314,12 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// private IEnumerable FindPartialViewFilesInViewsFolder() { + // TODO: This is inconsistent. We have FileSystems.MacroPartialsFileSystem but we basically don't use + // that at all except to render the tree. In the future we may want to use it. This also means that + // we are storing the virtual path of the macro like ~/Views/MacroPartials/Login.cshtml instead of the + // relative path which would work with the FileSystems.MacroPartialsFileSystem, but these are incompatible. + // At some point this should all be made consistent and probably just use FileSystems.MacroPartialsFileSystem. + var partialsDir = _hostingEnvironment.MapPathContentRoot(Constants.SystemDirectories.MacroPartials); return this.FindPartialViewFilesInFolder( From 5d21e07341b677ad4f45cb5b74b3a1b356b08b19 Mon Sep 17 00:00:00 2001 From: Kevin Jump Date: Thu, 8 Jul 2021 20:06:08 +0100 Subject: [PATCH 012/102] Fix for #10627 - UmbracoDictionaryTranslate checks. --- .../Services/LocalizedTextServiceExtensions.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs index dc20774142..2506d1b44d 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -76,7 +76,7 @@ namespace Umbraco.Extensions if (text == null) return null; - if (text.StartsWith("#") == false) + if (text.StartsWith("#") == false || text.IndexOf('_') == -1) return text; text = text.Substring(1); @@ -88,6 +88,9 @@ namespace Umbraco.Extensions var areaAndKey = text.Split('_'); + if (areaAndKey.Length < 2) + return text; + value = manager.Localize(areaAndKey[0], areaAndKey[1]); return value.StartsWith("[") ? text : value; } From 5a1a24556e4d358b82434436b98070230db7323e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 8 Jul 2021 13:11:39 -0600 Subject: [PATCH 013/102] Gets packaging scripts and partial views working and fixes packaging stylesheets and cleans things up a bit. --- .../ContentEditing/UmbracoEntityTypes.cs | 17 +- .../Models/Mapping/CodeFileMapDefinition.cs | 33 +- .../Models/Packaging/CompiledPackage.cs | 2 + .../Packaging/CompiledPackageXmlParser.cs | 6 +- .../Packaging/ConflictingPackageData.cs | 2 +- .../Packaging/InstallationSummary.cs | 2 + .../Packaging/PackageDefinition.cs | 6 + .../Packaging/PackageDefinitionXmlParser.cs | 26 +- .../Packaging/PackagesRepository.cs | 97 ++- .../Services/IEntityXmlSerializer.cs | 2 +- src/Umbraco.Core/Services/IFileService.cs | 17 +- .../UmbracoBuilder.Services.cs | 1 + .../Packaging/PackageDataInstallation.cs | 143 +++- .../Services/Implement/EntityXmlSerializer.cs | 7 +- .../Services/Implement/FileService.cs | 23 +- .../CreatedPackagesRepositoryTests.cs | 3 + .../Packaging/PackageDataInstallationTests.cs | 10 +- .../Controllers/CodeFileController.cs | 12 +- .../Controllers/EntityController.cs | 26 +- .../Controllers/PackageController.cs | 5 +- .../Controllers/StylesheetController.cs | 2 +- .../src/views/packages/edit.controller.js | 770 +++++++++--------- .../src/views/packages/edit.html | 274 ++++--- 23 files changed, 871 insertions(+), 615 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs b/src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs index 7e8cf39ffd..c77500c531 100644 --- a/src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs +++ b/src/Umbraco.Core/Models/ContentEditing/UmbracoEntityTypes.cs @@ -1,15 +1,10 @@ -namespace Umbraco.Cms.Core.Models.ContentEditing +namespace Umbraco.Cms.Core.Models.ContentEditing { /// /// Represents the type's of Umbraco entities that can be resolved from the EntityController /// public enum UmbracoEntityTypes { - /// - /// Domain - /// - Domain, - /// /// Language /// @@ -65,6 +60,16 @@ /// Stylesheet, + /// + /// Script + /// + Script, + + /// + /// Partial View + /// + PartialView, + /// /// Member /// diff --git a/src/Umbraco.Core/Models/Mapping/CodeFileMapDefinition.cs b/src/Umbraco.Core/Models/Mapping/CodeFileMapDefinition.cs index d2fd28f510..b185bb586e 100644 --- a/src/Umbraco.Core/Models/Mapping/CodeFileMapDefinition.cs +++ b/src/Umbraco.Core/Models/Mapping/CodeFileMapDefinition.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core.Mapping; +using Umbraco.Cms.Core.Mapping; using Umbraco.Cms.Core.Models.ContentEditing; namespace Umbraco.Cms.Core.Models.Mapping @@ -8,9 +8,14 @@ namespace Umbraco.Cms.Core.Models.Mapping public void DefineMaps(IUmbracoMapper mapper) { mapper.Define((source, context) => new EntityBasic(), Map); - mapper.Define((source, context) => new CodeFileDisplay(), Map); - mapper.Define((source, context) => new CodeFileDisplay(), Map); mapper.Define((source, context) => new CodeFileDisplay(), Map); + + mapper.Define((source, context) => new EntityBasic(), Map); + mapper.Define((source, context) => new CodeFileDisplay(), Map); + + mapper.Define((source, context) => new EntityBasic(), Map); + mapper.Define((source, context) => new CodeFileDisplay(), Map); + mapper.Define(Map); mapper.Define(Map); @@ -27,6 +32,28 @@ namespace Umbraco.Cms.Core.Models.Mapping target.Path = source.Path; } + // Umbraco.Code.MapAll -Trashed -Udi -Icon + private static void Map(IScript source, EntityBasic target, MapperContext context) + { + target.Alias = source.Alias; + target.Id = source.Id; + target.Key = source.Key; + target.Name = source.Name; + target.ParentId = -1; + target.Path = source.Path; + } + + // Umbraco.Code.MapAll -Trashed -Udi -Icon + private static void Map(IPartialView source, EntityBasic target, MapperContext context) + { + target.Alias = source.Alias; + target.Id = source.Id; + target.Key = source.Key; + target.Name = source.Name; + target.ParentId = -1; + target.Path = source.Path; + } + // Umbraco.Code.MapAll -FileType -Notifications -Path -Snippet private static void Map(IPartialView source, CodeFileDisplay target, MapperContext context) { diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs index e3596a268e..6e963bd2da 100644 --- a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -17,6 +17,8 @@ namespace Umbraco.Cms.Core.Models.Packaging public IEnumerable MacroPartialViews { get; set; } // TODO: make strongly typed public IEnumerable Templates { get; set; } // TODO: make strongly typed public IEnumerable Stylesheets { get; set; } // TODO: make strongly typed + public IEnumerable Scripts { get; set; } // TODO: make strongly typed + public IEnumerable PartialViews { get; set; } // TODO: make strongly typed public IEnumerable DataTypes { get; set; } // TODO: make strongly typed public IEnumerable Languages { get; set; } // TODO: make strongly typed public IEnumerable DictionaryItems { get; set; } // TODO: make strongly typed diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs index f4ee9738ab..2b6b3b9e1c 100644 --- a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -38,9 +38,11 @@ namespace Umbraco.Cms.Core.Packaging PackageFile = null, Name = package.Element("name")?.Value, Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), - MacroPartialViews = xml.Root.Element("MacroPartialViews")?.Elements("view") ?? Enumerable.Empty(), + MacroPartialViews = xml.Root.Element("MacroPartialViews")?.Elements("View") ?? Enumerable.Empty(), + PartialViews = xml.Root.Element("PartialViews")?.Elements("View") ?? Enumerable.Empty(), Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), - Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), + Stylesheets = xml.Root.Element("Stylesheets")?.Elements("Stylesheet") ?? Enumerable.Empty(), + Scripts = xml.Root.Element("Scripts")?.Elements("Script") ?? Enumerable.Empty(), DataTypes = xml.Root.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), Languages = xml.Root.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), DictionaryItems = xml.Root.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index 27e96d8c82..0d71b9f70e 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.Packaging if (xElement == null) throw new FormatException("Missing \"Name\" element"); - return _fileService.GetStylesheetByName(xElement.Value) as IFile; + return _fileService.GetStylesheet(xElement.Value) as IFile; }) .Where(v => v != null); } diff --git a/src/Umbraco.Core/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Packaging/InstallationSummary.cs index 3c519a4a4c..005d5859fb 100644 --- a/src/Umbraco.Core/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Packaging/InstallationSummary.cs @@ -27,6 +27,8 @@ namespace Umbraco.Cms.Core.Packaging public IEnumerable DocumentTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable MediaTypesInstalled { get; set; } = Enumerable.Empty(); public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ScriptsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable PartialViewsInstalled { get; set; } = Enumerable.Empty(); public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); public IEnumerable MediaInstalled { get; set; } = Enumerable.Empty(); public string PackageName { get; } diff --git a/src/Umbraco.Core/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Packaging/PackageDefinition.cs index 3c808d4de0..345d669b15 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinition.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinition.cs @@ -52,6 +52,9 @@ namespace Umbraco.Cms.Core.Packaging [DataMember(Name = "templates")] public IList Templates { get; set; } = new List(); + [DataMember(Name = "partialViews")] + public IList PartialViews { get; set; } = new List(); + [DataMember(Name = "documentTypes")] public IList DocumentTypes { get; set; } = new List(); @@ -61,6 +64,9 @@ namespace Umbraco.Cms.Core.Packaging [DataMember(Name = "stylesheets")] public IList Stylesheets { get; set; } = new List(); + [DataMember(Name = "scripts")] + public IList Scripts { get; set; } = new List(); + [DataMember(Name = "dataTypes")] public IList DataTypes { get; set; } = new List(); diff --git a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs index 69c109aee4..7cca061401 100644 --- a/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs +++ b/src/Umbraco.Core/Packaging/PackageDefinitionXmlParser.cs @@ -12,6 +12,10 @@ namespace Umbraco.Cms.Core.Packaging /// public class PackageDefinitionXmlParser { + private static readonly IList s_emptyStringList = new List(); + private static readonly IList s_emptyGuidUdiList = new List(); + + public PackageDefinition ToPackageDefinition(XElement xml) { if (xml == null) @@ -27,16 +31,18 @@ namespace Umbraco.Cms.Core.Packaging PackageId = xml.AttributeValue("packageGuid"), ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, - MediaUdis = xml.Element("media")?.Elements("nodeUdi").Select(x => (GuidUdi)UdiParser.Parse(x.Value)).ToList() ?? new List(), + MediaUdis = xml.Element("media")?.Elements("nodeUdi").Select(x => (GuidUdi)UdiParser.Parse(x.Value)).ToList() ?? s_emptyGuidUdiList, MediaLoadChildNodes = xml.Element("media")?.AttributeValue("loadChildNodes") ?? false, - Macros = xml.Element("macros")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Templates = xml.Element("templates")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Stylesheets = xml.Element("stylesheets")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DocumentTypes = xml.Element("documentTypes")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - MediaTypes = xml.Element("mediaTypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - Languages = xml.Element("languages")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), - DataTypes = xml.Element("datatypes")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Macros = xml.Element("macros")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + Templates = xml.Element("templates")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + Stylesheets = xml.Element("stylesheets")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + Scripts = xml.Element("scripts")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + PartialViews = xml.Element("partialViews")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + DocumentTypes = xml.Element("documentTypes")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + MediaTypes = xml.Element("mediaTypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + Languages = xml.Element("languages")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, + DataTypes = xml.Element("datatypes")?.Value.Split(Constants.CharArrays.Comma, StringSplitOptions.RemoveEmptyEntries).ToList() ?? s_emptyStringList, }; return retVal; @@ -57,6 +63,8 @@ namespace Umbraco.Cms.Core.Packaging new XElement("templates", string.Join(",", def.Templates ?? Array.Empty())), new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty())), + new XElement("scripts", string.Join(",", def.Scripts ?? Array.Empty())), + new XElement("partialViews", string.Join(",", def.PartialViews ?? Array.Empty())), new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty())), new XElement("mediaTypes", string.Join(",", def.MediaTypes ?? Array.Empty())), new XElement("macros", string.Join(",", def.Macros ?? Array.Empty())), diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs index f8e651c7fa..ffc67663cc 100644 --- a/src/Umbraco.Core/Packaging/PackagesRepository.cs +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -6,9 +6,7 @@ using System.IO.Compression; using System.Linq; using System.Text; using System.Xml.Linq; -using System.Xml.XPath; using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; @@ -40,6 +38,7 @@ namespace Umbraco.Cms.Core.Packaging private readonly IMediaService _mediaService; private readonly IMediaTypeService _mediaTypeService; private readonly MediaFileManager _mediaFileManager; + private readonly FileSystems _fileSystems; /// /// Constructor @@ -72,6 +71,7 @@ namespace Umbraco.Cms.Core.Packaging IMediaService mediaService, IMediaTypeService mediaTypeService, MediaFileManager mediaFileManager, + FileSystems fileSystems, string packageRepositoryFileName, string tempFolderPath = null, string packagesFolderPath = null, @@ -97,6 +97,7 @@ namespace Umbraco.Cms.Core.Packaging _mediaService = mediaService; _mediaTypeService = mediaTypeService; _mediaFileManager = mediaFileManager; + _fileSystems = fileSystems; } private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + _packageRepositoryFileName; @@ -190,7 +191,7 @@ namespace Umbraco.Cms.Core.Packaging try { //Init package file - var compiledPackageXml = CreateCompiledPackageXml(out var root); + XDocument compiledPackageXml = CreateCompiledPackageXml(out XElement root); //Info section root.Add(GetPackageInfoXml(definition)); @@ -200,6 +201,8 @@ namespace Umbraco.Cms.Core.Packaging PackageMediaTypes(definition, root); PackageTemplates(definition, root); PackageStylesheets(definition, root); + PackageStaticFiles(definition.Scripts, root, "Scripts", "Script", _fileSystems.ScriptsFileSystem); + PackageStaticFiles(definition.PartialViews, root, "PartialViews", "View", _fileSystems.PartialViewsFileSystem); PackageMacros(definition, root); PackageDictionaryItems(definition, root); PackageLanguages(definition, root); @@ -396,23 +399,62 @@ namespace Umbraco.Cms.Core.Packaging // get the partial views for macros and package those IEnumerable views = packagedMacros.Select(x => x.MacroSource).Where(x => x.EndsWith(".cshtml")); - PackagePartialViews(views, root, "MacroPartialViews"); + PackageMacroPartialViews(views, root); } private void PackageStylesheets(PackageDefinition definition, XContainer root) { var stylesheetsXml = new XElement("Stylesheets"); - foreach (var stylesheetName in definition.Stylesheets) + foreach (var stylesheet in definition.Stylesheets) { - if (stylesheetName.IsNullOrWhiteSpace()) + if (stylesheet.IsNullOrWhiteSpace()) + { continue; - var xml = GetStylesheetXml(stylesheetName, true); + } + + XElement xml = GetStylesheetXml(stylesheet, true); if (xml != null) + { stylesheetsXml.Add(xml); + } } root.Add(stylesheetsXml); } + private void PackageStaticFiles( + IEnumerable filePaths, + XContainer root, + string containerName, + string elementName, + IFileSystem fileSystem) + { + var scriptsXml = new XElement(containerName); + foreach (var file in filePaths) + { + if (file.IsNullOrWhiteSpace()) + { + continue; + } + + if (!fileSystem.FileExists(file)) + { + throw new InvalidOperationException("No file found with path " + file); + } + + using (Stream stream = fileSystem.OpenFile(file)) + using (var reader = new StreamReader(stream)) + { + var fileContents = reader.ReadToEnd(); + scriptsXml.Add( + new XElement( + elementName, + new XAttribute("path", file), + new XCData(fileContents))); + } + } + root.Add(scriptsXml); + } + private void PackageTemplates(PackageDefinition definition, XContainer root) { var templatesXml = new XElement("Templates"); @@ -428,9 +470,9 @@ namespace Umbraco.Cms.Core.Packaging root.Add(templatesXml); } - private void PackagePartialViews(IEnumerable viewPaths, XContainer root, string elementName) + private void PackageMacroPartialViews(IEnumerable viewPaths, XContainer root) { - var viewsXml = new XElement(elementName); + var viewsXml = new XElement("MacroPartialViews"); foreach (var viewPath in viewPaths) { // TODO: See TODO note in MacrosController about the inconsistencies of usages of partial views @@ -447,7 +489,7 @@ namespace Umbraco.Cms.Core.Packaging viewsXml.Add( new XElement( - "view", + "View", new XAttribute("path", viewPath), new XCData(fileContents))); } @@ -631,34 +673,23 @@ namespace Umbraco.Cms.Core.Packaging /// /// Converts a umbraco stylesheet to a package xml node /// - /// The name of the stylesheet. + /// The path of the stylesheet. /// if set to true [include properties]. /// - private XElement GetStylesheetXml(string name, bool includeProperties) + private XElement GetStylesheetXml(string path, bool includeProperties) { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("Value cannot be null or whitespace.", nameof(name)); - var sts = _fileService.GetStylesheetByName(name); - if (sts == null) - return null; - var stylesheetXml = new XElement("Stylesheet"); - stylesheetXml.Add(new XElement("Name", sts.Alias)); - stylesheetXml.Add(new XElement("FileName", sts.Name)); - stylesheetXml.Add(new XElement("Content", new XCData(sts.Content))); - - if (!includeProperties) - return stylesheetXml; - - var properties = new XElement("Properties"); - foreach (var ssP in sts.Properties) + if (string.IsNullOrWhiteSpace(path)) { - var property = new XElement("Property"); - property.Add(new XElement("Name", ssP.Name)); - property.Add(new XElement("Alias", ssP.Alias)); - property.Add(new XElement("Value", ssP.Value)); + throw new ArgumentException("Value cannot be null or whitespace.", nameof(path)); } - stylesheetXml.Add(properties); - return stylesheetXml; + + IStylesheet stylesheet = _fileService.GetStylesheet(path); + if (stylesheet == null) + { + return null; + } + + return _serializer.Serialize(stylesheet, includeProperties); } private void AddDocumentType(IContentType dt, HashSet dtl) diff --git a/src/Umbraco.Core/Services/IEntityXmlSerializer.cs b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs index 97838b1a69..c3e8a29e8e 100644 --- a/src/Umbraco.Core/Services/IEntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs @@ -56,7 +56,7 @@ namespace Umbraco.Cms.Core.Services /// containing the xml representation of the IDictionaryItem object XElement Serialize(IDictionaryItem dictionaryItem, bool includeChildren); - XElement Serialize(Stylesheet stylesheet); + XElement Serialize(IStylesheet stylesheet, bool includeProperties); /// /// Exports a list of items to xml as an diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 5df5602bc6..903603c415 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -15,6 +15,13 @@ namespace Umbraco.Cms.Core.Services void CreatePartialViewMacroFolder(string folderPath); void DeletePartialViewFolder(string folderPath); void DeletePartialViewMacroFolder(string folderPath); + + /// + /// Gets a list of all objects + /// + /// An enumerable list of objects + IEnumerable GetPartialViews(params string[] names); + IPartialView GetPartialView(string path); IPartialView GetPartialViewMacro(string path); Attempt CreatePartialView(IPartialView partialView, string snippetName = null, int userId = Constants.Security.SuperUserId); @@ -77,7 +84,7 @@ namespace Umbraco.Cms.Core.Services /// /// Name of the stylesheet incl. extension /// A object - IStylesheet GetStylesheetByName(string name); + IStylesheet GetStylesheet(string name); /// /// Saves a @@ -127,12 +134,18 @@ namespace Umbraco.Cms.Core.Services /// The size of the stylesheet. long GetStylesheetFileSize(string filepath); + /// + /// Gets a list of all objects + /// + /// An enumerable list of objects + IEnumerable GetScripts(params string[] names); + /// /// Gets a object by its name /// /// Name of the script incl. extension /// A object - IScript GetScriptByName(string name); + IScript GetScript(string name); /// /// Saves a diff --git a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs index 14da8f7aa6..8b34289c9c 100644 --- a/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs +++ b/src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs @@ -95,6 +95,7 @@ namespace Umbraco.Cms.Infrastructure.DependencyInjection factory.GetRequiredService(), factory.GetRequiredService(), factory.GetRequiredService(), + factory.GetRequiredService(), packageRepoFileName); private static LocalizedTextServiceFileSources SourcesFactory(IServiceProvider container) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 8d87570229..91e3829806 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Text; @@ -106,6 +107,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging var importedMediaTypes = installationSummary.MediaTypesInstalled.ToDictionary(x => x.Alias, x => x); installationSummary.StylesheetsInstalled = ImportStylesheets(compiledPackage.Stylesheets, userId); + installationSummary.PartialViewsInstalled = ImportPartialViews(compiledPackage.PartialViews, userId); + installationSummary.ScriptsInstalled = ImportScripts(compiledPackage.Scripts, userId); installationSummary.ContentInstalled = ImportContentBase(compiledPackage.Documents, importedDocTypes, userId, _contentTypeService, _contentService); installationSummary.MediaInstalled = ImportContentBase(compiledPackage.Media, importedMediaTypes, userId, _mediaTypeService, _mediaService); @@ -1239,30 +1242,6 @@ namespace Umbraco.Cms.Infrastructure.Packaging #endregion - private void ImportPartialViews(IEnumerable viewElements) - { - foreach(XElement element in viewElements) - { - var path = element.AttributeValue("path"); - if (path == null) - { - throw new InvalidOperationException("No path attribute found"); - } - var contents = element.Value; - if (contents.IsNullOrWhiteSpace()) - { - throw new InvalidOperationException("No content found for partial view"); - } - - var physicalPath = _hostingEnvironment.MapPathContentRoot(path); - // TODO: Do we overwrite? IMO I don't think so since these will be views a user will change. - if (!System.IO.File.Exists(physicalPath)) - { - System.IO.File.WriteAllText(physicalPath, contents, Encoding.UTF8); - } - } - } - #region Macros /// @@ -1283,11 +1262,35 @@ namespace Umbraco.Cms.Infrastructure.Packaging _macroService.Save(macro, userId); } - ImportPartialViews(macroPartialViewsElements); + ImportMacroPartialViews(macroPartialViewsElements); return macros; } + private void ImportMacroPartialViews(IEnumerable viewElements) + { + foreach (XElement element in viewElements) + { + var path = element.AttributeValue("path"); + if (path == null) + { + throw new InvalidOperationException("No path attribute found"); + } + var contents = element.Value; + if (contents.IsNullOrWhiteSpace()) + { + throw new InvalidOperationException("No content found for partial view"); + } + + var physicalPath = _hostingEnvironment.MapPathContentRoot(path); + // TODO: Do we overwrite? IMO I don't think so since these will be views a user will change. + if (!System.IO.File.Exists(physicalPath)) + { + System.IO.File.WriteAllText(physicalPath, contents, Encoding.UTF8); + } + } + } + private IMacro ParseMacroElement(XElement macroElement) { var macroKey = Guid.Parse(macroElement.Element("key").Value); @@ -1368,30 +1371,98 @@ namespace Umbraco.Cms.Infrastructure.Packaging #endregion + public IReadOnlyList ImportScripts(IEnumerable scriptElements, int userId) + { + var result = new List(); + + foreach (XElement scriptXml in scriptElements) + { + var path = scriptXml.AttributeValue("path"); + + if (path.IsNullOrWhiteSpace()) + { + continue; + } + + IScript script = _fileService.GetScript(path); + + // only update if it doesn't exist + if (script == null) + { + var content = scriptXml.Value; + if (content == null) + { + continue; + } + + script = new Script(path) { Content = content }; + _fileService.SaveScript(script, userId); + result.Add(script); + } + } + + return result; + } + + public IReadOnlyList ImportPartialViews(IEnumerable partialViewElements, int userId) + { + var result = new List(); + + foreach (XElement partialViewXml in partialViewElements) + { + var path = partialViewXml.AttributeValue("path"); + + if (path.IsNullOrWhiteSpace()) + { + continue; + } + + IPartialView partialView = _fileService.GetPartialView(path); + + // only update if it doesn't exist + if (partialView == null) + { + var content = partialViewXml.Value; + if (content == null) + { + continue; + } + + partialView = new PartialView(PartialViewType.PartialView, path) { Content = content }; + _fileService.SavePartialView(partialView, userId); + result.Add(partialView); + } + } + + return result; + } + #region Stylesheets public IReadOnlyList ImportStylesheets(IEnumerable stylesheetElements, int userId) { var result = new List(); - foreach (var n in stylesheetElements) + foreach (XElement n in stylesheetElements) { - var stylesheetName = n.Element("Name")?.Value; - if (stylesheetName.IsNullOrWhiteSpace()) - continue; + var stylesheetPath = n.Element("FileName")?.Value; - var s = _fileService.GetStylesheetByName(stylesheetName); + if (stylesheetPath.IsNullOrWhiteSpace()) + { + continue; + } + + IStylesheet s = _fileService.GetStylesheet(stylesheetPath); if (s == null) { - var fileName = n.Element("FileName")?.Value; - if (fileName == null) - continue; var content = n.Element("Content")?.Value; if (content == null) + { continue; + } - s = new Stylesheet(fileName) { Content = content }; - _fileService.SaveStylesheet(s); + s = new Stylesheet(stylesheetPath) { Content = content }; + _fileService.SaveStylesheet(s, userId); } foreach (var prop in n.XPathSelectElements("Properties/Property")) @@ -1419,7 +1490,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging sp.Alias = alias; sp.Value = prop.Element("Value")?.Value; } - _fileService.SaveStylesheet(s); + _fileService.SaveStylesheet(s, userId); result.Add(s); } diff --git a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs index 2e46b0b975..11fbd87232 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/EntityXmlSerializer.cs @@ -264,13 +264,18 @@ namespace Umbraco.Cms.Core.Services.Implement return xml; } - public XElement Serialize(Stylesheet stylesheet) + public XElement Serialize(IStylesheet stylesheet, bool includeProperties) { var xml = new XElement("Stylesheet", new XElement("Name", stylesheet.Alias), new XElement("FileName", stylesheet.Path), new XElement("Content", new XCData(stylesheet.Content))); + if (!includeProperties) + { + return xml; + } + var props = new XElement("Properties"); xml.Add(props); diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 6d7d5b4490..64ae7c5dfc 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -65,11 +65,11 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public IStylesheet GetStylesheetByName(string name) + public IStylesheet GetStylesheet(string path) { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - return _stylesheetRepository.Get(name); + return _stylesheetRepository.Get(path); } } @@ -177,7 +177,16 @@ namespace Umbraco.Cms.Core.Services.Implement #region Scripts /// - public IScript GetScriptByName(string name) + public IEnumerable GetScripts(params string[] names) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _scriptRepository.GetMany(names); + } + } + + /// + public IScript GetScript(string name) { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -647,6 +656,14 @@ namespace Umbraco.Cms.Core.Services.Implement } } + public IEnumerable GetPartialViews(params string[] names) + { + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + return _partialViewRepository.GetMany(names); + } + } + public IPartialView GetPartialView(string path) { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs index 892c7595d5..ca0b3dc9e4 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs @@ -59,6 +59,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging private MediaFileManager MediaFileManager => GetRequiredService(); + private FileSystems FileSystems => GetRequiredService(); + public ICreatedPackagesRepository PackageBuilder => new PackagesRepository( ContentService, ContentTypeService, @@ -72,6 +74,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging MediaService, MediaTypeService, MediaFileManager, + FileSystems, "createdPackages.config", // temp paths diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs index cd2f438c14..1c094b97f0 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Packaging/PackageDataInstallationTests.cs @@ -648,7 +648,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging XElement macrosElement = xml.Descendants("Macros").First(); // Act - var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); + var macros = PackageDataInstallation.ImportMacros( + macrosElement.Elements("macro"), + Enumerable.Empty(), + 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); @@ -669,7 +672,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Packaging XElement macrosElement = xml.Descendants("Macros").First(); // Act - var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList(); + var macros = PackageDataInstallation.ImportMacros( + macrosElement.Elements("macro"), + Enumerable.Empty(), + 0).ToList(); // Assert Assert.That(macros.Any(), Is.True); diff --git a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs index a88cdc5087..64a47c4cc4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/CodeFileController.cs @@ -213,7 +213,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } break; case Constants.Trees.Scripts: - var script = _fileService.GetScriptByName(virtualPath); + var script = _fileService.GetScript(virtualPath); if (script != null) { var display = _umbracoMapper.Map(script); @@ -224,7 +224,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers } break; case Constants.Trees.Stylesheets: - var stylesheet = _fileService.GetStylesheetByName(virtualPath); + var stylesheet = _fileService.GetStylesheet(virtualPath); if (stylesheet != null) { var display = _umbracoMapper.Map(stylesheet); @@ -371,7 +371,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _fileService.DeleteScriptFolder(virtualPath); return Ok(); } - if (_fileService.GetScriptByName(virtualPath) != null) + if (_fileService.GetScript(virtualPath) != null) { _fileService.DeleteScript(virtualPath, currentUser.Id); return Ok(); @@ -383,7 +383,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _fileService.DeleteStyleSheetFolder(virtualPath); return Ok(); } - if (_fileService.GetStylesheetByName(virtualPath) != null) + if (_fileService.GetStylesheet(virtualPath) != null) { _fileService.DeleteStylesheet(virtualPath, currentUser.Id); return Ok(); @@ -540,7 +540,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private IScript CreateOrUpdateScript(CodeFileDisplay display) { return CreateOrUpdateFile(display, ".js", _fileSystems.ScriptsFileSystem, - name => _fileService.GetScriptByName(name), + name => _fileService.GetScript(name), (script, userId) => _fileService.SaveScript(script, userId), name => new Script(name)); } @@ -548,7 +548,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private IStylesheet CreateOrUpdateStylesheet(CodeFileDisplay display) { return CreateOrUpdateFile(display, ".css", _fileSystems.StylesheetsFileSystem, - name => _fileService.GetStylesheetByName(name), + name => _fileService.GetStylesheet(name), (stylesheet, userId) => _fileService.SaveStylesheet(stylesheet, userId), name => new Stylesheet(name) ); diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index 6924780cd2..fdfdb90210 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -545,7 +545,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //now we need to convert the unknown ones switch (type) { - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -705,7 +704,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case UmbracoEntityTypes.PropertyType: case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -791,7 +789,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case UmbracoEntityTypes.PropertyType: case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -835,7 +832,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers //now we need to convert the unknown ones switch (entityType) { - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -902,7 +898,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case UmbracoEntityTypes.PropertyType: case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -934,7 +929,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case UmbracoEntityTypes.PropertyType: case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -966,7 +960,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers { case UmbracoEntityTypes.PropertyType: case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: case UmbracoEntityTypes.Macro: @@ -994,8 +987,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: - case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: @@ -1026,8 +1017,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers case UmbracoEntityTypes.PropertyGroup: - case UmbracoEntityTypes.Domain: - case UmbracoEntityTypes.Language: case UmbracoEntityTypes.User: @@ -1140,6 +1129,20 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return _fileService.GetStylesheets().Select(MapEntities()); + case UmbracoEntityTypes.Script: + + if (!postFilter.IsNullOrWhiteSpace()) + throw new NotSupportedException("Filtering on scripts is not currently supported"); + + return _fileService.GetScripts().Select(MapEntities()); + + case UmbracoEntityTypes.PartialView: + + if (!postFilter.IsNullOrWhiteSpace()) + throw new NotSupportedException("Filtering on scripts is not currently supported"); + + return _fileService.GetPartialViews().Select(MapEntities()); + case UmbracoEntityTypes.Language: if (!postFilter.IsNullOrWhiteSpace()) @@ -1153,7 +1156,6 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return GetAllDictionaryItems(); - case UmbracoEntityTypes.Domain: default: throw new NotSupportedException("The " + typeof(EntityController) + " does not currently support data for the type " + entityType); } diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs index b9cac44c04..dca3a320ec 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs @@ -75,10 +75,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers return package; } - public PackageDefinition GetEmpty() - { - return new PackageDefinition(); - } + public PackageDefinition GetEmpty() => new PackageDefinition(); /// /// Creates or updates a package diff --git a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs index 0eb18cf7cd..4f64a34632 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/StylesheetController.cs @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers public IEnumerable GetRulesByName(string name) { - var css = _fileService.GetStylesheetByName(name.EnsureEndsWith(".css")); + var css = _fileService.GetStylesheet(name.EnsureEndsWith(".css")); if (css == null) return Enumerable.Empty(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js index 2c6c489e22..f5db75ebe1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -1,401 +1,443 @@ (function () { - "use strict"; + "use strict"; - function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, packageResource, editorService, formHelper, localizationService) { + function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, packageResource, editorService, formHelper, localizationService) { - const vm = this; + const vm = this; - const packageId = $routeParams.id; - const create = $routeParams.create; + const packageId = $routeParams.id; + const create = $routeParams.create; - vm.showBackButton = true; + vm.showBackButton = true; - // open all expansion panels - vm.loading = true; - vm.mediaNodeDisplayModels = []; - vm.back = back; - vm.createOrUpdatePackage = createOrUpdatePackage; - vm.removeContentItem = removeContentItem; - vm.openContentPicker = openContentPicker; - vm.openViewPicker = openViewPicker; - vm.removePackageView = removePackageView; - vm.downloadFile = downloadFile; + // open all expansion panels + vm.loading = true; + vm.mediaNodeDisplayModels = []; + vm.back = back; + vm.createOrUpdatePackage = createOrUpdatePackage; + vm.removeContentItem = removeContentItem; + vm.openContentPicker = openContentPicker; + vm.openViewPicker = openViewPicker; + vm.removePackageView = removePackageView; + vm.downloadFile = downloadFile; - vm.selectDocumentType = selectDocumentType; - vm.selectMediaType = selectMediaType; - vm.selectTemplate = selectTemplate; - vm.selectStyleSheet = selectStyleSheet; - vm.selectMacro = selectMacro; - vm.selectLanguage = selectLanguage; - vm.selectDictionaryItem = selectDictionaryItem; - vm.selectDataType = selectDataType; + vm.selectDocumentType = selectDocumentType; + vm.selectMediaType = selectMediaType; + vm.selectTemplate = selectTemplate; + vm.selectStyleSheet = selectStyleSheet; + vm.selectScript = selectScript; + vm.selectPartialView = selectPartialView; + vm.selectMacro = selectMacro; + vm.selectLanguage = selectLanguage; + vm.selectDictionaryItem = selectDictionaryItem; + vm.selectDataType = selectDataType; - vm.mediaPickerModel = { - hideLabel: true, - view: "mediapicker", - value: "", - config: { - multiPicker: true, - allowEdit:false - } - } - vm.labels = {}; - - vm.versionRegex = /^(\d+\.)(\d+\.)(\*|\d+)$/; - - vm.aceOption = { - mode: "xml", - theme: "chrome", - showPrintMargin: false, - advanced: { - fontSize: '14px', - enableSnippets: true, - enableBasicAutocompletion: true, - enableLiveAutocompletion: false - }, - onLoad: function (_editor) { - vm.editor = _editor; - - vm.editor.setValue(vm.package.actions); - } - }; - - function onInit() { - - if (create) { - // Pre populate package with some values - packageResource.getEmpty().then(scaffold => { - vm.package = scaffold; - - loadResources(); - - vm.loading = false; - }); - - localizationService.localizeMany(["general_create", "packager_includeAllChildNodes"]).then(function (values) { - vm.labels.button = values[0]; - vm.labels.includeAllChildNodes = values[1]; - }); - } else { - // Load package - packageResource.getCreatedById(packageId).then(createdPackage => { - vm.package = createdPackage; - - loadResources(); - - vm.loading = false; - - // Get render model for content node - if (vm.package.contentNodeId) { - entityResource.getById(vm.package.contentNodeId, "Document") - .then((entity) => { - vm.contentNodeDisplayModel = entity; - }); - } - - vm.mediaPickerModel.value = vm.package.mediaUdis.join(','); - }); - - - localizationService.localizeMany(["buttons_save", "packager_includeAllChildNodes"]).then(function (values) { - vm.labels.button = values[0]; - vm.labels.includeAllChildNodes = values[1]; - }); - } - } - - function loadResources() { - - // Get all document types - entityResource.getAll("DocumentType").then(documentTypes => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - documentTypes.forEach(documentType => { - documentType.id = documentType.id.toString(); - documentType.selected = vm.package.documentTypes.indexOf(documentType.id) !== -1; - }); - vm.documentTypes = documentTypes; - }); - - // Get all media types - entityResource.getAll("MediaType").then(mediaTypes => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - mediaTypes.forEach(mediaType => { - mediaType.id = mediaType.id.toString(); - mediaType.selected = vm.package.mediaTypes.indexOf(mediaType.id) !== -1; - }); - vm.mediaTypes = mediaTypes; - }); - - // Get all templates - entityResource.getAll("Template").then(templates => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - templates.forEach(template => { - template.id = template.id.toString(); - template.selected = vm.package.templates.indexOf(template.id) >= 0; - }); - vm.templates = templates; - }); - - // Get all stylesheets - entityResource.getAll("Stylesheet").then(stylesheets => { - stylesheets.forEach(stylesheet => { - stylesheet.selected = vm.package.stylesheets.indexOf(stylesheet.name) >= 0; - }); - vm.stylesheets = stylesheets; - }); - - // Get all macros - entityResource.getAll("Macro").then(macros => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - macros.forEach(macro => { - macro.id = macro.id.toString(); - macro.selected = vm.package.macros.indexOf(macro.id) !== -1; - }); - vm.macros = macros; - }); - - // Get all languages - entityResource.getAll("Language").then(languages => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - languages.forEach(language => { - language.id = language.id.toString(); - language.selected = vm.package.languages.indexOf(language.id) !== -1; - }); - vm.languages = languages; - }); - - // Get all dictionary items - entityResource.getAll("DictionaryItem").then(dictionaryItems => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - dictionaryItems.forEach(dictionaryItem => { - dictionaryItem.id = dictionaryItem.id.toString(); - dictionaryItem.selected = vm.package.dictionaryItems.indexOf(dictionaryItem.id) !== -1; - }); - vm.dictionaryItems = dictionaryItems; - }); - - // Get all data types - entityResource.getAll("DataType").then(dataTypes => { - // a package stores the id as a string so we - // need to convert all ids to string for comparison - dataTypes.forEach(dataType => { - dataType.id = dataType.id.toString(); - dataType.selected = vm.package.dataTypes.indexOf(dataType.id) !== -1; - }); - vm.dataTypes = dataTypes; - }); - - } - - function downloadFile(id) { - var url = umbRequestHelper.getApiUrl( - "packageApiBaseUrl", - "DownloadCreatedPackage", - { id: id }); - - umbRequestHelper.downloadFile(url).then(function () { - - }); - } - - function back() { - $location.path("packages/packages/created").search("create", null).search("packageId", null); - } - - function createOrUpdatePackage(editPackageForm) { - - // Split by comma and remove empty entries - vm.package.mediaUdis = vm.mediaPickerModel.value.split(",").filter(i => i); - if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { - - vm.buttonState = "busy"; - - packageResource.savePackage(vm.package).then((updatedPackage) => { - - vm.package = updatedPackage; - vm.buttonState = "success"; - - formHelper.resetForm({ scope: $scope, formCtrl: editPackageForm }); - - if (create) { - //if we are creating, then redirect to the correct url and reload - $location.path("packages/packages/edit/" + vm.package.id).search("create", null); - //don't add a browser history for this - $location.replace(); - } - - }, function (err) { - formHelper.resetForm({ scope: $scope, formCtrl: editPackageForm, hasErrors: true }); - formHelper.handleError(err); - vm.buttonState = "error"; - }); - } - } - - function removeContentItem() { - vm.package.contentNodeId = null; - } - - function openContentPicker() { - const contentPicker = { - submit: function (model) { - if (model.selection && model.selection.length > 0) { - vm.package.contentNodeId = model.selection[0].id.toString(); - vm.contentNodeDisplayModel = model.selection[0]; - } - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.contentPicker(contentPicker); + vm.mediaPickerModel = { + hideLabel: true, + view: "mediapicker", + value: "", + config: { + multiPicker: true, + allowEdit: false } + } + vm.labels = {}; - function openViewPicker() { - const controlPicker = { - title: "Select view", - section: "settings", - treeAlias: "files", - entityType: "file", - onlyInitialized: false, - filter: function (i) { - if (i.name.indexOf(".html") === -1 && - i.name.indexOf(".htm") === -1) { - return true; - } - }, - filterCssClass: "not-allowed", - select: function (node) { - const id = decodeURIComponent(node.id.replace(/\+/g, " ")); - vm.package.packageView = id; - editorService.close(); - }, - close: function () { - editorService.close(); - } - }; - editorService.treePicker(controlPicker); - } + vm.versionRegex = /^(\d+\.)(\d+\.)(\*|\d+)$/; - function removePackageView() { - vm.package.packageView = null; - } + vm.aceOption = { + mode: "xml", + theme: "chrome", + showPrintMargin: false, + advanced: { + fontSize: '14px', + enableSnippets: true, + enableBasicAutocompletion: true, + enableLiveAutocompletion: false + }, + onLoad: function (_editor) { + vm.editor = _editor; - function selectDocumentType(doctype) { + vm.editor.setValue(vm.package.actions); + } + }; - // Check if the document type is already selected. - var index = vm.package.documentTypes.indexOf(doctype.id); + function onInit() { - if (index === -1) { - vm.package.documentTypes.push(doctype.id); - } else { - vm.package.documentTypes.splice(index, 1); - } - } + if (create) { + // Pre populate package with some values + packageResource.getEmpty().then(scaffold => { + vm.package = scaffold; - function selectMediaType(mediatype) { + loadResources(); - // Check if the document type is already selected. - var index = vm.package.mediaTypes.indexOf(mediatype.id); + vm.loading = false; + }); - if (index === -1) { - vm.package.mediaTypes.push(mediatype.id); - } else { - vm.package.mediaTypes.splice(index, 1); + localizationService.localizeMany(["general_create", "packager_includeAllChildNodes"]).then(function (values) { + vm.labels.button = values[0]; + vm.labels.includeAllChildNodes = values[1]; + }); + } else { + // Load package + packageResource.getCreatedById(packageId).then(createdPackage => { + vm.package = createdPackage; + + loadResources(); + + vm.loading = false; + + // Get render model for content node + if (vm.package.contentNodeId) { + entityResource.getById(vm.package.contentNodeId, "Document") + .then((entity) => { + vm.contentNodeDisplayModel = entity; + }); } - } - function selectTemplate(template) { + vm.mediaPickerModel.value = vm.package.mediaUdis.join(','); + }); - // Check if the template is already selected. - var index = vm.package.templates.indexOf(template.id); - if (index === -1) { - vm.package.templates.push(template.id); - } else { - vm.package.templates.splice(index, 1); - } - } + localizationService.localizeMany(["buttons_save", "packager_includeAllChildNodes"]).then(function (values) { + vm.labels.button = values[0]; + vm.labels.includeAllChildNodes = values[1]; + }); + } + } - function selectStyleSheet(stylesheet) { + function loadResources() { - // Check if the style sheet is already selected. - var index = vm.package.stylesheets.indexOf(stylesheet.name); + // Get all document types + entityResource.getAll("DocumentType").then(documentTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + documentTypes.forEach(documentType => { + documentType.id = documentType.id.toString(); + documentType.selected = vm.package.documentTypes.indexOf(documentType.id) !== -1; + }); + vm.documentTypes = documentTypes; + }); - if (index === -1) { - vm.package.stylesheets.push(stylesheet.name); - } else { - vm.package.stylesheets.splice(index, 1); - } - } + // Get all media types + entityResource.getAll("MediaType").then(mediaTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + mediaTypes.forEach(mediaType => { + mediaType.id = mediaType.id.toString(); + mediaType.selected = vm.package.mediaTypes.indexOf(mediaType.id) !== -1; + }); + vm.mediaTypes = mediaTypes; + }); - function selectMacro(macro) { + // Get all templates + entityResource.getAll("Template").then(templates => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + templates.forEach(template => { + template.id = template.id.toString(); + template.selected = vm.package.templates.indexOf(template.id) >= 0; + }); + vm.templates = templates; + }); - // Check if the macro is already selected. - var index = vm.package.macros.indexOf(macro.id); + // Get all stylesheets + entityResource.getAll("Stylesheet").then(stylesheets => { + stylesheets.forEach(stylesheet => { + stylesheet.selected = vm.package.stylesheets.indexOf(stylesheet.path) >= 0; + }); + vm.stylesheets = stylesheets; + }); - if (index === -1) { - vm.package.macros.push(macro.id); - } else { - vm.package.macros.splice(index, 1); - } - } + // Get all scripts + entityResource.getAll("Script").then(scripts => { + scripts.forEach(script => { + script.selected = vm.package.scripts.indexOf(script.path) >= 0; + }); + vm.scripts = scripts; + }); - function selectLanguage(language) { + // Get all partial views + entityResource.getAll("PartialView").then(partialViews => { + partialViews.forEach(view => { + view.selected = vm.package.partialViews.indexOf(view.path) >= 0; + }); + vm.partialViews = partialViews; + }); - // Check if the language is already selected. - var index = vm.package.languages.indexOf(language.id); + // Get all macros + entityResource.getAll("Macro").then(macros => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + macros.forEach(macro => { + macro.id = macro.id.toString(); + macro.selected = vm.package.macros.indexOf(macro.id) !== -1; + }); + vm.macros = macros; + }); - if (index === -1) { - vm.package.languages.push(language.id); - } else { - vm.package.languages.splice(index, 1); - } - } + // Get all languages + entityResource.getAll("Language").then(languages => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + languages.forEach(language => { + language.id = language.id.toString(); + language.selected = vm.package.languages.indexOf(language.id) !== -1; + }); + vm.languages = languages; + }); - function selectDictionaryItem(dictionaryItem) { + // Get all dictionary items + entityResource.getAll("DictionaryItem").then(dictionaryItems => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + dictionaryItems.forEach(dictionaryItem => { + dictionaryItem.id = dictionaryItem.id.toString(); + dictionaryItem.selected = vm.package.dictionaryItems.indexOf(dictionaryItem.id) !== -1; + }); + vm.dictionaryItems = dictionaryItems; + }); - // Check if the dictionary item is already selected. - var index = vm.package.dictionaryItems.indexOf(dictionaryItem.id); - - if (index === -1) { - vm.package.dictionaryItems.push(dictionaryItem.id); - } else { - vm.package.dictionaryItems.splice(index, 1); - } - } - - function selectDataType(dataType) { - - // Check if the dictionary item is already selected. - var index = vm.package.dataTypes.indexOf(dataType.id); - - if (index === -1) { - vm.package.dataTypes.push(dataType.id); - } else { - vm.package.dataTypes.splice(index, 1); - } - } - - function getVals(array) { - var vals = []; - for (var i = 0; i < array.length; i++) { - vals.push({ value: array[i] }); - } - return vals; - } - - onInit(); + // Get all data types + entityResource.getAll("DataType").then(dataTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + dataTypes.forEach(dataType => { + dataType.id = dataType.id.toString(); + dataType.selected = vm.package.dataTypes.indexOf(dataType.id) !== -1; + }); + vm.dataTypes = dataTypes; + }); } - angular.module("umbraco").controller("Umbraco.Editors.Packages.EditController", EditController); + function downloadFile(id) { + var url = umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "DownloadCreatedPackage", + { id: id }); + + umbRequestHelper.downloadFile(url).then(function () { + + }); + } + + function back() { + $location.path("packages/packages/created").search("create", null).search("packageId", null); + } + + function createOrUpdatePackage(editPackageForm) { + + // Split by comma and remove empty entries + vm.package.mediaUdis = vm.mediaPickerModel.value.split(",").filter(i => i); + if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { + + vm.buttonState = "busy"; + + packageResource.savePackage(vm.package).then((updatedPackage) => { + + vm.package = updatedPackage; + vm.buttonState = "success"; + + formHelper.resetForm({ scope: $scope, formCtrl: editPackageForm }); + + if (create) { + //if we are creating, then redirect to the correct url and reload + $location.path("packages/packages/edit/" + vm.package.id).search("create", null); + //don't add a browser history for this + $location.replace(); + } + + }, function (err) { + formHelper.resetForm({ scope: $scope, formCtrl: editPackageForm, hasErrors: true }); + formHelper.handleError(err); + vm.buttonState = "error"; + }); + } + } + + function removeContentItem() { + vm.package.contentNodeId = null; + } + + function openContentPicker() { + const contentPicker = { + submit: function (model) { + if (model.selection && model.selection.length > 0) { + vm.package.contentNodeId = model.selection[0].id.toString(); + vm.contentNodeDisplayModel = model.selection[0]; + } + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.contentPicker(contentPicker); + } + + function openViewPicker() { + const controlPicker = { + title: "Select view", + section: "settings", + treeAlias: "files", + entityType: "file", + onlyInitialized: false, + filter: function (i) { + if (i.name.indexOf(".html") === -1 && + i.name.indexOf(".htm") === -1) { + return true; + } + }, + filterCssClass: "not-allowed", + select: function (node) { + const id = decodeURIComponent(node.id.replace(/\+/g, " ")); + vm.package.packageView = id; + editorService.close(); + }, + close: function () { + editorService.close(); + } + }; + editorService.treePicker(controlPicker); + } + + function removePackageView() { + vm.package.packageView = null; + } + + function selectDocumentType(doctype) { + + // Check if the document type is already selected. + var index = vm.package.documentTypes.indexOf(doctype.id); + + if (index === -1) { + vm.package.documentTypes.push(doctype.id); + } else { + vm.package.documentTypes.splice(index, 1); + } + } + + function selectMediaType(mediatype) { + + // Check if the document type is already selected. + var index = vm.package.mediaTypes.indexOf(mediatype.id); + + if (index === -1) { + vm.package.mediaTypes.push(mediatype.id); + } else { + vm.package.mediaTypes.splice(index, 1); + } + } + + function selectTemplate(template) { + + // Check if the template is already selected. + var index = vm.package.templates.indexOf(template.id); + + if (index === -1) { + vm.package.templates.push(template.id); + } else { + vm.package.templates.splice(index, 1); + } + } + + function selectStyleSheet(stylesheet) { + + // Check if the style sheet is already selected. + var index = vm.package.stylesheets.indexOf(stylesheet.path); + + if (index === -1) { + vm.package.stylesheets.push(stylesheet.path); + } else { + vm.package.stylesheets.splice(index, 1); + } + } + + function selectScript(script) { + + // Check if the script is already selected. + var index = vm.package.scripts.indexOf(script.path); + + if (index === -1) { + vm.package.scripts.push(script.path); + } else { + vm.package.scripts.splice(index, 1); + } + } + + function selectPartialView(view) { + + // Check if the view is already selected. + var index = vm.package.partialViews.indexOf(view.path); + + if (index === -1) { + vm.package.partialViews.push(view.path); + } else { + vm.package.partialViews.splice(index, 1); + } + } + + function selectMacro(macro) { + + // Check if the macro is already selected. + var index = vm.package.macros.indexOf(macro.id); + + if (index === -1) { + vm.package.macros.push(macro.id); + } else { + vm.package.macros.splice(index, 1); + } + } + + function selectLanguage(language) { + + // Check if the language is already selected. + var index = vm.package.languages.indexOf(language.id); + + if (index === -1) { + vm.package.languages.push(language.id); + } else { + vm.package.languages.splice(index, 1); + } + } + + function selectDictionaryItem(dictionaryItem) { + + // Check if the dictionary item is already selected. + var index = vm.package.dictionaryItems.indexOf(dictionaryItem.id); + + if (index === -1) { + vm.package.dictionaryItems.push(dictionaryItem.id); + } else { + vm.package.dictionaryItems.splice(index, 1); + } + } + + function selectDataType(dataType) { + + // Check if the dictionary item is already selected. + var index = vm.package.dataTypes.indexOf(dataType.id); + + if (index === -1) { + vm.package.dataTypes.push(dataType.id); + } else { + vm.package.dataTypes.splice(index, 1); + } + } + + function getVals(array) { + var vals = []; + for (var i = 0; i < array.length; i++) { + vals.push({ value: array[i] }); + } + return vals; + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Packages.EditController", EditController); })(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html index 8a8ff0fb38..578117a469 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -1,177 +1,193 @@
-
+ - + - - + + - + - + -
+
-
- Package Content -
+
+ Package Content +
-
+
- + - - + - + - - + + - + - + - - - + + + - - - + + + - -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
- -
- - -
-
+ +
+ + +
+
-
+ +
+ + +
+
-
+ +
+ + +
+
- +
- +
- +
- - + - - + - + + - + + -
+ - + + +
+ +
From 69060bbc15e26970fe93f6ad84f76e875affa98e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 8 Jul 2021 13:58:27 -0600 Subject: [PATCH 014/102] fixes tests --- .../Packaging/PackageMigrationResource.cs | 12 ++++++++---- .../Packaging/CreatedPackagesRepositoryTests.cs | 16 +++++++++------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs index 14cffc041f..8f777d2b0a 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs @@ -45,10 +45,11 @@ namespace Umbraco.Cms.Core.Packaging } public static ZipArchive GetEmbeddedPackageDataManifest(Type planType, out XDocument packageXml) - { - Stream stream = GetEmbeddedPackageStream(planType); + => GetPackageDataManifest(GetEmbeddedPackageStream(planType), out packageXml); - var zip = new ZipArchive(stream, ZipArchiveMode.Read); + public static ZipArchive GetPackageDataManifest(Stream packageZipStream, out XDocument packageXml) + { + var zip = new ZipArchive(packageZipStream, ZipArchiveMode.Read); ZipArchiveEntry packageXmlEntry = zip.GetEntry("package.xml"); if (packageXmlEntry == null) { @@ -56,7 +57,10 @@ namespace Umbraco.Cms.Core.Packaging } using (Stream packageXmlStream = packageXmlEntry.Open()) - using (var xmlReader = XmlReader.Create(packageXmlStream)) + using (var xmlReader = XmlReader.Create(packageXmlStream, new XmlReaderSettings + { + IgnoreWhitespace = true + })) { packageXml = XDocument.Load(xmlReader); } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs index 892c7595d5..339fa704b7 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs @@ -204,10 +204,10 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging string packageXmlPath = PackageBuilder.ExportPackage(def); - using (var stream = File.OpenRead(packageXmlPath)) + using (var packageZipStream = File.OpenRead(packageXmlPath)) + using (ZipArchive zipArchive = PackageMigrationResource.GetPackageDataManifest(packageZipStream, out XDocument packageXml)) { - var xml = XDocument.Load(stream); - var dictionaryItems = xml.Root.Element("DictionaryItems"); + var dictionaryItems = packageXml.Root.Element("DictionaryItems"); Assert.IsNotNull(dictionaryItems); var rootItems = dictionaryItems.Elements("DictionaryItem").ToList(); Assert.AreEqual(2, rootItems.Count); @@ -244,12 +244,14 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging def = PackageBuilder.GetById(def.Id); // re-get Assert.IsNotNull(def.PackagePath); - using (var packageXmlStream = File.OpenRead(packageXmlPath)) + using (FileStream packageZipStream = File.OpenRead(packageXmlPath)) + using (ZipArchive zipArchive = PackageMigrationResource.GetPackageDataManifest(packageZipStream, out XDocument packageXml)) { - var xml = XDocument.Load(packageXmlStream); - Assert.AreEqual("umbPackage", xml.Root.Name.ToString()); + Assert.AreEqual("umbPackage", packageXml.Root.Name.ToString()); - Assert.AreEqual($"", xml.Element("umbPackage").Element("Templates").ToString(SaveOptions.DisableFormatting)); + Assert.AreEqual( + $"", + packageXml.Element("umbPackage").Element("Templates").ToString(SaveOptions.DisableFormatting)); // TODO: There's a whole lot more assertions to be done From 6b6525cc9891cc82d5be8701b91394bdb8ff5a9d Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 9 Jul 2021 19:33:42 +1200 Subject: [PATCH 015/102] Add whether the current content is published. Allowing for compression of just non published, non media content. --- .../IPropertyCacheCompression.cs | 10 +++++++-- .../IPropertyCacheCompressionOptions.cs | 9 +++++++- .../NoopPropertyCacheCompressionOptions.cs | 2 +- .../PropertyCacheCompression.cs | 10 ++++----- .../ContentSerializationTests.cs | 8 +++---- .../NuCache/DataSource/DatabaseDataSource.cs | 15 +++++++------ .../DataSource/IContentCacheDataSerializer.cs | 4 ++-- .../JsonContentNestedDataSerializer.cs | 4 ++-- .../MsgPackContentNestedDataSerializer.cs | 21 ++++++++++--------- ...gPackContentNestedDataSerializerFactory.cs | 2 +- .../NuCache/PublishedSnapshotService.cs | 2 +- 11 files changed, 52 insertions(+), 35 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs index 96a559630b..eb89173581 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs @@ -9,7 +9,13 @@ namespace Umbraco.Core.PropertyEditors /// /// public interface IPropertyCacheCompression - { - bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias); + { + /// + /// Whether a property on the content is/should be compressed + /// + /// The content + /// The property to compress or not + /// Whether this content is the published version + bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias,bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs index 2fa0153f9e..71eb782ee2 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs @@ -4,6 +4,13 @@ namespace Umbraco.Core.PropertyEditors { public interface IPropertyCacheCompressionOptions { - bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor); + /// + /// Whether a property on the content is/should be compressed + /// + /// The content + /// The property to compress or not + /// The datatype of the property to compress or not + /// Whether this content is the published version + bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor,bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs index 1f12d45769..f566172cd9 100644 --- a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs @@ -7,6 +7,6 @@ namespace Umbraco.Core.PropertyEditors ///
internal class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions { - public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor) => false; + public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor,bool published) => false; } } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs index 6be21fca7f..be0553ce65 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyCacheCompression.cs @@ -14,13 +14,13 @@ namespace Umbraco.Core.PropertyEditors private readonly IPropertyCacheCompressionOptions _compressionOptions; private readonly IReadOnlyDictionary _contentTypes; private readonly PropertyEditorCollection _propertyEditors; - private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias), bool> _isCompressedCache; + private readonly ConcurrentDictionary<(int contentTypeId, string propertyAlias,bool published), bool> _isCompressedCache; public PropertyCacheCompression( IPropertyCacheCompressionOptions compressionOptions, IReadOnlyDictionary contentTypes, PropertyEditorCollection propertyEditors, - ConcurrentDictionary<(int, string), bool> compressedStoragePropertyEditorCache) + ConcurrentDictionary<(int, string,bool), bool> compressedStoragePropertyEditorCache) { _compressionOptions = compressionOptions; _contentTypes = contentTypes ?? throw new System.ArgumentNullException(nameof(contentTypes)); @@ -28,9 +28,9 @@ namespace Umbraco.Core.PropertyEditors _isCompressedCache = compressedStoragePropertyEditorCache; } - public bool IsCompressed(IReadOnlyContentBase content, string alias) + public bool IsCompressed(IReadOnlyContentBase content, string alias, bool published) { - var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias), x => + var compressedStorage = _isCompressedCache.GetOrAdd((content.ContentTypeId, alias, published), x => { if (!_contentTypes.TryGetValue(x.contentTypeId, out var ct)) return false; @@ -40,7 +40,7 @@ namespace Umbraco.Core.PropertyEditors if (!_propertyEditors.TryGet(propertyType.PropertyEditorAlias, out var propertyEditor)) return false; - return _compressionOptions.IsCompressed(content, propertyType, propertyEditor); + return _compressionOptions.IsCompressed(content, propertyType, propertyEditor, published); }); return compressedStorage; diff --git a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs index b3543dad1a..9acad53364 100644 --- a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs +++ b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs @@ -56,14 +56,14 @@ namespace Umbraco.Tests.PublishedContent var content = Mock.Of(x => x.ContentTypeId == 1); - var json = jsonSerializer.Serialize(content, cacheModel).StringData; - var msgPack = msgPackSerializer.Serialize(content, cacheModel).ByteData; + var json = jsonSerializer.Serialize(content, cacheModel,false).StringData; + var msgPack = msgPackSerializer.Serialize(content, cacheModel, false).ByteData; Console.WriteLine(json); Console.WriteLine(msgPackSerializer.ToJson(msgPack)); - var jsonContent = jsonSerializer.Deserialize(content, json, null); - var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack); + var jsonContent = jsonSerializer.Deserialize(content, json, null,false); + var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack,false); CollectionAssert.AreEqual(jsonContent.CultureData.Keys, msgPackContent.CultureData.Keys); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs index c112cc6efa..5ec51bffb8 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/DatabaseDataSource.cs @@ -393,12 +393,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); + bool published = false; + var deserializedContent = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); d = new ContentData { Name = dto.EditName, - Published = false, + Published = published, TemplateId = dto.EditTemplateId, VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, @@ -420,13 +421,14 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } else { - var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw); + bool published = true; + var deserializedContent = serializer.Deserialize(dto, dto.PubData, dto.PubDataRaw, published); p = new ContentData { Name = dto.PubName, UrlSegment = deserializedContent.UrlSegment, - Published = true, + Published = published, TemplateId = dto.PubTemplateId, VersionId = dto.VersionId, VersionDate = dto.PubVersionDate, @@ -456,12 +458,13 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource if (dto.EditData == null && dto.EditDataRaw == null) throw new InvalidOperationException("No data for media " + dto.Id); - var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw); + bool published = true; + var deserializedMedia = serializer.Deserialize(dto, dto.EditData, dto.EditDataRaw, published); var p = new ContentData { Name = dto.EditName, - Published = true, + Published = published, TemplateId = -1, VersionId = dto.VersionId, VersionDate = dto.EditVersionDate, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs index d1a83d8452..6c37046e8b 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs @@ -14,12 +14,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// Deserialize the data into a /// - ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData); + ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published); /// /// Serializes the /// - ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model); + ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs index 21cd0bf763..0cc10f5f98 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource DateFormatString = "o" }; private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable(); - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData,bool published) { if (stringData == null && byteData != null) throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); @@ -39,7 +39,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) { // note that numeric values (which are Int32) are serialized without their // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs index 6ae872ef69..f69232aad3 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs @@ -39,7 +39,8 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource _options = defaultOptions .WithResolver(resolver) - .WithCompression(MessagePackCompression.Lz4BlockArray); + .WithCompression(MessagePackCompression.Lz4BlockArray) + .WithSecurity(MessagePackSecurity.UntrustedData); } public string ToJson(byte[] bin) @@ -48,12 +49,12 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return json; } - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData,bool published) { if (byteData != null) { var cacheModel = MessagePackSerializer.Deserialize(byteData, _options); - Expand(content, cacheModel); + Expand(content, cacheModel, published); return cacheModel; } else if (stringData != null) @@ -61,7 +62,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // NOTE: We don't really support strings but it's possible if manually used (i.e. tests) var bin = Convert.FromBase64String(stringData); var cacheModel = MessagePackSerializer.Deserialize(bin, _options); - Expand(content, cacheModel); + Expand(content, cacheModel,published); return cacheModel; } else @@ -70,9 +71,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) { - Compress(content, model); + Compress(content, model, published); var bytes = MessagePackSerializer.Serialize(model, _options); return new ContentCacheDataSerializationResult(null, bytes); } @@ -88,11 +89,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant /// memory savings but could also affect performance of first rendering pages while decompression occurs. /// - private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model) + private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) { foreach(var propertyAliasToData in model.PropertyData) { - if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published)) { foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { @@ -106,11 +107,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// Used during deserialization to map the property data as lazy or expand the value ///
/// - private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData) + private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData,bool published) { foreach (var propertyAliasToData in nestedData.PropertyData) { - if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published)) { foreach (var property in propertyAliasToData.Value.Where(x => x.Value != null)) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs index fcc3fa2bb8..29378caf0f 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource private readonly IMemberTypeService _memberTypeService; private readonly PropertyEditorCollection _propertyEditors; private readonly IPropertyCacheCompressionOptions _compressionOptions; - private readonly ConcurrentDictionary<(int, string), bool> _isCompressedCache = new ConcurrentDictionary<(int, string), bool>(); + private readonly ConcurrentDictionary<(int, string,bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string,bool), bool>(); public MsgPackContentNestedDataSerializerFactory( IContentTypeService contentTypeService, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index f9c25b7b35..7ca425aad9 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -1491,7 +1491,7 @@ namespace Umbraco.Web.PublishedCache.NuCache UrlSegment = content.GetUrlSegment(_urlSegmentProviders) }; - var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData); + var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData,published); var dto = new ContentNuDto { From 044c900c2ba52428b7acecb8d15fc0769c5b9eaa Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 9 Jul 2021 20:14:35 +1200 Subject: [PATCH 016/102] Make IPropertyCacheCompressionOptions useful out of the box. key = "Umbraco.Web.PublishedCache.NuCache.CompressUnPublishedContent" value = "true" will compress all ntext properties on unpublished content --- ...dContentPropertyCacheCompressionOptions.cs | 29 +++++++++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 1 + .../PublishedCache/NuCache/NuCacheComposer.cs | 12 ++++++-- .../NuCache/NuCacheSerializerComponent.cs | 1 + 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs diff --git a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs new file mode 100644 index 0000000000..51433e476e --- /dev/null +++ b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models; + +namespace Umbraco.Core.PropertyEditors +{ + /// + /// Compress large, non published text properties + /// + internal class UnPublishedContentPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions + { + public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published) + { + if (published) + { + return false; + } + if (propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext) + { + //Only compress non published content that supports publishing and the property is text + return true; + } + return false; + } + } +} diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 931cef070f..f6a523441e 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -171,6 +171,7 @@ + diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs index c6b214102b..4b3d7b20fa 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -26,8 +26,16 @@ namespace Umbraco.Web.PublishedCache.NuCache { composition.RegisterUnique(); } - - composition.RegisterUnique(); + var unPublishedContentCompression = ConfigurationManager.AppSettings[NuCacheSerializerComponent.Nucache_UnPublishedContentCompression_Key]; + if ("MsgPack" == serializer && "true" == unPublishedContentCompression) + { + composition.RegisterUnique(); + } + else + { + composition.RegisterUnique(); + } + composition.RegisterUnique(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer())); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs index a1d3ed2b12..c2d24b16ac 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs @@ -16,6 +16,7 @@ namespace Umbraco.Web.PublishedCache.NuCache public class NuCacheSerializerComponent : IComponent { internal const string Nucache_Serializer_Key = "Umbraco.Web.PublishedCache.NuCache.Serializer"; + internal const string Nucache_UnPublishedContentCompression_Key = "Umbraco.Web.PublishedCache.NuCache.CompressUnPublishedContent"; private const string JSON_SERIALIZER_VALUE = "JSON"; private readonly Lazy _service; private readonly IKeyValueService _keyValueService; From b119201480da1f99bf463776f71d2e73901f2aef Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 9 Jul 2021 20:55:03 +1200 Subject: [PATCH 017/102] fix serializer swap message --- .../PublishedCache/NuCache/NuCacheSerializerComponent.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs index c2d24b16ac..41afb2b781 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { _profilingLogger.Warn($"Database NuCache was serialized using {currentSerializer}. Currently configured NuCache serializer {serializer}. Rebuilding Nucache"); - using (_profilingLogger.TraceDuration($"Rebuilding NuCache database with {currentSerializer} serializer")) + using (_profilingLogger.TraceDuration($"Rebuilding NuCache database with {serializer} serializer")) { _service.Value.Rebuild(); _keyValueService.SetValue(Nucache_Serializer_Key, serializer); From 6a8ed8012cc282e87b8d55c23f518f9991c0daba Mon Sep 17 00:00:00 2001 From: nzdev <834725+nzdev@users.noreply.github.com> Date: Fri, 9 Jul 2021 21:22:30 +1200 Subject: [PATCH 018/102] simplify --- .../UnPublishedContentPropertyCacheCompressionOptions.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs index 51433e476e..7a5212a985 100644 --- a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs @@ -14,11 +14,7 @@ namespace Umbraco.Core.PropertyEditors { public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published) { - if (published) - { - return false; - } - if (propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext) + if (!published && propertyType.SupportsPublishing && propertyType.ValueStorageType == ValueStorageType.Ntext) { //Only compress non published content that supports publishing and the property is text return true; From 9d3daa1959d0306188c89f7eb574069d94356b1d Mon Sep 17 00:00:00 2001 From: Erik-Jan Westendorp Date: Fri, 9 Jul 2021 13:34:54 +0200 Subject: [PATCH 019/102] Update error.html Change path of the log folder since the App_Data folder doesn't exist anymore. --- src/Umbraco.Web.UI.Client/src/installer/steps/error.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/installer/steps/error.html b/src/Umbraco.Web.UI.Client/src/installer/steps/error.html index 4fb5823afa..aab5b18e9f 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/steps/error.html +++ b/src/Umbraco.Web.UI.Client/src/installer/steps/error.html @@ -3,7 +3,7 @@

{{installer.current.model.message}}

-

See the log for full details (logs can typically be found in the App_Data\Logs folder).

+

See the log for full details (logs can typically be found in the umbraco\Logs folder).

From 5e8b4572a0a322b8cfbe27fea3038367c76415fd Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 09:40:31 -0600 Subject: [PATCH 020/102] Ensures that the content nu data column is updated to support null during migration. --- .../Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs | 3 +++ .../PublishedCache/NuCache/NuCacheSerializerComponent.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs index 3eab1a812e..9eeaa67189 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs @@ -16,6 +16,9 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0 var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); AddColumnIfNotExists(columns, "dataRaw"); + + // allow null + AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); } } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs index a1d3ed2b12..fcbb5a3715 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs @@ -54,7 +54,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { _profilingLogger.Warn($"Database NuCache was serialized using {currentSerializer}. Currently configured NuCache serializer {serializer}. Rebuilding Nucache"); - using (_profilingLogger.TraceDuration($"Rebuilding NuCache database with {currentSerializer} serializer")) + using (_profilingLogger.TraceDuration($"Rebuilding NuCache database with {serializer} serializer")) { _service.Value.Rebuild(); _keyValueService.SetValue(Nucache_Serializer_Key, serializer); From 7dc03256edf1bbe15b5b44c5d44a033369ce0e07 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 12:05:19 -0600 Subject: [PATCH 021/102] Fixes SQLCE migration for nucache table. --- .../Create/Table/CreateTableOfDtoBuilder.cs | 2 + .../V_8_15_0/AddCmsContentNuByteColumn.cs | 47 +++++++++++++++++-- .../SqlSyntax/SqlSyntaxProviderBase.cs | 15 +++--- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs b/src/Umbraco.Core/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs index 4b73e9435d..a5e2fca1f7 100644 --- a/src/Umbraco.Core/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs +++ b/src/Umbraco.Core/Migrations/Expressions/Create/Table/CreateTableOfDtoBuilder.cs @@ -9,6 +9,8 @@ namespace Umbraco.Core.Migrations.Expressions.Create.Table public class CreateTableOfDtoBuilder : IExecutableBuilder { private readonly IMigrationContext _context; + + // TODO: This doesn't do anything. private readonly DatabaseType[] _supportedDatabaseTypes; public CreateTableOfDtoBuilder(IMigrationContext context, params DatabaseType[] supportedDatabaseTypes) diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs index 9eeaa67189..5217fc9870 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_15_0/AddCmsContentNuByteColumn.cs @@ -1,5 +1,10 @@ -using System.Linq; +using NPoco; +using System.Data; +using System.Linq; +using Umbraco.Core.Persistence; +using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.Dtos; +using Umbraco.Core.PropertyEditors; namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0 { @@ -13,12 +18,46 @@ namespace Umbraco.Core.Migrations.Upgrade.V_8_15_0 public override void Migrate() { + // allow null for the `data` field + if (DatabaseType.IsSqlCe()) + { + // SQLCE does not support altering NTEXT, so we have to jump through some hoops to do it + // All column ordering must remain the same as what is defined in the DTO so we need to create a temp table, + // drop orig and then re-create/copy. + Create.Table(withoutKeysAndIndexes: true).Do(); + Execute.Sql($"INSERT INTO [{TempTableName}] SELECT nodeId, published, data, rv FROM [{Constants.DatabaseSchema.Tables.NodeData}]").Do(); + Delete.Table(Constants.DatabaseSchema.Tables.NodeData).Do(); + Create.Table().Do(); + Execute.Sql($"INSERT INTO [{Constants.DatabaseSchema.Tables.NodeData}] SELECT nodeId, published, data, rv, NULL FROM [{TempTableName}]").Do(); + } + else + { + AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); + } + var columns = SqlSyntax.GetColumnsInSchema(Context.Database).ToList(); - AddColumnIfNotExists(columns, "dataRaw"); + } - // allow null - AlterColumn(Constants.DatabaseSchema.Tables.NodeData, "data"); + private const string TempTableName = Constants.DatabaseSchema.TableNamePrefix + "cms" + "ContentNuTEMP"; + + [TableName(TempTableName)] + [ExplicitColumns] + private class ContentNuDtoTemp + { + [Column("nodeId")] + public int NodeId { get; set; } + + [Column("published")] + public bool Published { get; set; } + + [Column("data")] + [SpecialDbType(SpecialDbTypes.NTEXT)] + [NullSetting(NullSetting = NullSettings.Null)] + public string Data { get; set; } + + [Column("rv")] + public long Rv { get; set; } } } } diff --git a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs index 4d6b2eeea1..0d2d7aeb21 100644 --- a/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs +++ b/src/Umbraco.Core/Persistence/SqlSyntax/SqlSyntaxProviderBase.cs @@ -352,7 +352,7 @@ namespace Umbraco.Core.Persistence.SqlSyntax sql.Append(" "); sql.Append(FormatIdentity(column)); - var isNullable = column.IsNullable; + //var isNullable = column.IsNullable; //var constraint = FormatConstraint(column)?.TrimStart("CONSTRAINT "); //var hasConstraint = !string.IsNullOrWhiteSpace(constraint); @@ -360,11 +360,14 @@ namespace Umbraco.Core.Persistence.SqlSyntax //var defaultValue = FormatDefaultValue(column); //var hasDefaultValue = !string.IsNullOrWhiteSpace(defaultValue); - if (isNullable /*&& !hasConstraint && !hasDefaultValue*/) - { - sqls = Enumerable.Empty(); - return sql.ToString(); - } + // TODO: This used to exit if nullable but that means this would never work + // to return SQL if the column was nullable?!? I don't get it. This was here + // 4 years ago, I've removed it so that this works for nullable columns. + //if (isNullable /*&& !hasConstraint && !hasDefaultValue*/) + //{ + // sqls = Enumerable.Empty(); + // return sql.ToString(); + //} var msql = new List(); sqls = msql; From 93f5dd76317b437f14f37db49f1ac39ace300c39 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 13:35:43 -0600 Subject: [PATCH 022/102] Changes bulk copy timeout to be infinite. Changes bulk copy to not allocate an arbitrary array. --- .../NPocoDatabaseExtensions-Bulk.cs | 35 ++++++++++++++----- .../Persistence/PocoDataDataReader.cs | 5 +-- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs index bff682d095..77cc0d6601 100644 --- a/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs +++ b/src/Umbraco.Core/Persistence/NPocoDatabaseExtensions-Bulk.cs @@ -62,26 +62,33 @@ namespace Umbraco.Core.Persistence /// The number of records that were inserted. public static int BulkInsertRecords(this IUmbracoDatabase database, IEnumerable records, bool useNativeBulkInsert = true) { - var recordsA = records.ToArray(); - if (recordsA.Length == 0) return 0; + if (!records.Any()) return 0; var pocoData = database.PocoDataFactory.ForType(typeof(T)); if (pocoData == null) throw new InvalidOperationException("Could not find PocoData for " + typeof(T)); if (database.DatabaseType.IsSqlCe()) { - if (useNativeBulkInsert) return BulkInsertRecordsSqlCe(database, pocoData, recordsA); + if (useNativeBulkInsert) + { + return BulkInsertRecordsSqlCe(database, pocoData, records); + } + // else, no other choice - foreach (var record in recordsA) + var count = 0; + foreach (var record in records) + { database.Insert(record); - return recordsA.Length; + count++; + } + return count; } if (database.DatabaseType.IsSqlServer()) { return useNativeBulkInsert && database.DatabaseType.IsSqlServer2008OrLater() - ? BulkInsertRecordsSqlServer(database, pocoData, recordsA) - : BulkInsertRecordsWithCommands(database, recordsA); + ? BulkInsertRecordsSqlServer(database, pocoData, records) + : BulkInsertRecordsWithCommands(database, records.ToArray()); } throw new NotSupportedException(); } @@ -96,7 +103,9 @@ namespace Umbraco.Core.Persistence private static int BulkInsertRecordsWithCommands(IUmbracoDatabase database, T[] records) { foreach (var command in database.GenerateBulkInsertCommands(records)) + { command.ExecuteNonQuery(); + } return records.Length; // what else? } @@ -241,6 +250,10 @@ namespace Umbraco.Core.Persistence /// The number of records that were inserted. internal static int BulkInsertRecordsSqlServer(IUmbracoDatabase database, PocoData pocoData, IEnumerable records) { + // TODO: The main reason this exists is because the NPoco InsertBulk method doesn't return the number of items. + // It is worth investigating the performance of this vs NPoco's because we use a custom BulkDataReader + // which in theory should be more efficient than NPocos way of building up an in-memory DataTable. + // create command against the original database.Connection using (var command = database.CreateCommand(database.Connection, CommandType.Text, string.Empty)) { @@ -252,7 +265,13 @@ namespace Umbraco.Core.Persistence var syntax = database.SqlContext.SqlSyntax as SqlServerSyntaxProvider; if (syntax == null) throw new NotSupportedException("SqlSyntax must be SqlServerSyntaxProvider."); - using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) { BulkCopyTimeout = 10000, DestinationTableName = tableName }) + using (var copy = new SqlBulkCopy(tConnection, SqlBulkCopyOptions.Default, tTransaction) + { + BulkCopyTimeout = 0, // 0 = no bulk copy timeout. If a timeout occurs it will be an connection/command timeout. + DestinationTableName = tableName, + // be consistent with NPoco: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L50 + BatchSize = 4096 + }) using (var bulkReader = new PocoDataDataReader(records, pocoData, syntax)) { //we need to add column mappings here because otherwise columns will be matched by their order and if the order of them are different in the DB compared diff --git a/src/Umbraco.Core/Persistence/PocoDataDataReader.cs b/src/Umbraco.Core/Persistence/PocoDataDataReader.cs index 460a4d3d90..397d903cc2 100644 --- a/src/Umbraco.Core/Persistence/PocoDataDataReader.cs +++ b/src/Umbraco.Core/Persistence/PocoDataDataReader.cs @@ -40,9 +40,10 @@ namespace Umbraco.Core.Persistence _tableDefinition = DefinitionFactory.GetTableDefinition(pd.Type, sqlSyntaxProvider); if (_tableDefinition == null) throw new InvalidOperationException("No table definition found for type " + pd.Type); - // only real columns, exclude result columns + // only real columns, exclude result/computed columns + // Like NPoco does: https://github.com/schotime/NPoco/blob/5117a55fde57547e928246c044fd40bd00b2d7d1/src/NPoco.SqlServer/SqlBulkCopyHelper.cs#L59 _readerColumns = pd.Columns - .Where(x => x.Value.ResultColumn == false) + .Where(x => x.Value.ResultColumn == false && x.Value.ComputedColumn == false) .Select(x => x.Value) .ToArray(); From a1efccbc68bf199f3667f67ae187b8eed6342731 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 14:48:12 -0600 Subject: [PATCH 023/102] Add extension to get hash from stream --- src/Umbraco.Core/IO/FileSystemExtensions.cs | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index 6f1f47007f..23be195e4b 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -1,5 +1,7 @@ -using System; +using System; using System.IO; +using System.Security.Cryptography; +using System.Text; using System.Threading; using Umbraco.Cms.Core.IO; @@ -7,6 +9,25 @@ namespace Umbraco.Extensions { public static class FileSystemExtensions { + public static string GetStreamHash(this Stream fileStream) + { + if (fileStream.CanSeek) + { + fileStream.Seek(0, SeekOrigin.Begin); + } + + using HashAlgorithm alg = SHA1.Create(); + + // create a string output for the hash + var stringBuilder = new StringBuilder(); + var hashedByteArray = alg.ComputeHash(fileStream); + foreach (var b in hashedByteArray) + { + stringBuilder.Append(b.ToString("x2")); + } + return stringBuilder.ToString(); + } + /// /// Attempts to open the file at filePath up to maxRetries times, /// with a thread sleep time of sleepPerRetryInMilliseconds between retries. From 5b21b9fe2c83b75381bc61a06329e9bbc9d29767 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 14:48:53 -0600 Subject: [PATCH 024/102] uses ext method --- .../Packaging/PackageMigrationResource.cs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs index 8f777d2b0a..2f407df88f 100644 --- a/src/Umbraco.Core/Packaging/PackageMigrationResource.cs +++ b/src/Umbraco.Core/Packaging/PackageMigrationResource.cs @@ -6,6 +6,7 @@ using System.Security.Cryptography; using System.Text; using System.Xml; using System.Xml.Linq; +using Umbraco.Extensions; namespace Umbraco.Cms.Core.Packaging { @@ -32,16 +33,7 @@ namespace Umbraco.Cms.Core.Packaging // several very large package.zips. using Stream stream = GetEmbeddedPackageStream(planType); - using HashAlgorithm alg = SHA1.Create(); - - // create a string output for the hash - var stringBuilder = new StringBuilder(); - var hashedByteArray = alg.ComputeHash(stream); - foreach (var b in hashedByteArray) - { - stringBuilder.Append(b.ToString("x2")); - } - return stringBuilder.ToString(); + return stream.GetStreamHash(); } public static ZipArchive GetEmbeddedPackageDataManifest(Type planType, out XDocument packageXml) From 1ef5290576c43489b88783f50cfe8ef24ce5d24c Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 14:50:37 -0600 Subject: [PATCH 025/102] Updates runtime hash calc to be based on the assemblies returned from the assembly provider. --- .../DefaultUmbracoAssemblyProvider.cs | 11 ++++- src/Umbraco.Core/Composing/RuntimeHash.cs | 46 ++++++++++--------- src/Umbraco.Core/Composing/TypeFinder.cs | 4 +- src/Umbraco.Core/HashGenerator.cs | 8 ++-- src/Umbraco.Core/IO/FileSystemExtensions.cs | 23 +++++++++- .../UmbracoTestServerTestBase.cs | 2 - .../Testing/UmbracoIntegrationTest.cs | 1 - .../UmbracoBuilderExtensions.cs | 2 +- .../Extensions/ServiceCollectionExtensions.cs | 20 +++++--- 9 files changed, 76 insertions(+), 41 deletions(-) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 60aa666034..52967a58e7 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using Microsoft.Extensions.Logging; @@ -16,6 +17,7 @@ namespace Umbraco.Cms.Core.Composing { private readonly Assembly _entryPointAssembly; private readonly ILoggerFactory _loggerFactory; + private List _discovered; public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly, ILoggerFactory loggerFactory) { @@ -33,8 +35,15 @@ namespace Umbraco.Cms.Core.Composing { get { + if (_discovered != null) + { + return _discovered; + } + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, Constants.Composing.UmbracoCoreAssemblyNames, true, _loggerFactory); - return finder.Find(); + _discovered = finder.Find().ToList(); + + return _discovered; } } } diff --git a/src/Umbraco.Core/Composing/RuntimeHash.cs b/src/Umbraco.Core/Composing/RuntimeHash.cs index 4eb70cea1f..7ca35855de 100644 --- a/src/Umbraco.Core/Composing/RuntimeHash.cs +++ b/src/Umbraco.Core/Composing/RuntimeHash.cs @@ -1,7 +1,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; using Umbraco.Cms.Core.Logging; +using Umbraco.Extensions; namespace Umbraco.Cms.Core.Composing { @@ -12,6 +14,7 @@ namespace Umbraco.Cms.Core.Composing { private readonly IProfilingLogger _logger; private readonly RuntimeHashPaths _paths; + private string _calculated; public RuntimeHash(IProfilingLogger logger, RuntimeHashPaths paths) { @@ -22,13 +25,18 @@ namespace Umbraco.Cms.Core.Composing public string GetHashValue() { - var allPaths = _paths.GetFolders() - .Select(x => ((FileSystemInfo) x, false)) - .Concat(_paths.GetFiles().Select(x => ((FileSystemInfo) x.Key, x.Value))); + if (_calculated != null) + { + return _calculated; + } - var hash = GetFileHash(allPaths); + IEnumerable<(FileSystemInfo, bool)> allPaths = _paths.GetFolders() + .Select(x => ((FileSystemInfo)x, false)) + .Concat(_paths.GetFiles().Select(x => ((FileSystemInfo)x.Key, x.Value))); - return hash; + _calculated = GetFileHash(allPaths); + + return _calculated; } /// @@ -48,7 +56,7 @@ namespace Umbraco.Cms.Core.Composing using var generator = new HashGenerator(); - foreach (var (fileOrFolder, scanFileContent) in filesAndFolders) + foreach ((FileSystemInfo fileOrFolder, bool scanFileContent) in filesAndFolders) { if (scanFileContent) { @@ -56,9 +64,16 @@ namespace Umbraco.Cms.Core.Composing // normalize the content for cr/lf and case-sensitivity if (uniqContent.Add(fileOrFolder.FullName)) { - if (File.Exists(fileOrFolder.FullName) == false) continue; - var content = RemoveCrLf(File.ReadAllText(fileOrFolder.FullName)); - generator.AddCaseInsensitiveString(content); + if (File.Exists(fileOrFolder.FullName) == false) + { + continue; + } + + using (FileStream fileStream = File.OpenRead(fileOrFolder.FullName)) + { + var hash = fileStream.GetStreamHash(); + generator.AddCaseInsensitiveString(hash); + } } } else @@ -74,18 +89,5 @@ namespace Umbraco.Cms.Core.Composing } } - // fast! (yes, according to benchmarks) - private static string RemoveCrLf(string s) - { - var buffer = new char[s.Length]; - var count = 0; - // ReSharper disable once ForCanBeConvertedToForeach - no! - for (var i = 0; i < s.Length; i++) - { - if (s[i] != '\r' && s[i] != '\n') - buffer[count++] = s[i]; - } - return new string(buffer, 0, count); - } } } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 1979f33d10..97b8e3847c 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Composing private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); - private static readonly ConcurrentDictionary TypeNamesCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary s_typeNamesCache = new ConcurrentDictionary(); private readonly ITypeFinderConfig _typeFinderConfig; // used for benchmark tests @@ -255,7 +255,7 @@ namespace Umbraco.Cms.Core.Composing } // It didn't parse, so try loading from each already loaded assembly and cache it - return TypeNamesCache.GetOrAdd(name, s => + return s_typeNamesCache.GetOrAdd(name, s => AppDomain.CurrentDomain.GetAssemblies() .Select(x => x.GetType(s)) .FirstOrDefault(x => x != null)); diff --git a/src/Umbraco.Core/HashGenerator.cs b/src/Umbraco.Core/HashGenerator.cs index 255cf381af..944e0bdf49 100644 --- a/src/Umbraco.Core/HashGenerator.cs +++ b/src/Umbraco.Core/HashGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using System.Security.Cryptography; using System.Text; @@ -73,14 +73,12 @@ namespace Umbraco.Cms.Core AddDateTime(f.LastWriteTimeUtc); //check if it is a file or folder - var fileInfo = f as FileInfo; - if (fileInfo != null) + if (f is FileInfo fileInfo) { AddLong(fileInfo.Length); } - var dirInfo = f as DirectoryInfo; - if (dirInfo != null) + if (f is DirectoryInfo dirInfo) { foreach (var d in dirInfo.GetFiles()) { diff --git a/src/Umbraco.Core/IO/FileSystemExtensions.cs b/src/Umbraco.Core/IO/FileSystemExtensions.cs index 6f1f47007f..23be195e4b 100644 --- a/src/Umbraco.Core/IO/FileSystemExtensions.cs +++ b/src/Umbraco.Core/IO/FileSystemExtensions.cs @@ -1,5 +1,7 @@ -using System; +using System; using System.IO; +using System.Security.Cryptography; +using System.Text; using System.Threading; using Umbraco.Cms.Core.IO; @@ -7,6 +9,25 @@ namespace Umbraco.Extensions { public static class FileSystemExtensions { + public static string GetStreamHash(this Stream fileStream) + { + if (fileStream.CanSeek) + { + fileStream.Seek(0, SeekOrigin.Begin); + } + + using HashAlgorithm alg = SHA1.Create(); + + // create a string output for the hash + var stringBuilder = new StringBuilder(); + var hashedByteArray = alg.ComputeHash(fileStream); + foreach (var b in hashedByteArray) + { + stringBuilder.Append(b.ToString("x2")); + } + return stringBuilder.ToString(); + } + /// /// Attempts to open the file at filePath up to maxRetries times, /// with a thread sleep time of sleepPerRetryInMilliseconds between retries. diff --git a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs index b91a034420..59b970ebbc 100644 --- a/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs +++ b/src/Umbraco.Tests.Integration/TestServerTest/UmbracoTestServerTestBase.cs @@ -134,10 +134,8 @@ namespace Umbraco.Cms.Tests.Integration.TestServerTest public override void ConfigureServices(IServiceCollection services) { services.AddTransient(); - IWebHostEnvironment webHostEnvironment = TestHelper.GetWebHostEnvironment(); TypeLoader typeLoader = services.AddTypeLoader( GetType().Assembly, - webHostEnvironment, TestHelper.GetHostingEnvironment(), TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index 9629375553..b3a168e119 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -204,7 +204,6 @@ namespace Umbraco.Cms.Tests.Integration.Testing // Add it! TypeLoader typeLoader = services.AddTypeLoader( GetType().Assembly, - webHostEnvironment, TestHelper.GetHostingEnvironment(), TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index e329d9fb67..b4273446f9 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -108,7 +108,7 @@ namespace Umbraco.Extensions services.AddUnique(profiler); ILoggerFactory loggerFactory = LoggerFactory.Create(cfg => cfg.AddSerilog(Log.Logger, false)); - TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), webHostEnvironment, tempHostingEnvironment, loggerFactory, appCaches, config, profiler); + TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), tempHostingEnvironment, loggerFactory, appCaches, config, profiler); // adds the umbraco startup filter which will call UseUmbraco early on before // other start filters are applied (depending on the ordering of IStartupFilters in DI). diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index 7fe337a9f9..16a83543ef 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -63,20 +63,29 @@ namespace Umbraco.Extensions internal static ITypeFinder AddTypeFinder( this IServiceCollection services, ILoggerFactory loggerFactory, - IWebHostEnvironment webHostEnvironment, Assembly entryAssembly, IConfiguration config, IProfilingLogger profilingLogger) { + TypeFinderSettings typeFinderSettings = config.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); - var typeFinderSettings = config.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); + var assemblyProvider = new DefaultUmbracoAssemblyProvider(entryAssembly, loggerFactory); + + var runtimeHashPaths = new RuntimeHashPaths(); + foreach(Assembly assembly in assemblyProvider.Assemblies) + { + // TODO: We need to test this on a published website + if (!assembly.IsDynamic && assembly.Location != null) + { + runtimeHashPaths.AddFile(new FileInfo(assembly.Location)); + } + } - var runtimeHashPaths = new RuntimeHashPaths().AddFolder(new DirectoryInfo(Path.Combine(webHostEnvironment.ContentRootPath, "bin"))); var runtimeHash = new RuntimeHash(profilingLogger, runtimeHashPaths); var typeFinder = new TypeFinder( loggerFactory.CreateLogger(), - new DefaultUmbracoAssemblyProvider(entryAssembly, loggerFactory), + assemblyProvider, runtimeHash, new TypeFinderConfig(Options.Create(typeFinderSettings)) ); @@ -89,7 +98,6 @@ namespace Umbraco.Extensions public static TypeLoader AddTypeLoader( this IServiceCollection services, Assembly entryAssembly, - IWebHostEnvironment webHostEnvironment, IHostingEnvironment hostingEnvironment, ILoggerFactory loggerFactory, AppCaches appCaches, @@ -97,7 +105,7 @@ namespace Umbraco.Extensions IProfiler profiler) { var profilingLogger = new ProfilingLogger(loggerFactory.CreateLogger(), profiler); - var typeFinder = services.AddTypeFinder(loggerFactory, webHostEnvironment, entryAssembly, configuration, profilingLogger); + var typeFinder = services.AddTypeFinder(loggerFactory, entryAssembly, configuration, profilingLogger); var typeLoader = new TypeLoader( typeFinder, From 7ec3afa5ff3db3b37fa60a478d5524511b792974 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 15:31:01 -0600 Subject: [PATCH 026/102] Adds new ctor to runtime hash class. Adds startup objects to the IUmbracoBuilder that are not otherwise available during configuration. --- .../Composing/RuntimeHashPaths.cs | 21 ++++++++++++++- src/Umbraco.Core/Composing/TypeLoader.cs | 14 ++++++---- .../DependencyInjection/IUmbracoBuilder.cs | 20 ++++++++++++++ .../DependencyInjection/UmbracoBuilder.cs | 27 ++++++++++++++++--- src/Umbraco.Core/Logging/ProfilingLogger.cs | 2 +- .../TypeLoaderBenchmarks.cs | 4 +-- src/Umbraco.Tests.Common/TestHelperBase.cs | 2 +- .../Testing/UmbracoIntegrationTest.cs | 5 ++-- .../Umbraco.Core/Components/ComponentTests.cs | 4 +-- .../Composing/ComposingTestBase.cs | 6 +---- .../Umbraco.Core/Composing/TypeLoaderTests.cs | 2 +- .../UmbracoBuilderExtensions.cs | 8 +++--- .../Extensions/ServiceCollectionExtensions.cs | 23 +++++++--------- 13 files changed, 96 insertions(+), 42 deletions(-) diff --git a/src/Umbraco.Core/Composing/RuntimeHashPaths.cs b/src/Umbraco.Core/Composing/RuntimeHashPaths.cs index 12a878ee94..eac2f83bcd 100644 --- a/src/Umbraco.Core/Composing/RuntimeHashPaths.cs +++ b/src/Umbraco.Core/Composing/RuntimeHashPaths.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; +using System.Reflection; namespace Umbraco.Cms.Core.Composing { @@ -17,6 +18,24 @@ namespace Umbraco.Cms.Core.Composing return this; } + /// + /// Creates a runtime hash based on the assembly provider + /// + /// + /// + public RuntimeHashPaths AddAssemblies(IAssemblyProvider assemblyProvider) + { + foreach (Assembly assembly in assemblyProvider.Assemblies) + { + // TODO: We need to test this on a published website + if (!assembly.IsDynamic && assembly.Location != null) + { + AddFile(new FileInfo(assembly.Location)); + } + } + return this; + } + public void AddFile(FileInfo fileInfo, bool scanFileContent = false) => _files.Add(fileInfo, scanFileContent); public IEnumerable GetFolders() => _paths; diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 39b70b831b..fa66780914 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Text; using System.Threading; @@ -55,8 +54,8 @@ namespace Umbraco.Cms.Core.Composing /// Files storage location. /// A profiling logger. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfilingLogger profilingLogger, IEnumerable assembliesToScan = null) - : this(typeFinder, runtimeCache, localTempPath, logger, profilingLogger, true, assembliesToScan) + public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, IEnumerable assembliesToScan = null) + : this(typeFinder, runtimeCache, localTempPath, logger, profiler, true, assembliesToScan) { } /// @@ -68,13 +67,18 @@ namespace Umbraco.Cms.Core.Composing /// A profiling logger. /// Whether to detect changes using hashes. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfilingLogger profilingLogger, bool detectChanges, IEnumerable assembliesToScan = null) + public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, bool detectChanges, IEnumerable assembliesToScan = null) { + if (profiler is null) + { + throw new ArgumentNullException(nameof(profiler)); + } + TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); _localTempPath = localTempPath; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); - _profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger)); + _profilingLogger = new ProfilingLogger(logger, profiler); _assemblies = assembliesToScan; _fileBasePath = new Lazy(GetFileBasePath); diff --git a/src/Umbraco.Core/DependencyInjection/IUmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/IUmbracoBuilder.cs index 0b65c3443c..738cfed26e 100644 --- a/src/Umbraco.Core/DependencyInjection/IUmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/IUmbracoBuilder.cs @@ -1,7 +1,10 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; +using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Logging; namespace Umbraco.Cms.Core.DependencyInjection { @@ -10,7 +13,24 @@ namespace Umbraco.Cms.Core.DependencyInjection IServiceCollection Services { get; } IConfiguration Config { get; } TypeLoader TypeLoader { get; } + + /// + /// A Logger factory created specifically for the . This is NOT the same + /// instance that will be resolved from DI. Use only if required during configuration. + /// ILoggerFactory BuilderLoggerFactory { get; } + + /// + /// A hosting environment created specifically for the . This is NOT the same + /// instance that will be resolved from DI. Use only if required during configuration. + /// + /// + /// This may be null. + /// + IHostingEnvironment BuilderHostingEnvironment { get; } + + IProfiler Profiler { get; } + AppCaches AppCaches { get; } TBuilder WithCollectionBuilder() where TBuilder : ICollectionBuilder, new(); void Build(); } diff --git a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs index 5dd2499624..648af8f082 100644 --- a/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs +++ b/src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs @@ -52,23 +52,41 @@ namespace Umbraco.Cms.Core.DependencyInjection public TypeLoader TypeLoader { get; } + /// public ILoggerFactory BuilderLoggerFactory { get; } + /// + public IHostingEnvironment BuilderHostingEnvironment { get; } + + public IProfiler Profiler { get; } + + public AppCaches AppCaches { get; } + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class primarily for testing. /// public UmbracoBuilder(IServiceCollection services, IConfiguration config, TypeLoader typeLoader) - : this(services, config, typeLoader, NullLoggerFactory.Instance) + : this(services, config, typeLoader, NullLoggerFactory.Instance, new NoopProfiler(), AppCaches.Disabled, null) { } /// /// Initializes a new instance of the class. /// - public UmbracoBuilder(IServiceCollection services, IConfiguration config, TypeLoader typeLoader, ILoggerFactory loggerFactory) + public UmbracoBuilder( + IServiceCollection services, + IConfiguration config, + TypeLoader typeLoader, + ILoggerFactory loggerFactory, + IProfiler profiler, + AppCaches appCaches, + IHostingEnvironment hostingEnvironment) { Services = services; Config = config; BuilderLoggerFactory = loggerFactory; + BuilderHostingEnvironment = hostingEnvironment; + Profiler = profiler; + AppCaches = appCaches; TypeLoader = typeLoader; AddCoreServices(); @@ -106,6 +124,9 @@ namespace Umbraco.Cms.Core.DependencyInjection private void AddCoreServices() { + Services.AddSingleton(AppCaches); + Services.AddSingleton(Profiler); + // Register as singleton to allow injection everywhere. Services.AddSingleton(p => p.GetService); Services.AddSingleton(); diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 9c66c95d24..0e340baf90 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -21,7 +21,7 @@ namespace Umbraco.Cms.Core.Logging /// /// Initializes a new instance of the class. /// - public ProfilingLogger(ILogger logger, IProfiler profiler) + public ProfilingLogger(ILogger logger, IProfiler profiler) { Logger = logger ?? throw new ArgumentNullException(nameof(logger)); Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); diff --git a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs index 380e3fca99..00d6820302 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Benchmarks cache, null, new NullLogger(), - new ProfilingLogger(new NullLogger(), new NoopProfiler())); + new NoopProfiler()); // populate the cache cache.Insert( @@ -43,7 +43,7 @@ namespace Umbraco.Tests.Benchmarks NoAppCache.Instance, null, new NullLogger(), - new ProfilingLogger(new NullLogger(), new NoopProfiler())); + new NoopProfiler()); } /// diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index 79f5e4d578..ec76e0ba08 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -50,7 +50,7 @@ namespace Umbraco.Cms.Tests.Common public ITypeFinder GetTypeFinder() => _typeFinder; public TypeLoader GetMockedTypeLoader() => - new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); //// public Configs GetConfigs() => GetConfigsFactory().Create(); diff --git a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs index b3a168e119..bb4be6f368 100644 --- a/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs +++ b/src/Umbraco.Tests.Integration/Testing/UmbracoIntegrationTest.cs @@ -202,14 +202,15 @@ namespace Umbraco.Cms.Tests.Integration.Testing services.AddRequiredNetCoreServices(TestHelper, webHostEnvironment); // Add it! + Core.Hosting.IHostingEnvironment hostingEnvironment = TestHelper.GetHostingEnvironment(); TypeLoader typeLoader = services.AddTypeLoader( GetType().Assembly, - TestHelper.GetHostingEnvironment(), + hostingEnvironment, TestHelper.ConsoleLoggerFactory, AppCaches.NoCache, Configuration, TestHelper.Profiler); - var builder = new UmbracoBuilder(services, Configuration, typeLoader, TestHelper.ConsoleLoggerFactory); + var builder = new UmbracoBuilder(services, Configuration, typeLoader, TestHelper.ConsoleLoggerFactory, TestHelper.Profiler, AppCaches.NoCache, hostingEnvironment); builder.Services.AddLogger(TestHelper.GetHostingEnvironment(), TestHelper.GetLoggingConfiguration(), Configuration); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index 11fee778e2..bd8fc3103f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components private static IServiceCollection MockRegister() => new ServiceCollection(); - private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); [Test] public void Boot1A() @@ -438,7 +438,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components public void AllComposers() { ITypeFinder typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + var typeLoader = new TypeLoader(typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); IServiceCollection register = MockRegister(); var builder = new UmbracoBuilder(register, Mock.Of(), TestHelper.GetMockedTypeLoader()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs index 17f271888b..3a38f0db11 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs @@ -19,15 +19,11 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing { protected TypeLoader TypeLoader { get; private set; } - protected IProfilingLogger ProfilingLogger { get; private set; } - [SetUp] public void Initialize() { - ProfilingLogger = new ProfilingLogger(Mock.Of>(), Mock.Of()); - var typeFinder = TestHelper.GetTypeFinder(); - TypeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), ProfilingLogger, false, AssembliesToScan); + TypeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of(), false, AssembliesToScan); } protected virtual IEnumerable AssembliesToScan diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs index 794665c79d..e81fcfdabd 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), - new ProfilingLogger(Mock.Of>(), Mock.Of()), + Mock.Of(), false, assemblies); } diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index b4273446f9..5f9027db46 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -101,12 +101,10 @@ namespace Umbraco.Extensions services.AddSingleton(httpContextAccessor); var requestCache = new HttpContextRequestAppCache(httpContextAccessor); - var appCaches = AppCaches.Create(requestCache); - services.AddUnique(appCaches); + var appCaches = AppCaches.Create(requestCache); IProfiler profiler = GetWebProfiler(config); - services.AddUnique(profiler); - + ILoggerFactory loggerFactory = LoggerFactory.Create(cfg => cfg.AddSerilog(Log.Logger, false)); TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), tempHostingEnvironment, loggerFactory, appCaches, config, profiler); @@ -114,7 +112,7 @@ namespace Umbraco.Extensions // other start filters are applied (depending on the ordering of IStartupFilters in DI). services.AddTransient(); - return new UmbracoBuilder(services, config, typeLoader, loggerFactory); + return new UmbracoBuilder(services, config, typeLoader, loggerFactory, profiler, appCaches, tempHostingEnvironment); } /// diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index 16a83543ef..d5a02027a2 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -65,23 +65,19 @@ namespace Umbraco.Extensions ILoggerFactory loggerFactory, Assembly entryAssembly, IConfiguration config, - IProfilingLogger profilingLogger) + IProfiler profiler) { TypeFinderSettings typeFinderSettings = config.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); var assemblyProvider = new DefaultUmbracoAssemblyProvider(entryAssembly, loggerFactory); - var runtimeHashPaths = new RuntimeHashPaths(); - foreach(Assembly assembly in assemblyProvider.Assemblies) - { - // TODO: We need to test this on a published website - if (!assembly.IsDynamic && assembly.Location != null) - { - runtimeHashPaths.AddFile(new FileInfo(assembly.Location)); - } - } + RuntimeHashPaths runtimeHashPaths = new RuntimeHashPaths().AddAssemblies(assemblyProvider); - var runtimeHash = new RuntimeHash(profilingLogger, runtimeHashPaths); + var runtimeHash = new RuntimeHash( + new ProfilingLogger( + loggerFactory.CreateLogger(), + profiler), + runtimeHashPaths); var typeFinder = new TypeFinder( loggerFactory.CreateLogger(), @@ -104,15 +100,14 @@ namespace Umbraco.Extensions IConfiguration configuration, IProfiler profiler) { - var profilingLogger = new ProfilingLogger(loggerFactory.CreateLogger(), profiler); - var typeFinder = services.AddTypeFinder(loggerFactory, entryAssembly, configuration, profilingLogger); + ITypeFinder typeFinder = services.AddTypeFinder(loggerFactory, entryAssembly, configuration, profiler); var typeLoader = new TypeLoader( typeFinder, appCaches.RuntimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), loggerFactory.CreateLogger(), - profilingLogger + profiler ); services.AddUnique(typeLoader); From f7b8a93a35e6f5acbd79e74bc15bbdbe5f0846d6 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 16:15:37 -0600 Subject: [PATCH 027/102] Fixes typefinder/loader dependency refs, allows config to specify additional entry assemblies --- .../DefaultUmbracoAssemblyProvider.cs | 11 ++- .../FindAssembliesWithReferencesTo.cs | 7 +- src/Umbraco.Core/Composing/ITypeFinder.cs | 11 +-- src/Umbraco.Core/Composing/TypeFinder.cs | 23 +++--- .../Composing/TypeFinderConfig.cs | 10 +-- src/Umbraco.Core/Composing/TypeLoader.cs | 33 +++++--- .../Models/TypeFinderSettings.cs | 9 +++ .../TypeFinderBenchmarks.cs | 4 +- .../TypeLoaderBenchmarks.cs | 5 +- src/Umbraco.Tests.Common/TestHelperBase.cs | 4 +- .../Umbraco.Core/Components/ComponentTests.cs | 4 +- .../Composing/ComposingTestBase.cs | 2 +- .../Umbraco.Core/Composing/TypeFinderTests.cs | 4 +- .../Umbraco.Core/Composing/TypeLoaderTests.cs | 1 + .../UmbracoBuilderExtensions.cs | 9 ++- .../Extensions/ServiceCollectionExtensions.cs | 77 +++++++++++-------- 16 files changed, 127 insertions(+), 87 deletions(-) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index 52967a58e7..dce85264d1 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -17,12 +17,17 @@ namespace Umbraco.Cms.Core.Composing { private readonly Assembly _entryPointAssembly; private readonly ILoggerFactory _loggerFactory; + private readonly IEnumerable _additionalTargetAssemblies; private List _discovered; - public DefaultUmbracoAssemblyProvider(Assembly entryPointAssembly, ILoggerFactory loggerFactory) + public DefaultUmbracoAssemblyProvider( + Assembly entryPointAssembly, + ILoggerFactory loggerFactory, + IEnumerable additionalTargetAssemblies = null) { _entryPointAssembly = entryPointAssembly ?? throw new ArgumentNullException(nameof(entryPointAssembly)); _loggerFactory = loggerFactory; + _additionalTargetAssemblies = additionalTargetAssemblies; } // TODO: It would be worth investigating a netcore3 version of this which would use @@ -40,7 +45,9 @@ namespace Umbraco.Cms.Core.Composing return _discovered; } - var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, Constants.Composing.UmbracoCoreAssemblyNames, true, _loggerFactory); + IEnumerable additionalTargetAssemblies = _additionalTargetAssemblies.Concat(Constants.Composing.UmbracoCoreAssemblyNames); + + var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, additionalTargetAssemblies.ToArray(), true, _loggerFactory); _discovered = finder.Find().ToList(); return _discovered; diff --git a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs index 5b554a5321..78cdb80f58 100644 --- a/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs +++ b/src/Umbraco.Core/Composing/FindAssembliesWithReferencesTo.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; @@ -18,6 +18,7 @@ namespace Umbraco.Cms.Core.Composing private readonly string[] _targetAssemblies; private readonly bool _includeTargets; private readonly ILoggerFactory _loggerFactory; + private readonly ILogger _logger; /// /// Constructor @@ -32,6 +33,7 @@ namespace Umbraco.Cms.Core.Composing _targetAssemblies = targetAssemblyNames; _includeTargets = includeTargets; _loggerFactory = loggerFactory; + _logger = _loggerFactory.CreateLogger(); } public IEnumerable Find() @@ -50,9 +52,10 @@ namespace Umbraco.Cms.Core.Composing { referenceItems.Add(Assembly.Load(target)); } - catch (FileNotFoundException) + catch (FileNotFoundException ex) { // occurs if we cannot load this ... for example in a test project where we aren't currently referencing Umbraco.Web, etc... + _logger.LogDebug(ex, "Could not load assembly " + target); } } } diff --git a/src/Umbraco.Core/Composing/ITypeFinder.cs b/src/Umbraco.Core/Composing/ITypeFinder.cs index 2cf6ca23f7..2522c2f593 100644 --- a/src/Umbraco.Core/Composing/ITypeFinder.cs +++ b/src/Umbraco.Core/Composing/ITypeFinder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; @@ -51,14 +51,5 @@ namespace Umbraco.Cms.Core.Composing Type attributeType, IEnumerable assemblies, bool onlyConcreteClasses); - - /// - /// Gets a hash value of the current runtime - /// - /// - /// This is used to detect if the runtime itself has changed, like a DLL has changed or another dynamically compiled - /// part of the application has changed. This is used to detect if we need to re-type scan. - /// - string GetRuntimeHash(); } } diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index 97b8e3847c..4299328b5d 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -17,7 +17,6 @@ namespace Umbraco.Cms.Core.Composing { private readonly ILogger _logger; private readonly IAssemblyProvider _assemblyProvider; - private readonly IRuntimeHash _runtimeHash; private volatile HashSet _localFilteredAssemblyCache; private readonly object _localFilteredAssemblyCacheLocker = new object(); private readonly List _notifiedLoadExceptionAssemblies = new List(); @@ -27,13 +26,12 @@ namespace Umbraco.Cms.Core.Composing // used for benchmark tests internal bool QueryWithReferencingAssemblies { get; set; } = true; - public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, IRuntimeHash runtimeHash, ITypeFinderConfig typeFinderConfig = null) + public TypeFinder(ILogger logger, IAssemblyProvider assemblyProvider, ITypeFinderConfig typeFinderConfig = null) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _assemblyProvider = assemblyProvider; - _runtimeHash = runtimeHash; _typeFinderConfig = typeFinderConfig; - } + } private string[] _assembliesAcceptingLoadExceptions = null; @@ -64,9 +62,12 @@ namespace Umbraco.Cms.Core.Composing var name = a.GetName().Name; // simple name of the assembly return AssembliesAcceptingLoadExceptions.Any(pattern => { - if (pattern.Length > name.Length) return false; // pattern longer than name - if (pattern.Length == name.Length) return pattern.InvariantEquals(name); // same length, must be identical - if (pattern[pattern.Length] != '.') return false; // pattern is shorter than name, must end with dot + if (pattern.Length > name.Length) + return false; // pattern longer than name + if (pattern.Length == name.Length) + return pattern.InvariantEquals(name); // same length, must be identical + if (pattern[pattern.Length] != '.') + return false; // pattern is shorter than name, must end with dot return name.StartsWith(pattern); // and name must start with pattern }); } @@ -112,6 +113,8 @@ namespace Umbraco.Cms.Core.Composing && exclusionFilter.Any(f => x.FullName.StartsWith(f)) == false); } + // TODO: Kill this + /// /// this is our assembly filter to filter out known types that def don't contain types we'd like to find or plugins /// @@ -232,9 +235,6 @@ namespace Umbraco.Cms.Core.Composing return GetClassesWithAttribute(attributeType, assemblyList, onlyConcreteClasses); } - /// - public string GetRuntimeHash() => _runtimeHash.GetHashValue(); - /// /// Returns a Type for the string type name /// @@ -455,7 +455,8 @@ namespace Umbraco.Cms.Core.Composing var ex = new ReflectionTypeLoadException(rex.Types, rex.LoaderExceptions, sb.ToString()); // rethrow with new message, unless accepted - if (AcceptsLoadExceptions(a) == false) throw ex; + if (AcceptsLoadExceptions(a) == false) + throw ex; // log a warning, and return what we can lock (_notifiedLoadExceptionAssemblies) diff --git a/src/Umbraco.Core/Composing/TypeFinderConfig.cs b/src/Umbraco.Core/Composing/TypeFinderConfig.cs index 7940773231..8e63958a06 100644 --- a/src/Umbraco.Core/Composing/TypeFinderConfig.cs +++ b/src/Umbraco.Core/Composing/TypeFinderConfig.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Configuration.UmbracoSettings; @@ -15,17 +16,16 @@ namespace Umbraco.Cms.Core.Composing private readonly TypeFinderSettings _settings; private IEnumerable _assembliesAcceptingLoadExceptions; - public TypeFinderConfig(IOptions settings) - { - _settings = settings.Value; - } + public TypeFinderConfig(IOptions settings) => _settings = settings.Value; public IEnumerable AssembliesAcceptingLoadExceptions { get { if (_assembliesAcceptingLoadExceptions != null) + { return _assembliesAcceptingLoadExceptions; + } var s = _settings.AssembliesAcceptingLoadExceptions; return _assembliesAcceptingLoadExceptions = string.IsNullOrWhiteSpace(s) diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index fa66780914..5e6808fb13 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -25,9 +25,8 @@ namespace Umbraco.Cms.Core.Composing /// on a hash of the DLLs in the ~/bin folder to check for cache expiration. /// public sealed class TypeLoader - { - internal const string CacheKey = "umbraco-types.list"; - + { + private readonly IRuntimeHash _runtimeHash; private readonly IAppPolicyCache _runtimeCache; private readonly ILogger _logger; private readonly IProfilingLogger _profilingLogger; @@ -44,7 +43,9 @@ namespace Umbraco.Cms.Core.Composing private bool _reportedChange; private readonly DirectoryInfo _localTempPath; private readonly Lazy _fileBasePath; - private readonly Dictionary<(string, string), IEnumerable> EmptyCache = new Dictionary<(string, string), IEnumerable>(); + private readonly Dictionary<(string, string), IEnumerable> _emptyCache = new Dictionary<(string, string), IEnumerable>(); + private string _typesListFilePath; + private string _typesHashFilePath; /// /// Initializes a new instance of the class. @@ -54,8 +55,8 @@ namespace Umbraco.Cms.Core.Composing /// Files storage location. /// A profiling logger. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, IEnumerable assembliesToScan = null) - : this(typeFinder, runtimeCache, localTempPath, logger, profiler, true, assembliesToScan) + public TypeLoader(ITypeFinder typeFinder, IRuntimeHash runtimeHash, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, IEnumerable assembliesToScan = null) + : this(typeFinder, runtimeHash, runtimeCache, localTempPath, logger, profiler, true, assembliesToScan) { } /// @@ -67,14 +68,18 @@ namespace Umbraco.Cms.Core.Composing /// A profiling logger. /// Whether to detect changes using hashes. /// - public TypeLoader(ITypeFinder typeFinder, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, bool detectChanges, IEnumerable assembliesToScan = null) + public TypeLoader(ITypeFinder typeFinder, IRuntimeHash runtimeHash, IAppPolicyCache runtimeCache, DirectoryInfo localTempPath, ILogger logger, IProfiler profiler, bool detectChanges, IEnumerable assembliesToScan = null) { if (profiler is null) { throw new ArgumentNullException(nameof(profiler)); } + var runtimeHashValue = runtimeHash.GetHashValue(); + CacheKey = runtimeHashValue + "umbraco-types.list"; + TypeFinder = typeFinder ?? throw new ArgumentNullException(nameof(typeFinder)); + _runtimeHash = runtimeHash; _runtimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache)); _localTempPath = localTempPath; _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -119,6 +124,8 @@ namespace Umbraco.Cms.Core.Composing } } + internal string CacheKey { get; } + /// /// Returns the underlying /// @@ -202,9 +209,11 @@ namespace Umbraco.Cms.Core.Composing get { if (_currentAssembliesHash != null) + { return _currentAssembliesHash; + } - _currentAssembliesHash = TypeFinder.GetRuntimeHash(); + _currentAssembliesHash = _runtimeHash.GetHashValue(); return _currentAssembliesHash; } @@ -268,7 +277,7 @@ namespace Umbraco.Cms.Core.Composing } } - return EmptyCache; + return _emptyCache; } // internal for tests @@ -277,7 +286,7 @@ namespace Umbraco.Cms.Core.Composing var typesListFilePath = GetTypesListFilePath(); if (typesListFilePath == null || File.Exists(typesListFilePath) == false) { - return EmptyCache; + return _emptyCache; } var cache = new Dictionary<(string, string), IEnumerable>(); @@ -332,9 +341,9 @@ namespace Umbraco.Cms.Core.Composing } // internal for tests - public string GetTypesListFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".list"; + public string GetTypesListFilePath() => _typesListFilePath ??= _fileBasePath.Value == null ? null : _fileBasePath.Value + ".list"; - private string GetTypesHashFilePath() => _fileBasePath.Value == null ? null : _fileBasePath.Value + ".hash"; + private string GetTypesHashFilePath() => _typesHashFilePath ??= _fileBasePath.Value == null ? null : _fileBasePath.Value + ".hash"; /// /// Used to produce the Lazy value of _fileBasePath diff --git a/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs b/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs index d942f47b4f..e41921de8c 100644 --- a/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs +++ b/src/Umbraco.Core/Configuration/Models/TypeFinderSettings.cs @@ -1,6 +1,9 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System.Collections; +using System.Collections.Generic; + namespace Umbraco.Cms.Core.Configuration.Models { /// @@ -13,5 +16,11 @@ namespace Umbraco.Cms.Core.Configuration.Models /// Gets or sets a value for the assemblies that accept load exceptions during type finder operations. /// public string AssembliesAcceptingLoadExceptions { get; set; } + + /// + /// By default the entry assemblies for scanning plugin types is the Umbraco DLLs. If you require + /// scanning for plugins based on different root referenced assemblies you can add the assembly name to this list. + /// + public IEnumerable AdditionalEntryAssemblies { get; set; } } } diff --git a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs index 54dc414762..c3ba9ad393 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeFinderBenchmarks.cs @@ -16,8 +16,8 @@ namespace Umbraco.Tests.Benchmarks public TypeFinderBenchmarks() { - _typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); - _typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()) + _typeFinder1 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); + _typeFinder2 = new TypeFinder(new NullLogger(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)) { QueryWithReferencingAssemblies = false }; diff --git a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs index 00d6820302..1bd875142d 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs @@ -21,12 +21,12 @@ namespace Umbraco.Tests.Benchmarks { var typeFinder1 = new TypeFinder( new NullLogger(), - new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), - new VaryingRuntimeHash()); + new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); var cache = new ObjectCacheAppCache(); _typeLoader1 = new TypeLoader( typeFinder1, + new VaryingRuntimeHash(), cache, null, new NullLogger(), @@ -40,6 +40,7 @@ namespace Umbraco.Tests.Benchmarks _typeLoader2 = new TypeLoader( typeFinder1, + new VaryingRuntimeHash(), NoAppCache.Instance, null, new NullLogger(), diff --git a/src/Umbraco.Tests.Common/TestHelperBase.cs b/src/Umbraco.Tests.Common/TestHelperBase.cs index ec76e0ba08..5a7bd1abc7 100644 --- a/src/Umbraco.Tests.Common/TestHelperBase.cs +++ b/src/Umbraco.Tests.Common/TestHelperBase.cs @@ -44,13 +44,13 @@ namespace Umbraco.Cms.Tests.Common protected TestHelperBase(Assembly entryAssembly) { MainDom = new SimpleMainDom(); - _typeFinder = new TypeFinder(NullLoggerFactory.Instance.CreateLogger(), new DefaultUmbracoAssemblyProvider(entryAssembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + _typeFinder = new TypeFinder(NullLoggerFactory.Instance.CreateLogger(), new DefaultUmbracoAssemblyProvider(entryAssembly, NullLoggerFactory.Instance)); } public ITypeFinder GetTypeFinder() => _typeFinder; public TypeLoader GetMockedTypeLoader() => - new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + new TypeLoader(Mock.Of(), new VaryingRuntimeHash(), Mock.Of(), new DirectoryInfo(GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); //// public Configs GetConfigs() => GetConfigsFactory().Create(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index bd8fc3103f..af221514e3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -78,7 +78,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components private static IServiceCollection MockRegister() => new ServiceCollection(); - private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + private static TypeLoader MockTypeLoader() => new TypeLoader(Mock.Of(), new VaryingRuntimeHash(), Mock.Of(), new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); [Test] public void Boot1A() @@ -438,7 +438,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components public void AllComposers() { ITypeFinder typeFinder = TestHelper.GetTypeFinder(); - var typeLoader = new TypeLoader(typeFinder, AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); + var typeLoader = new TypeLoader(typeFinder, new VaryingRuntimeHash(), AppCaches.Disabled.RuntimeCache, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of()); IServiceCollection register = MockRegister(); var builder = new UmbracoBuilder(register, Mock.Of(), TestHelper.GetMockedTypeLoader()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs index 3a38f0db11..ac536b7aee 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/ComposingTestBase.cs @@ -23,7 +23,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing public void Initialize() { var typeFinder = TestHelper.GetTypeFinder(); - TypeLoader = new TypeLoader(typeFinder, NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of(), false, AssembliesToScan); + TypeLoader = new TypeLoader(typeFinder, new VaryingRuntimeHash(), NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), Mock.Of(), false, AssembliesToScan); } protected virtual IEnumerable AssembliesToScan diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs index 027b851529..bbe6519817 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeFinderTests.cs @@ -40,7 +40,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing [Test] public void Find_Class_Of_Type_With_Attribute() { - var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); IEnumerable typesFound = typeFinder.FindClassesOfTypeWithAttribute(_assemblies); Assert.AreEqual(2, typesFound.Count()); } @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing [Test] public void Find_Classes_With_Attribute() { - var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance), new VaryingRuntimeHash()); + var typeFinder = new TypeFinder(Mock.Of>(), new DefaultUmbracoAssemblyProvider(GetType().Assembly, NullLoggerFactory.Instance)); IEnumerable typesFound = typeFinder.FindClassesWithAttribute(_assemblies); Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs index e81fcfdabd..cea18c5176 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/TypeLoaderTests.cs @@ -48,6 +48,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing }; _typeLoader = new TypeLoader( typeFinder, + new VaryingRuntimeHash(), NoAppCache.Instance, new DirectoryInfo(TestHelper.GetHostingEnvironment().MapPathContentRoot(Constants.SystemDirectories.TempData)), Mock.Of>(), diff --git a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs index 5f9027db46..957d5976e1 100644 --- a/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/DependencyInjection/UmbracoBuilderExtensions.cs @@ -106,7 +106,14 @@ namespace Umbraco.Extensions IProfiler profiler = GetWebProfiler(config); ILoggerFactory loggerFactory = LoggerFactory.Create(cfg => cfg.AddSerilog(Log.Logger, false)); - TypeLoader typeLoader = services.AddTypeLoader(Assembly.GetEntryAssembly(), tempHostingEnvironment, loggerFactory, appCaches, config, profiler); + + TypeLoader typeLoader = services.AddTypeLoader( + Assembly.GetEntryAssembly(), + tempHostingEnvironment, + loggerFactory, + appCaches, + config, + profiler); // adds the umbraco startup filter which will call UseUmbraco early on before // other start filters are applied (depending on the ordering of IStartupFilters in DI). diff --git a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs index d5a02027a2..df7ffd4328 100644 --- a/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ServiceCollectionExtensions.cs @@ -3,6 +3,7 @@ using System.Reflection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Serilog; @@ -60,37 +61,22 @@ namespace Umbraco.Extensions return services; } - internal static ITypeFinder AddTypeFinder( - this IServiceCollection services, - ILoggerFactory loggerFactory, - Assembly entryAssembly, - IConfiguration config, - IProfiler profiler) - { - TypeFinderSettings typeFinderSettings = config.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); - - var assemblyProvider = new DefaultUmbracoAssemblyProvider(entryAssembly, loggerFactory); - - RuntimeHashPaths runtimeHashPaths = new RuntimeHashPaths().AddAssemblies(assemblyProvider); - - var runtimeHash = new RuntimeHash( - new ProfilingLogger( - loggerFactory.CreateLogger(), - profiler), - runtimeHashPaths); - - var typeFinder = new TypeFinder( - loggerFactory.CreateLogger(), - assemblyProvider, - runtimeHash, - new TypeFinderConfig(Options.Create(typeFinderSettings)) - ); - - services.AddUnique(typeFinder); - - return typeFinder; - } - + /// + /// Called to create the to assign to the + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// This should never be called in a web project. It is used internally by Umbraco but could be used in unit tests. + /// If called in a web project it will have no affect except to create and return a new TypeLoader but this will not + /// be the instance in DI. + /// public static TypeLoader AddTypeLoader( this IServiceCollection services, Assembly entryAssembly, @@ -100,17 +86,42 @@ namespace Umbraco.Extensions IConfiguration configuration, IProfiler profiler) { - ITypeFinder typeFinder = services.AddTypeFinder(loggerFactory, entryAssembly, configuration, profiler); + TypeFinderSettings typeFinderSettings = configuration.GetSection(Cms.Core.Constants.Configuration.ConfigTypeFinder).Get() ?? new TypeFinderSettings(); + + var assemblyProvider = new DefaultUmbracoAssemblyProvider( + entryAssembly, + loggerFactory, + typeFinderSettings.AdditionalEntryAssemblies); + + RuntimeHashPaths runtimeHashPaths = new RuntimeHashPaths().AddAssemblies(assemblyProvider); + + var runtimeHash = new RuntimeHash( + new ProfilingLogger( + loggerFactory.CreateLogger(), + profiler), + runtimeHashPaths); + + var typeFinderConfig = new TypeFinderConfig(Options.Create(typeFinderSettings)); + + var typeFinder = new TypeFinder( + loggerFactory.CreateLogger(), + assemblyProvider, + typeFinderConfig + ); var typeLoader = new TypeLoader( typeFinder, + runtimeHash, appCaches.RuntimeCache, new DirectoryInfo(hostingEnvironment.LocalTempPath), loggerFactory.CreateLogger(), profiler ); - services.AddUnique(typeLoader); + // This will add it ONCE and not again which is what we want since we don't actually want people to call this method + // in the web project. + services.TryAddSingleton(typeFinder); + services.TryAddSingleton(typeLoader); return typeLoader; } From 582fdec2138c6e73ccf6491fbfb439d9508b0a53 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 9 Jul 2021 16:37:37 -0600 Subject: [PATCH 028/102] Adds null check and adds back orig ctor for ProfilingLogger --- .../Composing/DefaultUmbracoAssemblyProvider.cs | 6 +++++- src/Umbraco.Core/Logging/ProfilingLogger.cs | 9 +++++++++ src/Umbraco.Core/Templates/HtmlUrlParser.cs | 7 ++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs index dce85264d1..56d798ea0c 100644 --- a/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs +++ b/src/Umbraco.Core/Composing/DefaultUmbracoAssemblyProvider.cs @@ -45,7 +45,11 @@ namespace Umbraco.Cms.Core.Composing return _discovered; } - IEnumerable additionalTargetAssemblies = _additionalTargetAssemblies.Concat(Constants.Composing.UmbracoCoreAssemblyNames); + IEnumerable additionalTargetAssemblies = Constants.Composing.UmbracoCoreAssemblyNames; + if (_additionalTargetAssemblies != null) + { + additionalTargetAssemblies = additionalTargetAssemblies.Concat(_additionalTargetAssemblies); + } var finder = new FindAssembliesWithReferencesTo(new[] { _entryPointAssembly }, additionalTargetAssemblies.ToArray(), true, _loggerFactory); _discovered = finder.Find().ToList(); diff --git a/src/Umbraco.Core/Logging/ProfilingLogger.cs b/src/Umbraco.Core/Logging/ProfilingLogger.cs index 0e340baf90..a4843ccc29 100644 --- a/src/Umbraco.Core/Logging/ProfilingLogger.cs +++ b/src/Umbraco.Core/Logging/ProfilingLogger.cs @@ -18,6 +18,15 @@ namespace Umbraco.Cms.Core.Logging /// public IProfiler Profiler { get; } + /// + /// Initializes a new instance of the class. + /// + public ProfilingLogger(ILogger logger, IProfiler profiler) + { + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + Profiler = profiler ?? throw new ArgumentNullException(nameof(profiler)); + } + /// /// Initializes a new instance of the class. /// diff --git a/src/Umbraco.Core/Templates/HtmlUrlParser.cs b/src/Umbraco.Core/Templates/HtmlUrlParser.cs index 97bab9276a..50dc937466 100644 --- a/src/Umbraco.Core/Templates/HtmlUrlParser.cs +++ b/src/Umbraco.Core/Templates/HtmlUrlParser.cs @@ -1,4 +1,4 @@ -using System.Text.RegularExpressions; +using System.Text.RegularExpressions; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Umbraco.Cms.Core.Configuration.Models; @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Templates private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace); - public HtmlUrlParser(IOptions contentSettings, ILogger logger ,IProfilingLogger profilingLogger, IIOHelper ioHelper) + public HtmlUrlParser(IOptions contentSettings, ILogger logger, IProfilingLogger profilingLogger, IIOHelper ioHelper) { _contentSettings = contentSettings.Value; _logger = logger; @@ -36,7 +36,8 @@ namespace Umbraco.Cms.Core.Templates /// public string EnsureUrls(string text) { - if (_contentSettings.ResolveUrlsFromTextString == false) return text; + if (_contentSettings.ResolveUrlsFromTextString == false) + return text; using (var timer = _profilingLogger.DebugDuration(typeof(IOHelper), "ResolveUrlsFromTextString starting", "ResolveUrlsFromTextString complete")) { From a37f6c604f399b1c6f387adcc97fa71d0dc9abea Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Sat, 10 Jul 2021 14:25:26 +0200 Subject: [PATCH 029/102] Inconsistent formatting --- .../IPropertyCacheCompression.cs | 4 ++-- .../IPropertyCacheCompressionOptions.cs | 2 +- .../NoopPropertyCacheCompressionOptions.cs | 2 +- ...edContentPropertyCacheCompressionOptions.cs | 7 +------ .../ContentSerializationTests.cs | 6 +++--- .../DataSource/IContentCacheDataSerializer.cs | 4 ++-- .../JsonContentNestedDataSerializer.cs | 4 ++-- .../MsgPackContentNestedDataSerializer.cs | 18 +++++++++++------- ...sgPackContentNestedDataSerializerFactory.cs | 2 +- .../PublishedCache/NuCache/NuCacheComposer.cs | 8 ++++---- .../NuCache/PublishedSnapshotService.cs | 8 ++++---- 11 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs index eb89173581..69d10a2276 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompression.cs @@ -6,7 +6,7 @@ namespace Umbraco.Core.PropertyEditors /// Determines if a property type's value should be compressed in memory /// /// - /// + /// /// public interface IPropertyCacheCompression { @@ -16,6 +16,6 @@ namespace Umbraco.Core.PropertyEditors /// The content /// The property to compress or not /// Whether this content is the published version - bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias,bool published); + bool IsCompressed(IReadOnlyContentBase content, string propertyTypeAlias, bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs index 71eb782ee2..e4603d55e3 100644 --- a/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/IPropertyCacheCompressionOptions.cs @@ -11,6 +11,6 @@ namespace Umbraco.Core.PropertyEditors /// The property to compress or not /// The datatype of the property to compress or not /// Whether this content is the published version - bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor,bool published); + bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published); } } diff --git a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs index f566172cd9..ea3b8d8a2e 100644 --- a/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/NoopPropertyCacheCompressionOptions.cs @@ -7,6 +7,6 @@ namespace Umbraco.Core.PropertyEditors /// internal class NoopPropertyCacheCompressionOptions : IPropertyCacheCompressionOptions { - public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor,bool published) => false; + public bool IsCompressed(IReadOnlyContentBase content, PropertyType propertyType, IDataEditor dataEditor, bool published) => false; } } diff --git a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs index 7a5212a985..ece25479cc 100644 --- a/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs +++ b/src/Umbraco.Core/PropertyEditors/UnPublishedContentPropertyCacheCompressionOptions.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Umbraco.Core.Models; +using Umbraco.Core.Models; namespace Umbraco.Core.PropertyEditors { diff --git a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs index 9acad53364..9a44cf35f9 100644 --- a/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs +++ b/src/Umbraco.Tests/PublishedContent/ContentSerializationTests.cs @@ -56,14 +56,14 @@ namespace Umbraco.Tests.PublishedContent var content = Mock.Of(x => x.ContentTypeId == 1); - var json = jsonSerializer.Serialize(content, cacheModel,false).StringData; + var json = jsonSerializer.Serialize(content, cacheModel, false).StringData; var msgPack = msgPackSerializer.Serialize(content, cacheModel, false).ByteData; Console.WriteLine(json); Console.WriteLine(msgPackSerializer.ToJson(msgPack)); - var jsonContent = jsonSerializer.Deserialize(content, json, null,false); - var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack,false); + var jsonContent = jsonSerializer.Deserialize(content, json, null, false); + var msgPackContent = msgPackSerializer.Deserialize(content, null, msgPack, false); CollectionAssert.AreEqual(jsonContent.CultureData.Keys, msgPackContent.CultureData.Keys); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs index 6c37046e8b..4bdf7e9665 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs @@ -17,9 +17,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published); /// - /// Serializes the + /// Serializes the /// - ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published); + ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published); } } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs index 0cc10f5f98..358561cabd 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource DateFormatString = "o" }; private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable(); - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData,bool published) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) { if (stringData == null && byteData != null) throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); @@ -39,7 +39,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { // note that numeric values (which are Int32) are serialized without their // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs index f69232aad3..f1400382e6 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializer.cs @@ -40,7 +40,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource _options = defaultOptions .WithResolver(resolver) .WithCompression(MessagePackCompression.Lz4BlockArray) - .WithSecurity(MessagePackSecurity.UntrustedData); + .WithSecurity(MessagePackSecurity.UntrustedData); } public string ToJson(byte[] bin) @@ -49,7 +49,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource return json; } - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData,bool published) + public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData, bool published) { if (byteData != null) { @@ -62,7 +62,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource // NOTE: We don't really support strings but it's possible if manually used (i.e. tests) var bin = Convert.FromBase64String(stringData); var cacheModel = MessagePackSerializer.Deserialize(bin, _options); - Expand(content, cacheModel,published); + Expand(content, cacheModel, published); return cacheModel; } else @@ -71,7 +71,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource } } - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) + public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { Compress(content, model, published); var bytes = MessagePackSerializer.Serialize(model, _options); @@ -81,7 +81,9 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// Used during serialization to compress properties /// + /// /// + /// /// /// This will essentially 'double compress' property data. The MsgPack data as a whole will already be compressed /// but this will go a step further and double compress property data so that it is stored in the nucache file @@ -89,11 +91,11 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// read/decompressed as a string to be displayed on the front-end. This allows for potentially a significant /// memory savings but could also affect performance of first rendering pages while decompression occurs. /// - private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model,bool published) + private void Compress(IReadOnlyContentBase content, ContentCacheDataModel model, bool published) { foreach(var propertyAliasToData in model.PropertyData) { - if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key,published)) + if (_propertyOptions.IsCompressed(content, propertyAliasToData.Key, published)) { foreach(var property in propertyAliasToData.Value.Where(x => x.Value != null && x.Value is string)) { @@ -106,8 +108,10 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource /// /// Used during deserialization to map the property data as lazy or expand the value /// + /// /// - private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData,bool published) + /// + private void Expand(IReadOnlyContentBase content, ContentCacheDataModel nestedData, bool published) { foreach (var propertyAliasToData in nestedData.PropertyData) { diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs index 29378caf0f..5245df8353 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/MsgPackContentNestedDataSerializerFactory.cs @@ -13,7 +13,7 @@ namespace Umbraco.Web.PublishedCache.NuCache.DataSource private readonly IMemberTypeService _memberTypeService; private readonly PropertyEditorCollection _propertyEditors; private readonly IPropertyCacheCompressionOptions _compressionOptions; - private readonly ConcurrentDictionary<(int, string,bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string,bool), bool>(); + private readonly ConcurrentDictionary<(int, string,bool), bool> _isCompressedCache = new ConcurrentDictionary<(int, string, bool), bool>(); public MsgPackContentNestedDataSerializerFactory( IContentTypeService contentTypeService, diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs index 4b3d7b20fa..dd3907e254 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheComposer.cs @@ -24,10 +24,10 @@ namespace Umbraco.Web.PublishedCache.NuCache } else { - composition.RegisterUnique(); + composition.RegisterUnique(); } - var unPublishedContentCompression = ConfigurationManager.AppSettings[NuCacheSerializerComponent.Nucache_UnPublishedContentCompression_Key]; - if ("MsgPack" == serializer && "true" == unPublishedContentCompression) + var unPublishedContentCompression = ConfigurationManager.AppSettings[NuCacheSerializerComponent.Nucache_UnPublishedContentCompression_Key]; + if (serializer == "MsgPack" && unPublishedContentCompression == "true") { composition.RegisterUnique(); } @@ -35,7 +35,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { composition.RegisterUnique(); } - + composition.RegisterUnique(factory => new ContentDataSerializer(new DictionaryOfPropertyDataSerializer())); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 7ca425aad9..f3373dab6c 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -89,7 +89,7 @@ namespace Umbraco.Web.PublishedCache.NuCache IPublishedModelFactory publishedModelFactory, UrlSegmentProviderCollection urlSegmentProviders, ISyncBootStateAccessor syncBootStateAccessor, - IContentCacheDataSerializerFactory contentCacheDataSerializerFactory, + IContentCacheDataSerializerFactory contentCacheDataSerializerFactory, ContentDataSerializer contentDataSerializer = null) : base(publishedSnapshotAccessor, variationContextAccessor) { @@ -262,7 +262,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (!okMedia) _logger.Warn("Loading media from local db raised warnings, will reload from database."); } - + if (!okContent) LockAndLoadContent(scope => LoadContentFromDatabaseLocked(scope, true)); @@ -1168,7 +1168,7 @@ namespace Umbraco.Web.PublishedCache.NuCache if (Volatile.Read(ref _isReady) == false) { throw new InvalidOperationException("The published snapshot service has not properly initialized."); - } + } var preview = previewToken.IsNullOrWhiteSpace() == false; return new PublishedSnapshot(this, preview); @@ -1491,7 +1491,7 @@ namespace Umbraco.Web.PublishedCache.NuCache UrlSegment = content.GetUrlSegment(_urlSegmentProviders) }; - var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData,published); + var serialized = serializer.Serialize(ReadOnlyContentBaseAdapter.Create(content), contentCacheData, published); var dto = new ContentNuDto { From f69ca3392a723083b60fbc3e64428ca557e4c743 Mon Sep 17 00:00:00 2001 From: Marc Goodson Date: Sun, 11 Jul 2021 12:35:08 +0100 Subject: [PATCH 030/102] Typo in DataTypeList Media Picke (legacy) should be Media Picker (legacy) --- .../Migrations/Install/DatabaseDataCreator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs index 83adbc6e8a..a7cf92e2a9 100644 --- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs +++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseDataCreator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Microsoft.Extensions.Logging; using NPoco; @@ -151,7 +151,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install //New UDI pickers with newer Ids _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1046, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1046", SortOrder = 2, UniqueId = new Guid("FD1E0DA5-5606-4862-B679-5D0CF3A52A59"), Text = "Content Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1047, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1047", SortOrder = 2, UniqueId = new Guid("1EA2E01F-EBD8-4CE1-8D71-6B1149E63548"), Text = "Member Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); - _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picke (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); + _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1048, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1048", SortOrder = 2, UniqueId = new Guid("135D60E0-64D9-49ED-AB08-893C9BA44AE5"), Text = "Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1049, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1049", SortOrder = 2, UniqueId = new Guid("9DBBCBBB-2327-434A-B355-AF1B84E5010A"), Text = "Multiple Media Picker (legacy)", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); _database.Insert(Cms.Core.Constants.DatabaseSchema.Tables.Node, "id", false, new NodeDto { NodeId = 1050, Trashed = false, ParentId = -1, UserId = -1, Level = 1, Path = "-1,1050", SortOrder = 2, UniqueId = new Guid("B4E3535A-1753-47E2-8568-602CF8CFEE6F"), Text = "Multi URL Picker", NodeObjectType = Cms.Core.Constants.ObjectTypes.DataType, CreateDate = DateTime.Now }); From 2a6bdf73300b358ef5be2b1e4b3a480d2e790ef8 Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Mon, 12 Jul 2021 10:48:21 +0200 Subject: [PATCH 031/102] Fixes umbraco/Umbraco-CMS#10402 We added an overload that would conflict for people with existing templates, that was a bit too premature. Disabled for now with a note to look into it for v9 again. --- src/Umbraco.Web/UrlHelperRenderExtensions.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs index 2c547c841e..33e657e7cf 100644 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ b/src/Umbraco.Web/UrlHelperRenderExtensions.cs @@ -281,14 +281,15 @@ namespace Umbraco.Web return CreateHtmlString(url, htmlEncode); } - public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, string cropAlias, bool htmlEncode = true) - { - if (imageCropperValue == null || string.IsNullOrEmpty(imageCropperValue.Src)) return EmptyHtmlString; - - var url = imageCropperValue.Src.GetCropUrl(imageCropperValue, cropAlias: cropAlias, useCropDimensions: true); - - return CreateHtmlString(url, htmlEncode); - } + // TODO: enable again in v9 and make sure to document that `@Url.GetCropUrl(Model.Property, cropAlias: "Featured")` needs to be updated - see https://github.com/umbraco/Umbraco-CMS/pull/10527 for alternatives + // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, string cropAlias, bool htmlEncode = true) + // { + // if (imageCropperValue == null || string.IsNullOrEmpty(imageCropperValue.Src)) return EmptyHtmlString; + // + // var url = imageCropperValue.Src.GetCropUrl(imageCropperValue, cropAlias: cropAlias, useCropDimensions: true); + // + // return CreateHtmlString(url, htmlEncode); + // } public static IHtmlString GetCropUrl(this UrlHelper urlHelper, ImageCropperValue imageCropperValue, From b3fa5f5f351487324aca46ca2022a41e3fab7c5f Mon Sep 17 00:00:00 2001 From: Mole Date: Mon, 12 Jul 2021 12:53:04 +0200 Subject: [PATCH 032/102] Update src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs --- src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs index 446290f83f..4c745c8503 100644 --- a/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Infrastructure/Examine/MediaValueSetBuilder.cs @@ -46,8 +46,6 @@ namespace Umbraco.Cms.Infrastructure.Examine { foreach (IMedia m in media) { - var umbracoFilePath = string.Empty; - var umbracoFile = string.Empty; var urlValue = m.GetUrlSegment(_shortStringHelper, _urlSegmentProviders); From 113b6598edacce78f828c0bff9a8dc514caadf49 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 09:46:56 -0600 Subject: [PATCH 033/102] removes raiseEvent params --- .../Events/IScopedNotificationPublisher.cs | 7 ++ .../Events/ScopedNotificationPublisher.cs | 59 +++++++++++- src/Umbraco.Core/Services/IContentService.cs | 16 ++-- .../Services/IContentServiceBase.cs | 4 +- src/Umbraco.Core/Services/IDataTypeService.cs | 12 +-- src/Umbraco.Core/Services/IMediaService.cs | 10 +-- .../Services/IMemberGroupService.cs | 2 +- .../Services/IMembershipMemberService.cs | 10 +-- src/Umbraco.Core/Services/IUserService.cs | 6 +- .../Services/Implement/ContentService.cs | 89 +++++++++---------- .../Services/Implement/DataTypeService.cs | 21 +---- .../Services/Implement/MediaService.cs | 30 +++---- .../Services/Implement/MemberGroupService.cs | 9 +- .../Services/Implement/MemberService.cs | 19 ++-- .../Services/Implement/UserService.cs | 37 +++----- .../Umbraco.Infrastructure.csproj | 4 - 16 files changed, 167 insertions(+), 168 deletions(-) diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index 7df9167ce6..75fbf83860 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -1,6 +1,7 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Threading.Tasks; using Umbraco.Cms.Core.Notifications; @@ -8,6 +9,12 @@ namespace Umbraco.Cms.Core.Events { public interface IScopedNotificationPublisher { + /// + /// Supresses all notifications from being added/created until the result object is disposed. + /// + /// + IDisposable Supress(); + /// /// Publishes a cancelable notification to the notification subscribers /// diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index c9b0080218..6ea7ee5b6a 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -12,6 +12,8 @@ namespace Umbraco.Cms.Core.Events { private readonly IEventAggregator _eventAggregator; private readonly List _notificationOnScopeCompleted; + private readonly object _locker = new object(); + private bool _isSuppressed = false; public ScopedNotificationPublisher(IEventAggregator eventAggregator) { @@ -26,6 +28,11 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } + if (_isSuppressed) + { + return false; + } + _eventAggregator.Publish(notification); return notification.Cancel; } @@ -37,6 +44,11 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } + if (_isSuppressed) + { + return false; + } + await _eventAggregator.PublishAsync(notification); return notification.Cancel; } @@ -48,6 +60,11 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } + if (_isSuppressed) + { + return; + } + _notificationOnScopeCompleted.Add(notification); } @@ -57,7 +74,7 @@ namespace Umbraco.Cms.Core.Events { if (completed) { - foreach (var notification in _notificationOnScopeCompleted) + foreach (INotification notification in _notificationOnScopeCompleted) { _eventAggregator.Publish(notification); } @@ -68,5 +85,45 @@ namespace Umbraco.Cms.Core.Events _notificationOnScopeCompleted.Clear(); } } + + public IDisposable Supress() + { + lock(_locker) + { + if (_isSuppressed) + { + throw new InvalidOperationException("Notifications are already suppressed"); + } + return new Suppressor(this); + } + } + + private class Suppressor : IDisposable + { + private bool _disposedValue; + private readonly ScopedNotificationPublisher _scopedNotificationPublisher; + + public Suppressor(ScopedNotificationPublisher scopedNotificationPublisher) + { + _scopedNotificationPublisher = scopedNotificationPublisher; + _scopedNotificationPublisher._isSuppressed = true; + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + lock (_scopedNotificationPublisher._locker) + { + _scopedNotificationPublisher._isSuppressed = false; + } + } + _disposedValue = true; + } + } + public void Dispose() => Dispose(disposing: true); + } } } diff --git a/src/Umbraco.Core/Services/IContentService.cs b/src/Umbraco.Core/Services/IContentService.cs index cef8a8c6d6..e58664fef8 100644 --- a/src/Umbraco.Core/Services/IContentService.cs +++ b/src/Umbraco.Core/Services/IContentService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; @@ -236,13 +236,13 @@ namespace Umbraco.Cms.Core.Services /// /// Saves a document. /// - OperationResult Save(IContent content, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + OperationResult Save(IContent content, int userId = Constants.Security.SuperUserId); /// /// Saves documents. /// // TODO: why only 1 result not 1 per content?! - OperationResult Save(IEnumerable contents, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + OperationResult Save(IEnumerable contents, int userId = Constants.Security.SuperUserId); /// /// Deletes a document. @@ -325,12 +325,12 @@ namespace Umbraco.Cms.Core.Services /// /// Sorts documents. /// - OperationResult Sort(IEnumerable items, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + OperationResult Sort(IEnumerable items, int userId = Constants.Security.SuperUserId); /// /// Sorts documents. /// - OperationResult Sort(IEnumerable ids, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + OperationResult Sort(IEnumerable ids, int userId = Constants.Security.SuperUserId); #endregion @@ -349,8 +349,7 @@ namespace Umbraco.Cms.Core.Services /// The document to publish. /// The culture to publish. /// The identifier of the user performing the action. - /// A value indicating whether to raise events. - PublishResult SaveAndPublish(IContent content, string culture = "*", int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + PublishResult SaveAndPublish(IContent content, string culture = "*", int userId = Constants.Security.SuperUserId); /// /// Saves and publishes a document. @@ -363,8 +362,7 @@ namespace Umbraco.Cms.Core.Services /// The document to publish. /// The cultures to publish. /// The identifier of the user performing the action. - /// A value indicating whether to raise events. - PublishResult SaveAndPublish(IContent content, string[] cultures, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + PublishResult SaveAndPublish(IContent content, string[] cultures, int userId = Constants.Security.SuperUserId); /// /// Saves and publishes a document branch. diff --git a/src/Umbraco.Core/Services/IContentServiceBase.cs b/src/Umbraco.Core/Services/IContentServiceBase.cs index 9ab7fc7acc..a62d039abb 100644 --- a/src/Umbraco.Core/Services/IContentServiceBase.cs +++ b/src/Umbraco.Core/Services/IContentServiceBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Core.Services where TItem: class, IContentBase { TItem GetById(Guid key); - Attempt Save(IEnumerable contents, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + Attempt Save(IEnumerable contents, int userId = Constants.Security.SuperUserId); } /// diff --git a/src/Umbraco.Core/Services/IDataTypeService.cs b/src/Umbraco.Core/Services/IDataTypeService.cs index f8e91d07d8..c7b13c04e1 100644 --- a/src/Umbraco.Core/Services/IDataTypeService.cs +++ b/src/Umbraco.Core/Services/IDataTypeService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; @@ -68,15 +68,7 @@ namespace Umbraco.Cms.Core.Services /// to save /// Id of the user issuing the save void Save(IEnumerable dataTypeDefinitions, int userId = Constants.Security.SuperUserId); - - /// - /// Saves a collection of - /// - /// to save - /// Id of the user issuing the save - /// Boolean indicating whether or not to raise events - void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents); - + /// /// Deletes an /// diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index fc4d4fd612..6c9bf21ecc 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using Umbraco.Cms.Core.Models; @@ -198,16 +198,14 @@ namespace Umbraco.Cms.Core.Services /// /// The to save /// Id of the User saving the Media - /// Optional boolean indicating whether or not to raise events. - Attempt Save(IMedia media, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + Attempt Save(IMedia media, int userId = Constants.Security.SuperUserId); /// /// Saves a collection of objects /// /// Collection of to save /// Id of the User saving the Media - /// Optional boolean indicating whether or not to raise events. - Attempt Save(IEnumerable medias, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + Attempt Save(IEnumerable medias, int userId = Constants.Security.SuperUserId); /// /// Gets an object by its 'UniqueId' @@ -304,7 +302,7 @@ namespace Umbraco.Cms.Core.Services /// /// /// True if sorting succeeded, otherwise False - bool Sort(IEnumerable items, int userId = Constants.Security.SuperUserId, bool raiseEvents = true); + bool Sort(IEnumerable items, int userId = Constants.Security.SuperUserId); /// /// Creates an object using the alias of the diff --git a/src/Umbraco.Core/Services/IMemberGroupService.cs b/src/Umbraco.Core/Services/IMemberGroupService.cs index 16028ded3f..0b72906c2f 100644 --- a/src/Umbraco.Core/Services/IMemberGroupService.cs +++ b/src/Umbraco.Core/Services/IMemberGroupService.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.Services IMemberGroup GetById(Guid id); IEnumerable GetByIds(IEnumerable ids); IMemberGroup GetByName(string name); - void Save(IMemberGroup memberGroup, bool raiseEvents = true); + void Save(IMemberGroup memberGroup); void Delete(IMemberGroup memberGroup); } } diff --git a/src/Umbraco.Core/Services/IMembershipMemberService.cs b/src/Umbraco.Core/Services/IMembershipMemberService.cs index c91eba5250..6093c0a4fe 100644 --- a/src/Umbraco.Core/Services/IMembershipMemberService.cs +++ b/src/Umbraco.Core/Services/IMembershipMemberService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Membership; @@ -125,18 +125,14 @@ namespace Umbraco.Cms.Core.Services /// /// An can be of type or /// or to Save - /// Optional parameter to raise events. - /// Default is True otherwise set to False to not raise events - void Save(T entity, bool raiseEvents = true); + void Save(T entity); /// /// Saves a list of objects /// /// An can be of type or /// to save - /// Optional parameter to raise events. - /// Default is True otherwise set to False to not raise events - void Save(IEnumerable entities, bool raiseEvents = true); + void Save(IEnumerable entities); /// /// Finds a list of objects by a partial email string diff --git a/src/Umbraco.Core/Services/IUserService.cs b/src/Umbraco.Core/Services/IUserService.cs index a4af73e084..25ce49b65d 100644 --- a/src/Umbraco.Core/Services/IUserService.cs +++ b/src/Umbraco.Core/Services/IUserService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Models.Membership; using Umbraco.Cms.Core.Persistence.Querying; @@ -243,9 +243,7 @@ namespace Umbraco.Cms.Core.Services /// If null than no changes are made to the users who are assigned to this group, however if a value is passed in /// than all users will be removed from this group and only these users will be added /// - /// Optional parameter to raise events. - /// Default is True otherwise set to False to not raise events - void Save(IUserGroup userGroup, int[] userIds = null, bool raiseEvents = true); + void Save(IUserGroup userGroup, int[] userIds = null); /// /// Deletes a UserGroup diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 3d4c6dfbbe..b6fa009d2e 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -381,8 +381,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - Attempt IContentServiceBase.Save(IEnumerable contents, int userId, - bool raiseEvents) => Attempt.Succeed(Save(contents, userId, raiseEvents)); + Attempt IContentServiceBase.Save(IEnumerable contents, int userId) => Attempt.Succeed(Save(contents, userId)); /// /// Gets objects by Ids @@ -756,7 +755,7 @@ namespace Umbraco.Cms.Core.Services.Implement #region Save, Publish, Unpublish /// - public OperationResult Save(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public OperationResult Save(IContent content, int userId = Cms.Core.Constants.Security.SuperUserId) { PublishedState publishedState = content.PublishedState; if (publishedState != PublishedState.Published && publishedState != PublishedState.Unpublished) @@ -774,7 +773,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new ContentSavingNotification(content, eventMessages); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Cancel(eventMessages); @@ -800,10 +799,12 @@ namespace Umbraco.Cms.Core.Services.Implement _documentRepository.Save(content); - if (raiseEvents) - { - scope.Notifications.Publish(new ContentSavedNotification(content, eventMessages).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new ContentSavedNotification(content, eventMessages).WithStateFrom(savingNotification)); + + // TODO: we had code here to FORCE that this event can never be suppressed. But that just doesn't make a ton of sense?! + // I understand that if its suppressed that the caches aren't updated, but that would be expected. If someone + // is supressing events then I think it's expected that nothing will happen. They are probably doing it for perf + // reasons like bulk import and in those cases we don't want this occuring. scope.Notifications.Publish(new ContentTreeChangeNotification(content, TreeChangeTypes.RefreshNode, eventMessages)); if (culturesChanging != null) @@ -823,7 +824,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public OperationResult Save(IEnumerable contents, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public OperationResult Save(IEnumerable contents, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages eventMessages = EventMessagesFactory.Get(); IContent[] contentsA = contents.ToArray(); @@ -831,7 +832,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new ContentSavingNotification(contentsA, eventMessages); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Cancel(eventMessages); @@ -850,11 +851,10 @@ namespace Umbraco.Cms.Core.Services.Implement _documentRepository.Save(content); } - if (raiseEvents) - { - scope.Notifications.Publish(new ContentSavedNotification(contentsA, eventMessages).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new ContentSavedNotification(contentsA, eventMessages).WithStateFrom(savingNotification)); + // TODO: See note above about supressing events scope.Notifications.Publish(new ContentTreeChangeNotification(contentsA, TreeChangeTypes.RefreshNode, eventMessages)); + Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Saved multiple content"); scope.Complete(); @@ -864,7 +864,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public PublishResult SaveAndPublish(IContent content, string culture = "*", int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public PublishResult SaveAndPublish(IContent content, string culture = "*", int userId = Cms.Core.Constants.Security.SuperUserId) { var evtMsgs = EventMessagesFactory.Get(); @@ -912,14 +912,14 @@ namespace Umbraco.Cms.Core.Services.Implement // we don't care about the response here, this response will be rechecked below but we need to set the culture info values now. content.PublishCulture(impact); - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId); scope.Complete(); return result; } } /// - public PublishResult SaveAndPublish(IContent content, string[] cultures, int userId = 0, bool raiseEvents = true) + public PublishResult SaveAndPublish(IContent content, string[] cultures, int userId = 0) { if (content == null) throw new ArgumentNullException(nameof(content)); if (cultures == null) throw new ArgumentNullException(nameof(cultures)); @@ -938,7 +938,7 @@ namespace Umbraco.Cms.Core.Services.Implement var evtMsgs = EventMessagesFactory.Get(); var savingNotification = new ContentSavingNotification(content, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { return new PublishResult(PublishResultType.FailedPublishCancelledByEvent, evtMsgs, content); } @@ -948,7 +948,7 @@ namespace Umbraco.Cms.Core.Services.Implement if (cultures.Length == 0 && !varies) { //no cultures specified and doesn't vary, so publish it, else nothing to publish - return SaveAndPublish(content, userId: userId, raiseEvents: raiseEvents); + return SaveAndPublish(content, userId: userId); } if (cultures.Any(x => x == null || x == "*")) @@ -959,9 +959,11 @@ namespace Umbraco.Cms.Core.Services.Implement // publish the culture(s) // we don't care about the response here, this response will be rechecked below but we need to set the culture info values now. foreach (var impact in impacts) + { content.PublishCulture(impact); + } - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId); scope.Complete(); return result; } @@ -1067,7 +1069,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// The document is *always* saved, even when publishing fails. /// internal PublishResult CommitDocumentChanges(IContent content, - int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + int userId = Cms.Core.Constants.Security.SuperUserId) { using (var scope = ScopeProvider.CreateScope()) { @@ -1083,7 +1085,7 @@ namespace Umbraco.Cms.Core.Services.Implement var allLangs = _languageRepository.GetMany().ToList(); - var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId, raiseEvents); + var result = CommitDocumentChangesInternal(scope, content, evtMsgs, allLangs, savingNotification.State, userId); scope.Complete(); return result; } @@ -2383,7 +2385,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// Result indicating what action was taken when handling the command. - public OperationResult Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public OperationResult Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId) { var evtMsgs = EventMessagesFactory.Get(); @@ -2394,7 +2396,7 @@ namespace Umbraco.Cms.Core.Services.Implement { scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); - var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents); + var ret = Sort(scope, itemsA, userId, evtMsgs); scope.Complete(); return ret; } @@ -2412,7 +2414,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// Result indicating what action was taken when handling the command. - public OperationResult Sort(IEnumerable ids, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public OperationResult Sort(IEnumerable ids, int userId = Cms.Core.Constants.Security.SuperUserId) { var evtMsgs = EventMessagesFactory.Get(); @@ -2424,29 +2426,27 @@ namespace Umbraco.Cms.Core.Services.Implement scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); var itemsA = GetByIds(idsA).ToArray(); - var ret = Sort(scope, itemsA, userId, evtMsgs, raiseEvents); + var ret = Sort(scope, itemsA, userId, evtMsgs); scope.Complete(); return ret; } } - private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages eventMessages, bool raiseEvents) + private OperationResult Sort(IScope scope, IContent[] itemsA, int userId, EventMessages eventMessages) { var sortingNotification = new ContentSortingNotification(itemsA, eventMessages); var savingNotification = new ContentSavingNotification(itemsA, eventMessages); - if (raiseEvents) - { - // raise cancelable sorting event - if (scope.Notifications.PublishCancelable(sortingNotification)) - { - return OperationResult.Cancel(eventMessages); - } - // raise cancelable saving event - if (scope.Notifications.PublishCancelable(savingNotification)) - { - return OperationResult.Cancel(eventMessages); - } + // raise cancelable sorting event + if (scope.Notifications.PublishCancelable(sortingNotification)) + { + return OperationResult.Cancel(eventMessages); + } + + // raise cancelable saving event + if (scope.Notifications.PublishCancelable(savingNotification)) + { + return OperationResult.Cancel(eventMessages); } var published = new List(); @@ -2479,16 +2479,13 @@ namespace Umbraco.Cms.Core.Services.Implement _documentRepository.Save(content); } - if (raiseEvents) - { - //first saved, then sorted - scope.Notifications.Publish(new ContentSavedNotification(itemsA, eventMessages).WithStateFrom(savingNotification)); - scope.Notifications.Publish(new ContentSortedNotification(itemsA, eventMessages).WithStateFrom(sortingNotification)); - } + //first saved, then sorted + scope.Notifications.Publish(new ContentSavedNotification(itemsA, eventMessages).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new ContentSortedNotification(itemsA, eventMessages).WithStateFrom(sortingNotification)); scope.Notifications.Publish(new ContentTreeChangeNotification(saved, TreeChangeTypes.RefreshNode, eventMessages)); - if (raiseEvents && published.Any()) + if (published.Any()) { scope.Notifications.Publish(new ContentPublishedNotification(published, eventMessages)); } diff --git a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs index b20be692df..50caca0ec8 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/DataTypeService.cs @@ -434,18 +434,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// to save /// Id of the user issuing the save - public void Save(IEnumerable dataTypeDefinitions, int userId = Cms.Core.Constants.Security.SuperUserId) - { - Save(dataTypeDefinitions, userId, true); - } - - /// - /// Saves a collection of - /// - /// to save - /// Id of the user issuing the save - /// Boolean indicating whether or not to raise events - public void Save(IEnumerable dataTypeDefinitions, int userId, bool raiseEvents) + public void Save(IEnumerable dataTypeDefinitions, int userId) { var evtMsgs = EventMessagesFactory.Get(); var dataTypeDefinitionsA = dataTypeDefinitions.ToArray(); @@ -453,7 +442,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { var savingDataTypeNotification = new DataTypeSavingNotification(dataTypeDefinitions, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingDataTypeNotification)) + if (scope.Notifications.PublishCancelable(savingDataTypeNotification)) { scope.Complete(); return; @@ -465,10 +454,8 @@ namespace Umbraco.Cms.Core.Services.Implement _dataTypeRepository.Save(dataTypeDefinition); } - if (raiseEvents) - { - scope.Notifications.Publish(new DataTypeSavedNotification(dataTypeDefinitions, evtMsgs).WithStateFrom(savingDataTypeNotification)); - } + scope.Notifications.Publish(new DataTypeSavedNotification(dataTypeDefinitions, evtMsgs).WithStateFrom(savingDataTypeNotification)); + Audit(AuditType.Save, userId, -1); scope.Complete(); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 34d1c2a5ce..bdf672ce7a 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -654,14 +654,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// The to save /// Id of the User saving the Media /// Optional boolean indicating whether or not to raise events. - public Attempt Save(IMedia media, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public Attempt Save(IMedia media, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages eventMessages = EventMessagesFactory.Get(); using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new MediaSavingNotification(media, eventMessages); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(eventMessages); @@ -685,10 +685,8 @@ namespace Umbraco.Cms.Core.Services.Implement } _mediaRepository.Save(media); - if (raiseEvents) - { - scope.Notifications.Publish(new MediaSavedNotification(media, eventMessages).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MediaSavedNotification(media, eventMessages).WithStateFrom(savingNotification)); + // TODO: See note about supressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(media, TreeChangeTypes.RefreshNode, eventMessages)); Audit(AuditType.Save, userId, media.Id); @@ -704,7 +702,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// Collection of to save /// Id of the User saving the Media /// Optional boolean indicating whether or not to raise events. - public Attempt Save(IEnumerable medias, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public Attempt Save(IEnumerable medias, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages messages = EventMessagesFactory.Get(); IMedia[] mediasA = medias.ToArray(); @@ -712,7 +710,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new MediaSavingNotification(mediasA, messages); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return OperationResult.Attempt.Cancel(messages); @@ -731,10 +729,8 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); } - if (raiseEvents) - { - scope.Notifications.Publish(new MediaSavedNotification(mediasA, messages).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MediaSavedNotification(mediasA, messages).WithStateFrom(savingNotification)); + // TODO: See note about supressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(treeChanges, messages)); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); @@ -1095,7 +1091,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// True if sorting succeeded, otherwise False - public bool Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId, bool raiseEvents = true) + public bool Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId) { IMedia[] itemsA = items.ToArray(); if (itemsA.Length == 0) @@ -1108,7 +1104,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new MediaSavingNotification(itemsA, messages); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return false; @@ -1135,10 +1131,8 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); } - if (raiseEvents) - { - scope.Notifications.Publish(new MediaSavedNotification(itemsA, messages).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MediaSavedNotification(itemsA, messages).WithStateFrom(savingNotification)); + // TODO: See note about supressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(saved, TreeChangeTypes.RefreshNode, messages)); Audit(AuditType.Sort, userId, 0); diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs index 096ff164a0..9d68415ad5 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberGroupService.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Core.Services.Implement } } - public void Save(IMemberGroup memberGroup, bool raiseEvents = true) + public void Save(IMemberGroup memberGroup) { if (string.IsNullOrWhiteSpace(memberGroup.Name)) { @@ -76,7 +76,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { var savingNotification = new MemberGroupSavingNotification(memberGroup, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -85,10 +85,7 @@ namespace Umbraco.Cms.Core.Services.Implement _memberGroupRepository.Save(memberGroup); scope.Complete(); - if (raiseEvents) - { - scope.Notifications.Publish(new MemberGroupSavedNotification(memberGroup, evtMsgs).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MemberGroupSavedNotification(memberGroup, evtMsgs).WithStateFrom(savingNotification)); } } diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index 94476ff1e1..f6aac98682 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -767,7 +767,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void Save(IMember member, bool raiseEvents = true) + public void Save(IMember member) { // trimming username and email to make sure we have no trailing space member.Username = member.Username.Trim(); @@ -778,7 +778,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (IScope scope = ScopeProvider.CreateScope()) { var savingNotification = new MemberSavingNotification(member, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -793,10 +793,7 @@ namespace Umbraco.Cms.Core.Services.Implement _memberRepository.Save(member); - if (raiseEvents) - { - scope.Notifications.Publish(new MemberSavedNotification(member, evtMsgs).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MemberSavedNotification(member, evtMsgs).WithStateFrom(savingNotification)); Audit(AuditType.Save, 0, member.Id); @@ -805,7 +802,7 @@ namespace Umbraco.Cms.Core.Services.Implement } /// - public void Save(IEnumerable members, bool raiseEvents = true) + public void Save(IEnumerable members) { var membersA = members.ToArray(); @@ -814,7 +811,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { var savingNotification = new MemberSavingNotification(membersA, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -831,10 +828,8 @@ namespace Umbraco.Cms.Core.Services.Implement _memberRepository.Save(member); } - if (raiseEvents) - { - scope.Notifications.Publish(new MemberSavedNotification(membersA, evtMsgs).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new MemberSavedNotification(membersA, evtMsgs).WithStateFrom(savingNotification)); + Audit(AuditType.Save, 0, -1, "Save multiple Members"); scope.Complete(); diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index 743b4816da..978dbb5bda 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Data.Common; using System.Globalization; @@ -273,16 +273,14 @@ namespace Umbraco.Cms.Core.Services.Implement /// Saves an /// /// to Save - /// Optional parameter to raise events. - /// Default is True otherwise set to False to not raise events - public void Save(IUser entity, bool raiseEvents = true) + public void Save(IUser entity) { var evtMsgs = EventMessagesFactory.Get(); using (var scope = ScopeProvider.CreateScope()) { var savingNotification = new UserSavingNotification(entity, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -297,10 +295,7 @@ namespace Umbraco.Cms.Core.Services.Implement try { _userRepository.Save(entity); - if (raiseEvents) - { - scope.Notifications.Publish(new UserSavedNotification(entity, evtMsgs).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new UserSavedNotification(entity, evtMsgs).WithStateFrom(savingNotification)); scope.Complete(); } @@ -321,9 +316,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// Saves a list of objects /// /// to save - /// Optional parameter to raise events. - /// Default is True otherwise set to False to not raise events - public void Save(IEnumerable entities, bool raiseEvents = true) + public void Save(IEnumerable entities) { var evtMsgs = EventMessagesFactory.Get(); @@ -332,7 +325,7 @@ namespace Umbraco.Cms.Core.Services.Implement using (var scope = ScopeProvider.CreateScope()) { var savingNotification = new UserSavingNotification(entitiesA, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -350,10 +343,7 @@ namespace Umbraco.Cms.Core.Services.Implement } - if (raiseEvents) - { - scope.Notifications.Publish(new UserSavedNotification(entitiesA, evtMsgs).WithStateFrom(savingNotification)); - } + scope.Notifications.Publish(new UserSavedNotification(entitiesA, evtMsgs).WithStateFrom(savingNotification)); //commit the whole lot in one go scope.Complete(); @@ -818,7 +808,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Optional parameter to raise events. /// Default is True otherwise set to False to not raise events - public void Save(IUserGroup userGroup, int[] userIds = null, bool raiseEvents = true) + public void Save(IUserGroup userGroup, int[] userIds = null) { var evtMsgs = EventMessagesFactory.Get(); @@ -843,7 +833,7 @@ namespace Umbraco.Cms.Core.Services.Implement // this is the default/expected notification for the IUserGroup entity being saved var savingNotification = new UserGroupSavingNotification(userGroup, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingNotification)) + if (scope.Notifications.PublishCancelable(savingNotification)) { scope.Complete(); return; @@ -851,7 +841,7 @@ namespace Umbraco.Cms.Core.Services.Implement // this is an additional notification for special auditing var savingUserGroupWithUsersNotification = new UserGroupWithUsersSavingNotification(userGroupWithUsers, evtMsgs); - if (raiseEvents && scope.Notifications.PublishCancelable(savingUserGroupWithUsersNotification)) + if (scope.Notifications.PublishCancelable(savingUserGroupWithUsersNotification)) { scope.Complete(); return; @@ -859,11 +849,8 @@ namespace Umbraco.Cms.Core.Services.Implement _userGroupRepository.AddOrUpdateGroupWithUsers(userGroup, userIds); - if (raiseEvents) - { - scope.Notifications.Publish(new UserGroupSavedNotification(userGroup, evtMsgs).WithStateFrom(savingNotification)); - scope.Notifications.Publish(new UserGroupWithUsersSavedNotification(userGroupWithUsers, evtMsgs).WithStateFrom(savingUserGroupWithUsersNotification)); - } + scope.Notifications.Publish(new UserGroupSavedNotification(userGroup, evtMsgs).WithStateFrom(savingNotification)); + scope.Notifications.Publish(new UserGroupWithUsersSavedNotification(userGroupWithUsers, evtMsgs).WithStateFrom(savingUserGroupWithUsersNotification)); scope.Complete(); } diff --git a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj index d759c8da9b..712323656d 100644 --- a/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj +++ b/src/Umbraco.Infrastructure/Umbraco.Infrastructure.csproj @@ -111,10 +111,6 @@ - - - - From bc2aab5e67f6832a45fcf70be6b29b2c0a450894 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 09:49:27 -0600 Subject: [PATCH 034/102] fix benchmark build --- src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs index 1bd875142d..d11bd6920d 100644 --- a/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs +++ b/src/Umbraco.Tests.Benchmarks/TypeLoaderBenchmarks.cs @@ -34,7 +34,7 @@ namespace Umbraco.Tests.Benchmarks // populate the cache cache.Insert( - TypeLoader.CacheKey, + _typeLoader1.CacheKey, GetCache, TimeSpan.FromDays(1)); From e04d2a84054266c8f1b16afc494c84d83925dcc9 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 09:58:19 -0600 Subject: [PATCH 035/102] adds debug logging --- src/Umbraco.Core/Composing/TypeLoader.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 5e6808fb13..6af8cb5ced 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -96,6 +96,8 @@ namespace Umbraco.Cms.Core.Composing //if they have changed, we need to write the new file if (RequiresRescanning) { + _logger.LogDebug("Plugin types are being re-scanned. Cached hash value: {CachedHash}, Current hash value: {CurrentHash}", CachedAssembliesHash, CurrentAssembliesHash); + // if the hash has changed, clear out the persisted list no matter what, this will force // rescanning of all types including lazy ones. // http://issues.umbraco.org/issue/U4-4789 From a72797e8c9f6c41983412b74b0397363a7ef0b35 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 10:07:46 -0600 Subject: [PATCH 036/102] handles xml data consistently between partial views and partial view macros --- .../Packaging/PackageDataInstallation.cs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index 91e3829806..32ce247c2b 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -1276,12 +1276,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging { throw new InvalidOperationException("No path attribute found"); } - var contents = element.Value; - if (contents.IsNullOrWhiteSpace()) - { - throw new InvalidOperationException("No content found for partial view"); - } - + var contents = element.Value ?? string.Empty; + var physicalPath = _hostingEnvironment.MapPathContentRoot(path); // TODO: Do we overwrite? IMO I don't think so since these will be views a user will change. if (!System.IO.File.Exists(physicalPath)) @@ -1412,9 +1408,9 @@ namespace Umbraco.Cms.Infrastructure.Packaging { var path = partialViewXml.AttributeValue("path"); - if (path.IsNullOrWhiteSpace()) + if (path == null) { - continue; + throw new InvalidOperationException("No path attribute found"); } IPartialView partialView = _fileService.GetPartialView(path); @@ -1422,12 +1418,8 @@ namespace Umbraco.Cms.Infrastructure.Packaging // only update if it doesn't exist if (partialView == null) { - var content = partialViewXml.Value; - if (content == null) - { - continue; - } - + var content = partialViewXml.Value ?? string.Empty; + partialView = new PartialView(PartialViewType.PartialView, path) { Content = content }; _fileService.SavePartialView(partialView, userId); result.Add(partialView); From b912f1361af67869d2fd51ad2a0e014e80456552 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 10:08:54 -0600 Subject: [PATCH 037/102] changes parameter to path instead of name. --- src/Umbraco.Core/Services/IFileService.cs | 6 +++--- .../Services/Implement/FileService.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Services/IFileService.cs b/src/Umbraco.Core/Services/IFileService.cs index 903603c415..fcf5f2b2f3 100644 --- a/src/Umbraco.Core/Services/IFileService.cs +++ b/src/Umbraco.Core/Services/IFileService.cs @@ -77,14 +77,14 @@ namespace Umbraco.Cms.Core.Services /// Gets a list of all objects /// /// An enumerable list of objects - IEnumerable GetStylesheets(params string[] names); + IEnumerable GetStylesheets(params string[] paths); /// /// Gets a object by its name /// - /// Name of the stylesheet incl. extension + /// Path of the stylesheet incl. extension /// A object - IStylesheet GetStylesheet(string name); + IStylesheet GetStylesheet(string path); /// /// Saves a diff --git a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs index 64ae7c5dfc..ed7bdc9c29 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/FileService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/FileService.cs @@ -56,11 +56,11 @@ namespace Umbraco.Cms.Core.Services.Implement #region Stylesheets /// - public IEnumerable GetStylesheets(params string[] names) + public IEnumerable GetStylesheets(params string[] paths) { using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - return _stylesheetRepository.GetMany(names); + return _stylesheetRepository.GetMany(paths); } } From 720aa6202a78c001b2e85c8c10799278e37c83ba Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Tue, 13 Jul 2021 02:09:15 +1000 Subject: [PATCH 038/102] Update src/Umbraco.Web.BackOffice/Controllers/EntityController.cs Co-authored-by: Mole --- src/Umbraco.Web.BackOffice/Controllers/EntityController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs index fdfdb90210..fcd0c81891 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/EntityController.cs @@ -1139,7 +1139,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers case UmbracoEntityTypes.PartialView: if (!postFilter.IsNullOrWhiteSpace()) - throw new NotSupportedException("Filtering on scripts is not currently supported"); + throw new NotSupportedException("Filtering on partial views is not currently supported"); return _fileService.GetPartialViews().Select(MapEntities()); From ff9645c289fc48b0bf6c07f6e899e9b91fc4c724 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 10:20:59 -0600 Subject: [PATCH 039/102] Updates usages. --- .../Packaging/PackageDataInstallation.cs | 2 +- .../Umbraco.Core/Services/SectionServiceTests.cs | 11 ++++++++--- .../Security/MemberManagerTests.cs | 2 +- .../Security/MemberRoleStoreTests.cs | 16 +++++----------- .../Security/MemberUserStoreTests.cs | 14 +++++--------- .../Controllers/MemberControllerUnitTests.cs | 2 +- 6 files changed, 21 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs index e680471486..2fc562b00d 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs @@ -1030,7 +1030,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging if (dataTypes.Count > 0) { - _dataTypeService.Save(dataTypes, userId, true); + _dataTypeService.Save(dataTypes, userId); } return dataTypes; diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs index b9b3d0569e..1e9a80faf6 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs @@ -1,11 +1,13 @@ // Copyright (c) Umbraco. // See LICENSE for more details. +using System; using System.Linq; using System.Threading; using NUnit.Framework; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Models.Membership; +using Umbraco.Cms.Core.Scoping; using Umbraco.Cms.Core.Services; using Umbraco.Cms.Tests.Common.Testing; using Umbraco.Cms.Tests.Integration.Testing; @@ -39,6 +41,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services private IUser CreateTestUser() { + using IScope scope = ScopeProvider.CreateScope(autoComplete: true); + using IDisposable _ = scope.Notifications.Supress(); + var globalSettings = new GlobalSettings(); var user = new User(globalSettings) { @@ -46,7 +51,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services Username = "testUser", Email = "testuser@test.com", }; - UserService.Save(user, false); + UserService.Save(user); var userGroupA = new UserGroup(ShortStringHelper) { @@ -57,7 +62,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services userGroupA.AddAllowedSection("settings"); // TODO: This is failing the test - UserService.Save(userGroupA, new[] { user.Id }, false); + UserService.Save(userGroupA, new[] { user.Id }); var userGroupB = new UserGroup(ShortStringHelper) { @@ -66,7 +71,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services }; userGroupB.AddAllowedSection("settings"); userGroupB.AddAllowedSection("member"); - UserService.Save(userGroupB, new[] { user.Id }, false); + UserService.Save(userGroupB, new[] { user.Id }); return UserService.GetUserById(user.Id); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs index c8f90050e2..86e6462472 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs @@ -246,7 +246,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security private void MockMemberServiceForCreateMember(IMember fakeMember) { _mockMemberService.Setup(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(fakeMember); - _mockMemberService.Setup(x => x.Save(fakeMember, false)); + _mockMemberService.Setup(x => x.Save(fakeMember)); } } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberRoleStoreTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberRoleStoreTests.cs index 412de11a9e..a82534c940 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberRoleStoreTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberRoleStoreTests.cs @@ -62,9 +62,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security IMemberGroup mockMemberGroup = Mock.Of(m => m.Name == "fakeGroupName" && m.CreatorId == 77); - bool raiseEvents = false; - - _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup, raiseEvents)); + _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup)); // act IdentityResult identityResult = await sut.CreateAsync(fakeRole, fakeCancellationToken); @@ -72,7 +70,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security // assert Assert.IsTrue(identityResult.Succeeded); Assert.IsTrue(!identityResult.Errors.Any()); - _mockMemberGroupService.Verify(x => x.Save(It.IsAny(), It.IsAny())); + _mockMemberGroupService.Verify(x => x.Save(It.IsAny())); } [Test] @@ -86,10 +84,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security IMemberGroup mockMemberGroup = Mock.Of(m => m.Name == "fakeGroupName" && m.CreatorId == 777); - bool raiseEvents = false; - _mockMemberGroupService.Setup(x => x.GetById(777)).Returns(mockMemberGroup); - _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup, raiseEvents)); + _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup)); // act IdentityResult identityResult = await sut.UpdateAsync(fakeRole, fakeCancellationToken); @@ -111,10 +107,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security IMemberGroup mockMemberGroup = Mock.Of(m => m.Name == "fakeGroupName" && m.CreatorId == 777); - bool raiseEvents = false; - _mockMemberGroupService.Setup(x => x.GetById(777)).Returns(mockMemberGroup); - _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup, raiseEvents)); + _mockMemberGroupService.Setup(x => x.Save(mockMemberGroup)); // act IdentityResult identityResult = await sut.UpdateAsync(fakeRole, fakeCancellationToken); @@ -122,7 +116,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security // assert Assert.IsTrue(identityResult.Succeeded); Assert.IsTrue(!identityResult.Errors.Any()); - _mockMemberGroupService.Verify(x => x.Save(It.IsAny(), It.IsAny())); + _mockMemberGroupService.Verify(x => x.Save(It.IsAny())); _mockMemberGroupService.Verify(x => x.GetById(777)); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs index 3fdb3c27f9..7a63ff4bc4 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security m.HasIdentity == false); _mockMemberService.Setup(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockMember); - _mockMemberService.Setup(x => x.Save(mockMember, It.IsAny())); + _mockMemberService.Setup(x => x.Save(mockMember)); // act IdentityResult actual = await sut.CreateAsync(null); @@ -118,10 +118,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security m.ContentTypeAlias == fakeMemberType.Alias && m.HasIdentity == true); - bool raiseEvents = false; - _mockMemberService.Setup(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(mockMember); - _mockMemberService.Setup(x => x.Save(mockMember, raiseEvents)); + _mockMemberService.Setup(x => x.Save(mockMember)); // act IdentityResult identityResult = await sut.CreateAsync(fakeUser, CancellationToken.None); @@ -130,7 +128,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security Assert.IsTrue(identityResult.Succeeded); Assert.IsTrue(!identityResult.Errors.Any()); _mockMemberService.Verify(x => x.CreateMember(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())); - _mockMemberService.Verify(x => x.Save(mockMember, It.IsAny())); + _mockMemberService.Verify(x => x.Save(mockMember)); } [Test] @@ -175,9 +173,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security m.RawPasswordValue == "xyz" && m.SecurityStamp == "xyz"); - bool raiseEvents = false; - - _mockMemberService.Setup(x => x.Save(mockMember, raiseEvents)); + _mockMemberService.Setup(x => x.Save(mockMember)); _mockMemberService.Setup(x => x.GetById(123)).Returns(mockMember); // act @@ -200,7 +196,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security Assert.AreEqual(fakeUser.SecurityStamp, mockMember.SecurityStamp); Assert.AreNotEqual(DateTime.MinValue, mockMember.EmailConfirmedDate.Value); - _mockMemberService.Verify(x => x.Save(mockMember, It.IsAny())); + _mockMemberService.Verify(x => x.Save(mockMember)); _mockMemberService.Verify(x => x.GetById(123)); _mockMemberService.Verify(x => x.ReplaceRoles(new[] { 123 }, new[] { "role1", "role2" })); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs index 8006bda3a4..e017844b2b 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -417,7 +417,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers Mock.Get(umbracoMembersUserManager) .Verify(u => u.AddToRolesAsync(membersIdentityUser, new[] { roleName })); Mock.Get(memberService) - .Verify(m => m.Save(It.IsAny(), true)); + .Verify(m => m.Save(It.IsAny())); AssertMemberDisplayPropertiesAreEqual(memberDisplay, result.Value); } From a15eab1e6b789c0831981a2ac5968ac0da5a7305 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 10:43:50 -0600 Subject: [PATCH 040/102] cleanup --- src/Umbraco.Core/Services/IMediaService.cs | 1 - .../Services/Implement/ContentService.cs | 13 +++---------- .../Services/Implement/MediaService.cs | 3 --- .../Services/Implement/UserService.cs | 1 - 4 files changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Umbraco.Core/Services/IMediaService.cs b/src/Umbraco.Core/Services/IMediaService.cs index 6c9bf21ecc..19b509134e 100644 --- a/src/Umbraco.Core/Services/IMediaService.cs +++ b/src/Umbraco.Core/Services/IMediaService.cs @@ -300,7 +300,6 @@ namespace Umbraco.Cms.Core.Services /// /// /// - /// /// True if sorting succeeded, otherwise False bool Sort(IEnumerable items, int userId = Constants.Security.SuperUserId); diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index b6fa009d2e..609a6bd426 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -379,7 +379,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// /// Attempt IContentServiceBase.Save(IEnumerable contents, int userId) => Attempt.Succeed(Save(contents, userId)); @@ -1099,7 +1098,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// /// /// /// @@ -1113,8 +1111,8 @@ namespace Umbraco.Cms.Core.Services.Implement private PublishResult CommitDocumentChangesInternal(IScope scope, IContent content, EventMessages eventMessages, IReadOnlyCollection allLangs, IDictionary notificationState, - int userId = Cms.Core.Constants.Security.SuperUserId, - bool raiseEvents = true, bool branchOne = false, bool branchRoot = false) + int userId = Constants.Security.SuperUserId, + bool branchOne = false, bool branchRoot = false) { if (scope == null) { @@ -1271,10 +1269,7 @@ namespace Umbraco.Cms.Core.Services.Implement SaveDocument(content); // raise the Saved event, always - if (raiseEvents) - { - scope.Notifications.Publish(new ContentSavedNotification(content, eventMessages).WithState(notificationState)); - } + scope.Notifications.Publish(new ContentSavedNotification(content, eventMessages).WithState(notificationState)); if (unpublishing) // we have tried to unpublish - won't happen in a branch { @@ -2383,7 +2378,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// /// Result indicating what action was taken when handling the command. public OperationResult Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId) { @@ -2412,7 +2406,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// /// Result indicating what action was taken when handling the command. public OperationResult Sort(IEnumerable ids, int userId = Cms.Core.Constants.Security.SuperUserId) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index bdf672ce7a..501f675fdc 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -653,7 +653,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// The to save /// Id of the User saving the Media - /// Optional boolean indicating whether or not to raise events. public Attempt Save(IMedia media, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages eventMessages = EventMessagesFactory.Get(); @@ -701,7 +700,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// Collection of to save /// Id of the User saving the Media - /// Optional boolean indicating whether or not to raise events. public Attempt Save(IEnumerable medias, int userId = Cms.Core.Constants.Security.SuperUserId) { EventMessages messages = EventMessagesFactory.Get(); @@ -1089,7 +1087,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// /// - /// /// True if sorting succeeded, otherwise False public bool Sort(IEnumerable items, int userId = Cms.Core.Constants.Security.SuperUserId) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs index 978dbb5bda..0500c18bdd 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/UserService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/UserService.cs @@ -806,7 +806,6 @@ namespace Umbraco.Cms.Core.Services.Implement /// If null than no changes are made to the users who are assigned to this group, however if a value is passed in /// than all users will be removed from this group and only these users will be added /// - /// Optional parameter to raise events. /// Default is True otherwise set to False to not raise events public void Save(IUserGroup userGroup, int[] userIds = null) { From e8bc2f6dac663b156d064e66c5e14ea012757a96 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 10:58:50 -0600 Subject: [PATCH 041/102] adds tests --- .../Scoping/SupressNotificationsTests.cs | 79 +++++++++++++++++++ .../Services/UserServiceTests.cs | 1 + 2 files changed, 80 insertions(+) create mode 100644 src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs new file mode 100644 index 0000000000..1481f303a7 --- /dev/null +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs @@ -0,0 +1,79 @@ +// Copyright (c) Umbraco. +// See LICENSE for more details. + +using System; +using NUnit.Framework; +using Umbraco.Cms.Core.DependencyInjection; +using Umbraco.Cms.Core.Events; +using Umbraco.Cms.Core.Models; +using Umbraco.Cms.Core.Notifications; +using Umbraco.Cms.Core.Scoping; +using Umbraco.Cms.Core.Services; +using Umbraco.Cms.Tests.Common.Builders; +using Umbraco.Cms.Tests.Common.Testing; +using Umbraco.Cms.Tests.Integration.Testing; + +namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class SupressNotificationsTests : UmbracoIntegrationTest + { + private IContentService ContentService => GetRequiredService(); + + private IContentTypeService ContentTypeService => GetRequiredService(); + + protected override void CustomTestSetup(IUmbracoBuilder builder) + { + base.CustomTestSetup(builder); + + builder.AddNotificationHandler(); + builder.AddNotificationHandler(); + } + + [Test] + public void GivenScope_WhenNotificationsSuppressed_ThenNotificationsDoNotExecute() + { + using IScope scope = ScopeProvider.CreateScope(autoComplete: true); + using IDisposable _ = scope.Notifications.Supress(); + + ContentType contentType = ContentTypeBuilder.CreateBasicContentType(); + ContentTypeService.Save(contentType); + Content content = ContentBuilder.CreateBasicContent(contentType); + ContentService.Save(content); + + Assert.Pass(); + } + + [Test] + public void GivenNestedScope_WhenOuterNotificationsSuppressed_ThenNotificationsDoNotExecute() + { + using (IScope parentScope = ScopeProvider.CreateScope(autoComplete: true)) + { + using IDisposable _ = parentScope.Notifications.Supress(); + + using (IScope childScope = ScopeProvider.CreateScope(autoComplete: true)) + { + ContentType contentType = ContentTypeBuilder.CreateBasicContentType(); + ContentTypeService.Save(contentType); + Content content = ContentBuilder.CreateBasicContent(contentType); + ContentService.Save(content); + + Assert.Pass(); + } + } + } + + private class TestContentNotificationHandler : INotificationHandler + { + public void Handle(ContentSavingNotification notification) + => Assert.Fail("Notification was sent"); + } + + private class TestContentTypeNotificationHandler : INotificationHandler + { + public void Handle(ContentTypeSavingNotification notification) + => Assert.Fail("Notification was sent"); + } + } +} diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs index f1d1af20c0..62b8456fc7 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/UserServiceTests.cs @@ -23,6 +23,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services { + /// /// Tests covering the UserService /// From cd20701929de05ba9a4de920c1a186ed8a091369 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 11:13:01 -0600 Subject: [PATCH 042/102] another test --- .../Scoping/SupressNotificationsTests.cs | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs index 1481f303a7..4c3fa23504 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs @@ -20,8 +20,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping public class SupressNotificationsTests : UmbracoIntegrationTest { private IContentService ContentService => GetRequiredService(); - private IContentTypeService ContentTypeService => GetRequiredService(); + private IMediaTypeService MediaTypeService => GetRequiredService(); + private IMediaService MediaService => GetRequiredService(); protected override void CustomTestSetup(IUmbracoBuilder builder) { @@ -29,6 +30,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping builder.AddNotificationHandler(); builder.AddNotificationHandler(); + builder.AddNotificationHandler(); } [Test] @@ -41,8 +43,6 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping ContentTypeService.Save(contentType); Content content = ContentBuilder.CreateBasicContent(contentType); ContentService.Save(content); - - Assert.Pass(); } [Test] @@ -58,18 +58,43 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping ContentTypeService.Save(contentType); Content content = ContentBuilder.CreateBasicContent(contentType); ContentService.Save(content); - - Assert.Pass(); } } } + [Test] + public void GivenSuppressedNotifications_WhenDisposed_ThenNotificationsExecute() + { + int asserted = 0; + using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) + { + using IDisposable suppressed = scope.Notifications.Supress(); + + MediaType mediaType = MediaTypeBuilder.CreateImageMediaType("test"); + MediaTypeService.Save(mediaType); + + suppressed.Dispose(); + + asserted = TestContext.CurrentContext.AssertCount; + Media media = MediaBuilder.CreateMediaImage(mediaType, -1); + MediaService.Save(media); + } + + Assert.AreEqual(asserted + 1, TestContext.CurrentContext.AssertCount); + } + private class TestContentNotificationHandler : INotificationHandler { public void Handle(ContentSavingNotification notification) => Assert.Fail("Notification was sent"); } + private class TestMediaNotificationHandler : INotificationHandler + { + public void Handle(MediaSavedNotification notification) + => Assert.IsNotNull(notification); + } + private class TestContentTypeNotificationHandler : INotificationHandler { public void Handle(ContentTypeSavingNotification notification) From dcae6924075bafe09bbe85ac6a662836a8e11653 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 11:54:38 -0600 Subject: [PATCH 043/102] Fix typo, adds [CannotSuppressNotification] --- .../Events/IScopedNotificationPublisher.cs | 2 +- .../Events/ScopedNotificationPublisher.cs | 12 ++++++++---- .../CannotSuppressNotificationAttribute.cs | 9 +++++++++ .../ContentRefreshNotification.cs | 2 ++ .../Notifications/MediaRefreshNotification.cs | 1 + .../MemberRefreshNotification.cs | 1 + .../ScopedEntityRemoveNotification.cs | 1 + .../Services/SectionServiceTests.cs | 2 +- .../Scoping/SupressNotificationsTests.cs | 19 ++++++++++++++++--- 9 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index 75fbf83860..8ac4f4312d 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Core.Events /// Supresses all notifications from being added/created until the result object is disposed. /// /// - IDisposable Supress(); + IDisposable Suppress(); /// /// Publishes a cancelable notification to the notification subscribers diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index 6ea7ee5b6a..cf6260291b 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading.Tasks; using Umbraco.Cms.Core.Notifications; @@ -28,7 +29,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (_isSuppressed) + if (CanSuppress(notification) && _isSuppressed) { return false; } @@ -44,7 +45,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (_isSuppressed) + if (CanSuppress(notification) && _isSuppressed) { return false; } @@ -60,7 +61,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (_isSuppressed) + if (CanSuppress(notification) && _isSuppressed) { return; } @@ -86,7 +87,7 @@ namespace Umbraco.Cms.Core.Events } } - public IDisposable Supress() + public IDisposable Suppress() { lock(_locker) { @@ -98,6 +99,9 @@ namespace Umbraco.Cms.Core.Events } } + private bool CanSuppress(INotification notification) + => notification.GetType().GetCustomAttribute() == null; + private class Suppressor : IDisposable { private bool _disposedValue; diff --git a/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs b/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs new file mode 100644 index 0000000000..8279ae4caf --- /dev/null +++ b/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Umbraco.Cms.Core.Notifications +{ + [AttributeUsage(AttributeTargets.Class)] + internal sealed class CannotSuppressNotificationAttribute : Attribute + { + } +} diff --git a/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs index 6957da7f70..f2c81d3fac 100644 --- a/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs @@ -5,7 +5,9 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { + [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] + [CannotSuppressNotification] [EditorBrowsable(EditorBrowsableState.Never)] public class ContentRefreshNotification : EntityRefreshNotification { diff --git a/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs index 1c8b8b9bea..4fe0f82d33 100644 --- a/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { + [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class MediaRefreshNotification : EntityRefreshNotification diff --git a/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs index a22c48348f..d4dfeef68f 100644 --- a/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { + [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class MemberRefreshNotification : EntityRefreshNotification diff --git a/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs index 307ae2103c..a86ea659bb 100644 --- a/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs +++ b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs @@ -5,6 +5,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { + [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class ScopedEntityRemoveNotification : ObjectNotification diff --git a/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs index 1e9a80faf6..d1f25a8f82 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Core/Services/SectionServiceTests.cs @@ -42,7 +42,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Services private IUser CreateTestUser() { using IScope scope = ScopeProvider.CreateScope(autoComplete: true); - using IDisposable _ = scope.Notifications.Supress(); + using IDisposable _ = scope.Notifications.Suppress(); var globalSettings = new GlobalSettings(); var user = new User(globalSettings) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs index 4c3fa23504..c40249b886 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs @@ -37,7 +37,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping public void GivenScope_WhenNotificationsSuppressed_ThenNotificationsDoNotExecute() { using IScope scope = ScopeProvider.CreateScope(autoComplete: true); - using IDisposable _ = scope.Notifications.Supress(); + using IDisposable _ = scope.Notifications.Suppress(); ContentType contentType = ContentTypeBuilder.CreateBasicContentType(); ContentTypeService.Save(contentType); @@ -50,7 +50,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { using (IScope parentScope = ScopeProvider.CreateScope(autoComplete: true)) { - using IDisposable _ = parentScope.Notifications.Supress(); + using IDisposable _ = parentScope.Notifications.Suppress(); using (IScope childScope = ScopeProvider.CreateScope(autoComplete: true)) { @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping int asserted = 0; using (IScope scope = ScopeProvider.CreateScope(autoComplete: true)) { - using IDisposable suppressed = scope.Notifications.Supress(); + using IDisposable suppressed = scope.Notifications.Suppress(); MediaType mediaType = MediaTypeBuilder.CreateImageMediaType("test"); MediaTypeService.Save(mediaType); @@ -83,6 +83,19 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping Assert.AreEqual(asserted + 1, TestContext.CurrentContext.AssertCount); } + [Test] + public void GivenSuppressedNotificationsOnParent_WhenChildSupresses_ThenExceptionIsThrown() + { + using (IScope parentScope = ScopeProvider.CreateScope(autoComplete: true)) + using (IDisposable parentSuppressed = parentScope.Notifications.Suppress()) + { + using (IScope childScope = ScopeProvider.CreateScope(autoComplete: true)) + { + Assert.Throws(() => childScope.Notifications.Suppress()); + } + } + } + private class TestContentNotificationHandler : INotificationHandler { public void Handle(ContentSavingNotification notification) From 461a199d1566cdd25289f1db35f3a9c35cdbd3b3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 13:34:14 -0600 Subject: [PATCH 044/102] Changes a couple of collection builders to not use the lazy (slightly more expensive ones) when not needed. --- .../Composing/LazyCollectionBuilderBase.cs | 17 ++++++++++++----- .../ManifestValueValidatorCollectionBuilder.cs | 4 ++-- .../MediaUrlGeneratorCollectionBuilder.cs | 2 +- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs index d41813f7d8..baae385af4 100644 --- a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs @@ -10,6 +10,9 @@ namespace Umbraco.Cms.Core.Composing /// The type of the builder. /// The type of the collection. /// The type of the items. + /// + /// This type of collection builder is typically used when type scanning is required (i.e. plugins). + /// public abstract class LazyCollectionBuilderBase : CollectionBuilderBase where TBuilder : LazyCollectionBuilderBase where TCollection : class, IBuilderCollection @@ -29,7 +32,7 @@ namespace Umbraco.Cms.Core.Composing { types.Clear(); _producers.Clear(); - _excluded.Clear(); + _excluded.Clear(); }); return This; } @@ -45,7 +48,8 @@ namespace Umbraco.Cms.Core.Composing Configure(types => { var type = typeof(T); - if (types.Contains(type) == false) types.Add(type); + if (types.Contains(type) == false) + types.Add(type); }); return This; } @@ -60,7 +64,8 @@ namespace Umbraco.Cms.Core.Composing Configure(types => { EnsureType(type, "register"); - if (types.Contains(type) == false) types.Add(type); + if (types.Contains(type) == false) + types.Add(type); }); return This; } @@ -90,7 +95,8 @@ namespace Umbraco.Cms.Core.Composing Configure(types => { var type = typeof(T); - if (_excluded.Contains(type) == false) _excluded.Add(type); + if (_excluded.Contains(type) == false) + _excluded.Add(type); }); return This; } @@ -105,7 +111,8 @@ namespace Umbraco.Cms.Core.Composing Configure(types => { EnsureType(type, "exclude"); - if (_excluded.Contains(type) == false) _excluded.Add(type); + if (_excluded.Contains(type) == false) + _excluded.Add(type); }); return This; } diff --git a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs index 2247d3e62f..66a967c828 100644 --- a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs +++ b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollectionBuilder.cs @@ -1,8 +1,8 @@ -using Umbraco.Cms.Core.Composing; +using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.PropertyEditors { - public class ManifestValueValidatorCollectionBuilder : LazyCollectionBuilderBase + public class ManifestValueValidatorCollectionBuilder : SetCollectionBuilderBase { protected override ManifestValueValidatorCollectionBuilder This => this; } diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs index 3ad03cb13c..57ab93832b 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollectionBuilder.cs @@ -3,7 +3,7 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.PropertyEditors { - public class MediaUrlGeneratorCollectionBuilder : LazyCollectionBuilderBase + public class MediaUrlGeneratorCollectionBuilder : SetCollectionBuilderBase { protected override MediaUrlGeneratorCollectionBuilder This => this; } From bc84ffe260c4919aea833a9cda8cd116761815b3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 15:28:46 -0600 Subject: [PATCH 045/102] Changes all collections from collection builders to resolve the concrete instances lazily. This means we don't have to inject Lazy all over the place when dealing with colleciton builders and circular references since this will automatically just work OOTB. This in theory should also allocate less instances during startup. --- src/Umbraco.Core/Actions/ActionCollection.cs | 14 +++--- .../Cache/CacheRefresherCollection.cs | 10 ++-- .../Composing/BuilderCollectionBase.cs | 25 ++++------ .../Composing/CollectionBuilderBase.cs | 16 ++++--- .../Composing/ComponentCollection.cs | 4 +- .../Composing/LazyReadOnlyCollection.cs | 48 +++++++++++++++++++ .../Composing/TypeCollectionBuilderBase.cs | 19 ++++---- .../ContentAppFactoryCollection.cs | 5 +- .../ContentAppFactoryCollectionBuilder.cs | 6 ++- .../Dashboards/DashboardCollection.cs | 9 ++-- .../Editors/EditorValidatorCollection.cs | 9 ++-- .../HealthChecks/HealthCheckCollection.cs | 9 ++-- ...HealthCheckNotificationMethodCollection.cs | 9 ++-- .../Manifest/ManifestFilterCollection.cs | 12 ++--- .../Mapping/MapDefinitionCollection.cs | 9 ++-- .../EmbedProvidersCollection.cs | 9 ++-- .../PropertyEditors/DataEditorCollection.cs | 9 ++-- .../DataValueReferenceFactoryCollection.cs | 8 ++-- .../ManifestValueValidatorCollection.cs | 8 ++-- .../MediaUrlGeneratorCollection.cs | 2 +- .../ParameterEditorCollection.cs | 4 +- .../PropertyEditorCollection.cs | 4 +- .../PropertyValueConverterCollection.cs | 8 ++-- .../Routing/ContentFinderCollection.cs | 9 ++-- .../Routing/MediaUrlProviderCollection.cs | 9 ++-- .../Routing/UrlProviderCollection.cs | 9 ++-- .../Sections/SectionCollection.cs | 9 ++-- .../Strings/UrlSegmentProviderCollection.cs | 9 ++-- src/Umbraco.Core/Tour/TourFilterCollection.cs | 12 ++--- .../Trees/SearchableTreeCollection.cs | 2 +- src/Umbraco.Core/Trees/TreeCollection.cs | 10 ++-- .../UmbracoApiControllerTypeCollection.cs | 8 ++-- .../CustomBackOfficeAssetsCollection.cs | 7 +-- .../DataTypes/PreValueMigratorCollection.cs | 7 +-- .../PackageMigrationPlanCollection.cs | 5 +- .../Persistence/DbProviderFactoryCreator.cs | 5 +- .../Persistence/Mappers/MapperCollection.cs | 4 +- .../Persistence/NPocoMapperCollection.cs | 3 +- .../Implement/ContentRepositoryBase.cs | 7 ++- .../Implement/DataTypeRepository.cs | 8 ++-- .../Implement/DocumentBlueprintRepository.cs | 4 +- .../Implement/DocumentRepository.cs | 2 +- .../Repositories/Implement/MediaRepository.cs | 2 +- .../Implement/MemberRepository.cs | 2 +- .../Persistence/SqlContext.cs | 22 ++------- .../SqlServerDbProviderFactoryCreator.cs | 11 +++-- .../Persistence/UmbracoDatabaseFactory.cs | 4 +- .../BlockEditorPropertyEditor.cs | 12 ++--- .../BlockListPropertyEditor.cs | 4 +- .../Runtime/SqlMainDomLock.cs | 3 +- .../SqlCeSpecificMapperFactory.cs | 4 +- .../AutoPublishedContentType.cs | 3 +- .../TestUmbracoDatabaseFactoryProvider.cs | 4 +- .../UmbracoExamine/IndexInitializer.cs | 4 +- .../Repositories/DocumentRepositoryTest.cs | 11 +++-- .../Repositories/MediaRepositoryTest.cs | 7 +-- .../Repositories/MemberRepositoryTest.cs | 5 +- .../Repositories/TemplateRepositoryTest.cs | 5 +- .../Services/MemberServiceTests.cs | 3 +- .../AutoFixture/AutoMoqDataAttribute.cs | 4 +- .../TestHelpers/BaseUsingSqlSyntax.cs | 2 +- .../DistributedCache/DistributedCacheTests.cs | 2 +- .../Umbraco.Core/Components/ComponentTests.cs | 4 +- .../Composing/CollectionBuildersTests.cs | 3 +- .../Composing/LazyCollectionBuilderTests.cs | 3 +- .../Manifest/ManifestParserTests.cs | 5 +- .../Umbraco.Core/Models/VariationTests.cs | 2 +- .../PendingPackageMigrationsTests.cs | 2 +- ...ataValueReferenceFactoryCollectionTests.cs | 13 ++--- .../PropertyEditorValueConverterTests.cs | 2 +- .../Umbraco.Core/Published/ConvertersTests.cs | 4 +- .../Published/NestedContentTests.cs | 4 +- .../Published/PropertyCacheLevelTests.cs | 6 +-- .../Templates/HtmlImageSourceParserTests.cs | 4 +- .../Templates/HtmlLocalLinkParserTests.cs | 4 +- .../HealthCheckNotifierTests.cs | 4 +- .../Mapping/MappingTests.cs | 14 +++--- .../Security/MemberManagerTests.cs | 2 +- .../Security/MemberUserStoreTests.cs | 2 +- .../PropertyValidationServiceTests.cs | 2 +- .../Controllers/MemberControllerUnitTests.cs | 6 +-- .../Routing/BackOfficeAreaRoutesTests.cs | 2 +- .../Trees/TreeCollectionBuilder.cs | 4 +- ...bracoApiControllerTypeCollectionBuilder.cs | 2 +- .../SurfaceControllerTypeCollection.cs | 8 ++-- 85 files changed, 327 insertions(+), 274 deletions(-) create mode 100644 src/Umbraco.Core/Composing/LazyReadOnlyCollection.cs diff --git a/src/Umbraco.Core/Actions/ActionCollection.cs b/src/Umbraco.Core/Actions/ActionCollection.cs index 3987e89305..2084df3dad 100644 --- a/src/Umbraco.Core/Actions/ActionCollection.cs +++ b/src/Umbraco.Core/Actions/ActionCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models.Membership; @@ -8,15 +9,12 @@ namespace Umbraco.Cms.Core.Actions { public class ActionCollection : BuilderCollectionBase { - public ActionCollection(IEnumerable items) - : base(items) - { } + public ActionCollection(Func> items) : base(items) + { + } public T GetAction() - where T : IAction - { - return this.OfType().FirstOrDefault(); - } + where T : IAction => this.OfType().FirstOrDefault(); public IEnumerable GetByLetters(IEnumerable letters) { diff --git a/src/Umbraco.Core/Cache/CacheRefresherCollection.cs b/src/Umbraco.Core/Cache/CacheRefresherCollection.cs index 2c9007cbe9..a61dd81368 100644 --- a/src/Umbraco.Core/Cache/CacheRefresherCollection.cs +++ b/src/Umbraco.Core/Cache/CacheRefresherCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Composing; @@ -7,11 +7,11 @@ namespace Umbraco.Cms.Core.Cache { public class CacheRefresherCollection : BuilderCollectionBase { - public CacheRefresherCollection(IEnumerable items) - : base(items) - { } + public CacheRefresherCollection(Func> items) : base(items) + { + } public ICacheRefresher this[Guid id] - => this.FirstOrDefault(x => x.RefresherUniqueId == id); + => this.FirstOrDefault(x => x.RefresherUniqueId == id); } } diff --git a/src/Umbraco.Core/Composing/BuilderCollectionBase.cs b/src/Umbraco.Core/Composing/BuilderCollectionBase.cs index a5bf33f9c1..1af9511fb7 100644 --- a/src/Umbraco.Core/Composing/BuilderCollectionBase.cs +++ b/src/Umbraco.Core/Composing/BuilderCollectionBase.cs @@ -1,43 +1,34 @@ -using System.Collections; +using System; +using System.Collections; using System.Collections.Generic; -using System.Linq; namespace Umbraco.Cms.Core.Composing { + /// /// Provides a base class for builder collections. /// /// The type of the items. public abstract class BuilderCollectionBase : IBuilderCollection { - private readonly TItem[] _items; + private readonly LazyReadOnlyCollection _items; - /// /// Initializes a new instance of the with items. /// /// The items. - protected BuilderCollectionBase(IEnumerable items) - { - _items = items.ToArray(); - } + public BuilderCollectionBase(Func> items) => _items = new LazyReadOnlyCollection(items); /// - public int Count => _items.Length; + public int Count => _items.Count; /// /// Gets an enumerator. /// - public IEnumerator GetEnumerator() - { - return ((IEnumerable) _items).GetEnumerator(); - } + public IEnumerator GetEnumerator() => ((IEnumerable)_items).GetEnumerator(); /// /// Gets an enumerator. /// - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index ab33a6ebef..81d80bb57c 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -13,7 +13,7 @@ namespace Umbraco.Cms.Core.Composing /// The type of the collection. /// The type of the items. public abstract class CollectionBuilderBase : ICollectionBuilder - where TBuilder: CollectionBuilderBase + where TBuilder : CollectionBuilderBase where TCollection : class, IBuilderCollection { private readonly List _types = new List(); @@ -73,7 +73,8 @@ namespace Umbraco.Cms.Core.Composing { lock (_locker) { - if (_registeredTypes != null) return; + if (_registeredTypes != null) + return; var types = GetRegisteringTypes(_types).ToArray(); @@ -110,7 +111,7 @@ namespace Umbraco.Cms.Core.Composing /// Creates a collection item. /// protected virtual TItem CreateItem(IServiceProvider factory, Type itemType) - => (TItem) factory.GetRequiredService(itemType); + => (TItem)factory.GetRequiredService(itemType); /// /// Creates a collection. @@ -118,9 +119,10 @@ namespace Umbraco.Cms.Core.Composing /// A collection. /// Creates a new collection each time it is invoked. public virtual TCollection CreateCollection(IServiceProvider factory) - { - return factory.CreateInstance( CreateItems(factory)); - } + => factory.CreateInstance(CreateItemsFactory(factory)); + + // used to resolve a Func> parameter + private Func> CreateItemsFactory(IServiceProvider factory) => () => CreateItems(factory); protected Type EnsureType(Type type, string action) { @@ -139,7 +141,7 @@ namespace Umbraco.Cms.Core.Composing public virtual bool Has() where T : TItem { - return _types.Contains(typeof (T)); + return _types.Contains(typeof(T)); } /// diff --git a/src/Umbraco.Core/Composing/ComponentCollection.cs b/src/Umbraco.Core/Composing/ComponentCollection.cs index 1cd505027b..c39dd503e0 100644 --- a/src/Umbraco.Core/Composing/ComponentCollection.cs +++ b/src/Umbraco.Core/Composing/ComponentCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Core.Composing private readonly IProfilingLogger _profilingLogger; private readonly ILogger _logger; - public ComponentCollection(IEnumerable items, IProfilingLogger profilingLogger, ILogger logger) + public ComponentCollection(Func> items, IProfilingLogger profilingLogger, ILogger logger) : base(items) { _profilingLogger = profilingLogger; diff --git a/src/Umbraco.Core/Composing/LazyReadOnlyCollection.cs b/src/Umbraco.Core/Composing/LazyReadOnlyCollection.cs new file mode 100644 index 0000000000..7fee8eaf26 --- /dev/null +++ b/src/Umbraco.Core/Composing/LazyReadOnlyCollection.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; + +namespace Umbraco.Cms.Core.Composing +{ + public sealed class LazyReadOnlyCollection : IReadOnlyCollection + { + private readonly Lazy> _lazyCollection; + private int? _count; + + public LazyReadOnlyCollection(Lazy> lazyCollection) => _lazyCollection = lazyCollection; + + public LazyReadOnlyCollection(Func> lazyCollection) => _lazyCollection = new Lazy>(lazyCollection); + + public IEnumerable Value => EnsureCollection(); + + private IEnumerable EnsureCollection() + { + if (_lazyCollection == null) + { + _count = 0; + return Enumerable.Empty(); + } + + IEnumerable val = _lazyCollection.Value; + if (_count == null) + { + _count = val.Count(); + } + return val; + } + + public int Count + { + get + { + EnsureCollection(); + return _count.Value; + } + } + + public IEnumerator GetEnumerator() => Value.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs index 0bebf8bf8b..40ce3d8a46 100644 --- a/src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/TypeCollectionBuilderBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Umbraco.Extensions; @@ -37,7 +37,11 @@ namespace Umbraco.Cms.Core.Composing public TBuilder Add(IEnumerable types) { - foreach (var type in types) Add(type); + foreach (var type in types) + { + Add(type); + } + return This; } @@ -54,13 +58,12 @@ namespace Umbraco.Cms.Core.Composing } public TCollection CreateCollection(IServiceProvider factory) - { - return factory.CreateInstance(_types); - } + => factory.CreateInstance(CreateItemsFactory()); public void RegisterWith(IServiceCollection services) - { - services.Add(new ServiceDescriptor(typeof(TCollection), CreateCollection, ServiceLifetime.Singleton)); - } + => services.Add(new ServiceDescriptor(typeof(TCollection), CreateCollection, ServiceLifetime.Singleton)); + + // used to resolve a Func> parameter + private Func> CreateItemsFactory() => () => _types; } } diff --git a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs index d8b3e04772..0c6e6f0f68 100644 --- a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; @@ -14,7 +15,7 @@ namespace Umbraco.Cms.Core.ContentApps private readonly ILogger _logger; private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; - public ContentAppFactoryCollection(IEnumerable items, ILogger logger, IBackOfficeSecurityAccessor backOfficeSecurityAccessor) + public ContentAppFactoryCollection(Func> items, ILogger logger, IBackOfficeSecurityAccessor backOfficeSecurityAccessor) : base(items) { _logger = logger; diff --git a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs index 3c35054e9b..ff1223d983 100644 --- a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; @@ -21,7 +21,9 @@ namespace Umbraco.Cms.Core.ContentApps // get the logger factory just-in-time - see note below for manifest parser var loggerFactory = factory.GetRequiredService(); var backOfficeSecurityAccessor = factory.GetRequiredService(); - return new ContentAppFactoryCollection(CreateItems(factory), loggerFactory.CreateLogger(), backOfficeSecurityAccessor); + return new ContentAppFactoryCollection( + () => CreateItems(factory), + loggerFactory.CreateLogger(), backOfficeSecurityAccessor); } protected override IEnumerable CreateItems(IServiceProvider factory) diff --git a/src/Umbraco.Core/Dashboards/DashboardCollection.cs b/src/Umbraco.Core/Dashboards/DashboardCollection.cs index 9fa13f1d3d..e5c8378139 100644 --- a/src/Umbraco.Core/Dashboards/DashboardCollection.cs +++ b/src/Umbraco.Core/Dashboards/DashboardCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Dashboards { public class DashboardCollection : BuilderCollectionBase { - public DashboardCollection(IEnumerable items) - : base(items) - { } + public DashboardCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Editors/EditorValidatorCollection.cs b/src/Umbraco.Core/Editors/EditorValidatorCollection.cs index 3b07be5553..91bc3e191b 100644 --- a/src/Umbraco.Core/Editors/EditorValidatorCollection.cs +++ b/src/Umbraco.Core/Editors/EditorValidatorCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Editors { public class EditorValidatorCollection : BuilderCollectionBase { - public EditorValidatorCollection(IEnumerable items) - : base(items) - { } + public EditorValidatorCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/HealthChecks/HealthCheckCollection.cs b/src/Umbraco.Core/HealthChecks/HealthCheckCollection.cs index 2987ff1112..bcbee9036b 100644 --- a/src/Umbraco.Core/HealthChecks/HealthCheckCollection.cs +++ b/src/Umbraco.Core/HealthChecks/HealthCheckCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.HealthChecks { public class HealthCheckCollection : BuilderCollectionBase { - public HealthCheckCollection(IEnumerable items) - : base(items) - { } + public HealthCheckCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/HealthChecks/HealthCheckNotificationMethodCollection.cs b/src/Umbraco.Core/HealthChecks/HealthCheckNotificationMethodCollection.cs index 7fa8486df6..af964857d8 100644 --- a/src/Umbraco.Core/HealthChecks/HealthCheckNotificationMethodCollection.cs +++ b/src/Umbraco.Core/HealthChecks/HealthCheckNotificationMethodCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.HealthChecks.NotificationMethods; @@ -6,8 +7,8 @@ namespace Umbraco.Cms.Core.HealthChecks { public class HealthCheckNotificationMethodCollection : BuilderCollectionBase { - public HealthCheckNotificationMethodCollection(IEnumerable items) - : base(items) - { } + public HealthCheckNotificationMethodCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs b/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs index 20a3468c36..9c692f69b3 100644 --- a/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs +++ b/src/Umbraco.Core/Manifest/ManifestFilterCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Manifest @@ -8,12 +9,9 @@ namespace Umbraco.Cms.Core.Manifest /// public class ManifestFilterCollection : BuilderCollectionBase { - /// - /// Initializes a new instance of the class. - /// - public ManifestFilterCollection(IEnumerable items) - : base(items) - { } + public ManifestFilterCollection(Func> items) : base(items) + { + } /// /// Filters package manifests. diff --git a/src/Umbraco.Core/Mapping/MapDefinitionCollection.cs b/src/Umbraco.Core/Mapping/MapDefinitionCollection.cs index d9cc08ad43..27d4ad73d0 100644 --- a/src/Umbraco.Core/Mapping/MapDefinitionCollection.cs +++ b/src/Umbraco.Core/Mapping/MapDefinitionCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Mapping { public class MapDefinitionCollection : BuilderCollectionBase { - public MapDefinitionCollection(IEnumerable items) - : base(items) - { } + public MapDefinitionCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Media/EmbedProviders/EmbedProvidersCollection.cs b/src/Umbraco.Core/Media/EmbedProviders/EmbedProvidersCollection.cs index 490ff64357..615d16f51c 100644 --- a/src/Umbraco.Core/Media/EmbedProviders/EmbedProvidersCollection.cs +++ b/src/Umbraco.Core/Media/EmbedProviders/EmbedProvidersCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Media.EmbedProviders { public class EmbedProvidersCollection : BuilderCollectionBase { - public EmbedProvidersCollection(IEnumerable items) - : base(items) - { } + public EmbedProvidersCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs index d4ddc21827..0c4ca93fc1 100644 --- a/src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/DataEditorCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.PropertyEditors { public class DataEditorCollection : BuilderCollectionBase { - public DataEditorCollection(IEnumerable items) - : base(items) - { } + public DataEditorCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs index 9a9efa5e2c..a83f925dd3 100644 --- a/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollection.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Editors; @@ -7,9 +7,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { public class DataValueReferenceFactoryCollection : BuilderCollectionBase { - public DataValueReferenceFactoryCollection(IEnumerable items) - : base(items) - { } + public DataValueReferenceFactoryCollection(System.Func> items) : base(items) + { + } // TODO: We could further reduce circular dependencies with PropertyEditorCollection by not having IDataValueReference implemented // by property editors and instead just use the already built in IDataValueReferenceFactory and/or refactor that into a more normal collection diff --git a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs index 45a2dc51bb..9e26362bc2 100644 --- a/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/ManifestValueValidatorCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Composing; @@ -8,9 +8,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { public class ManifestValueValidatorCollection : BuilderCollectionBase { - public ManifestValueValidatorCollection(IEnumerable items) - : base(items) - { } + public ManifestValueValidatorCollection(Func> items) : base(items) + { + } public IManifestValueValidator Create(string name) { diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs index cd3a36c607..7e01f6b1cb 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs @@ -6,7 +6,7 @@ namespace Umbraco.Cms.Core.PropertyEditors { public class MediaUrlGeneratorCollection : BuilderCollectionBase { - public MediaUrlGeneratorCollection(IEnumerable items) : base(items) + public MediaUrlGeneratorCollection(System.Func> items) : base(items) { } diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs index 5ad20665f4..039de8cb6a 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs @@ -1,4 +1,4 @@ -using System.Linq; +using System.Linq; using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Manifest; @@ -7,7 +7,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public class ParameterEditorCollection : BuilderCollectionBase { public ParameterEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) - : base(dataEditors + : base(() => dataEditors .Where(x => (x.Type & EditorType.MacroParameter) > 0) .Union(manifestParser.Manifest.PropertyEditors)) { } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs index e5b6e96c7b..4b551d3257 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs @@ -7,13 +7,13 @@ namespace Umbraco.Cms.Core.PropertyEditors public class PropertyEditorCollection : BuilderCollectionBase { public PropertyEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) - : base(dataEditors + : base(() => dataEditors .Where(x => (x.Type & EditorType.PropertyValue) > 0) .Union(manifestParser.Manifest.PropertyEditors)) { } public PropertyEditorCollection(DataEditorCollection dataEditors) - : base(dataEditors + : base(() => dataEditors .Where(x => (x.Type & EditorType.PropertyValue) > 0)) { } diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs index 136967e7ab..a60ba4a6e5 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueConverterCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Umbraco.Cms.Core.Composing; @@ -8,9 +8,9 @@ namespace Umbraco.Cms.Core.PropertyEditors { public class PropertyValueConverterCollection : BuilderCollectionBase { - public PropertyValueConverterCollection(IEnumerable items) - : base(items) - { } + public PropertyValueConverterCollection(Func> items) : base(items) + { + } private readonly object _locker = new object(); private Dictionary _defaultConverters; diff --git a/src/Umbraco.Core/Routing/ContentFinderCollection.cs b/src/Umbraco.Core/Routing/ContentFinderCollection.cs index d63c269f50..8965d9d447 100644 --- a/src/Umbraco.Core/Routing/ContentFinderCollection.cs +++ b/src/Umbraco.Core/Routing/ContentFinderCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Routing { public class ContentFinderCollection : BuilderCollectionBase { - public ContentFinderCollection(IEnumerable items) - : base(items) - { } + public ContentFinderCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs b/src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs index 0ac0ae265e..264be41d60 100644 --- a/src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs +++ b/src/Umbraco.Core/Routing/MediaUrlProviderCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Routing { public class MediaUrlProviderCollection : BuilderCollectionBase { - public MediaUrlProviderCollection(IEnumerable items) - : base(items) - { } + public MediaUrlProviderCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Routing/UrlProviderCollection.cs b/src/Umbraco.Core/Routing/UrlProviderCollection.cs index 4a383f82bf..c17417c83c 100644 --- a/src/Umbraco.Core/Routing/UrlProviderCollection.cs +++ b/src/Umbraco.Core/Routing/UrlProviderCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Routing { public class UrlProviderCollection : BuilderCollectionBase { - public UrlProviderCollection(IEnumerable items) - : base(items) - { } + public UrlProviderCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Sections/SectionCollection.cs b/src/Umbraco.Core/Sections/SectionCollection.cs index a93b36a161..5ff0157d14 100644 --- a/src/Umbraco.Core/Sections/SectionCollection.cs +++ b/src/Umbraco.Core/Sections/SectionCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Sections { public class SectionCollection : BuilderCollectionBase { - public SectionCollection(IEnumerable items) - : base(items) - { } + public SectionCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs b/src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs index 7307c8964a..551efc475a 100644 --- a/src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs +++ b/src/Umbraco.Core/Strings/UrlSegmentProviderCollection.cs @@ -1,12 +1,13 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Strings { public class UrlSegmentProviderCollection : BuilderCollectionBase { - public UrlSegmentProviderCollection(IEnumerable items) - : base(items) - { } + public UrlSegmentProviderCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Tour/TourFilterCollection.cs b/src/Umbraco.Core/Tour/TourFilterCollection.cs index 5c55d0e0cd..2864abbced 100644 --- a/src/Umbraco.Core/Tour/TourFilterCollection.cs +++ b/src/Umbraco.Core/Tour/TourFilterCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Tour @@ -8,11 +9,8 @@ namespace Umbraco.Cms.Core.Tour /// public class TourFilterCollection : BuilderCollectionBase { - /// - /// Initializes a new instance of the class. - /// - public TourFilterCollection(IEnumerable items) - : base(items) - { } + public TourFilterCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/Trees/SearchableTreeCollection.cs b/src/Umbraco.Core/Trees/SearchableTreeCollection.cs index 8f1b20a7b1..ff42b5e8c3 100644 --- a/src/Umbraco.Core/Trees/SearchableTreeCollection.cs +++ b/src/Umbraco.Core/Trees/SearchableTreeCollection.cs @@ -11,7 +11,7 @@ namespace Umbraco.Cms.Core.Trees { private readonly Dictionary _dictionary; - public SearchableTreeCollection(IEnumerable items, ITreeService treeService) + public SearchableTreeCollection(Func> items, ITreeService treeService) : base(items) { _dictionary = CreateDictionary(treeService); diff --git a/src/Umbraco.Core/Trees/TreeCollection.cs b/src/Umbraco.Core/Trees/TreeCollection.cs index 45a405217f..59fa99819c 100644 --- a/src/Umbraco.Core/Trees/TreeCollection.cs +++ b/src/Umbraco.Core/Trees/TreeCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Trees @@ -8,8 +9,9 @@ namespace Umbraco.Cms.Core.Trees /// public class TreeCollection : BuilderCollectionBase { - public TreeCollection(IEnumerable items) - : base(items) - { } + + public TreeCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs b/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs index 9ff5073d17..66ad608881 100644 --- a/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs +++ b/src/Umbraco.Core/UmbracoApiControllerTypeCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Composing; @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Core { public class UmbracoApiControllerTypeCollection : BuilderCollectionBase { - public UmbracoApiControllerTypeCollection(IEnumerable items) - : base(items) - { } + public UmbracoApiControllerTypeCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs index 8648a59d32..2595afe40e 100644 --- a/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs +++ b/src/Umbraco.Core/WebAssets/CustomBackOfficeAssetsCollection.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Composing; @@ -5,8 +6,8 @@ namespace Umbraco.Cms.Core.WebAssets { public class CustomBackOfficeAssetsCollection : BuilderCollectionBase { - public CustomBackOfficeAssetsCollection(IEnumerable items) - : base(items) - { } + public CustomBackOfficeAssetsCollection(Func> items) : base(items) + { + } } } diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs index 5f9d87269d..ca4ec9bfe1 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_8_0_0/DataTypes/PreValueMigratorCollection.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections; +using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Composing; @@ -9,11 +11,10 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_8_0_0.DataTypes { private readonly ILogger _logger; - public PreValueMigratorCollection(IEnumerable items, ILogger logger) + public PreValueMigratorCollection(Func> items, ILogger logger) : base(items) { _logger = logger; - _logger.LogDebug("Migrators: " + string.Join(", ", items.Select(x => x.GetType().Name))); } public IPreValueMigrator GetMigrator(string editorAlias) diff --git a/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollection.cs b/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollection.cs index 2a17add2e6..aa390dcaa4 100644 --- a/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollection.cs +++ b/src/Umbraco.Infrastructure/Packaging/PackageMigrationPlanCollection.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Core.Packaging @@ -8,7 +9,7 @@ namespace Umbraco.Cms.Core.Packaging /// public class PackageMigrationPlanCollection : BuilderCollectionBase { - public PackageMigrationPlanCollection(IEnumerable items) : base(items) + public PackageMigrationPlanCollection(Func> items) : base(items) { } } diff --git a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs index 8814c01761..797400b7cc 100644 --- a/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/DbProviderFactoryCreator.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Data.Common; using System.Linq; using NPoco; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; namespace Umbraco.Cms.Infrastructure.Persistence @@ -73,7 +74,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence return mapperFactory.Mappers; } - return new NPocoMapperCollection(Array.Empty()); + return new NPocoMapperCollection(() => Enumerable.Empty()); } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollection.cs b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollection.cs index a719308443..feaacd714b 100644 --- a/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollection.cs +++ b/src/Umbraco.Infrastructure/Persistence/Mappers/MapperCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using Umbraco.Cms.Core.Composing; @@ -8,7 +8,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Mappers { public class MapperCollection : BuilderCollectionBase, IMapperCollection { - public MapperCollection(IEnumerable items) + public MapperCollection(Func> items) : base(items) { diff --git a/src/Umbraco.Infrastructure/Persistence/NPocoMapperCollection.cs b/src/Umbraco.Infrastructure/Persistence/NPocoMapperCollection.cs index a1b61198d3..3d71c0225e 100644 --- a/src/Umbraco.Infrastructure/Persistence/NPocoMapperCollection.cs +++ b/src/Umbraco.Infrastructure/Persistence/NPocoMapperCollection.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using NPoco; using Umbraco.Cms.Core.Composing; @@ -6,7 +7,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence { public sealed class NPocoMapperCollection : BuilderCollectionBase { - public NPocoMapperCollection(IEnumerable items) : base(items) + public NPocoMapperCollection(Func> items) : base(items) { } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs index 2055006415..c72e11f595 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/ContentRepositoryBase.cs @@ -37,7 +37,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement where TEntity : class, IContentBase where TRepository : class, IRepository { - private readonly Lazy _propertyEditors; private readonly DataValueReferenceFactoryCollection _dataValueReferenceFactories; private readonly IEventAggregator _eventAggregator; @@ -58,7 +57,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, - Lazy propertyEditors, + PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeService dataTypeService, IEventAggregator eventAggregator) @@ -68,7 +67,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement LanguageRepository = languageRepository; RelationRepository = relationRepository; RelationTypeRepository = relationTypeRepository; - _propertyEditors = propertyEditors; + PropertyEditors = propertyEditors; _dataValueReferenceFactories = dataValueReferenceFactories; _eventAggregator = eventAggregator; } @@ -85,7 +84,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement protected IRelationRepository RelationRepository { get; } protected IRelationTypeRepository RelationTypeRepository { get; } - protected PropertyEditorCollection PropertyEditors => _propertyEditors.Value; + protected PropertyEditorCollection PropertyEditors { get; } #region Versions diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs index 001f2db000..f70048c4f9 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DataTypeRepository.cs @@ -29,14 +29,14 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement /// internal class DataTypeRepository : EntityRepositoryBase, IDataTypeRepository { - private readonly Lazy _editors; + private readonly PropertyEditorCollection _editors; private readonly IConfigurationEditorJsonSerializer _serializer; private readonly ILogger _dataTypeLogger; public DataTypeRepository( IScopeAccessor scopeAccessor, AppCaches cache, - Lazy editors, + PropertyEditorCollection editors, ILogger logger, ILoggerFactory loggerFactory, IConfigurationEditorJsonSerializer serializer) @@ -68,7 +68,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } var dtos = Database.Fetch(dataTypeSql); - return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, _dataTypeLogger, _serializer)).ToArray(); + return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors, _dataTypeLogger, _serializer)).ToArray(); } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -79,7 +79,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement var dtos = Database.Fetch(sql); - return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors.Value, _dataTypeLogger, _serializer)).ToArray(); + return dtos.Select(x => DataTypeFactory.BuildEntity(x, _editors, _dataTypeLogger, _serializer)).ToArray(); } #endregion diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs index 828c521f93..79663c292e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentBlueprintRepository.cs @@ -1,4 +1,4 @@ -using System; +using System; using Microsoft.Extensions.Logging; using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Events; @@ -32,7 +32,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, - Lazy propertyEditorCollection, + PropertyEditorCollection propertyEditorCollection, IDataTypeService dataTypeService, DataValueReferenceFactoryCollection dataValueReferenceFactories, IJsonSerializer serializer, diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index b246839440..b4a4599d14 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, - Lazy propertyEditors, + PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeService dataTypeService, IJsonSerializer serializer, diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs index b69907d71b..cc188787ba 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MediaRepository.cs @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement ILanguageRepository languageRepository, IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, - Lazy propertyEditorCollection, + PropertyEditorCollection propertyEditorCollection, MediaUrlGeneratorCollection mediaUrlGenerators, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeService dataTypeService, diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs index 698d0fffa7..cc1ce7aec0 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/MemberRepository.cs @@ -51,7 +51,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement IRelationRepository relationRepository, IRelationTypeRepository relationTypeRepository, IPasswordHasher passwordHasher, - Lazy propertyEditors, + PropertyEditorCollection propertyEditors, DataValueReferenceFactoryCollection dataValueReferenceFactories, IDataTypeService dataTypeService, IJsonSerializer serializer, diff --git a/src/Umbraco.Infrastructure/Persistence/SqlContext.cs b/src/Umbraco.Infrastructure/Persistence/SqlContext.cs index e1ced4f375..f1f3233a45 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlContext.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlContext.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Linq; using NPoco; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Persistence.Querying; using Umbraco.Cms.Infrastructure.Persistence.Mappers; using Umbraco.Cms.Infrastructure.Persistence.Querying; @@ -14,8 +15,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// public class SqlContext : ISqlContext { - private readonly Lazy _mappers; - /// /// Initializes a new instance of the class. /// @@ -23,21 +22,10 @@ namespace Umbraco.Cms.Infrastructure.Persistence /// The Poco data factory. /// The database type. /// The mappers. - public SqlContext(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, IMapperCollection mappers = null) - : this(sqlSyntax, databaseType, pocoDataFactory, new Lazy(() => mappers ?? new MapperCollection(Enumerable.Empty()))) - { } - - /// - /// Initializes a new instance of the class. - /// - /// The sql syntax provider. - /// The Poco data factory. - /// The database type. - /// The mappers. - public SqlContext(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, Lazy mappers) + public SqlContext(ISqlSyntaxProvider sqlSyntax, DatabaseType databaseType, IPocoDataFactory pocoDataFactory, IMapperCollection mappers = null) { // for tests - _mappers = mappers; + Mappers = mappers; SqlSyntax = sqlSyntax ?? throw new ArgumentNullException(nameof(sqlSyntax)); PocoDataFactory = pocoDataFactory ?? throw new ArgumentNullException(nameof(pocoDataFactory)); @@ -67,6 +55,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence public IPocoDataFactory PocoDataFactory { get; } /// - public IMapperCollection Mappers => _mappers.Value; + public IMapperCollection Mappers { get; } } } diff --git a/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs index c6c0c6b0bb..756490c531 100644 --- a/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs +++ b/src/Umbraco.Infrastructure/Persistence/SqlServerDbProviderFactoryCreator.cs @@ -1,7 +1,9 @@ -using System; +using System; using System.Data.Common; +using System.Linq; using Microsoft.Extensions.Options; using NPoco; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; @@ -49,10 +51,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence } public void CreateDatabase(string providerName) - { - throw new NotSupportedException("Embedded databases are not supported"); - } + => throw new NotSupportedException("Embedded databases are not supported"); - public NPocoMapperCollection ProviderSpecificMappers(string providerName) => new NPocoMapperCollection(Array.Empty()); + public NPocoMapperCollection ProviderSpecificMappers(string providerName) + => new NPocoMapperCollection(() => Enumerable.Empty()); } } diff --git a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs index 0f973101e3..03977a0abe 100644 --- a/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Infrastructure/Persistence/UmbracoDatabaseFactory.cs @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence private readonly DatabaseSchemaCreatorFactory _databaseSchemaCreatorFactory; private readonly NPocoMapperCollection _npocoMappers; private readonly IOptions _globalSettings; - private readonly Lazy _mappers; + private readonly IMapperCollection _mappers; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; @@ -81,7 +81,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence ILoggerFactory loggerFactory, IOptions globalSettings, IOptions connectionStrings, - Lazy mappers, + IMapperCollection mappers, IDbProviderFactoryCreator dbProviderFactoryCreator, DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory, NPocoMapperCollection npocoMappers) diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs index 49cc6b2902..550a64b14d 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockEditorPropertyEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -26,18 +26,14 @@ namespace Umbraco.Cms.Core.PropertyEditors { public const string ContentTypeKeyPropertyKey = "contentTypeKey"; public const string UdiPropertyKey = "udi"; - private readonly Lazy _propertyEditors; public BlockEditorPropertyEditor( IDataValueEditorFactory dataValueEditorFactory, - Lazy propertyEditors) + PropertyEditorCollection propertyEditors) : base(dataValueEditorFactory) - { - _propertyEditors = propertyEditors; - } + => PropertyEditors = propertyEditors; - // has to be lazy else circular dep in ctor - private PropertyEditorCollection PropertyEditors => _propertyEditors.Value; + private PropertyEditorCollection PropertyEditors { get; } #region Value Editor diff --git a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs index 469d908cb3..0955aa5198 100644 --- a/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs +++ b/src/Umbraco.Infrastructure/PropertyEditors/BlockListPropertyEditor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Umbraco. +// Copyright (c) Umbraco. // See LICENSE for more details. using System; @@ -27,7 +27,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public BlockListPropertyEditor( IDataValueEditorFactory dataValueEditorFactory, - Lazy propertyEditors, + PropertyEditorCollection propertyEditors, IIOHelper ioHelper) : base(dataValueEditorFactory, propertyEditors) { diff --git a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs index dc25007e7c..f206e063a3 100644 --- a/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs +++ b/src/Umbraco.Infrastructure/Runtime/SqlMainDomLock.cs @@ -10,6 +10,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using NPoco; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.Runtime; @@ -61,7 +62,7 @@ namespace Umbraco.Cms.Infrastructure.Runtime loggerFactory, _globalSettings, connectionStrings, - new Lazy(() => new MapperCollection(Enumerable.Empty())), + new MapperCollection(() => Enumerable.Empty()), dbProviderFactoryCreator, databaseSchemaCreatorFactory, npocoMappers); diff --git a/src/Umbraco.Persistence.SqlCe/SqlCeSpecificMapperFactory.cs b/src/Umbraco.Persistence.SqlCe/SqlCeSpecificMapperFactory.cs index 0e53561929..3646218fce 100644 --- a/src/Umbraco.Persistence.SqlCe/SqlCeSpecificMapperFactory.cs +++ b/src/Umbraco.Persistence.SqlCe/SqlCeSpecificMapperFactory.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core; +using Umbraco.Cms.Core; using Umbraco.Cms.Infrastructure.Persistence; namespace Umbraco.Cms.Persistence.SqlCe @@ -6,6 +6,6 @@ namespace Umbraco.Cms.Persistence.SqlCe public class SqlCeSpecificMapperFactory : IProviderSpecificMapperFactory { public string ProviderName => Constants.DatabaseProviders.SqlCe; - public NPocoMapperCollection Mappers => new NPocoMapperCollection(new[] {new SqlCeImageMapper()}); + public NPocoMapperCollection Mappers => new NPocoMapperCollection(() => new[] {new SqlCeImageMapper()}); } } diff --git a/src/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs b/src/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs index 5d546cb8d5..b36f7e90d1 100644 --- a/src/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs +++ b/src/Umbraco.Tests.Common/TestHelpers/PublishedContent/AutoPublishedContentType.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using Moq; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.PublishedContent; using Umbraco.Cms.Core.PropertyEditors; @@ -34,7 +35,7 @@ namespace Umbraco.Cms.Tests.Common.TestHelpers.PublishedContent }; dataTypeServiceMock.Setup(x => x.GetAll()).Returns(dataType.Yield); - var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(Array.Empty()), dataTypeServiceMock.Object); + var factory = new PublishedContentTypeFactory(Mock.Of(), new PropertyValueConverterCollection(() => Enumerable.Empty()), dataTypeServiceMock.Object); Default = factory.CreatePropertyType("*", 666); } diff --git a/src/Umbraco.Tests.Integration/Testing/TestUmbracoDatabaseFactoryProvider.cs b/src/Umbraco.Tests.Integration/Testing/TestUmbracoDatabaseFactoryProvider.cs index 344a616e76..8d702e82d8 100644 --- a/src/Umbraco.Tests.Integration/Testing/TestUmbracoDatabaseFactoryProvider.cs +++ b/src/Umbraco.Tests.Integration/Testing/TestUmbracoDatabaseFactoryProvider.cs @@ -20,7 +20,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing private readonly ILoggerFactory _loggerFactory; private readonly IOptions _globalSettings; private readonly IOptions _connectionStrings; - private readonly Lazy _mappers; + private readonly IMapperCollection _mappers; private readonly IDbProviderFactoryCreator _dbProviderFactoryCreator; private readonly DatabaseSchemaCreatorFactory _databaseSchemaCreatorFactory; private readonly NPocoMapperCollection _npocoMappers; @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Tests.Integration.Testing ILoggerFactory loggerFactory, IOptions globalSettings, IOptions connectionStrings, - Lazy mappers, + IMapperCollection mappers, IDbProviderFactoryCreator dbProviderFactoryCreator, DatabaseSchemaCreatorFactory databaseSchemaCreatorFactory, NPocoMapperCollection npocoMappers) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs index c0a1b1700c..987ba90070 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs @@ -59,7 +59,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { var contentValueSetBuilder = new ContentValueSetBuilder( _propertyEditors, - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), + new UrlSegmentProviderCollection(() => new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), GetMockUserService(), _shortStringHelper, _scopeProvider, @@ -85,7 +85,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Examine.Lucene.UmbracoExamine { var mediaValueSetBuilder = new MediaValueSetBuilder( _propertyEditors, - new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), + new UrlSegmentProviderCollection(() => new[] { new DefaultUrlSegmentProvider(_shortStringHelper) }), _mediaUrlGenerators, GetMockUserService(), _shortStringHelper, diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs index 8cf2c8c932..a7e1641839 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DocumentRepositoryTest.cs @@ -10,6 +10,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.IO; @@ -99,8 +100,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos appCaches ??= AppCaches; DocumentRepository ctRepository = CreateRepository(scopeAccessor, out contentTypeRepository, out TemplateRepository tr); - var editors = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); - dtdRepository = new DataTypeRepository(scopeAccessor, appCaches, new Lazy(() => editors), LoggerFactory.CreateLogger(), LoggerFactory, ConfigurationEditorJsonSerializer); + var editors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); + dtdRepository = new DataTypeRepository(scopeAccessor, appCaches, editors, LoggerFactory.CreateLogger(), LoggerFactory, ConfigurationEditorJsonSerializer); return ctRepository; } @@ -121,8 +122,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger(), relationTypeRepository, entityRepository); - var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); + var dataValueReferences = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); var repository = new DocumentRepository( scopeAccessor, appCaches, @@ -775,7 +776,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos // One variant (by culture) content type named "umbVariantTextPage" // with properties, every 2nd one being variant (by culture), the other being invariant - ContentType variantCt = ContentTypeBuilder.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage", defaultTemplateId: template.Id); + ContentType variantCt = ContentTypeBuilder.CreateSimpleContentType("umbVariantTextpage", "Variant Textpage", defaultTemplateId: template.Id); variantCt.Variations = ContentVariation.Culture; var propTypes = variantCt.PropertyTypes.ToList(); for (int i = 0; i < propTypes.Count; i++) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs index 2d7e6937ec..fc98b8ef10 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MediaRepositoryTest.cs @@ -9,6 +9,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Models; @@ -63,9 +64,9 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger(), relationTypeRepository, entityRepository); - var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var mediaUrlGenerators = new MediaUrlGeneratorCollection(Enumerable.Empty()); - var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); + var mediaUrlGenerators = new MediaUrlGeneratorCollection(() => Enumerable.Empty()); + var dataValueReferences = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); var repository = new MediaRepository(scopeAccessor, appCaches, LoggerFactory.CreateLogger(), LoggerFactory, mediaTypeRepository, tagRepository, Mock.Of(), relationRepository, relationTypeRepository, propertyEditors, mediaUrlGenerators, dataValueReferences, DataTypeService, JsonSerializer, Mock.Of()); return repository; } diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs index 57d64638fb..d8bf4be14a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/MemberRepositoryTest.cs @@ -12,6 +12,7 @@ using NPoco; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; @@ -55,8 +56,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos ITagRepository tagRepo = GetRequiredService(); IRelationTypeRepository relationTypeRepository = GetRequiredService(); IRelationRepository relationRepository = GetRequiredService(); - var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); + var dataValueReferences = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); return new MemberRepository( accessor, AppCaches.Disabled, diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs index f9587179dd..d137ef8d05 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/TemplateRepositoryTest.cs @@ -11,6 +11,7 @@ using Moq; using NUnit.Framework; using Umbraco.Cms.Core; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Events; using Umbraco.Cms.Core.Hosting; @@ -267,8 +268,8 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Persistence.Repos var relationTypeRepository = new RelationTypeRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger()); var entityRepository = new EntityRepository(scopeAccessor, AppCaches.Disabled); var relationRepository = new RelationRepository(scopeAccessor, LoggerFactory.CreateLogger(), relationTypeRepository, entityRepository); - var propertyEditors = new Lazy(() => new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty()))); - var dataValueReferences = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => Enumerable.Empty())); + var dataValueReferences = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); var contentRepo = new DocumentRepository(scopeAccessor, AppCaches.Disabled, LoggerFactory.CreateLogger(), LoggerFactory, contentTypeRepository, templateRepository, tagRepository, languageRepository, relationRepository, relationTypeRepository, propertyEditors, dataValueReferences, dataTypeService, serializer, Mock.Of()); Template template = TemplateBuilder.CreateTextPageTemplate(); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs index 2d81228257..eb10bb010a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs @@ -8,6 +8,7 @@ using System.Threading; using NPoco; using NUnit.Framework; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; @@ -107,7 +108,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services member = MemberService.GetById(member.Id); Assert.AreEqual("xemail", member.Email); - var contentTypeFactory = new PublishedContentTypeFactory(new NoopPublishedModelFactory(), new PropertyValueConverterCollection(Enumerable.Empty()), GetRequiredService()); + var contentTypeFactory = new PublishedContentTypeFactory(new NoopPublishedModelFactory(), new PropertyValueConverterCollection(() => Enumerable.Empty()), GetRequiredService()); var pmemberType = new PublishedContentType(memberType, contentTypeFactory); var publishedSnapshotAccessor = new TestPublishedSnapshotAccessor(); diff --git a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs index 3e728a96c9..178d480102 100644 --- a/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs +++ b/src/Umbraco.Tests.UnitTests/AutoFixture/AutoMoqDataAttribute.cs @@ -2,6 +2,7 @@ // See LICENSE for more details. using System; +using System.Linq; using AutoFixture; using AutoFixture.AutoMoq; using AutoFixture.Kernel; @@ -11,6 +12,7 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Options; using Moq; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Configuration; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; @@ -74,7 +76,7 @@ namespace Umbraco.Cms.Tests.UnitTests.AutoFixture Options.Create(new GlobalSettings()), Mock.Of(x => x.ToAbsolute(It.IsAny()) == "/umbraco" && x.ApplicationVirtualPath == string.Empty), Mock.Of(x => x.Level == RuntimeLevel.Run), - new UmbracoApiControllerTypeCollection(Array.Empty())))); + new UmbracoApiControllerTypeCollection(() => Enumerable.Empty())))); fixture.Customize(u => u.FromFactory( () => new PreviewRoutes( diff --git a/src/Umbraco.Tests.UnitTests/TestHelpers/BaseUsingSqlSyntax.cs b/src/Umbraco.Tests.UnitTests/TestHelpers/BaseUsingSqlSyntax.cs index db739d9369..023c9ad952 100644 --- a/src/Umbraco.Tests.UnitTests/TestHelpers/BaseUsingSqlSyntax.cs +++ b/src/Umbraco.Tests.UnitTests/TestHelpers/BaseUsingSqlSyntax.cs @@ -47,7 +47,7 @@ namespace Umbraco.Cms.Tests.UnitTests.TestHelpers }; var pocoDataFactory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, pocoMappers).Init()); var sqlSyntax = new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())); - SqlContext = new SqlContext(sqlSyntax, DatabaseType.SqlServer2012, pocoDataFactory, new Lazy(() => factory.GetRequiredService())); + SqlContext = new SqlContext(sqlSyntax, DatabaseType.SqlServer2012, pocoDataFactory, factory.GetRequiredService()); Mappers = factory.GetRequiredService(); } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DistributedCache/DistributedCacheTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DistributedCache/DistributedCacheTests.cs index 0273690f09..7397e06d1d 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DistributedCache/DistributedCacheTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Cache/DistributedCache/DistributedCacheTests.cs @@ -28,7 +28,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Cache.DistributedCache ServerRegistrar = new TestServerRegistrar(); ServerMessenger = new TestServerMessenger(); - var cacheRefresherCollection = new CacheRefresherCollection(new[] + var cacheRefresherCollection = new CacheRefresherCollection(() => new[] { new TestCacheRefresher() }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs index 71498bfba8..0c9b94a978 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Components/ComponentTests.cs @@ -49,13 +49,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Components ILogger logger = loggerFactory.CreateLogger("GenericLogger"); var globalSettings = new GlobalSettings(); var connectionStrings = new ConnectionStrings(); - var mapperCollection = new NPocoMapperCollection(new[] { new NullableDateMapper() }); + var mapperCollection = new NPocoMapperCollection(() => new[] { new NullableDateMapper() }); var f = new UmbracoDatabaseFactory( loggerFactory.CreateLogger(), loggerFactory, Options.Create(globalSettings), Options.Create(connectionStrings), - new Lazy(() => new MapperCollection(Enumerable.Empty())), + new MapperCollection(() => Enumerable.Empty()), TestHelper.DbProviderFactoryCreator, new DatabaseSchemaCreatorFactory(loggerFactory.CreateLogger(), loggerFactory, new UmbracoVersion(), Mock.Of()), mapperCollection); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs index 5fe93ecf42..ad9a19b3f8 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs @@ -537,8 +537,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing // ReSharper disable once ClassNeverInstantiated.Local private class TestCollection : BuilderCollectionBase { - public TestCollection(IEnumerable items) - : base(items) + public TestCollection(Func> items) : base(items) { } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/LazyCollectionBuilderTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/LazyCollectionBuilderTests.cs index 0a45415647..ec36e1c670 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/LazyCollectionBuilderTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/LazyCollectionBuilderTests.cs @@ -184,8 +184,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing // ReSharper disable once ClassNeverInstantiated.Local private class TestCollection : BuilderCollectionBase { - public TestCollection(IEnumerable items) - : base(items) + public TestCollection(Func> items) : base(items) { } } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs index 93c8e21eed..c93e5087b7 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Manifest/ManifestParserTests.cs @@ -12,6 +12,7 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NUnit.Framework; using Umbraco.Cms.Core.Cache; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Dashboards; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Manifest; @@ -44,8 +45,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Manifest NullLoggerFactory loggerFactory = NullLoggerFactory.Instance; _parser = new ManifestParser( AppCaches.Disabled, - new ManifestValueValidatorCollection(validators), - new ManifestFilterCollection(Array.Empty()), + new ManifestValueValidatorCollection(() => validators), + new ManifestFilterCollection(() => Enumerable.Empty()), loggerFactory.CreateLogger(), _ioHelper, TestHelper.GetHostingEnvironment(), diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs index 4c0853aaf0..892ef696c3 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Models/VariationTests.cs @@ -599,7 +599,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Models Mock.Get(dataTypeService).Setup(x => x.GetDataType(It.Is(y => y == Constants.DataTypes.Textbox))) .Returns(new DataType(textBoxEditor, serializer)); - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(new[] { textBoxEditor })); + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(() => new[] { textBoxEditor })); return new PropertyValidationService( propertyEditorCollection, dataTypeService, diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Packaging/PendingPackageMigrationsTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Packaging/PendingPackageMigrationsTests.cs index d726cf32f8..119b95d545 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Packaging/PendingPackageMigrationsTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Packaging/PendingPackageMigrationsTests.cs @@ -31,7 +31,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Packaging private PendingPackageMigrations GetPendingPackageMigrations() => new PendingPackageMigrations( Mock.Of>(), - new PackageMigrationPlanCollection(new[] + new PackageMigrationPlanCollection(() => new[] { new TestPackageMigrationPlan() })); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs index 86496fdead..944b35023f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/DataValueReferenceFactoryCollectionTests.cs @@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using NUnit.Framework; using Umbraco.Cms.Core; +using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Hosting; using Umbraco.Cms.Core.IO; using Umbraco.Cms.Core.Models; @@ -36,13 +37,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors [Test] public void GetAllReferences_All_Variants_With_IDataValueReferenceFactory() { - var collection = new DataValueReferenceFactoryCollection(new TestDataValueReferenceFactory().Yield()); + var collection = new DataValueReferenceFactoryCollection(() => new TestDataValueReferenceFactory().Yield()); // label does not implement IDataValueReference var labelEditor = new LabelPropertyEditor( DataValueEditorFactory, IOHelper); - var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(labelEditor.Yield())); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => labelEditor.Yield())); var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); @@ -102,13 +103,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors [Test] public void GetAllReferences_All_Variants_With_IDataValueReference_Editor() { - var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); // mediaPicker does implement IDataValueReference var mediaPicker = new MediaPickerPropertyEditor( DataValueEditorFactory, IOHelper); - var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => mediaPicker.Yield())); var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); @@ -168,13 +169,13 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors [Test] public void GetAllReferences_Invariant_With_IDataValueReference_Editor() { - var collection = new DataValueReferenceFactoryCollection(Enumerable.Empty()); + var collection = new DataValueReferenceFactoryCollection(() => Enumerable.Empty()); // mediaPicker does implement IDataValueReference var mediaPicker = new MediaPickerPropertyEditor( DataValueEditorFactory, IOHelper); - var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(mediaPicker.Yield())); + var propertyEditors = new PropertyEditorCollection(new DataEditorCollection(() => mediaPicker.Yield())); var trackedUdi1 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi2 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); var trackedUdi3 = Udi.Create(Constants.UdiEntityType.Media, Guid.NewGuid()).ToString(); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/PropertyEditorValueConverterTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/PropertyEditorValueConverterTests.cs index 06d34a8fa1..f6edb2a105 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/PropertyEditorValueConverterTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/PropertyEditorValueConverterTests.cs @@ -102,7 +102,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.PropertyEditors var publishedPropType = new PublishedPropertyType( new PublishedContentType(Guid.NewGuid(), 1234, "test", PublishedItemType.Content, Enumerable.Empty(), Enumerable.Empty(), ContentVariation.Nothing), new PropertyType(Mock.Of(), "test", ValueStorageType.Nvarchar) { DataTypeId = 123 }, - new PropertyValueConverterCollection(Enumerable.Empty()), + new PropertyValueConverterCollection(() => Enumerable.Empty()), Mock.Of(), mockPublishedContentTypeFactory.Object); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ConvertersTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ConvertersTests.cs index e73d29f251..76a536c2e0 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ConvertersTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/ConvertersTests.cs @@ -26,7 +26,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published [Test] public void SimpleConverter1Test() { - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { new SimpleConverter1(), }); @@ -107,7 +107,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot).Returns(publishedSnapshotMock.Object); IPublishedSnapshotAccessor publishedSnapshotAccessor = publishedSnapshotAccessorMock.Object; - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { new SimpleConverter2(publishedSnapshotAccessor), }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/NestedContentTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/NestedContentTests.cs index daa3dbdab2..5f5de0cf4e 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/NestedContentTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/NestedContentTests.cs @@ -38,7 +38,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published PropertyEditorCollection editors = null; var editor = new NestedContentPropertyEditor(Mock.Of(),Mock.Of()); - editors = new PropertyEditorCollection(new DataEditorCollection(new DataEditor[] { editor })); + editors = new PropertyEditorCollection(new DataEditorCollection(() => new DataEditor[] { editor })); var serializer = new ConfigurationEditorJsonSerializer(); @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published .Setup(x => x.PublishedSnapshot) .Returns(publishedSnapshot.Object); - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { new NestedContentSingleValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog), new NestedContentManyValueConverter(publishedSnapshotAccessor.Object, publishedModelFactory.Object, proflog), diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheLevelTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheLevelTests.cs index edb8cd17ee..a57355afd9 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheLevelTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheLevelTests.cs @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published { var converter = new CacheConverter1(cacheLevel); - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { converter, }); @@ -119,7 +119,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published { var converter = new CacheConverter1(converterCacheLevel); - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { converter, }); @@ -199,7 +199,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Published { var converter = new CacheConverter1(PropertyCacheLevel.Unknown); - var converters = new PropertyValueConverterCollection(new IPropertyValueConverter[] + var converters = new PropertyValueConverterCollection(() => new IPropertyValueConverter[] { converter, }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs index ba338cdf31..4d0e643d7f 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlImageSourceParserTests.cs @@ -82,8 +82,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Templates var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, Options.Create(webRoutingSettings), - new UrlProviderCollection(Enumerable.Empty()), - new MediaUrlProviderCollection(new[] { mediaUrlProvider.Object }), + new UrlProviderCollection(() => Enumerable.Empty()), + new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of()); using (UmbracoContextReference reference = umbracoContextFactory.EnsureUmbracoContext()) { diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs index b3c4d96328..670f528bbd 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Templates/HtmlLocalLinkParserTests.cs @@ -79,8 +79,8 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Templates var publishedUrlProvider = new UrlProvider( umbracoContextAccessor, Microsoft.Extensions.Options.Options.Create(webRoutingSettings), - new UrlProviderCollection(new[] { contentUrlProvider.Object }), - new MediaUrlProviderCollection(new[] { mediaUrlProvider.Object }), + new UrlProviderCollection(() => new[] { contentUrlProvider.Object }), + new MediaUrlProviderCollection(() => new[] { mediaUrlProvider.Object }), Mock.Of()); using (UmbracoContextReference reference = umbracoContextFactory.EnsureUmbracoContext()) { diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs index 60c0a7476a..f128d49128 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/HostedServices/HealthCheckNotifierTests.cs @@ -124,7 +124,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices new DisabledHealthCheckSettings { Id = Guid.Parse(Check2Id) } } }; - var checks = new HealthCheckCollection(new List + var checks = new HealthCheckCollection(() => new List { new TestHealthCheck1(), new TestHealthCheck2(), @@ -133,7 +133,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.HostedServices _mockNotificationMethod = new Mock(); _mockNotificationMethod.SetupGet(x => x.Enabled).Returns(notificationEnabled); - var notifications = new HealthCheckNotificationMethodCollection(new List { _mockNotificationMethod.Object }); + var notifications = new HealthCheckNotificationMethodCollection(() => new List { _mockNotificationMethod.Object }); var mockRunTimeState = new Mock(); mockRunTimeState.SetupGet(x => x.Level).Returns(runtimeLevel); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs index 0db64a6ec9..9c8f9da75d 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Mapping/MappingTests.cs @@ -42,7 +42,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void SimpleMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition1(), }); @@ -67,7 +67,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void EnumerableMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition1(), }); @@ -101,7 +101,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void InheritedMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition1(), }); @@ -126,7 +126,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void CollectionsMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition2(), }); @@ -141,7 +141,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Explicit] public void ConcurrentMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition1(), new MapperDefinition3(), @@ -201,7 +201,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void EnumMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition4(), }); @@ -225,7 +225,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Mapping [Test] public void NullPropertyMap() { - var definitions = new MapDefinitionCollection(new IMapDefinition[] + var definitions = new MapDefinitionCollection(() => new IMapDefinition[] { new MapperDefinition5(), }); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs index c8f90050e2..fabe1f3a0c 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberManagerTests.cs @@ -49,7 +49,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security _fakeMemberStore = new MemberUserStore( _mockMemberService.Object, - new UmbracoMapper(new MapDefinitionCollection(mapDefinitions), scopeProvider), + new UmbracoMapper(new MapDefinitionCollection(() => mapDefinitions), scopeProvider), scopeProvider, new IdentityErrorDescriber(), Mock.Of()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs index 3fdb3c27f9..96d25d4d04 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Security/MemberUserStoreTests.cs @@ -34,7 +34,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Security return new MemberUserStore( _mockMemberService.Object, - new UmbracoMapper(new MapDefinitionCollection(new List()), mockScopeProvider.Object), + new UmbracoMapper(new MapDefinitionCollection(() => new List()), mockScopeProvider.Object), mockScopeProvider.Object, new IdentityErrorDescriber(), Mock.Of()); diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs index dc70e1e8ce..bfbe6258cc 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Services/PropertyValidationServiceTests.cs @@ -43,7 +43,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Services Mock.Get(dataEditor).Setup(x => x.GetValueEditor(It.IsAny())) .Returns(new CustomTextOnlyValueEditor(new DataEditorAttribute(Constants.PropertyEditors.Aliases.TextBox, "Test Textbox", "textbox"), textService.Object, Mock.Of(), new JsonNetSerializer(), Mock.Of())); - var propEditors = new PropertyEditorCollection(new DataEditorCollection(new[] { dataEditor })); + var propEditors = new PropertyEditorCollection(new DataEditorCollection(() => new[] { dataEditor })); validationService = new PropertyValidationService(propEditors, dataTypeService.Object, Mock.Of()); } diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs index 8006bda3a4..cde267d7dc 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.BackOffice/Controllers/MemberControllerUnitTests.cs @@ -453,7 +453,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers var mockContentAppFactoryCollection = new Mock>(); var hybridBackOfficeSecurityAccessor = new BackOfficeSecurityAccessor(httpContextAccessor); var contentAppFactoryCollection = new ContentAppFactoryCollection( - contentAppFactories.Object, + () => contentAppFactories.Object, mockContentAppFactoryCollection.Object, hybridBackOfficeSecurityAccessor); var mockUserService = new Mock(); @@ -471,7 +471,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers && x.Alias == Constants.PropertyEditors.Aliases.Label); Mock.Get(dataEditor).Setup(x => x.GetValueEditor()).Returns(new TextOnlyValueEditor( new DataEditorAttribute(Constants.PropertyEditors.Aliases.TextBox, "Test Textbox", "textbox"), textService.Object, Mock.Of(), Mock.Of(), Mock.Of())); - var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(new[] { dataEditor })); + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(() => new[] { dataEditor })); IMapDefinition memberMapDefinition = new MemberMapDefinition( commonMapper, @@ -487,7 +487,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.BackOffice.Controllers contentTypeBaseServiceProvider.Object, propertyEditorCollection)); - var map = new MapDefinitionCollection(new List() + var map = new MapDefinitionCollection(() => new List() { new global::Umbraco.Cms.Core.Models.Mapping.MemberMapDefinition(), memberMapDefinition, diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs index 60185a1846..fa7e307c23 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Web.Common/Routing/BackOfficeAreaRoutesTests.cs @@ -86,7 +86,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Web.Common.Routing Options.Create(globalSettings), Mock.Of(x => x.ToAbsolute(It.IsAny()) == "/umbraco" && x.ApplicationVirtualPath == string.Empty), Mock.Of(x => x.Level == level), - new UmbracoApiControllerTypeCollection(new[] { typeof(Testing1Controller) })); + new UmbracoApiControllerTypeCollection(() => new[] { typeof(Testing1Controller) })); return routes; } diff --git a/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs b/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs index 2257f80d88..37a857f78d 100644 --- a/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs +++ b/src/Umbraco.Web.BackOffice/Trees/TreeCollectionBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Microsoft.Extensions.DependencyInjection; using Umbraco.Cms.Core.Composing; @@ -14,7 +14,7 @@ namespace Umbraco.Cms.Web.BackOffice.Trees { private readonly List _trees = new List(); - public TreeCollection CreateCollection(IServiceProvider factory) => new TreeCollection(_trees); + public TreeCollection CreateCollection(IServiceProvider factory) => new TreeCollection(() => _trees); public void RegisterWith(IServiceCollection services) => services.Add(new ServiceDescriptor(typeof(TreeCollection), CreateCollection, ServiceLifetime.Singleton)); diff --git a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs index 30dec7842b..9f0c353092 100644 --- a/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs +++ b/src/Umbraco.Web.Common/Controllers/UmbracoApiControllerTypeCollectionBuilder.cs @@ -1,4 +1,4 @@ -using Umbraco.Cms.Core; +using Umbraco.Cms.Core; using Umbraco.Cms.Core.Composing; namespace Umbraco.Cms.Web.Common.Controllers diff --git a/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs index e77b11a3d8..95c9208df6 100644 --- a/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs +++ b/src/Umbraco.Web.Website/Collections/SurfaceControllerTypeCollection.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Umbraco.Cms.Core.Composing; @@ -6,8 +6,8 @@ namespace Umbraco.Cms.Web.Website.Collections { public class SurfaceControllerTypeCollection : BuilderCollectionBase { - public SurfaceControllerTypeCollection(IEnumerable items) - : base(items) - { } + public SurfaceControllerTypeCollection(Func> items) : base(items) + { + } } } From c08b5173a2426a78f1b81b4e91ac96f66bcf282a Mon Sep 17 00:00:00 2001 From: Shannon Date: Mon, 12 Jul 2021 16:26:19 -0600 Subject: [PATCH 046/102] Fixes issue that the update culture dates were not synced with the version date like they are for the published culture dates. --- .../Models/ContentRepositoryExtensions.cs | 33 ++++++- .../Implement/DocumentRepository.cs | 88 +++++++++++++------ .../Services/ContentServiceTests.cs | 9 +- 3 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs b/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs index a50890bee0..ece903d8e6 100644 --- a/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs +++ b/src/Umbraco.Core/Models/ContentRepositoryExtensions.cs @@ -40,21 +40,48 @@ namespace Umbraco.Extensions /// This is so that in an operation where (for example) 2 languages are updates like french and english, it is possible that /// these dates assigned to them differ by a couple of Ticks, but we need to ensure they are persisted at the exact same time. /// - public static void AdjustDates(this IContent content, DateTime date) + public static void AdjustDates(this IContent content, DateTime date, bool publishing) { + foreach(var culture in content.EditedCultures.ToList()) + { + if (!content.CultureInfos.TryGetValue(culture, out ContentCultureInfos editedInfos)) + { + continue; + } + + // if it's not dirty, it means it hasn't changed so there's nothing to adjust + if (!editedInfos.IsDirty()) + { + continue; + } + + content.CultureInfos.AddOrUpdate(culture, editedInfos.Name, date); + } + + if (!publishing) + { + return; + } + foreach (var culture in content.PublishedCultures.ToList()) { - if (!content.PublishCultureInfos.TryGetValue(culture, out var publishInfos)) + if (!content.PublishCultureInfos.TryGetValue(culture, out ContentCultureInfos publishInfos)) + { continue; + } // if it's not dirty, it means it hasn't changed so there's nothing to adjust if (!publishInfos.IsDirty()) + { continue; + } content.PublishCultureInfos.AddOrUpdate(culture, publishInfos.Name, date); - if (content.CultureInfos.TryGetValue(culture, out var infos)) + if (content.CultureInfos.TryGetValue(culture, out ContentCultureInfos infos)) + { SetCultureInfo(content, culture, infos.Name, date); + } } } diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index b246839440..37cbff5476 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -451,20 +451,30 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } // persist the property data - var propertyDataDtos = PropertyFactory.BuildDtos(entity.ContentType.Variations, entity.VersionId, entity.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out var editedCultures); - foreach (var propertyDataDto in propertyDataDtos) + IEnumerable propertyDataDtos = PropertyFactory.BuildDtos(entity.ContentType.Variations, entity.VersionId, entity.PublishedVersionId, entity.Properties, LanguageRepository, out var edited, out HashSet editedCultures); + foreach (PropertyDataDto propertyDataDto in propertyDataDtos) + { Database.Insert(propertyDataDto); + } + + // refresh content + entity.SetCultureEdited(editedCultures); // if !publishing, we may have a new name != current publish name, // also impacts 'edited' if (!publishing && entity.PublishName != entity.Name) + { edited = true; + } // persist the document dto // at that point, when publishing, the entity still has its old Published value // so we need to explicitly update the dto to persist the correct value if (entity.PublishedState == PublishedState.Publishing) + { dto.Published = true; + } + dto.NodeId = nodeDto.NodeId; entity.Edited = dto.Edited = !dto.Published || edited; // if not published, always edited Database.Insert(dto); @@ -472,19 +482,21 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement //insert the schedule PersistContentSchedule(entity, false); - // persist the variations if (entity.ContentType.VariesByCulture()) { // bump dates to align cultures to version - if (publishing) - entity.AdjustDates(contentVersionDto.VersionDate); + entity.AdjustDates(contentVersionDto.VersionDate, publishing); // names also impact 'edited' // ReSharper disable once UseDeconstruction - foreach (var cultureInfo in entity.CultureInfos) + foreach (ContentCultureInfos cultureInfo in entity.CultureInfos) + { if (cultureInfo.Name != entity.GetPublishName(cultureInfo.Culture)) + { (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(cultureInfo.Culture); + } + } // insert content variations Database.BulkInsertRecords(GetContentVariationDtos(entity, publishing)); @@ -493,9 +505,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement Database.BulkInsertRecords(GetDocumentVariationDtos(entity, editedCultures)); } - // refresh content - entity.SetCultureEdited(editedCultures); - // trigger here, before we reset Published etc OnUowRefreshedEntity(new ContentRefreshNotification(entity, new EventMessages())); @@ -547,7 +556,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // check if we need to make any database changes at all if ((entity.PublishedState == PublishedState.Published || entity.PublishedState == PublishedState.Unpublished) && !isEntityDirty && !entity.IsAnyUserPropertyDirty()) + { return; // no change to save, do nothing, don't even update dates + } // whatever we do, we must check that we are saving the current version var version = Database.Fetch(SqlContext.Sql().Select().From().Where(x => x.Id == entity.VersionId)).FirstOrDefault(); @@ -633,26 +644,32 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement Database.Insert(documentVersionDto); } - // replace the property data (rather than updating) + // replace the property data (rather than updating) // only need to delete for the version that existed, the new version (if any) has no property data yet - var versionToDelete = publishing ? entity.PublishedVersionId : entity.VersionId; - // insert property data - ReplacePropertyValues(entity, versionToDelete, publishing ? entity.PublishedVersionId : 0, out var edited, out var editedCultures); + var versionToDelete = publishing ? entity.PublishedVersionId : entity.VersionId; + + // insert property data + ReplacePropertyValues(entity, versionToDelete, publishing ? entity.PublishedVersionId : 0, out var edited, out HashSet editedCultures); + + // refresh content + entity.SetCultureEdited(editedCultures); // if !publishing, we may have a new name != current publish name, // also impacts 'edited' if (!publishing && entity.PublishName != entity.Name) + { edited = true; + } if (entity.ContentType.VariesByCulture()) { // bump dates to align cultures to version - if (publishing) - entity.AdjustDates(contentVersionDto.VersionDate); + entity.AdjustDates(contentVersionDto.VersionDate, publishing); // names also impact 'edited' // ReSharper disable once UseDeconstruction - foreach (var cultureInfo in entity.CultureInfos) + foreach (var cultureInfo in entity.CultureInfos) + { if (cultureInfo.Name != entity.GetPublishName(cultureInfo.Culture)) { edited = true; @@ -664,6 +681,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // when the name is set, and it all works because the controller does it - but, if someone uses a // service to change a property value and save (without setting name), the update date does not change. } + } // replace the content version variations (rather than updating) // only need to delete for the version that existed, the new version (if any) has no property data yet @@ -687,27 +705,33 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement Database.BulkInsertRecords(GetDocumentVariationDtos(entity, editedCultures)); } - // refresh content - entity.SetCultureEdited(editedCultures); - // update the document dto // at that point, when un/publishing, the entity still has its old Published value // so we need to explicitly update the dto to persist the correct value if (entity.PublishedState == PublishedState.Publishing) + { dto.Published = true; + } else if (entity.PublishedState == PublishedState.Unpublishing) + { dto.Published = false; + } + entity.Edited = dto.Edited = !dto.Published || edited; // if not published, always edited Database.Update(dto); //update the schedule - if (entity.IsPropertyDirty("ContentSchedule")) + if (entity.IsPropertyDirty(nameof(entity.ContentSchedule))) + { PersistContentSchedule(entity, true); + } // if entity is publishing, update tags, else leave tags there // means that implicitly unpublished, or trashed, entities *still* have tags in db if (entity.PublishedState == PublishedState.Publishing) + { SetEntityTags(entity, _tagRepository, _serializer); + } } // trigger here, before we reset Published etc @@ -1224,7 +1248,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement { Template1Id = dto.DocumentVersionDto.TemplateId }; - if (dto.Published) temp.Template2Id = dto.PublishedVersionDto.TemplateId; + if (dto.Published) + temp.Template2Id = dto.PublishedVersionDto.TemplateId; temps.Add(temp); } @@ -1397,7 +1422,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (temp.PublishedVersionId > 0) versions.Add(temp.PublishedVersionId); } - if (versions.Count == 0) return new Dictionary>(); + if (versions.Count == 0) + return new Dictionary>(); var dtos = Database.FetchByGroups(versions, 2000, batch => Sql() @@ -1467,7 +1493,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // if not publishing, we're just updating the 'current' (non-published) version, // so there are no DTOs to create for the 'published' version which remains unchanged - if (!publishing) yield break; + if (!publishing) + yield break; // create dtos for the 'published' version, for published cultures (those having a name) // ReSharper disable once UseDeconstruction @@ -1588,7 +1615,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement private void EnsureVariantNamesAreUnique(IContent content, bool publishing) { - if (!EnsureUniqueNaming || !content.ContentType.VariesByCulture() || content.CultureInfos.Count == 0) return; + if (!EnsureUniqueNaming || !content.ContentType.VariesByCulture() || content.CultureInfos.Count == 0) + return; // get names per culture, at same level (ie all siblings) var sql = SqlEnsureVariantNamesAreUnique.Sql(true, NodeObjectTypeId, content.ParentId, content.Id); @@ -1596,7 +1624,8 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement .GroupBy(x => x.LanguageId) .ToDictionary(x => x.Key, x => x); - if (names.Count == 0) return; + if (names.Count == 0) + return; // note: the code below means we are going to unique-ify every culture names, regardless // of whether the name has changed (ie the culture has been updated) - some saving culture @@ -1605,14 +1634,17 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement foreach (var cultureInfo in content.CultureInfos) { var langId = LanguageRepository.GetIdByIsoCode(cultureInfo.Culture); - if (!langId.HasValue) continue; - if (!names.TryGetValue(langId.Value, out var cultureNames)) continue; + if (!langId.HasValue) + continue; + if (!names.TryGetValue(langId.Value, out var cultureNames)) + continue; // get a unique name var otherNames = cultureNames.Select(x => new SimilarNodeName { Id = x.Id, Name = x.Name }); var uniqueName = SimilarNodeName.GetUniqueName(otherNames, 0, cultureInfo.Name); - if (uniqueName == content.GetCultureName(cultureInfo.Culture)) continue; + if (uniqueName == content.GetCultureName(cultureInfo.Culture)) + continue; // update the name, and the publish name if published content.SetCultureName(uniqueName, cultureInfo.Culture); diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs index 45fbf7582e..60441a013d 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/ContentServiceTests.cs @@ -2374,16 +2374,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services // what we do in the controller to get rollback versions IContent[] versionsSlimFr = versionsSlim.Where(x => x.UpdateDate == x.GetUpdateDate(langFr.IsoCode)).ToArray(); - // TODO this should expect 4, but as the comment below tells we are "*not* properly track 'dirty' for culture" - // This should be changed to 4 as soon a this functionality works. Currently it is always 3 due to the sleep - // before we save versionId5 - Assert.AreEqual(3, versionsSlimFr.Length); + Assert.AreEqual(4, versionsSlimFr.Length); // alas, at the moment we do *not* properly track 'dirty' for cultures, meaning // that we cannot synchronize dates the way we do with publish dates - and so this // would fail - the version UpdateDate is greater than the cultures'. - // Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langFr.IsoCode)); - // Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langDa.IsoCode)); + Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langFr.IsoCode)); + Assert.AreEqual(versions[0].UpdateDate, versions[0].GetUpdateDate(langDa.IsoCode)); // now roll french back to its very first version page.CopyFrom(versions[4], langFr.IsoCode); // only the pure FR values From 3b611d6551429cabdca99791ff37dc8f4861663c Mon Sep 17 00:00:00 2001 From: Mole Date: Tue, 13 Jul 2021 15:20:50 +0200 Subject: [PATCH 047/102] Convert string[] to dictionary vars --- src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs index 2506d1b44d..ebb09f62c2 100644 --- a/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs +++ b/src/Umbraco.Core/Services/LocalizedTextServiceExtensions.cs @@ -55,7 +55,7 @@ namespace Umbraco.Extensions /// /// public static string Localize(this ILocalizedTextService manager, string area, string alias, CultureInfo culture, string[] tokens) - => manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, tokens); + => manager.Localize(area, alias, Thread.CurrentThread.CurrentUICulture, ConvertToDictionaryVars(tokens)); /// /// Convert an array of strings to a dictionary of indices -> values @@ -88,7 +88,7 @@ namespace Umbraco.Extensions var areaAndKey = text.Split('_'); - if (areaAndKey.Length < 2) + if (areaAndKey.Length < 2) return text; value = manager.Localize(areaAndKey[0], areaAndKey[1]); From d045003fb5ff352611b38d75fbe032be884cab6c Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Tue, 13 Jul 2021 11:02:45 -0400 Subject: [PATCH 048/102] add userId to IX_ExternalLogin_LoginProvider --- .../Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs index ef29207093..a582157c6c 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs @@ -45,8 +45,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 Create .Index(indexName1) .OnTable(ExternalLoginDto.TableName) - .OnColumn("loginProvider") - .Ascending() + .OnColumn("loginProvider").Ascending() + .OnColumn("userId").Ascending() .WithOptions() .Unique() .WithOptions() From 1fc4bf6dcde744d1b4ec67e1c590ef08118ac170 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:06 +1000 Subject: [PATCH 049/102] Update src/Umbraco.Core/Events/IScopedNotificationPublisher.cs Co-authored-by: Andy Butland --- src/Umbraco.Core/Events/IScopedNotificationPublisher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs index 8ac4f4312d..58fdafc341 100644 --- a/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/IScopedNotificationPublisher.cs @@ -10,7 +10,7 @@ namespace Umbraco.Cms.Core.Events public interface IScopedNotificationPublisher { /// - /// Supresses all notifications from being added/created until the result object is disposed. + /// Suppresses all notifications from being added/created until the result object is disposed. /// /// IDisposable Suppress(); From 47a8a3f5ffa71f76c92f09357e8ec1ad2246acaf Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:14 +1000 Subject: [PATCH 050/102] Update src/Umbraco.Infrastructure/Services/Implement/MediaService.cs Co-authored-by: Andy Butland --- src/Umbraco.Infrastructure/Services/Implement/MediaService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 501f675fdc..f26bb68342 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -685,7 +685,7 @@ namespace Umbraco.Cms.Core.Services.Implement _mediaRepository.Save(media); scope.Notifications.Publish(new MediaSavedNotification(media, eventMessages).WithStateFrom(savingNotification)); - // TODO: See note about supressing events in content service + // TODO: See note about suppressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(media, TreeChangeTypes.RefreshNode, eventMessages)); Audit(AuditType.Save, userId, media.Id); From 127c0e1be0cea803da641262aefd5bcf76faa000 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:19 +1000 Subject: [PATCH 051/102] Update src/Umbraco.Infrastructure/Services/Implement/MediaService.cs Co-authored-by: Andy Butland --- src/Umbraco.Infrastructure/Services/Implement/MediaService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index f26bb68342..9dbf33a4d1 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -728,7 +728,7 @@ namespace Umbraco.Cms.Core.Services.Implement } scope.Notifications.Publish(new MediaSavedNotification(mediasA, messages).WithStateFrom(savingNotification)); - // TODO: See note about supressing events in content service + // TODO: See note about suppressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(treeChanges, messages)); Audit(AuditType.Save, userId == -1 ? 0 : userId, Cms.Core.Constants.System.Root, "Bulk save media"); From a39f5da65b0e2fc74b7636a8fbbfa1d5b7d8d701 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:26 +1000 Subject: [PATCH 052/102] Update src/Umbraco.Infrastructure/Services/Implement/MediaService.cs Co-authored-by: Andy Butland --- src/Umbraco.Infrastructure/Services/Implement/MediaService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs index 9dbf33a4d1..0239ad8e60 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MediaService.cs @@ -1129,7 +1129,7 @@ namespace Umbraco.Cms.Core.Services.Implement } scope.Notifications.Publish(new MediaSavedNotification(itemsA, messages).WithStateFrom(savingNotification)); - // TODO: See note about supressing events in content service + // TODO: See note about suppressing events in content service scope.Notifications.Publish(new MediaTreeChangeNotification(saved, TreeChangeTypes.RefreshNode, messages)); Audit(AuditType.Sort, userId, 0); From e30bc55ffede0e7d80bb8df12f83718a56c77c6d Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:35 +1000 Subject: [PATCH 053/102] Update src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs Co-authored-by: Andy Butland --- .../Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs index c40249b886..4d71959417 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs @@ -17,7 +17,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping { [TestFixture] [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] - public class SupressNotificationsTests : UmbracoIntegrationTest + public class SuppressNotificationsTests : UmbracoIntegrationTest { private IContentService ContentService => GetRequiredService(); private IContentTypeService ContentTypeService => GetRequiredService(); From 3e586991df01b0030ba58b8aecbf2940faa37e88 Mon Sep 17 00:00:00 2001 From: Shannon Deminick Date: Wed, 14 Jul 2021 01:19:40 +1000 Subject: [PATCH 054/102] Update src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs Co-authored-by: Andy Butland --- .../Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs index 4d71959417..c5cff66d0a 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Scoping/SupressNotificationsTests.cs @@ -84,7 +84,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Scoping } [Test] - public void GivenSuppressedNotificationsOnParent_WhenChildSupresses_ThenExceptionIsThrown() + public void GivenSuppressedNotificationsOnParent_WhenChildSuppresses_ThenExceptionIsThrown() { using (IScope parentScope = ScopeProvider.CreateScope(autoComplete: true)) using (IDisposable parentSuppressed = parentScope.Notifications.Suppress()) From 961d70e352a606bf6009fdea873c7c30e57dfbc8 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 13 Jul 2021 09:52:31 -0600 Subject: [PATCH 055/102] Fixes when SetCultureEdited and AdjustDates is called. --- .../Implement/DocumentRepository.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs index 37cbff5476..3f735e2083 100644 --- a/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs +++ b/src/Umbraco.Infrastructure/Persistence/Repositories/Implement/DocumentRepository.cs @@ -457,9 +457,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement Database.Insert(propertyDataDto); } - // refresh content - entity.SetCultureEdited(editedCultures); - // if !publishing, we may have a new name != current publish name, // also impacts 'edited' if (!publishing && entity.PublishName != entity.Name) @@ -485,19 +482,22 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // persist the variations if (entity.ContentType.VariesByCulture()) { - // bump dates to align cultures to version - entity.AdjustDates(contentVersionDto.VersionDate, publishing); - // names also impact 'edited' // ReSharper disable once UseDeconstruction foreach (ContentCultureInfos cultureInfo in entity.CultureInfos) { if (cultureInfo.Name != entity.GetPublishName(cultureInfo.Culture)) { - (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(cultureInfo.Culture); + (editedCultures ??= new HashSet(StringComparer.OrdinalIgnoreCase)).Add(cultureInfo.Culture); } } + // refresh content + entity.SetCultureEdited(editedCultures); + + // bump dates to align cultures to version + entity.AdjustDates(contentVersionDto.VersionDate, publishing); + // insert content variations Database.BulkInsertRecords(GetContentVariationDtos(entity, publishing)); @@ -651,9 +651,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement // insert property data ReplacePropertyValues(entity, versionToDelete, publishing ? entity.PublishedVersionId : 0, out var edited, out HashSet editedCultures); - // refresh content - entity.SetCultureEdited(editedCultures); - // if !publishing, we may have a new name != current publish name, // also impacts 'edited' if (!publishing && entity.PublishName != entity.Name) @@ -663,9 +660,6 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (entity.ContentType.VariesByCulture()) { - // bump dates to align cultures to version - entity.AdjustDates(contentVersionDto.VersionDate, publishing); - // names also impact 'edited' // ReSharper disable once UseDeconstruction foreach (var cultureInfo in entity.CultureInfos) @@ -673,7 +667,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement if (cultureInfo.Name != entity.GetPublishName(cultureInfo.Culture)) { edited = true; - (editedCultures ?? (editedCultures = new HashSet(StringComparer.OrdinalIgnoreCase))).Add(cultureInfo.Culture); + (editedCultures ??= new HashSet(StringComparer.OrdinalIgnoreCase)).Add(cultureInfo.Culture); // TODO: change tracking // at the moment, we don't do any dirty tracking on property values, so we don't know whether the @@ -683,6 +677,12 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement } } + // refresh content + entity.SetCultureEdited(editedCultures); + + // bump dates to align cultures to version + entity.AdjustDates(contentVersionDto.VersionDate, publishing); + // replace the content version variations (rather than updating) // only need to delete for the version that existed, the new version (if any) has no property data yet var deleteContentVariations = Sql().Delete().Where(x => x.VersionId == versionToDelete); From 7913ae5463869005225e4fc00ce4e94930ee7129 Mon Sep 17 00:00:00 2001 From: Shannon Date: Tue, 13 Jul 2021 10:04:47 -0600 Subject: [PATCH 056/102] fix test --- .../Umbraco.Core/Composing/CollectionBuildersTests.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs index ad9a19b3f8..b375e3a7c6 100644 --- a/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs +++ b/src/Umbraco.Tests.UnitTests/Umbraco.Core/Composing/CollectionBuildersTests.cs @@ -406,9 +406,14 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Core.Composing using (IServiceScope scope = serviceProvider.CreateScope()) { col2 = scope.ServiceProvider.GetRequiredService(); - } - AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); + // NOTE: We must assert here so that the lazy collection is resolved + // within this service provider scope, else if you resolve the collection + // after the service provider scope is disposed, you'll get an object + // disposed error (expected). + AssertCollection(col2, typeof(Resolved1), typeof(Resolved2)); + } + AssertNotSameCollection(col1A, col2); } From d4760f546794cc82ce65507d5b22ef2da5762f3d Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Tue, 13 Jul 2021 13:19:28 -0400 Subject: [PATCH 057/102] include userId in IX_umbracoExternalLogin_LoginProvider --- .../Persistence/Dtos/ExternalLoginDto.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs index 3fc65b28a5..95ff357abe 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs @@ -25,9 +25,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos public int UserId { get; set; } [Column("loginProvider")] - [Length(4000)] // TODO: This value seems WAY too high, this is just a name + [Length(400)] [NullSetting(NullSetting = NullSettings.NotNull)] - [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_LoginProvider")] + [Index(IndexTypes.UniqueNonClustered, ForColumns = "loginProvider,userId", Name = "IX_" + TableName + "_LoginProvider")] public string LoginProvider { get; set; } [Column("providerKey")] From 0ca4dd176ca35a9d43d0c4db292a7c2a99a90942 Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Tue, 13 Jul 2021 13:55:44 -0400 Subject: [PATCH 058/102] Add some comments --- .../Persistence/Dtos/ExternalLoginDto.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs index 95ff357abe..69bf1b837e 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs @@ -24,12 +24,18 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos [Index(IndexTypes.NonClustered)] public int UserId { get; set; } + /// + /// Used to store the name of the provider (i.e. Facebook, Google) + /// [Column("loginProvider")] [Length(400)] [NullSetting(NullSetting = NullSettings.NotNull)] [Index(IndexTypes.UniqueNonClustered, ForColumns = "loginProvider,userId", Name = "IX_" + TableName + "_LoginProvider")] public string LoginProvider { get; set; } + /// + /// Stores the key the provider uses to lookup the login + /// [Column("providerKey")] [Length(4000)] [NullSetting(NullSetting = NullSettings.NotNull)] From 0c125492f4200fe6b856509632951a0e6aa3a851 Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Tue, 13 Jul 2021 20:58:37 -0400 Subject: [PATCH 059/102] Fix External Login Providers using wrong property for icon --- .../src/views/components/application/umb-login.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 02429fa164..56d5aa8c93 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -129,7 +129,7 @@ ng-class="login.properties.ButtonStyle" id="{{login.authType}}" name="provider" value="{{login.authType}}" title="Log in using your {{login.caption}} account"> - + Sign in with {{login.caption}} From acda7706bec0a6e038a438bc8f1856d0a080019c Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Tue, 13 Jul 2021 21:04:47 -0400 Subject: [PATCH 060/102] Remove changes from temp-10656 --- .../Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs | 4 ++-- .../Persistence/Dtos/ExternalLoginDto.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs index a582157c6c..ef29207093 100644 --- a/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs +++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/V_9_0_0/ExternalLoginTableIndexes.cs @@ -45,8 +45,8 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade.V_9_0_0 Create .Index(indexName1) .OnTable(ExternalLoginDto.TableName) - .OnColumn("loginProvider").Ascending() - .OnColumn("userId").Ascending() + .OnColumn("loginProvider") + .Ascending() .WithOptions() .Unique() .WithOptions() diff --git a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs index 95ff357abe..3fc65b28a5 100644 --- a/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs +++ b/src/Umbraco.Infrastructure/Persistence/Dtos/ExternalLoginDto.cs @@ -25,9 +25,9 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Dtos public int UserId { get; set; } [Column("loginProvider")] - [Length(400)] + [Length(4000)] // TODO: This value seems WAY too high, this is just a name [NullSetting(NullSetting = NullSettings.NotNull)] - [Index(IndexTypes.UniqueNonClustered, ForColumns = "loginProvider,userId", Name = "IX_" + TableName + "_LoginProvider")] + [Index(IndexTypes.UniqueNonClustered, Name = "IX_" + TableName + "_LoginProvider")] public string LoginProvider { get; set; } [Column("providerKey")] From 96837a18a18ce60854d94726be1b51e8b4f794a3 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 14 Jul 2021 11:03:09 +0200 Subject: [PATCH 061/102] Correctly return false from TryGetMediaPath if the value is null --- .../PropertyEditors/MediaUrlGeneratorCollection.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs index cd3a36c607..35913d3fe4 100644 --- a/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/MediaUrlGeneratorCollection.cs @@ -12,6 +12,14 @@ namespace Umbraco.Cms.Core.PropertyEditors public bool TryGetMediaPath(string propertyEditorAlias, object value, out string mediaPath) { + // We can't get a media path from a null value + // The value will be null when uploading a brand new image, since we try to get the "old path" which doesn't exist yet. + if (value is null) + { + mediaPath = null; + return false; + } + foreach(IMediaUrlGenerator generator in this) { if (generator.TryGetMediaPath(propertyEditorAlias, value, out var mp)) From 2ea40a103a596c35dc1d4bd8f270033a839dfb54 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 14 Jul 2021 14:18:22 +0200 Subject: [PATCH 062/102] Make ValidationProblem methods unambiguous --- .../Controllers/UmbracoAuthorizedApiController.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index a370f48ebe..41e5e3639d 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -116,7 +116,16 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// /// /// - protected virtual ActionResult ValidationProblem(object value, int statusCode = StatusCodes.Status400BadRequest) + protected virtual ActionResult ValidationProblem(object value, int statusCode) => new ValidationErrorResult(value, statusCode); + + /// + /// Returns an Umbraco compatible validation problem for the object result + /// + /// + /// + /// + protected virtual ActionResult ValidationProblem(INotificationModel model, int statusCode = StatusCodes.Status400BadRequest) + => new ValidationErrorResult(model, statusCode); } } From 5cd0545b2f3d085307e0a06b24a811ab325ed6f8 Mon Sep 17 00:00:00 2001 From: Mole Date: Wed, 14 Jul 2021 14:27:36 +0200 Subject: [PATCH 063/102] Tweak xml header --- .../Controllers/UmbracoAuthorizedApiController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs index 41e5e3639d..826527aa1f 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/UmbracoAuthorizedApiController.cs @@ -120,7 +120,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers => new ValidationErrorResult(value, statusCode); /// - /// Returns an Umbraco compatible validation problem for the object result + /// Returns an Umbraco compatible validation problem for the given notification model /// /// /// From ef4621e6f139009094cfa04fb1ec8571e6d4c6b4 Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Wed, 14 Jul 2021 09:10:57 -0400 Subject: [PATCH 064/102] Login screen replace i tag with umb-icon directive --- .../views/components/application/umb-login.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html index 56d5aa8c93..eb3a334f53 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/application/umb-login.html @@ -124,14 +124,14 @@
- +
From 8e20b7fda5f0023a21c17f790495a474e241768c Mon Sep 17 00:00:00 2001 From: Evan Moore Date: Wed, 14 Jul 2021 12:29:59 -0400 Subject: [PATCH 065/102] Fix broken icon on user overlay --- .../src/views/common/overlays/user/user.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html index 330a57ab7d..d5f9a0e363 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/overlays/user/user.html @@ -59,7 +59,7 @@ ng-class="login.properties.ButtonStyle" id="{{login.authType}}"> - + Link your {{login.caption}} account From 2fc0fc685020a91b2cf710b55a810c782ace2c68 Mon Sep 17 00:00:00 2001 From: Shannon Date: Wed, 14 Jul 2021 11:56:30 -0600 Subject: [PATCH 066/102] Removes CannotSuppressNotificationAttribute --- src/Umbraco.Core/Events/ScopedNotificationPublisher.cs | 9 +++------ .../Notifications/CannotSuppressNotificationAttribute.cs | 9 --------- .../Notifications/ContentRefreshNotification.cs | 1 - .../Notifications/MediaRefreshNotification.cs | 1 - .../Notifications/MemberRefreshNotification.cs | 1 - .../Notifications/ScopedEntityRemoveNotification.cs | 1 - 6 files changed, 3 insertions(+), 19 deletions(-) delete mode 100644 src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs diff --git a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs index cf6260291b..7261b514bf 100644 --- a/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs +++ b/src/Umbraco.Core/Events/ScopedNotificationPublisher.cs @@ -29,7 +29,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (CanSuppress(notification) && _isSuppressed) + if (_isSuppressed) { return false; } @@ -45,7 +45,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (CanSuppress(notification) && _isSuppressed) + if (_isSuppressed) { return false; } @@ -61,7 +61,7 @@ namespace Umbraco.Cms.Core.Events throw new ArgumentNullException(nameof(notification)); } - if (CanSuppress(notification) && _isSuppressed) + if (_isSuppressed) { return; } @@ -99,9 +99,6 @@ namespace Umbraco.Cms.Core.Events } } - private bool CanSuppress(INotification notification) - => notification.GetType().GetCustomAttribute() == null; - private class Suppressor : IDisposable { private bool _disposedValue; diff --git a/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs b/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs deleted file mode 100644 index 8279ae4caf..0000000000 --- a/src/Umbraco.Core/Notifications/CannotSuppressNotificationAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Umbraco.Cms.Core.Notifications -{ - [AttributeUsage(AttributeTargets.Class)] - internal sealed class CannotSuppressNotificationAttribute : Attribute - { - } -} diff --git a/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs index f2c81d3fac..b9cda7722c 100644 --- a/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/ContentRefreshNotification.cs @@ -7,7 +7,6 @@ namespace Umbraco.Cms.Core.Notifications { [Obsolete("This is only used for the internal cache and will change, use saved notifications instead")] - [CannotSuppressNotification] [EditorBrowsable(EditorBrowsableState.Never)] public class ContentRefreshNotification : EntityRefreshNotification { diff --git a/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs index 4fe0f82d33..1c8b8b9bea 100644 --- a/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MediaRefreshNotification.cs @@ -5,7 +5,6 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { - [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class MediaRefreshNotification : EntityRefreshNotification diff --git a/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs index d4dfeef68f..a22c48348f 100644 --- a/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs +++ b/src/Umbraco.Core/Notifications/MemberRefreshNotification.cs @@ -5,7 +5,6 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { - [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class MemberRefreshNotification : EntityRefreshNotification diff --git a/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs index a86ea659bb..307ae2103c 100644 --- a/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs +++ b/src/Umbraco.Core/Notifications/ScopedEntityRemoveNotification.cs @@ -5,7 +5,6 @@ using Umbraco.Cms.Core.Models; namespace Umbraco.Cms.Core.Notifications { - [CannotSuppressNotification] [Obsolete("This is only used for the internal cache and will change, use tree change notifications instead")] [EditorBrowsable(EditorBrowsableState.Never)] public class ScopedEntityRemoveNotification : ObjectNotification From 883ad7d2562acc942cceea93c7dde43edd14ef73 Mon Sep 17 00:00:00 2001 From: Adam Hearn Date: Thu, 15 Jul 2021 01:05:54 +0100 Subject: [PATCH 067/102] Added integration test for MemberService::CreateWithIdentity to highlight that whilst the method does not generate an error, no member is persisted --- .../Services/MemberServiceTests.cs | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs index eb10bb010a..0ef94ce9f1 100644 --- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs +++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/MemberServiceTests.cs @@ -3,12 +3,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using NPoco; using NUnit.Framework; using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Composing; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Models.Entities; using Umbraco.Cms.Core.Models.Membership; @@ -1306,5 +1306,23 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Services Assert.IsTrue(found.IsApproved); } + + [Test] + public void Can_CreateWithIdentity() + { + // Arrange + IMemberType memberType = MemberTypeBuilder.CreateSimpleMemberType(); + MemberTypeService.Save(memberType); + string username = Path.GetRandomFileName(); + + // Act + IMember member = MemberService.CreateMemberWithIdentity(username, $"{username}@domain.email", Path.GetFileNameWithoutExtension(username), memberType); + IMember found = MemberService.GetById(member.Id); + + // Assert + Assert.IsNotNull(member, "Verifying a member instance has been created"); + Assert.IsNotNull(found, "Verifying the created member instance has been retrieved"); + Assert.IsTrue(found?.Name == member?.Name, "Verifying the retrieved member instance has the expected name"); + } } } From f978ef15d02ed3b608a162b3bdff88877763573c Mon Sep 17 00:00:00 2001 From: Adam Hearn Date: Thu, 15 Jul 2021 01:28:24 +0100 Subject: [PATCH 068/102] Support member names using Down-Level Logon Name format --- .../Configuration/Models/SecuritySettings.cs | 7 +++++++ .../Security/ConfigureMemberIdentityOptions.cs | 11 +++++++++-- src/Umbraco.Web.UI.NetCore/appsettings.json | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs index 48e08d596a..7d4dd45fb8 100644 --- a/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs +++ b/src/Umbraco.Core/Configuration/Models/SecuritySettings.cs @@ -15,6 +15,7 @@ namespace Umbraco.Cms.Core.Configuration.Models internal const bool StaticHideDisabledUsersInBackOffice = false; internal const bool StaticAllowPasswordReset = true; internal const string StaticAuthCookieName = "UMB_UCONTEXT"; + internal const string StaticAllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\"; /// /// Gets or sets a value indicating whether to keep the user logged in. @@ -50,6 +51,12 @@ namespace Umbraco.Cms.Core.Configuration.Models /// public bool UsernameIsEmail { get; set; } = true; + /// + /// Gets or sets the set of allowed characters for a username + /// + [DefaultValue(StaticAllowedUserNameCharacters)] + public string AllowedUserNameCharacters { get; set; } = StaticAllowedUserNameCharacters; + /// /// Gets or sets a value for the user password settings. /// diff --git a/src/Umbraco.Web.Common/Security/ConfigureMemberIdentityOptions.cs b/src/Umbraco.Web.Common/Security/ConfigureMemberIdentityOptions.cs index cc19670f83..db82ff2b05 100644 --- a/src/Umbraco.Web.Common/Security/ConfigureMemberIdentityOptions.cs +++ b/src/Umbraco.Web.Common/Security/ConfigureMemberIdentityOptions.cs @@ -10,9 +10,13 @@ namespace Umbraco.Cms.Web.Common.Security public sealed class ConfigureMemberIdentityOptions : IConfigureOptions { private readonly MemberPasswordConfigurationSettings _memberPasswordConfiguration; + private readonly SecuritySettings _securitySettings; - public ConfigureMemberIdentityOptions(IOptions memberPasswordConfiguration) - => _memberPasswordConfiguration = memberPasswordConfiguration.Value; + public ConfigureMemberIdentityOptions(IOptions memberPasswordConfiguration, IOptions securitySettings) + { + _memberPasswordConfiguration = memberPasswordConfiguration.Value; + _securitySettings = securitySettings.Value; + } public void Configure(IdentityOptions options) { @@ -22,6 +26,9 @@ namespace Umbraco.Cms.Web.Common.Security options.User.RequireUniqueEmail = true; + // Support validation of member names using Down-Level Logon Name format + options.User.AllowedUserNameCharacters = _securitySettings.AllowedUserNameCharacters; + options.Lockout.AllowedForNewUsers = true; // TODO: Implement this options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromDays(30); diff --git a/src/Umbraco.Web.UI.NetCore/appsettings.json b/src/Umbraco.Web.UI.NetCore/appsettings.json index 8fe1993bcb..a2c0e9c938 100644 --- a/src/Umbraco.Web.UI.NetCore/appsettings.json +++ b/src/Umbraco.Web.UI.NetCore/appsettings.json @@ -46,6 +46,7 @@ "KeepUserLoggedIn": false, "UsernameIsEmail": true, "HideDisabledUsersInBackoffice": false, + "AllowedUserNameCharacters": "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._@+\\", "UserPassword": { "RequiredLength": 10, "RequireNonLetterOrDigit": false, From 62352fc1a6223b4a67156e4d56ab83cd6b3f51a0 Mon Sep 17 00:00:00 2001 From: Dave de Moel Date: Thu, 15 Jul 2021 10:48:39 +0200 Subject: [PATCH 069/102] Moved nullcheck to top to catch possible nullreference exceptions --- .../Extensions/ApplicationBuilderExtensions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs index fb12e979ab..efc95a59f7 100644 --- a/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs +++ b/src/Umbraco.Web.Common/Extensions/ApplicationBuilderExtensions.cs @@ -27,10 +27,6 @@ namespace Umbraco.Extensions ///
public static IUmbracoApplicationBuilder UseUmbraco(this IApplicationBuilder app) { - IOptions startupOptions = app.ApplicationServices.GetRequiredService>(); - - app.RunPrePipeline(startupOptions.Value); - // TODO: Should we do some checks like this to verify that the corresponding "Add" methods have been called for the // corresponding "Use" methods? // https://github.com/dotnet/aspnetcore/blob/b795ac3546eb3e2f47a01a64feb3020794ca33bb/src/Mvc/Mvc.Core/src/Builder/MvcApplicationBuilderExtensions.cs#L132 @@ -39,6 +35,9 @@ namespace Umbraco.Extensions throw new ArgumentNullException(nameof(app)); } + IOptions startupOptions = app.ApplicationServices.GetRequiredService>(); + app.RunPrePipeline(startupOptions.Value); + app.UseUmbracoCore(); app.UseUmbracoRequestLogging(); From 33f53fcf354ca1ca491f1038e4625c5b2b4a3283 Mon Sep 17 00:00:00 2001 From: Mole Date: Thu, 15 Jul 2021 14:10:52 +0200 Subject: [PATCH 070/102] Force authentication of user when getting localized text and get culture from user identity --- .../Controllers/BackOfficeController.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 161896628b..b3e48963e4 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -4,7 +4,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Security.Claims; -using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; @@ -225,17 +224,25 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers /// [HttpGet] [AllowAnonymous] - public Dictionary> LocalizedText(string culture = null) + public async Task>> LocalizedText(string culture = null) { - var isAuthenticated = _backofficeSecurityAccessor.BackOfficeSecurity.IsAuthenticated(); + CultureInfo cultureInfo; + if (string.IsNullOrWhiteSpace(culture)) + { + // Force authentication to occur since this is not an authorized endpoint, we need this to get a user. + AuthenticateResult authenticationResult = await this.AuthenticateBackOfficeAsync(); + // We have to get the culture from the Identity, we can't rely on thread culture + // It's entirely likely for a user to have a different culture in the backoffice, than their system. + var user = authenticationResult.Principal?.Identity; - var cultureInfo = string.IsNullOrWhiteSpace(culture) - //if the user is logged in, get their culture, otherwise default to 'en' - ? isAuthenticated - //current culture is set at the very beginning of each request - ? Thread.CurrentThread.CurrentCulture - : CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage) - : CultureInfo.GetCultureInfo(culture); + cultureInfo = (authenticationResult.Succeeded && user is not null) + ? user.GetCulture() + : CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage); + } + else + { + cultureInfo = CultureInfo.GetCultureInfo(culture); + } var allValues = _textService.GetAllStoredValues(cultureInfo); var pathedValues = allValues.Select(kv => From 91d58209827a6fb6f9809d866af4459f3e69827a Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 15 Jul 2021 09:42:06 -0600 Subject: [PATCH 071/102] Fixes #10666 - CreateMemberWithIdentity was not working --- .../Services/Implement/ContentService.cs | 2 +- .../Services/Implement/MemberService.cs | 55 ++++++++----------- 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs index 609a6bd426..4ea399f500 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/ContentService.cs @@ -778,7 +778,7 @@ namespace Umbraco.Cms.Core.Services.Implement return OperationResult.Cancel(eventMessages); } - scope.WriteLock(Cms.Core.Constants.Locks.ContentTree); + scope.WriteLock(Constants.Locks.ContentTree); if (content.HasIdentity == false) { diff --git a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs index f6aac98682..e4633714ff 100644 --- a/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs +++ b/src/Umbraco.Infrastructure/Services/Implement/MemberService.cs @@ -155,9 +155,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// Is the member approved /// IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias) - { - return CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias); - } + => CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias); /// /// Creates and persists a new @@ -170,29 +168,19 @@ namespace Umbraco.Cms.Core.Services.Implement /// /// IMember IMembershipMemberService.CreateWithIdentity(string username, string email, string passwordValue, string memberTypeAlias, bool isApproved) - { - return CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias, isApproved); - } + => CreateMemberWithIdentity(username, email, username, passwordValue, memberTypeAlias, isApproved); public IMember CreateMemberWithIdentity(string username, string email, string memberTypeAlias) - { - return CreateMemberWithIdentity(username, email, username, "", memberTypeAlias); - } + => CreateMemberWithIdentity(username, email, username, "", memberTypeAlias); public IMember CreateMemberWithIdentity(string username, string email, string memberTypeAlias, bool isApproved) - { - return CreateMemberWithIdentity(username, email, username, "", memberTypeAlias, isApproved); - } + => CreateMemberWithIdentity(username, email, username, "", memberTypeAlias, isApproved); public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias) - { - return CreateMemberWithIdentity(username, email, name, "", memberTypeAlias); - } + => CreateMemberWithIdentity(username, email, name, "", memberTypeAlias); public IMember CreateMemberWithIdentity(string username, string email, string name, string memberTypeAlias, bool isApproved) - { - return CreateMemberWithIdentity(username, email, name, "", memberTypeAlias, isApproved); - } + => CreateMemberWithIdentity(username, email, name, "", memberTypeAlias, isApproved); /// /// Creates and persists a Member @@ -207,26 +195,29 @@ namespace Umbraco.Cms.Core.Services.Implement /// public IMember CreateMemberWithIdentity(string username, string email, string name, string passwordValue, string memberTypeAlias, bool isApproved = true) { - using (var scope = ScopeProvider.CreateScope()) + using (IScope scope = ScopeProvider.CreateScope()) { // locking the member tree secures member types too scope.WriteLock(Constants.Locks.MemberTree); - var memberType = GetMemberType(scope, memberTypeAlias); // + locks // + locks + IMemberType memberType = GetMemberType(scope, memberTypeAlias); // + locks // + locks if (memberType == null) + { throw new ArgumentException("No member type with that alias.", nameof(memberTypeAlias)); // causes rollback // causes rollback + } var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType, isApproved, -1); Save(member); + + scope.Complete(); + return member; } } public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType) - { - return CreateMemberWithIdentity(username, email, username, "", memberType); - } + => CreateMemberWithIdentity(username, email, username, "", memberType); /// /// Creates and persists a Member @@ -238,14 +229,10 @@ namespace Umbraco.Cms.Core.Services.Implement /// MemberType the Member should be based on /// public IMember CreateMemberWithIdentity(string username, string email, IMemberType memberType, bool isApproved) - { - return CreateMemberWithIdentity(username, email, username, "", memberType, isApproved); - } + => CreateMemberWithIdentity(username, email, username, "", memberType, isApproved); public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType) - { - return CreateMemberWithIdentity(username, email, name, "", memberType); - } + => CreateMemberWithIdentity(username, email, name, "", memberType); /// /// Creates and persists a Member @@ -258,9 +245,7 @@ namespace Umbraco.Cms.Core.Services.Implement /// MemberType the Member should be based on /// public IMember CreateMemberWithIdentity(string username, string email, string name, IMemberType memberType, bool isApproved) - { - return CreateMemberWithIdentity(username, email, name, "", memberType, isApproved); - } + => CreateMemberWithIdentity(username, email, name, "", memberType, isApproved); /// /// Creates and persists a Member @@ -286,10 +271,16 @@ namespace Umbraco.Cms.Core.Services.Implement var vrfy = GetMemberType(scope, memberType.Alias); // + locks if (vrfy == null || vrfy.Id != memberType.Id) + { throw new ArgumentException($"Member type with alias {memberType.Alias} does not exist or is a different member type."); // causes rollback + } + var member = new Member(name, email.ToLower().Trim(), username, passwordValue, memberType, isApproved, -1); Save(member); + + scope.Complete(); + return member; } } From df9c4a016030b553d3bd88f6114709a842829282 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 15 Jul 2021 13:26:32 -0600 Subject: [PATCH 072/102] Adds bundle options to the package manifest to more control over how bundling works for static file processing for app_plugins --- .../Configuration/Grid/GridEditorsConfig.cs | 2 +- .../ContentAppFactoryCollectionBuilder.cs | 2 +- .../Dashboards/DashboardCollectionBuilder.cs | 4 +- src/Umbraco.Core/Manifest/BundleOptions.cs | 26 ++++ .../Manifest/CompositePackageManifest.cs | 67 ++++++++++ src/Umbraco.Core/Manifest/IManifestParser.cs | 4 +- src/Umbraco.Core/Manifest/ManifestAssets.cs | 17 +++ src/Umbraco.Core/Manifest/PackageManifest.cs | 3 + .../ParameterEditorCollection.cs | 2 +- .../PropertyEditorCollection.cs | 2 +- .../Sections/SectionCollectionBuilder.cs | 2 +- .../Manifest/ManifestParser.cs | 67 ++++++---- .../WebAssets/BackOfficeWebAssets.cs | 74 +++++++++-- .../WebAssets/RuntimeMinifierExtensions.cs | 41 ------ .../Controllers/BackOfficeController.cs | 10 +- .../Extensions/RuntimeMinifierExtensions.cs | 124 ++++++++++++++++++ .../SmidgeRuntimeMinifier.cs | 16 ++- 17 files changed, 373 insertions(+), 90 deletions(-) create mode 100644 src/Umbraco.Core/Manifest/BundleOptions.cs create mode 100644 src/Umbraco.Core/Manifest/CompositePackageManifest.cs create mode 100644 src/Umbraco.Core/Manifest/ManifestAssets.cs delete mode 100644 src/Umbraco.Infrastructure/WebAssets/RuntimeMinifierExtensions.cs create mode 100644 src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs diff --git a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs index ab6a7e9396..f1a4f0643c 100644 --- a/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs +++ b/src/Umbraco.Core/Configuration/Grid/GridEditorsConfig.cs @@ -64,7 +64,7 @@ namespace Umbraco.Cms.Core.Configuration.Grid } // add manifest editors, skip duplicates - foreach (var gridEditor in _manifestParser.Manifest.GridEditors) + foreach (var gridEditor in _manifestParser.CombinedManifest.GridEditors) { if (editors.Contains(gridEditor) == false) editors.Add(gridEditor); } diff --git a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs index ff1223d983..a80c79a3ef 100644 --- a/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs +++ b/src/Umbraco.Core/ContentApps/ContentAppFactoryCollectionBuilder.cs @@ -33,7 +33,7 @@ namespace Umbraco.Cms.Core.ContentApps // its dependencies too, and that can create cycles or other oddities var manifestParser = factory.GetRequiredService(); var ioHelper = factory.GetRequiredService(); - return base.CreateItems(factory).Concat(manifestParser.Manifest.ContentApps.Select(x => new ManifestContentAppFactory(x, ioHelper))); + return base.CreateItems(factory).Concat(manifestParser.CombinedManifest.ContentApps.Select(x => new ManifestContentAppFactory(x, ioHelper))); } } } diff --git a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs index 55e840ad8e..348e81e383 100644 --- a/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs +++ b/src/Umbraco.Core/Dashboards/DashboardCollectionBuilder.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Extensions.DependencyInjection; @@ -19,7 +19,7 @@ namespace Umbraco.Cms.Core.Dashboards // its dependencies too, and that can create cycles or other oddities var manifestParser = factory.GetRequiredService(); - var dashboardSections = Merge(base.CreateItems(factory), manifestParser.Manifest.Dashboards); + var dashboardSections = Merge(base.CreateItems(factory), manifestParser.CombinedManifest.Dashboards); return dashboardSections; } diff --git a/src/Umbraco.Core/Manifest/BundleOptions.cs b/src/Umbraco.Core/Manifest/BundleOptions.cs new file mode 100644 index 0000000000..810efb6c45 --- /dev/null +++ b/src/Umbraco.Core/Manifest/BundleOptions.cs @@ -0,0 +1,26 @@ +namespace Umbraco.Cms.Core.Manifest +{ + public enum BundleOptions + { + /// + /// The default bundling behavior for assets in the package folder. + /// + /// + /// The assets will be bundled with the typical packages bundle. + /// + Default = 0, + + /// + /// The assets in the package will not be processed at all and will all be requested as individual assets. + /// + /// + /// This will essentially be a bundle that has composite processing turned off for both debug and production. + /// + None = 1, + + /// + /// The packages assets will be processed as it's own separate bundle. (in debug, files will not be processed) + /// + Independent = 2 + } +} diff --git a/src/Umbraco.Core/Manifest/CompositePackageManifest.cs b/src/Umbraco.Core/Manifest/CompositePackageManifest.cs new file mode 100644 index 0000000000..939d635fc3 --- /dev/null +++ b/src/Umbraco.Core/Manifest/CompositePackageManifest.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using Umbraco.Cms.Core.PropertyEditors; + +namespace Umbraco.Cms.Core.Manifest +{ + + /// + /// A package manifest made up of all combined manifests + /// + public class CompositePackageManifest + { + public CompositePackageManifest( + IReadOnlyList propertyEditors, + IReadOnlyList parameterEditors, + IReadOnlyList gridEditors, + IReadOnlyList contentApps, + IReadOnlyList dashboards, + IReadOnlyList sections, + IReadOnlyDictionary> scripts, + IReadOnlyDictionary> stylesheets) + { + PropertyEditors = propertyEditors ?? throw new ArgumentNullException(nameof(propertyEditors)); + ParameterEditors = parameterEditors ?? throw new ArgumentNullException(nameof(parameterEditors)); + GridEditors = gridEditors ?? throw new ArgumentNullException(nameof(gridEditors)); + ContentApps = contentApps ?? throw new ArgumentNullException(nameof(contentApps)); + Dashboards = dashboards ?? throw new ArgumentNullException(nameof(dashboards)); + Sections = sections ?? throw new ArgumentNullException(nameof(sections)); + Scripts = scripts ?? throw new ArgumentNullException(nameof(scripts)); + Stylesheets = stylesheets ?? throw new ArgumentNullException(nameof(stylesheets)); + } + + /// + /// Gets or sets the property editors listed in the manifest. + /// + public IReadOnlyList PropertyEditors { get; } + + /// + /// Gets or sets the parameter editors listed in the manifest. + /// + public IReadOnlyList ParameterEditors { get; } + + /// + /// Gets or sets the grid editors listed in the manifest. + /// + public IReadOnlyList GridEditors { get; } + + /// + /// Gets or sets the content apps listed in the manifest. + /// + public IReadOnlyList ContentApps { get; } + + /// + /// Gets or sets the dashboards listed in the manifest. + /// + public IReadOnlyList Dashboards { get; } + + /// + /// Gets or sets the sections listed in the manifest. + /// + public IReadOnlyCollection Sections { get; } + + public IReadOnlyDictionary> Scripts { get; } + + public IReadOnlyDictionary> Stylesheets { get; } + } +} diff --git a/src/Umbraco.Core/Manifest/IManifestParser.cs b/src/Umbraco.Core/Manifest/IManifestParser.cs index dc3a19714e..9e56ed17fc 100644 --- a/src/Umbraco.Core/Manifest/IManifestParser.cs +++ b/src/Umbraco.Core/Manifest/IManifestParser.cs @@ -4,13 +4,13 @@ namespace Umbraco.Cms.Core.Manifest { public interface IManifestParser { - string Path { get; set; } + //string Path { get; set; } /// /// Gets all manifests, merged into a single manifest object. /// /// - PackageManifest Manifest { get; } + CompositePackageManifest CombinedManifest { get; } /// /// Parses a manifest. diff --git a/src/Umbraco.Core/Manifest/ManifestAssets.cs b/src/Umbraco.Core/Manifest/ManifestAssets.cs new file mode 100644 index 0000000000..ad5dfaa0f0 --- /dev/null +++ b/src/Umbraco.Core/Manifest/ManifestAssets.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; + +namespace Umbraco.Cms.Core.Manifest +{ + public class ManifestAssets + { + public ManifestAssets(string packageName, IReadOnlyList assets) + { + PackageName = packageName ?? throw new ArgumentNullException(nameof(packageName)); + Assets = assets ?? throw new ArgumentNullException(nameof(assets)); + } + + public string PackageName { get; } + public IReadOnlyList Assets { get; } + } +} diff --git a/src/Umbraco.Core/Manifest/PackageManifest.cs b/src/Umbraco.Core/Manifest/PackageManifest.cs index 56c69ebb15..753ec0613a 100644 --- a/src/Umbraco.Core/Manifest/PackageManifest.cs +++ b/src/Umbraco.Core/Manifest/PackageManifest.cs @@ -6,6 +6,7 @@ using Umbraco.Extensions; namespace Umbraco.Cms.Core.Manifest { + /// /// Represents the content of a package manifest. /// @@ -47,6 +48,8 @@ namespace Umbraco.Cms.Core.Manifest /// [IgnoreDataMember] public string Source { get; set; } + [DataMember(Name = "bundleOptions")] + public BundleOptions BundleOptions { get; set; } /// /// Gets or sets the scripts listed in the manifest. diff --git a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs index 039de8cb6a..39647bb753 100644 --- a/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/ParameterEditorCollection.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public ParameterEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) : base(() => dataEditors .Where(x => (x.Type & EditorType.MacroParameter) > 0) - .Union(manifestParser.Manifest.PropertyEditors)) + .Union(manifestParser.CombinedManifest.PropertyEditors)) { } // note: virtual so it can be mocked diff --git a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs index 4b551d3257..1ddf150f93 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyEditorCollection.cs @@ -9,7 +9,7 @@ namespace Umbraco.Cms.Core.PropertyEditors public PropertyEditorCollection(DataEditorCollection dataEditors, IManifestParser manifestParser) : base(() => dataEditors .Where(x => (x.Type & EditorType.PropertyValue) > 0) - .Union(manifestParser.Manifest.PropertyEditors)) + .Union(manifestParser.CombinedManifest.PropertyEditors)) { } public PropertyEditorCollection(DataEditorCollection dataEditors) diff --git a/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs b/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs index 0c5b2d7ba9..219d634261 100644 --- a/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs +++ b/src/Umbraco.Core/Sections/SectionCollectionBuilder.cs @@ -18,7 +18,7 @@ namespace Umbraco.Cms.Core.Sections // its dependencies too, and that can create cycles or other oddities var manifestParser = factory.GetRequiredService(); - return base.CreateItems(factory).Concat(manifestParser.Manifest.Sections); + return base.CreateItems(factory).Concat(manifestParser.CombinedManifest.Sections); } } } diff --git a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs index 7d98a19091..529a148093 100644 --- a/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs +++ b/src/Umbraco.Infrastructure/Manifest/ManifestParser.cs @@ -62,22 +62,22 @@ namespace Umbraco.Cms.Core.Manifest /// /// Initializes a new instance of the class. /// - private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment) + private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string appPluginsPath, ILogger logger, IIOHelper ioHelper, IHostingEnvironment hostingEnvironment) { if (appCaches == null) throw new ArgumentNullException(nameof(appCaches)); _cache = appCaches.RuntimeCache; _validators = validators ?? throw new ArgumentNullException(nameof(validators)); _filters = filters ?? throw new ArgumentNullException(nameof(filters)); - if (path == null) throw new ArgumentNullException(nameof(path)); - if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path)); + if (appPluginsPath == null) throw new ArgumentNullException(nameof(appPluginsPath)); + if (string.IsNullOrWhiteSpace(appPluginsPath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(appPluginsPath)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _ioHelper = ioHelper; _hostingEnvironment = hostingEnvironment; - Path = path; + AppPluginsPath = appPluginsPath; } - public string Path + public string AppPluginsPath { get => _path; set => _path = value.StartsWith("~/") ? _hostingEnvironment.MapPathContentRoot(value) : value; @@ -87,11 +87,12 @@ namespace Umbraco.Cms.Core.Manifest /// Gets all manifests, merged into a single manifest object. /// /// - public PackageManifest Manifest - => _cache.GetCacheItem("Umbraco.Core.Manifest.ManifestParser::Manifests", () => + public CompositePackageManifest CombinedManifest + => _cache.GetCacheItem("Umbraco.Core.Manifest.ManifestParser::Manifests", () => { - var manifests = GetManifests(); + IEnumerable manifests = GetManifests(); return MergeManifests(manifests); + }, new TimeSpan(0, 4, 0)); /// @@ -130,10 +131,10 @@ namespace Umbraco.Cms.Core.Manifest /// /// Merges all manifests into one. /// - private static PackageManifest MergeManifests(IEnumerable manifests) + private static CompositePackageManifest MergeManifests(IEnumerable manifests) { - var scripts = new HashSet(); - var stylesheets = new HashSet(); + var scripts = new Dictionary>(); + var stylesheets = new Dictionary>(); var propertyEditors = new List(); var parameterEditors = new List(); var gridEditors = new List(); @@ -141,10 +142,28 @@ namespace Umbraco.Cms.Core.Manifest var dashboards = new List(); var sections = new List(); - foreach (var manifest in manifests) + foreach (PackageManifest manifest in manifests) { - if (manifest.Scripts != null) foreach (var script in manifest.Scripts) scripts.Add(script); - if (manifest.Stylesheets != null) foreach (var stylesheet in manifest.Stylesheets) stylesheets.Add(stylesheet); + if (manifest.Scripts != null) + { + if (!scripts.TryGetValue(manifest.BundleOptions, out List scriptsPerBundleOption)) + { + scriptsPerBundleOption = new List(); + scripts[manifest.BundleOptions] = scriptsPerBundleOption; + } + scriptsPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Scripts)); + } + + if (manifest.Stylesheets != null) + { + if (!stylesheets.TryGetValue(manifest.BundleOptions, out List stylesPerBundleOption)) + { + stylesPerBundleOption = new List(); + stylesheets[manifest.BundleOptions] = stylesPerBundleOption; + } + stylesPerBundleOption.Add(new ManifestAssets(manifest.PackageName, manifest.Stylesheets)); + } + if (manifest.PropertyEditors != null) propertyEditors.AddRange(manifest.PropertyEditors); if (manifest.ParameterEditors != null) parameterEditors.AddRange(manifest.ParameterEditors); if (manifest.GridEditors != null) gridEditors.AddRange(manifest.GridEditors); @@ -153,17 +172,15 @@ namespace Umbraco.Cms.Core.Manifest if (manifest.Sections != null) sections.AddRange(manifest.Sections.DistinctBy(x => x.Alias.ToLowerInvariant())); } - return new PackageManifest - { - Scripts = scripts.ToArray(), - Stylesheets = stylesheets.ToArray(), - PropertyEditors = propertyEditors.ToArray(), - ParameterEditors = parameterEditors.ToArray(), - GridEditors = gridEditors.ToArray(), - ContentApps = contentApps.ToArray(), - Dashboards = dashboards.ToArray(), - Sections = sections.ToArray() - }; + return new CompositePackageManifest( + propertyEditors, + parameterEditors, + gridEditors, + contentApps, + dashboards, + sections, + scripts.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value), + stylesheets.ToDictionary(x => x.Key, x => (IReadOnlyList)x.Value)); } // gets all manifest files (recursively) diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index 74668d3090..07de655fb8 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -73,25 +73,66 @@ namespace Umbraco.Cms.Infrastructure.WebAssets _runtimeMinifier.CreateJsBundle(UmbracoCoreJsBundleName, false, FormatPaths(GetScriptsForBackOfficeCore())); + + // get the property editor assets var propertyEditorAssets = ScanPropertyEditors() .GroupBy(x => x.AssetType) .ToDictionary(x => x.Key, x => x.Select(c => c.FilePath)); + // get the back office custom assets var customAssets = _customBackOfficeAssetsCollection.GroupBy(x => x.DependencyType).ToDictionary(x => x.Key, x => x.Select(c => c.FilePath)); - var jsAssets = (customAssets.TryGetValue(AssetType.Javascript, out var customScripts) ? customScripts : Enumerable.Empty()) - .Union(propertyEditorAssets.TryGetValue(AssetType.Javascript, out var scripts) ? scripts : Enumerable.Empty()); + // This bundle includes all scripts from property editor assets, + // custom back office assets, and any scripts found in package manifests + // that have the default bundle options. + + IEnumerable jsAssets = (customAssets.TryGetValue(AssetType.Javascript, out IEnumerable customScripts) ? customScripts : Enumerable.Empty()) + .Union(propertyEditorAssets.TryGetValue(AssetType.Javascript, out IEnumerable scripts) ? scripts : Enumerable.Empty()); + _runtimeMinifier.CreateJsBundle( - UmbracoExtensionsJsBundleName, true, + UmbracoExtensionsJsBundleName, + true, FormatPaths( GetScriptsForBackOfficeExtensions(jsAssets))); - var cssAssets = (customAssets.TryGetValue(AssetType.Css, out var customStyles) ? customStyles : Enumerable.Empty()) - .Union(propertyEditorAssets.TryGetValue(AssetType.Css, out var styles) ? styles : Enumerable.Empty()); + // Create a bundle per package manifest that is declaring an Independent bundle type + RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Scripts, AssetType.Javascript); + + // This bundle includes all CSS from property editor assets, + // custom back office assets, and any CSS found in package manifests + // that have the default bundle options. + + IEnumerable cssAssets = (customAssets.TryGetValue(AssetType.Css, out IEnumerable customStyles) ? customStyles : Enumerable.Empty()) + .Union(propertyEditorAssets.TryGetValue(AssetType.Css, out IEnumerable styles) ? styles : Enumerable.Empty()); + _runtimeMinifier.CreateCssBundle( - UmbracoCssBundleName, true, + UmbracoCssBundleName, + true, FormatPaths( GetStylesheetsForBackOffice(cssAssets))); + + // Create a bundle per package manifest that is declaring an Independent bundle type + RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Stylesheets, AssetType.Css); + } + + public static string GetIndependentPackageBundleName(ManifestAssets manifestAssets, AssetType assetType) + => $"{manifestAssets.PackageName.ToLowerInvariant()}-{(assetType == AssetType.Css ? "css" : "js")}"; + + private void RegisterPackageBundlesForIndependentOptions( + IReadOnlyDictionary> combinedPackageManifestAssets, + AssetType assetType) + { + // Create a bundle per package manifest that is declaring the matching BundleOptions + if (combinedPackageManifestAssets.TryGetValue(BundleOptions.Independent, out IReadOnlyList manifestAssetList)) + { + foreach (ManifestAssets manifestAssets in manifestAssetList) + { + _runtimeMinifier.CreateJsBundle( + GetIndependentPackageBundleName(manifestAssets, assetType), + true, + FormatPaths(manifestAssets.Assets.ToArray())); + } + } } /// @@ -100,10 +141,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets /// private string[] GetScriptsForBackOfficeExtensions(IEnumerable propertyEditorScripts) { - var scripts = new HashSet(); - foreach (string script in _parser.Manifest.Scripts) + var scripts = new HashSet(StringComparer.InvariantCultureIgnoreCase); + + // only include scripts with the default bundle options here + if (_parser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Default, out IReadOnlyList manifestAssets)) { - scripts.Add(script); + foreach (string script in manifestAssets.SelectMany(x => x.Assets)) + { + scripts.Add(script); + } } foreach (string script in propertyEditorScripts) @@ -130,11 +176,15 @@ namespace Umbraco.Cms.Infrastructure.WebAssets /// private string[] GetStylesheetsForBackOffice(IEnumerable propertyEditorStyles) { - var stylesheets = new HashSet(); + var stylesheets = new HashSet(StringComparer.InvariantCultureIgnoreCase); - foreach (string script in _parser.Manifest.Stylesheets) + // only include css with the default bundle options here + if (_parser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Default, out IReadOnlyList manifestAssets)) { - stylesheets.Add(script); + foreach (string script in manifestAssets.SelectMany(x => x.Assets)) + { + stylesheets.Add(script); + } } foreach (string stylesheet in propertyEditorStyles) diff --git a/src/Umbraco.Infrastructure/WebAssets/RuntimeMinifierExtensions.cs b/src/Umbraco.Infrastructure/WebAssets/RuntimeMinifierExtensions.cs deleted file mode 100644 index f33d48d8fd..0000000000 --- a/src/Umbraco.Infrastructure/WebAssets/RuntimeMinifierExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.WebAssets; -using Umbraco.Cms.Infrastructure.WebAssets; - -namespace Umbraco.Extensions -{ - public static class RuntimeMinifierExtensions - { - /// - /// Returns the JavaScript to load the back office's assets - /// - /// - public static async Task GetScriptForLoadingBackOfficeAsync(this IRuntimeMinifier minifier, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment) - { - var coreScripts = await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName); - var extensionsScripts = await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoExtensionsJsBundleName); - var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization(coreScripts.Union(extensionsScripts), "umbraco", globalSettings, hostingEnvironment); - result += await GetStylesheetInitializationAsync(minifier); - - return result; - } - - /// - /// Gets the back office css bundle paths and formats a JS call to lazy load them - /// - private static async Task GetStylesheetInitializationAsync(IRuntimeMinifier minifier) - { - var files = await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName); - var sb = new StringBuilder(); - foreach (var file in files) - sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file); - return sb.ToString(); - } - - } -} diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index 161896628b..ca1aea69a7 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -17,6 +17,7 @@ using Umbraco.Cms.Core.Cache; using Umbraco.Cms.Core.Configuration.Grid; using Umbraco.Cms.Core.Configuration.Models; using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Manifest; using Umbraco.Cms.Core.Models; using Umbraco.Cms.Core.Security; using Umbraco.Cms.Core.Serialization; @@ -63,6 +64,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers private readonly IBackOfficeExternalLoginProviders _externalLogins; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IBackOfficeTwoFactorOptions _backOfficeTwoFactorOptions; + private readonly IManifestParser _manifestParser; private readonly ServerVariablesParser _serverVariables; public BackOfficeController( @@ -81,6 +83,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers IBackOfficeExternalLoginProviders externalLogins, IHttpContextAccessor httpContextAccessor, IBackOfficeTwoFactorOptions backOfficeTwoFactorOptions, + IManifestParser manifestParser, ServerVariablesParser serverVariables) { _userManager = userManager; @@ -98,6 +101,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers _externalLogins = externalLogins; _httpContextAccessor = httpContextAccessor; _backOfficeTwoFactorOptions = backOfficeTwoFactorOptions; + _manifestParser = manifestParser; _serverVariables = serverVariables; } @@ -213,7 +217,11 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers [AllowAnonymous] public async Task Application() { - var result = await _runtimeMinifier.GetScriptForLoadingBackOfficeAsync(_globalSettings, _hostingEnvironment); + var result = await _runtimeMinifier.GetScriptForLoadingBackOfficeAsync( + _globalSettings, + _hostingEnvironment, + _manifestParser, + Url); return new JavaScriptResult(result); } diff --git a/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs new file mode 100644 index 0000000000..e436a57d83 --- /dev/null +++ b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Umbraco.Cms.Core.Configuration.Models; +using Umbraco.Cms.Core.Hosting; +using Umbraco.Cms.Core.Manifest; +using Umbraco.Cms.Core.WebAssets; +using Umbraco.Cms.Infrastructure.WebAssets; + +namespace Umbraco.Extensions +{ + public static class RuntimeMinifierExtensions + { + /// + /// Returns the JavaScript to load the back office's assets + /// + /// + public static async Task GetScriptForLoadingBackOfficeAsync( + this IRuntimeMinifier minifier, + GlobalSettings globalSettings, + IHostingEnvironment hostingEnvironment, + IManifestParser manifestParser, + IUrlHelper urlHelper) + { + var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); + foreach(var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName)) + { + files.Add(file); + } + + foreach (var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoExtensionsJsBundleName)) + { + files.Add(file); + } + + // process the independent bundles + if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.Independent, out IReadOnlyList independentManifestAssetsList)) + { + foreach (ManifestAssets manifestAssets in independentManifestAssetsList) + { + var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Javascript); + foreach(var asset in await minifier.GetJsAssetPathsAsync(bundleName)) + { + files.Add(asset); + } + } + } + + // process the "None" bundles, meaning we'll just render the script as-is + if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.None, out IReadOnlyList noneManifestAssetsList)) + { + foreach (ManifestAssets manifestAssets in noneManifestAssetsList) + { + foreach(var asset in manifestAssets.Assets) + { + files.Add(urlHelper.Content(asset)); + } + } + } + + var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization( + files, + "umbraco", + globalSettings, + hostingEnvironment); + + result += await GetStylesheetInitializationAsync(minifier, manifestParser, urlHelper); + + return result; + } + + /// + /// Gets the back office css bundle paths and formats a JS call to lazy load them + /// + private static async Task GetStylesheetInitializationAsync( + IRuntimeMinifier minifier, + IManifestParser manifestParser, + IUrlHelper urlHelper) + { + var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); + foreach(var file in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName)) + { + files.Add(file); + } + + // process the independent bundles + if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.Independent, out IReadOnlyList independentManifestAssetsList)) + { + foreach (ManifestAssets manifestAssets in independentManifestAssetsList) + { + var bundleName = BackOfficeWebAssets.GetIndependentPackageBundleName(manifestAssets, AssetType.Css); + foreach (var asset in await minifier.GetCssAssetPathsAsync(bundleName)) + { + files.Add(asset); + } + } + } + + // process the "None" bundles, meaning we'll just render the script as-is + if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.None, out IReadOnlyList noneManifestAssetsList)) + { + foreach (ManifestAssets manifestAssets in noneManifestAssetsList) + { + foreach (var asset in manifestAssets.Assets) + { + files.Add(urlHelper.Content(asset)); + } + } + } + + var sb = new StringBuilder(); + foreach (string file in files) + { + sb.AppendFormat("{0}LazyLoad.css('{1}');", Environment.NewLine, file); + } + + return sb.ToString(); + } + + } +} diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index 4353b2ee1a..c202c49980 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -60,9 +60,21 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification // replace the default JsMinifier with NuglifyJs and CssMinifier with NuglifyCss in the default pipelines // for use with our bundles only (not modifying global options) _jsOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultJs().Replace(_bundles.PipelineFactory)); - _jsNonOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultJs()); + _jsNonOptimizedPipeline = new Lazy(() => + { + PreProcessPipeline defaultJs = bundles.PipelineFactory.DefaultJs(); + // remove minification from this pipeline + defaultJs.Processors.RemoveAll(x => x is JsMinifier); + return defaultJs; + }); _cssOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultCss().Replace(_bundles.PipelineFactory)); - _cssNonOptimizedPipeline = new Lazy(() => bundles.PipelineFactory.DefaultCss()); + _cssNonOptimizedPipeline = new Lazy(() => + { + PreProcessPipeline defaultCss = bundles.PipelineFactory.DefaultCss(); + // remove minification from this pipeline + defaultCss.Processors.RemoveAll(x => x is CssMinifier); + return defaultCss; + }); Type cacheBusterType = _runtimeMinificationSettings.CacheBuster switch { From b16bf2f58379feac0ba7b13cefd9ad18190dc315 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 15 Jul 2021 14:06:33 -0600 Subject: [PATCH 073/102] Adds BundlingOptions for better bundling support and moves all logic to the bundler (don't manually process urls). --- src/Umbraco.Core/WebAssets/BundlingOptions.cs | 44 ++++++++++ .../WebAssets/IRuntimeMinifier.cs | 4 +- .../WebAssets/BackOfficeWebAssets.cs | 54 ++++++++++-- .../Controllers/BackOfficeController.cs | 3 +- .../Extensions/RuntimeMinifierExtensions.cs | 28 ++---- .../SmidgeRuntimeMinifier.cs | 88 ++++++------------- 6 files changed, 128 insertions(+), 93 deletions(-) create mode 100644 src/Umbraco.Core/WebAssets/BundlingOptions.cs diff --git a/src/Umbraco.Core/WebAssets/BundlingOptions.cs b/src/Umbraco.Core/WebAssets/BundlingOptions.cs new file mode 100644 index 0000000000..e9234b06a4 --- /dev/null +++ b/src/Umbraco.Core/WebAssets/BundlingOptions.cs @@ -0,0 +1,44 @@ +using System; + +namespace Umbraco.Cms.Core.WebAssets +{ + public struct BundlingOptions : IEquatable + { + public static BundlingOptions OptimizedAndComposite => new BundlingOptions(true, true); + public static BundlingOptions OptimizedNotComposite => new BundlingOptions(true, false); + public static BundlingOptions NotOptimizedNotComposite => new BundlingOptions(false, false); + public static BundlingOptions NotOptimizedAndComposite => new BundlingOptions(false, false); + + public BundlingOptions(bool optimizeOutput = true, bool enabledCompositeFiles = true) + { + OptimizeOutput = optimizeOutput; + EnabledCompositeFiles = enabledCompositeFiles; + } + + /// + /// If true, the files in the bundle will be minified + /// + public bool OptimizeOutput { get; } + + /// + /// If true, the files in the bundle will be combined, if false the files + /// will be served as individual files. + /// + public bool EnabledCompositeFiles { get; } + + public override bool Equals(object obj) => obj is BundlingOptions options && Equals(options); + public bool Equals(BundlingOptions other) => OptimizeOutput == other.OptimizeOutput && EnabledCompositeFiles == other.EnabledCompositeFiles; + + public override int GetHashCode() + { + int hashCode = 2130304063; + hashCode = hashCode * -1521134295 + OptimizeOutput.GetHashCode(); + hashCode = hashCode * -1521134295 + EnabledCompositeFiles.GetHashCode(); + return hashCode; + } + + public static bool operator ==(BundlingOptions left, BundlingOptions right) => left.Equals(right); + + public static bool operator !=(BundlingOptions left, BundlingOptions right) => !(left == right); + } +} diff --git a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs index f287c722ad..7ca1cd883b 100644 --- a/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs +++ b/src/Umbraco.Core/WebAssets/IRuntimeMinifier.cs @@ -25,7 +25,7 @@ namespace Umbraco.Cms.Core.WebAssets /// /// Thrown if any of the paths specified are not absolute /// - void CreateCssBundle(string bundleName, bool optimizeOutput, params string[] filePaths); + void CreateCssBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths); /// /// Renders the html link tag for the bundle @@ -48,7 +48,7 @@ namespace Umbraco.Cms.Core.WebAssets /// /// Thrown if any of the paths specified are not absolute /// - void CreateJsBundle(string bundleName, bool optimizeOutput, params string[] filePaths); + void CreateJsBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths); /// /// Renders the html script tag for the bundle diff --git a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs index 07de655fb8..8944691404 100644 --- a/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs +++ b/src/Umbraco.Infrastructure/WebAssets/BackOfficeWebAssets.cs @@ -21,6 +21,8 @@ namespace Umbraco.Cms.Infrastructure.WebAssets public const string UmbracoInitCssBundleName = "umbraco-backoffice-init-css"; public const string UmbracoCoreJsBundleName = "umbraco-backoffice-js"; public const string UmbracoExtensionsJsBundleName = "umbraco-backoffice-extensions-js"; + public const string UmbracoNonOptimizedPackageJsBundleName = "umbraco-backoffice-non-optimized-js"; + public const string UmbracoNonOptimizedPackageCssBundleName = "umbraco-backoffice-non-optimized-css"; public const string UmbracoTinyMceJsBundleName = "umbraco-tinymce-js"; public const string UmbracoUpgradeCssBundleName = "umbraco-authorize-upgrade-css"; @@ -51,26 +53,32 @@ namespace Umbraco.Cms.Infrastructure.WebAssets { // Create bundles - _runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName, false, + _runtimeMinifier.CreateCssBundle(UmbracoInitCssBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths("lib/bootstrap-social/bootstrap-social.css", "assets/css/umbraco.min.css", "lib/font-awesome/css/font-awesome.min.css")); - _runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName, false, + _runtimeMinifier.CreateCssBundle(UmbracoUpgradeCssBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths("assets/css/umbraco.min.css", "lib/bootstrap-social/bootstrap-social.css", "lib/font-awesome/css/font-awesome.min.css")); - _runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName, false, + _runtimeMinifier.CreateCssBundle(UmbracoPreviewCssBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths("assets/css/canvasdesigner.min.css")); - _runtimeMinifier.CreateJsBundle(UmbracoPreviewJsBundleName, false, + _runtimeMinifier.CreateJsBundle(UmbracoPreviewJsBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths(GetScriptsForPreview())); - _runtimeMinifier.CreateJsBundle(UmbracoTinyMceJsBundleName, false, + _runtimeMinifier.CreateJsBundle(UmbracoTinyMceJsBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths(GetScriptsForTinyMce())); - _runtimeMinifier.CreateJsBundle(UmbracoCoreJsBundleName, false, + _runtimeMinifier.CreateJsBundle(UmbracoCoreJsBundleName, + BundlingOptions.NotOptimizedAndComposite, FormatPaths(GetScriptsForBackOfficeCore())); @@ -91,13 +99,16 @@ namespace Umbraco.Cms.Infrastructure.WebAssets _runtimeMinifier.CreateJsBundle( UmbracoExtensionsJsBundleName, - true, + BundlingOptions.OptimizedAndComposite, FormatPaths( GetScriptsForBackOfficeExtensions(jsAssets))); // Create a bundle per package manifest that is declaring an Independent bundle type RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Scripts, AssetType.Javascript); + // Create a single non-optimized (no file processing) bundle for all manifests declaring None as a bundle option + RegisterPackageBundlesForNoneOption(_parser.CombinedManifest.Scripts, UmbracoNonOptimizedPackageJsBundleName); + // This bundle includes all CSS from property editor assets, // custom back office assets, and any CSS found in package manifests // that have the default bundle options. @@ -107,17 +118,42 @@ namespace Umbraco.Cms.Infrastructure.WebAssets _runtimeMinifier.CreateCssBundle( UmbracoCssBundleName, - true, + BundlingOptions.OptimizedAndComposite, FormatPaths( GetStylesheetsForBackOffice(cssAssets))); // Create a bundle per package manifest that is declaring an Independent bundle type RegisterPackageBundlesForIndependentOptions(_parser.CombinedManifest.Stylesheets, AssetType.Css); + + // Create a single non-optimized (no file processing) bundle for all manifests declaring None as a bundle option + RegisterPackageBundlesForNoneOption(_parser.CombinedManifest.Stylesheets, UmbracoNonOptimizedPackageCssBundleName); } public static string GetIndependentPackageBundleName(ManifestAssets manifestAssets, AssetType assetType) => $"{manifestAssets.PackageName.ToLowerInvariant()}-{(assetType == AssetType.Css ? "css" : "js")}"; + private void RegisterPackageBundlesForNoneOption( + IReadOnlyDictionary> combinedPackageManifestAssets, + string bundleName) + { + var assets = new HashSet(StringComparer.InvariantCultureIgnoreCase); + + // Create a bundle per package manifest that is declaring the matching BundleOptions + if (combinedPackageManifestAssets.TryGetValue(BundleOptions.None, out IReadOnlyList manifestAssetList)) + { + foreach(var asset in manifestAssetList.SelectMany(x => x.Assets)) + { + assets.Add(asset); + } + } + + _runtimeMinifier.CreateJsBundle( + bundleName, + // no optimization, no composite files, just render individual files + BundlingOptions.NotOptimizedNotComposite, + FormatPaths(assets.ToArray())); + } + private void RegisterPackageBundlesForIndependentOptions( IReadOnlyDictionary> combinedPackageManifestAssets, AssetType assetType) @@ -129,7 +165,7 @@ namespace Umbraco.Cms.Infrastructure.WebAssets { _runtimeMinifier.CreateJsBundle( GetIndependentPackageBundleName(manifestAssets, assetType), - true, + BundlingOptions.OptimizedAndComposite, FormatPaths(manifestAssets.Assets.ToArray())); } } diff --git a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs index ca1aea69a7..67dfaa29fe 100644 --- a/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs +++ b/src/Umbraco.Web.BackOffice/Controllers/BackOfficeController.cs @@ -220,8 +220,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers var result = await _runtimeMinifier.GetScriptForLoadingBackOfficeAsync( _globalSettings, _hostingEnvironment, - _manifestParser, - Url); + _manifestParser); return new JavaScriptResult(result); } diff --git a/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs index e436a57d83..0be88c9d2a 100644 --- a/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs +++ b/src/Umbraco.Web.BackOffice/Extensions/RuntimeMinifierExtensions.cs @@ -22,8 +22,7 @@ namespace Umbraco.Extensions this IRuntimeMinifier minifier, GlobalSettings globalSettings, IHostingEnvironment hostingEnvironment, - IManifestParser manifestParser, - IUrlHelper urlHelper) + IManifestParser manifestParser) { var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach(var file in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoCoreJsBundleName)) @@ -50,15 +49,9 @@ namespace Umbraco.Extensions } // process the "None" bundles, meaning we'll just render the script as-is - if (manifestParser.CombinedManifest.Scripts.TryGetValue(BundleOptions.None, out IReadOnlyList noneManifestAssetsList)) + foreach (var asset in await minifier.GetJsAssetPathsAsync(BackOfficeWebAssets.UmbracoNonOptimizedPackageJsBundleName)) { - foreach (ManifestAssets manifestAssets in noneManifestAssetsList) - { - foreach(var asset in manifestAssets.Assets) - { - files.Add(urlHelper.Content(asset)); - } - } + files.Add(asset); } var result = BackOfficeJavaScriptInitializer.GetJavascriptInitialization( @@ -67,7 +60,7 @@ namespace Umbraco.Extensions globalSettings, hostingEnvironment); - result += await GetStylesheetInitializationAsync(minifier, manifestParser, urlHelper); + result += await GetStylesheetInitializationAsync(minifier, manifestParser); return result; } @@ -77,8 +70,7 @@ namespace Umbraco.Extensions /// private static async Task GetStylesheetInitializationAsync( IRuntimeMinifier minifier, - IManifestParser manifestParser, - IUrlHelper urlHelper) + IManifestParser manifestParser) { var files = new HashSet(StringComparer.InvariantCultureIgnoreCase); foreach(var file in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoCssBundleName)) @@ -100,15 +92,9 @@ namespace Umbraco.Extensions } // process the "None" bundles, meaning we'll just render the script as-is - if (manifestParser.CombinedManifest.Stylesheets.TryGetValue(BundleOptions.None, out IReadOnlyList noneManifestAssetsList)) + foreach (var asset in await minifier.GetCssAssetPathsAsync(BackOfficeWebAssets.UmbracoNonOptimizedPackageCssBundleName)) { - foreach (ManifestAssets manifestAssets in noneManifestAssetsList) - { - foreach (var asset in manifestAssets.Assets) - { - files.Add(urlHelper.Content(asset)); - } - } + files.Add(asset); } var sb = new StringBuilder(); diff --git a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs index c202c49980..96188ba08c 100644 --- a/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs +++ b/src/Umbraco.Web.Common/RuntimeMinification/SmidgeRuntimeMinifier.cs @@ -90,7 +90,7 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification public string CacheBuster => (_cacheBuster ??= _cacheBusterResolver.GetCacheBuster(_cacheBusterType)).GetValue(); // only issue with creating bundles like this is that we don't have full control over the bundle options, though that could - public void CreateCssBundle(string bundleName, bool optimizeOutput, params string[] filePaths) + public void CreateCssBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths) { if (filePaths.Any(f => !f.StartsWith("/") && !f.StartsWith("~/"))) { @@ -102,39 +102,17 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification throw new InvalidOperationException($"The bundle name {bundleName} already exists"); } - if (optimizeOutput) - { - var bundle = _bundles.Create(bundleName, _cssOptimizedPipeline.Value, WebFileType.Css, filePaths) - .WithEnvironmentOptions( - BundleEnvironmentOptions.Create() - .ForDebug(builder => builder - // auto-invalidate bundle if files change in debug - .EnableFileWatcher() - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .ForProduction(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .Build()); - } - else - { - var bundle = _bundles.Create(bundleName, _cssNonOptimizedPipeline.Value, WebFileType.Css, filePaths) - .WithEnvironmentOptions( - BundleEnvironmentOptions.Create() - .ForDebug(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .ForProduction(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .Build()); - } + PreProcessPipeline pipeline = bundleOptions.OptimizeOutput + ? _cssOptimizedPipeline.Value + : _cssNonOptimizedPipeline.Value; + + Bundle bundle = _bundles.Create(bundleName, pipeline, WebFileType.Css, filePaths); + bundle.WithEnvironmentOptions(ConfigureBundleEnvironmentOptions(bundleOptions)); } public async Task RenderCssHereAsync(string bundleName) => (await _smidge.SmidgeHelper.CssHereAsync(bundleName, _hostingEnvironment.IsDebugMode)).ToString(); - public void CreateJsBundle(string bundleName, bool optimizeOutput, params string[] filePaths) + public void CreateJsBundle(string bundleName, BundlingOptions bundleOptions, params string[] filePaths) { if (filePaths.Any(f => !f.StartsWith("/") && !f.StartsWith("~/"))) { @@ -146,40 +124,32 @@ namespace Umbraco.Cms.Web.Common.RuntimeMinification throw new InvalidOperationException($"The bundle name {bundleName} already exists"); } - if (optimizeOutput) - { - var bundle = _bundles.Create(bundleName, _jsOptimizedPipeline.Value, WebFileType.Js, filePaths) - .WithEnvironmentOptions( - BundleEnvironmentOptions.Create() - .ForDebug(builder => builder - // auto-invalidate bundle if files change in debug - .EnableFileWatcher() - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .ForProduction(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .Build()); - } - else - { - var bundle = _bundles.Create(bundleName, _jsNonOptimizedPipeline.Value, WebFileType.Js, filePaths) - .WithEnvironmentOptions( - BundleEnvironmentOptions.Create() - .ForDebug(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .ForProduction(builder => builder - // use the cache buster defined in config - .SetCacheBusterType(_cacheBusterType)) - .Build()); - } + PreProcessPipeline pipeline = bundleOptions.OptimizeOutput + ? _jsOptimizedPipeline.Value + : _jsNonOptimizedPipeline.Value; + + Bundle bundle = _bundles.Create(bundleName, pipeline, WebFileType.Js, filePaths); + bundle.WithEnvironmentOptions(ConfigureBundleEnvironmentOptions(bundleOptions)); + } + + private BundleEnvironmentOptions ConfigureBundleEnvironmentOptions(BundlingOptions bundleOptions) + { + var bundleEnvironmentOptions = new BundleEnvironmentOptions(); + // auto-invalidate bundle if files change in debug + bundleEnvironmentOptions.DebugOptions.FileWatchOptions.Enabled = true; + // set cache busters + bundleEnvironmentOptions.DebugOptions.SetCacheBusterType(_cacheBusterType); + bundleEnvironmentOptions.ProductionOptions.SetCacheBusterType(_cacheBusterType); + // config if the files should be combined + bundleEnvironmentOptions.ProductionOptions.ProcessAsCompositeFile = bundleOptions.EnabledCompositeFiles; + + return bundleEnvironmentOptions; } public async Task RenderJsHereAsync(string bundleName) => (await _smidge.SmidgeHelper.JsHereAsync(bundleName, _hostingEnvironment.IsDebugMode)).ToString(); - public async Task> GetJsAssetPathsAsync(string bundleName) => await _smidge.SmidgeHelper.GenerateJsUrlsAsync(bundleName, _hostingEnvironment.IsDebugMode); + public async Task> GetCssAssetPathsAsync(string bundleName) => await _smidge.SmidgeHelper.GenerateCssUrlsAsync(bundleName, _hostingEnvironment.IsDebugMode); /// From 11cb1ef55deb5c39247ecbb1ac95a324cecd65e0 Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 15 Jul 2021 14:25:40 -0600 Subject: [PATCH 074/102] Removes Umbraco.Web. --- .../AspNetApplicationShutdownRegistry.cs | 47 -- .../AspNet/AspNetBackOfficeInfo.cs | 68 --- src/Umbraco.Web/AspNet/AspNetCookieManager.cs | 35 -- .../AspNet/AspNetHostingEnvironment.cs | 105 ---- .../AspNet/AspNetHttpContextAccessor.cs | 22 - src/Umbraco.Web/AspNet/AspNetIpResolver.cs | 16 - .../AspNet/AspNetPasswordHasher.cs | 22 - .../AspNet/AspNetSessionManager.cs | 28 - .../AspNetUmbracoApplicationLifetime.cs | 34 -- .../AspNet/AspNetUserAgentProvider.cs | 19 - src/Umbraco.Web/AspNet/FrameworkMarchal.cs | 13 - src/Umbraco.Web/Composing/Current.cs | 192 ------- .../HttpContextAccessorExtensions.cs | 18 - src/Umbraco.Web/HttpContextExtensions.cs | 54 -- .../HttpContextUmbracoContextAccessor.cs | 31 -- src/Umbraco.Web/HttpCookieExtensions.cs | 176 ------ src/Umbraco.Web/HttpRequestExtensions.cs | 82 --- src/Umbraco.Web/IHttpContextAccessor.cs | 9 - src/Umbraco.Web/ModelStateExtensions.cs | 224 -------- src/Umbraco.Web/Mvc/ControllerExtensions.cs | 47 -- src/Umbraco.Web/Mvc/PluginController.cs | 113 ---- .../Mvc/PluginControllerAttribute.cs | 29 - src/Umbraco.Web/Mvc/RenderRouteHandler.cs | 14 - src/Umbraco.Web/Mvc/RouteDefinition.cs | 28 - src/Umbraco.Web/Mvc/Strings.Designer.cs | 78 --- src/Umbraco.Web/Mvc/Strings.resx | 124 ----- src/Umbraco.Web/Mvc/SurfaceController.cs | 19 - .../Mvc/UmbracoVirtualNodeByIdRouteHandler.cs | 27 - .../UmbracoVirtualNodeByUdiRouteHandler.cs | 28 - .../Mvc/UmbracoVirtualNodeRouteHandler.cs | 102 ---- .../Mvc/ViewDataDictionaryExtensions.cs | 18 - src/Umbraco.Web/Properties/AssemblyInfo.cs | 32 -- .../ContentCacheDataSerializationResult.cs | 51 -- .../ContentCacheDataSerializerEntityType.cs | 13 - .../DataSource/IContentCacheDataSerializer.cs | 27 - .../IContentCacheDataSerializerFactory.cs | 16 - .../IDictionaryOfPropertyDataSerializer.cs | 12 - .../JsonContentNestedDataSerializer.cs | 95 ---- .../JsonContentNestedDataSerializerFactory.cs | 10 - .../DataSource/LazyCompressedString.cs | 109 ---- .../NuCache/NuCacheSerializerComponent.cs | 68 --- src/Umbraco.Web/RouteCollectionExtensions.cs | 135 ----- .../AspNetUmbracoBootPermissionChecker.cs | 13 - src/Umbraco.Web/Runtime/WebFinalComponent.cs | 24 - src/Umbraco.Web/Runtime/WebFinalComposer.cs | 11 - ...eDirectoryBackOfficeUserPasswordChecker.cs | 44 -- .../AuthenticationOptionsExtensions.cs | 103 ---- .../BackOfficeUserPasswordCheckerResult.cs | 12 - .../Security/BackofficeSecurity.cs | 29 - .../IBackOfficeUserPasswordChecker.cs | 25 - src/Umbraco.Web/StringExtensions.cs | 69 --- src/Umbraco.Web/TypeLoaderExtensions.cs | 28 - src/Umbraco.Web/Umbraco.Web.csproj | 270 ---------- src/Umbraco.Web/UmbracoApplication.cs | 19 - src/Umbraco.Web/UmbracoApplicationBase.cs | 328 ------------ src/Umbraco.Web/UmbracoContext.cs | 153 ------ src/Umbraco.Web/UmbracoContextFactory.cs | 90 ---- .../UmbracoDbProviderFactoryCreator.cs | 64 --- src/Umbraco.Web/UmbracoHelper.cs | 501 ------------------ src/Umbraco.Web/UmbracoHttpHandler.cs | 66 --- src/Umbraco.Web/UmbracoWebService.cs | 78 --- src/Umbraco.Web/UrlHelperExtensions.cs | 123 ----- src/Umbraco.Web/UrlHelperRenderExtensions.cs | 425 --------------- .../WebApi/EnableDetailedErrorsAttribute.cs | 17 - .../Filters/FeatureAuthorizeAttribute.cs | 38 -- .../Filters/ValidationFilterAttribute.cs | 29 - .../WebApi/HttpRequestMessageExtensions.cs | 65 --- .../WebApi/IsBackOfficeAttribute.cs | 11 - .../WebApi/NamespaceHttpControllerSelector.cs | 99 ---- .../ParameterSwapControllerActionSelector.cs | 159 ------ .../WebApi/SerializeVersionAttribute.cs | 15 - .../SessionHttpControllerRouteHandler.cs | 31 -- src/Umbraco.Web/WebApi/TrimModelBinder.cs | 23 - .../WebApi/UmbracoApiController.cs | 28 - .../WebApi/UmbracoApiControllerBase.cs | 127 ----- ...bracoApiControllerTypeCollectionBuilder.cs | 12 - .../WebApi/UmbracoAuthorizeAttribute.cs | 69 --- .../WebApi/UmbracoAuthorizedApiController.cs | 41 -- 78 files changed, 5629 deletions(-) delete mode 100644 src/Umbraco.Web/AspNet/AspNetApplicationShutdownRegistry.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetCookieManager.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetIpResolver.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetSessionManager.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs delete mode 100644 src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs delete mode 100644 src/Umbraco.Web/AspNet/FrameworkMarchal.cs delete mode 100644 src/Umbraco.Web/Composing/Current.cs delete mode 100644 src/Umbraco.Web/HttpContextAccessorExtensions.cs delete mode 100644 src/Umbraco.Web/HttpContextExtensions.cs delete mode 100644 src/Umbraco.Web/HttpContextUmbracoContextAccessor.cs delete mode 100644 src/Umbraco.Web/HttpCookieExtensions.cs delete mode 100644 src/Umbraco.Web/HttpRequestExtensions.cs delete mode 100644 src/Umbraco.Web/IHttpContextAccessor.cs delete mode 100644 src/Umbraco.Web/ModelStateExtensions.cs delete mode 100644 src/Umbraco.Web/Mvc/ControllerExtensions.cs delete mode 100644 src/Umbraco.Web/Mvc/PluginController.cs delete mode 100644 src/Umbraco.Web/Mvc/PluginControllerAttribute.cs delete mode 100644 src/Umbraco.Web/Mvc/RenderRouteHandler.cs delete mode 100644 src/Umbraco.Web/Mvc/RouteDefinition.cs delete mode 100644 src/Umbraco.Web/Mvc/Strings.Designer.cs delete mode 100644 src/Umbraco.Web/Mvc/Strings.resx delete mode 100644 src/Umbraco.Web/Mvc/SurfaceController.cs delete mode 100644 src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs delete mode 100644 src/Umbraco.Web/Mvc/UmbracoVirtualNodeByUdiRouteHandler.cs delete mode 100644 src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs delete mode 100644 src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs delete mode 100644 src/Umbraco.Web/Properties/AssemblyInfo.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializationResult.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializerEntityType.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializerFactory.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/DataSource/LazyCompressedString.cs delete mode 100644 src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs delete mode 100644 src/Umbraco.Web/RouteCollectionExtensions.cs delete mode 100644 src/Umbraco.Web/Runtime/AspNetUmbracoBootPermissionChecker.cs delete mode 100644 src/Umbraco.Web/Runtime/WebFinalComponent.cs delete mode 100644 src/Umbraco.Web/Runtime/WebFinalComposer.cs delete mode 100644 src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs delete mode 100644 src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs delete mode 100644 src/Umbraco.Web/Security/BackOfficeUserPasswordCheckerResult.cs delete mode 100644 src/Umbraco.Web/Security/BackofficeSecurity.cs delete mode 100644 src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs delete mode 100644 src/Umbraco.Web/StringExtensions.cs delete mode 100644 src/Umbraco.Web/TypeLoaderExtensions.cs delete mode 100644 src/Umbraco.Web/Umbraco.Web.csproj delete mode 100644 src/Umbraco.Web/UmbracoApplication.cs delete mode 100644 src/Umbraco.Web/UmbracoApplicationBase.cs delete mode 100644 src/Umbraco.Web/UmbracoContext.cs delete mode 100644 src/Umbraco.Web/UmbracoContextFactory.cs delete mode 100644 src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs delete mode 100644 src/Umbraco.Web/UmbracoHelper.cs delete mode 100644 src/Umbraco.Web/UmbracoHttpHandler.cs delete mode 100644 src/Umbraco.Web/UmbracoWebService.cs delete mode 100644 src/Umbraco.Web/UrlHelperExtensions.cs delete mode 100644 src/Umbraco.Web/UrlHelperRenderExtensions.cs delete mode 100644 src/Umbraco.Web/WebApi/EnableDetailedErrorsAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/Filters/ValidationFilterAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs delete mode 100644 src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs delete mode 100644 src/Umbraco.Web/WebApi/ParameterSwapControllerActionSelector.cs delete mode 100644 src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs delete mode 100644 src/Umbraco.Web/WebApi/TrimModelBinder.cs delete mode 100644 src/Umbraco.Web/WebApi/UmbracoApiController.cs delete mode 100644 src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs delete mode 100644 src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollectionBuilder.cs delete mode 100644 src/Umbraco.Web/WebApi/UmbracoAuthorizeAttribute.cs delete mode 100644 src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs diff --git a/src/Umbraco.Web/AspNet/AspNetApplicationShutdownRegistry.cs b/src/Umbraco.Web/AspNet/AspNetApplicationShutdownRegistry.cs deleted file mode 100644 index 12aa79c1bd..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetApplicationShutdownRegistry.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Web.Hosting; -using Umbraco.Cms.Core.Hosting; -using IRegisteredObject = Umbraco.Cms.Core.IRegisteredObject; - -namespace Umbraco.Web.Hosting -{ - public class AspNetApplicationShutdownRegistry : IApplicationShutdownRegistry - { - private readonly ConcurrentDictionary _registeredObjects = - new ConcurrentDictionary(); - - public void RegisterObject(IRegisteredObject registeredObject) - { - var wrapped = new RegisteredObjectWrapper(registeredObject); - if (!_registeredObjects.TryAdd(registeredObject, wrapped)) - { - throw new InvalidOperationException("Could not register object"); - } - HostingEnvironment.RegisterObject(wrapped); - } - - public void UnregisterObject(IRegisteredObject registeredObject) - { - if (_registeredObjects.TryGetValue(registeredObject, out var wrapped)) - { - HostingEnvironment.UnregisterObject(wrapped); - } - } - - private class RegisteredObjectWrapper : System.Web.Hosting.IRegisteredObject - { - private readonly IRegisteredObject _inner; - - public RegisteredObjectWrapper(IRegisteredObject inner) - { - _inner = inner; - } - - public void Stop(bool immediate) - { - _inner.Stop(immediate); - } - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs b/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs deleted file mode 100644 index 89222880ed..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetBackOfficeInfo.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Web; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.IO; -using Umbraco.Extensions; - -namespace Umbraco.Web -{ - public class AspNetBackOfficeInfo : IBackOfficeInfo - { - private readonly GlobalSettings _globalSettings; - private readonly IIOHelper _ioHelper; - private readonly ILogger _logger; - private readonly WebRoutingSettings _webRoutingSettings; - - public AspNetBackOfficeInfo(GlobalSettings globalSettings, IIOHelper ioHelper, ILogger logger, IOptions webRoutingSettings) - { - _globalSettings = globalSettings; - _ioHelper = ioHelper; - _logger = logger; - _webRoutingSettings = webRoutingSettings.Value; - } - - /// - public string GetAbsoluteUrl => GetAbsoluteUrlFromConfig() ?? GetAbsoluteUrlFromCurrentRequest() ?? null; - - private string GetAbsoluteUrlFromConfig() - { - var url = _webRoutingSettings.UmbracoApplicationUrl; - if (url.IsNullOrWhiteSpace() == false) - { - var umbracoApplicationUrl = url.TrimEnd('/'); - _logger.LogInformation("ApplicationUrl: {UmbracoAppUrl} (using web.routing/@umbracoApplicationUrl)", umbracoApplicationUrl); - return umbracoApplicationUrl; - } - - return null; - } - - - - private string GetAbsoluteUrlFromCurrentRequest() - { - var request = HttpContext.Current?.Request; - - if (request is null) return null; - - // if (HTTP and SSL not required) or (HTTPS and SSL required), - // use ports from request - // otherwise, - // if non-standard ports used, - // user may need to set umbracoApplicationUrl manually per - // https://our.umbraco.com/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks - var port = (request.IsSecureConnection == false && _globalSettings.UseHttps == false) - || (request.IsSecureConnection && _globalSettings.UseHttps) - ? ":" + request.ServerVariables["SERVER_PORT"] - : ""; - - var ssl = _globalSettings.UseHttps ? "s" : ""; // force, whatever the first request - var url = "http" + ssl + "://" + request.ServerVariables["SERVER_NAME"] + port + - _ioHelper.ResolveUrl(_globalSettings.UmbracoPath); - - return url.TrimEnd(Constants.CharArrays.ForwardSlash); - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetCookieManager.cs b/src/Umbraco.Web/AspNet/AspNetCookieManager.cs deleted file mode 100644 index 7d1cad5763..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetCookieManager.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web -{ - public class AspNetCookieManager : ICookieManager - { - private readonly IHttpContextAccessor _httpContextAccessor; - - public AspNetCookieManager(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public void ExpireCookie(string cookieName) - { - _httpContextAccessor.HttpContext?.ExpireCookie(cookieName); - } - - public string GetCookieValue(string cookieName) - { - return _httpContextAccessor.HttpContext?.Request.GetCookieValue(cookieName); - } - - public void SetCookieValue(string cookieName, string value) - { - _httpContextAccessor.HttpContext?.Response.Cookies.Set(new HttpCookie(cookieName, value)); - } - - public bool HasCookie(string cookieName) - { - return !(GetCookieValue(cookieName) is null); - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs b/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs deleted file mode 100644 index 745a3f1cca..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetHostingEnvironment.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.IO; -using System.Reflection; -using System.Web; -using System.Web.Hosting; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Web.Hosting -{ - // TODO: This has been migrated to netcore - public class AspNetHostingEnvironment : IHostingEnvironment - { - - private readonly HostingSettings _hostingSettings; - private string _localTempPath; - private Uri _applicationMainUrl; - - - public AspNetHostingEnvironment(IOptions hostingSettings) - { - _hostingSettings = hostingSettings.Value ?? throw new ArgumentNullException(nameof(hostingSettings)); - SiteName = HostingEnvironment.SiteName; - ApplicationId = HostingEnvironment.ApplicationID; - // when we are not hosted (i.e. unit test or otherwise) we'll need to get the root path from the executing assembly - ApplicationPhysicalPath = HostingEnvironment.ApplicationPhysicalPath ?? Assembly.GetExecutingAssembly().GetRootDirectorySafe(); - ApplicationVirtualPath = _hostingSettings.ApplicationVirtualPath?.EnsureStartsWith('/') - ?? HostingEnvironment.ApplicationVirtualPath?.EnsureStartsWith("/") - ?? "/"; - } - - public string SiteName { get; } - public string ApplicationId { get; } - public string ApplicationPhysicalPath { get; } - - public string ApplicationVirtualPath { get; } - - public bool IsDebugMode => HttpContext.Current?.IsDebuggingEnabled ?? _hostingSettings.Debug; - /// - public bool IsHosted => (HttpContext.Current != null || HostingEnvironment.IsHosted); - - public Uri ApplicationMainUrl - { - get => _applicationMainUrl; - set => _applicationMainUrl = value; - } - - public string MapPathWebRoot(string path) - { - if (HostingEnvironment.IsHosted) - return HostingEnvironment.MapPath(path); - - // this will be the case in unit tests, we'll manually map the path - var newPath = path.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar); - return newPath.StartsWith(ApplicationPhysicalPath) ? newPath : Path.Combine(ApplicationPhysicalPath, newPath.TrimStart(Constants.CharArrays.TildeForwardSlash)); - } - - public string MapPathContentRoot(string path) => MapPathWebRoot(path); - - public string ToAbsolute(string virtualPath) => VirtualPathUtility.ToAbsolute(virtualPath, ApplicationVirtualPath); - public void EnsureApplicationMainUrl(Uri currentApplicationUrl) => throw new NotImplementedException(); - - - public string LocalTempPath - { - get - { - if (_localTempPath != null) - return _localTempPath; - - switch (_hostingSettings.LocalTempStorageLocation) - { - case LocalTempStorage.EnvironmentTemp: - - // environment temp is unique, we need a folder per site - - // use a hash - // combine site name and application id - // site name is a Guid on Cloud - // application id is eg /LM/W3SVC/123456/ROOT - // the combination is unique on one server - // and, if a site moves from worker A to B and then back to A... - // hopefully it gets a new Guid or new application id? - - var hashString = SiteName + "::" + ApplicationId; - var hash = hashString.GenerateHash(); - var siteTemp = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData", hash); - - return _localTempPath = siteTemp; - - default: - return _localTempPath = MapPathContentRoot(Constants.SystemDirectories.TempData); - } - } - } - - } - - -} diff --git a/src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs b/src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs deleted file mode 100644 index d222d18438..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetHttpContextAccessor.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Web; - -namespace Umbraco.Web -{ - internal class AspNetHttpContextAccessor : IHttpContextAccessor - { - public HttpContextBase HttpContext - { - get - { - var httpContext = System.Web.HttpContext.Current; - - return httpContext is null ? null : new HttpContextWrapper(httpContext); - } - set - { - throw new NotSupportedException(); - } - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetIpResolver.cs b/src/Umbraco.Web/AspNet/AspNetIpResolver.cs deleted file mode 100644 index 6a5766e5c4..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetIpResolver.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core.Net; - -namespace Umbraco.Web -{ - internal class AspNetIpResolver : IIpResolver - { - public string GetCurrentRequestIpAddress() - { - var httpContext = HttpContext.Current is null ? null : new HttpContextWrapper(HttpContext.Current); - var ip = httpContext.GetCurrentRequestIpAddress(); - if (ip.ToLowerInvariant().StartsWith("unknown")) ip = ""; - return ip; - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs b/src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs deleted file mode 100644 index e7adae86a6..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetPasswordHasher.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using Microsoft.AspNet.Identity; -using IPasswordHasher = Umbraco.Cms.Core.Security.IPasswordHasher; - -namespace Umbraco.Web -{ - [Obsolete("Should be removed")] - public class AspNetPasswordHasher : Cms.Core.Security.IPasswordHasher - { - private PasswordHasher _underlyingHasher; - - public AspNetPasswordHasher() - { - _underlyingHasher = new PasswordHasher(); - } - - public string HashPassword(string password) - { - return _underlyingHasher.HashPassword(password); - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs b/src/Umbraco.Web/AspNet/AspNetSessionManager.cs deleted file mode 100644 index 349c8b1539..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetSessionManager.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core.Net; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web.AspNet -{ - public class AspNetSessionManager: ISessionManager, ISessionIdResolver - { - - public AspNetSessionManager() - { - } - - public string GetSessionValue(string sessionName) - { - return HttpContext.Current?.Session[sessionName]?.ToString(); - } - - public void SetSessionValue(string sessionName, string value) - { - var httpContext = HttpContext.Current; - if (httpContext is null) return; - HttpContext.Current.Session[sessionName] = value; - } - - public string SessionId => HttpContext.Current?.Session?.SessionID; - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs b/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs deleted file mode 100644 index 1b7261aae8..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetUmbracoApplicationLifetime.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Threading; -using System.Web; -using Umbraco.Cms.Core.Hosting; - -namespace Umbraco.Web.AspNet -{ - public class AspNetUmbracoApplicationLifetime : IUmbracoApplicationLifetime - { - private readonly IHttpContextAccessor _httpContextAccessor; - - public AspNetUmbracoApplicationLifetime(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public bool IsRestarting { get; set; } - - public void Restart() - { - IsRestarting = true; - - var httpContext = _httpContextAccessor.HttpContext; - if (httpContext != null) - { - // unload app domain - we must null out all identities otherwise we get serialization errors - // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue - httpContext.User = null; - } - - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } - } -} diff --git a/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs b/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs deleted file mode 100644 index f17dcadd50..0000000000 --- a/src/Umbraco.Web/AspNet/AspNetUserAgentProvider.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Cms.Core.Net; - -namespace Umbraco.Web.AspNet -{ - public class AspNetUserAgentProvider : IUserAgentProvider - { - private readonly IHttpContextAccessor _httpContextAccessor; - - public AspNetUserAgentProvider(IHttpContextAccessor httpContextAccessor) - { - _httpContextAccessor = httpContextAccessor; - } - - public string GetUserAgent() - { - return _httpContextAccessor.GetRequiredHttpContext().Request.UserAgent; - } - } -} diff --git a/src/Umbraco.Web/AspNet/FrameworkMarchal.cs b/src/Umbraco.Web/AspNet/FrameworkMarchal.cs deleted file mode 100644 index b53aa13e46..0000000000 --- a/src/Umbraco.Web/AspNet/FrameworkMarchal.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Runtime.InteropServices; -using Umbraco.Cms.Core.Diagnostics; - -namespace Umbraco.Web -{ - - public class FrameworkMarchal : IMarchal - { - // This thing is not available in net standard, but exists in both .Net 4 and .Net Core 3 - public IntPtr GetExceptionPointers() => Marshal.GetExceptionPointers(); - } -} diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs deleted file mode 100644 index 6240ba27bb..0000000000 --- a/src/Umbraco.Web/Composing/Current.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Actions; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Editors; -using Umbraco.Cms.Core.Events; -using Umbraco.Cms.Core.HealthChecks; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Net; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Scoping; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Sync; -using Umbraco.Cms.Core.Templates; -using Umbraco.Cms.Core.Trees; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Core.WebAssets; -using Umbraco.Web.Security; - -namespace Umbraco.Web.Composing -{ - // see notes in Umbraco.Core.Composing.Current. - public static class Current - { - private static readonly object Locker = new object(); - - private static IServiceProvider _factory; - - /// - /// Gets or sets the factory. - /// - public static IServiceProvider Factory - { - get - { - if (_factory == null) - throw new InvalidOperationException("No factory has been set."); - return _factory; - } - set - { - _factory = value; - } - } - - private static IUmbracoContextAccessor _umbracoContextAccessor; - - - #region Temp & Special - - // TODO: have to keep this until tests are refactored - // but then, it should all be managed properly in the container - public static IUmbracoContextAccessor UmbracoContextAccessor - { - get - { - if (_umbracoContextAccessor != null) return _umbracoContextAccessor; - return _umbracoContextAccessor = Factory.GetRequiredService(); - } - set => _umbracoContextAccessor = value; // for tests - } - - #endregion - - #region Web Getters - - public static IUmbracoContext UmbracoContext - => UmbracoContextAccessor.UmbracoContext; - - public static IBackOfficeSecurityAccessor BackOfficeSecurityAccessor - => Factory.GetRequiredService(); - - public static UmbracoHelper UmbracoHelper - => Factory.GetRequiredService(); - public static IUmbracoComponentRenderer UmbracoComponentRenderer - => Factory.GetRequiredService(); - public static ITagQuery TagQuery - => Factory.GetRequiredService(); - - public static IRuntimeMinifier RuntimeMinifier - => Factory.GetRequiredService(); - - public static DistributedCache DistributedCache - => Factory.GetRequiredService(); - - public static IPublishedSnapshot PublishedSnapshot - => Factory.GetRequiredService().PublishedSnapshot; - - public static EventMessages EventMessages - => Factory.GetRequiredService().GetOrDefault(); - - public static UrlProviderCollection UrlProviders - => Factory.GetRequiredService(); - - public static MediaUrlProviderCollection MediaUrlProviders - => Factory.GetRequiredService(); - - public static HealthCheckCollectionBuilder HealthCheckCollectionBuilder - => Factory.GetRequiredService(); - - internal static ActionCollectionBuilder ActionCollectionBuilder - => Factory.GetRequiredService(); - - public static ActionCollection Actions - => Factory.GetRequiredService(); - - public static ContentFinderCollection ContentFinders - => Factory.GetRequiredService(); - - public static IContentLastChanceFinder LastChanceContentFinder - => Factory.GetRequiredService(); - - internal static EditorValidatorCollection EditorValidators - => Factory.GetRequiredService(); - - internal static UmbracoApiControllerTypeCollection UmbracoApiControllerTypes - => Factory.GetRequiredService(); - - internal static IPublishedSnapshotService PublishedSnapshotService - => Factory.GetRequiredService(); - - public static ITreeService TreeService - => Factory.GetRequiredService(); - - public static ISectionService SectionService - => Factory.GetRequiredService(); - - public static IIconService IconService - => Factory.GetRequiredService(); - - #endregion - - - #region Core Getters - - // proxy Core for convenience - - public static MediaFileManager MediaFileManager => Factory.GetRequiredService(); - - public static UmbracoMapper Mapper => Factory.GetRequiredService(); - - public static IRuntimeState RuntimeState => Factory.GetRequiredService(); - - public static CacheRefresherCollection CacheRefreshers => Factory.GetRequiredService(); - - internal static IPublishedModelFactory PublishedModelFactory => Factory.GetRequiredService(); - - public static IServerMessenger ServerMessenger => Factory.GetRequiredService(); - - public static ILogger Logger => Factory.GetRequiredService>(); - - public static ILoggerFactory LoggerFactory => Factory.GetRequiredService(); - - public static IProfiler Profiler => Factory.GetRequiredService(); - - public static IProfilerHtml ProfilerHtml => Factory.GetRequiredService(); - - public static IProfilingLogger ProfilingLogger => Factory.GetRequiredService(); - - public static AppCaches AppCaches => Factory.GetRequiredService(); - - public static ServiceContext Services => Factory.GetRequiredService(); - - public static IScopeProvider ScopeProvider => Factory.GetRequiredService(); - - public static IPublishedContentTypeFactory PublishedContentTypeFactory => Factory.GetRequiredService(); - - public static IPublishedValueFallback PublishedValueFallback => Factory.GetRequiredService(); - - public static IVariationContextAccessor VariationContextAccessor => Factory.GetRequiredService(); - - public static IIOHelper IOHelper => Factory.GetRequiredService(); - public static IHostingEnvironment HostingEnvironment => Factory.GetRequiredService(); - public static IIpResolver IpResolver => Factory.GetRequiredService(); - public static IUmbracoVersion UmbracoVersion => Factory.GetRequiredService(); - public static IPublishedUrlProvider PublishedUrlProvider => Factory.GetRequiredService(); - public static IMenuItemCollectionFactory MenuItemCollectionFactory => Factory.GetRequiredService(); - public static IUmbracoApplicationLifetime UmbracoApplicationLifetime => Factory.GetRequiredService(); - public static IPublishedContentQuery PublishedContentQuery => Factory.GetRequiredService(); - - #endregion - } -} diff --git a/src/Umbraco.Web/HttpContextAccessorExtensions.cs b/src/Umbraco.Web/HttpContextAccessorExtensions.cs deleted file mode 100644 index 8901cf692c..0000000000 --- a/src/Umbraco.Web/HttpContextAccessorExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using System.Web; - -namespace Umbraco.Web -{ - public static class HttpContextAccessorExtensions - { - public static HttpContextBase GetRequiredHttpContext(this IHttpContextAccessor httpContextAccessor) - { - if (httpContextAccessor == null) throw new ArgumentNullException(nameof(httpContextAccessor)); - var httpContext = httpContextAccessor.HttpContext; - - if(httpContext is null) throw new InvalidOperationException("HttpContext is null"); - - return httpContext; - } - } -} diff --git a/src/Umbraco.Web/HttpContextExtensions.cs b/src/Umbraco.Web/HttpContextExtensions.cs deleted file mode 100644 index 40718715c9..0000000000 --- a/src/Umbraco.Web/HttpContextExtensions.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core; - -namespace Umbraco.Web -{ - public static class HttpContextExtensions - { - public static string GetCurrentRequestIpAddress(this HttpContextBase httpContext) - { - if (httpContext == null) - { - return "Unknown, httpContext is null"; - } - - HttpRequestBase request; - try - { - // is not null - throws - request = httpContext.Request; - } - catch - { - return "Unknown, httpContext.Request is null"; - } - - if (request.ServerVariables == null) - { - return "Unknown, httpContext.Request.ServerVariables is null"; - } - - // From: http://stackoverflow.com/a/740431/5018 - - try - { - var ipAddress = request.ServerVariables["HTTP_X_FORWARDED_FOR"]; - - if (string.IsNullOrEmpty(ipAddress)) - return request.UserHostAddress; - - var addresses = ipAddress.Split(Constants.CharArrays.Comma); - if (addresses.Length != 0) - return addresses[0]; - - return request.UserHostAddress; - } - catch (System.Exception ex) - { - //This try catch is to just always ensure that no matter what we're not getting any exceptions caused since - // that would cause people to not be able to login - return string.Format("Unknown, exception occurred trying to resolve IP {0}", ex); - } - } - } -} diff --git a/src/Umbraco.Web/HttpContextUmbracoContextAccessor.cs b/src/Umbraco.Web/HttpContextUmbracoContextAccessor.cs deleted file mode 100644 index ff8101053d..0000000000 --- a/src/Umbraco.Web/HttpContextUmbracoContextAccessor.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web -{ - internal class HttpContextUmbracoContextAccessor : IUmbracoContextAccessor - { - private readonly IRequestCache _requestCache; - private const string HttpContextItemKey = "Umbraco.Web.UmbracoContext"; - - public HttpContextUmbracoContextAccessor(IRequestCache requestCache) - { - _requestCache = requestCache; - } - - public IUmbracoContext UmbracoContext - { - get - { - if (!_requestCache.IsAvailable) throw new InvalidOperationException("No request cache available"); - return (IUmbracoContext) _requestCache.Get(HttpContextItemKey); - } - set - { - if (!_requestCache.IsAvailable) throw new InvalidOperationException("No request cache available"); - _requestCache.Set(HttpContextItemKey, value); - } - } - } -} diff --git a/src/Umbraco.Web/HttpCookieExtensions.cs b/src/Umbraco.Web/HttpCookieExtensions.cs deleted file mode 100644 index 7804c6b45e..0000000000 --- a/src/Umbraco.Web/HttpCookieExtensions.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Web; -using Umbraco.Extensions; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Web -{ - /// - /// Extension methods used to check/set cookie values - /// - /// - /// This should 100% supersede the StateManager.Cookies - /// - internal static class HttpCookieExtensions - { - /// - /// Retrieves an individual cookie from the cookies collection - /// - /// - /// - /// - /// - /// Adapted from: https://stackoverflow.com/a/29057304/5018 because there's an issue with .NET WebApi cookie parsing logic - /// when using requestHeaders.GetCookies() when an invalid cookie name is present. - /// - public static string GetCookieValue(this HttpRequestHeaders requestHeaders, string cookieName) - { - foreach (var header in requestHeaders) - { - if (header.Key.Equals("Cookie", StringComparison.InvariantCultureIgnoreCase) == false) - continue; - - var cookiesHeaderValue = header.Value.FirstOrDefault(); - if (cookiesHeaderValue == null) - return null; - - var cookieCollection = cookiesHeaderValue.Split(Constants.CharArrays.Semicolon, StringSplitOptions.RemoveEmptyEntries); - foreach (var cookieNameValue in cookieCollection) - { - var parts = cookieNameValue.Split(Constants.CharArrays.EqualsChar, StringSplitOptions.RemoveEmptyEntries); - if (parts.Length != 2) continue; - if (parts[0].Trim().Equals(cookieName, StringComparison.InvariantCultureIgnoreCase)) - return parts[1].Trim(); - } - } - - return null; - } - - /// - /// Removes the cookie from the request and the response if it exists - /// - /// - /// - public static void ExpireCookie(this HttpContextBase http, string cookieName) - { - //remove from the request - http.Request.Cookies.Remove(cookieName); - - //expire from the response - var cookie = http.Response.Cookies[cookieName]; - if (cookie != null) - { - //this will expire immediately and be removed from the browser - cookie.Expires = DateTime.Now.AddYears(-1); - } - else - { - //ensure there's def an expired cookie - http.Response.Cookies.Add(new HttpCookie(cookieName) { Expires = DateTime.Now.AddYears(-1) }); - } - } - - /// - /// Removes the cookie from the request and the response if it exists - /// - /// - /// - public static void ExpireCookie(this HttpContext http, string cookieName) - { - new HttpContextWrapper(http).ExpireCookie(cookieName); - } - - public static string GetPreviewCookieValue(this HttpRequestMessage request) - { - var cookie = request.Headers.GetCookies(Constants.Web.PreviewCookieName).FirstOrDefault(); - if (cookie != null) - { - if (cookie[Constants.Web.PreviewCookieName] != null) - { - return cookie[Constants.Web.PreviewCookieName].Value; - } - } - return null; - } - - /// - /// Returns the cookie's string value - /// - /// - /// - /// - public static string GetCookieValue(this HttpRequestBase request, string cookieName) - { - var cookie = request.Cookies.Get(cookieName); - if (cookie != null) - { - if (cookie.Value.IsNullOrWhiteSpace() == false) - { - return cookie.Value; - } - } - return null; - } - - /// - /// Returns the cookie's string value - /// - /// - /// - /// - public static string GetCookieValue(this HttpRequest request, string cookieName) - { - return new HttpRequestWrapper(request).GetCookieValue(cookieName); - } - - /// - /// Does a cookie exist with the specified key ? - /// - /// - /// - /// - public static bool HasCookie(this HttpRequestBase request, string key) - { - return request.Cookies[key] != null; - } - - /// - /// Does a cookie exist with the specified key ? - /// - /// - /// - /// - public static bool HasCookie(this HttpRequest request, string key) - { - return request.Cookies[key] != null; - } - - /// - /// Is there a cookie with the key supplied and does it have a value that is not empty - /// - /// - /// - /// - public static bool HasCookieValue(this HttpRequestBase request, string key) - { - return request.Cookies[key] != null - && request.Cookies[key].Value != null - && request.Cookies[key].Value.IsNullOrWhiteSpace() == false; - } - - /// - /// Is there a cookie with the key supplied and does it have a value that is not empty - /// - /// - /// - /// - public static bool HasCookieValue(this HttpRequest request, string key) - { - return new HttpRequestWrapper(request).HasCookieValue(key); - } - } -} diff --git a/src/Umbraco.Web/HttpRequestExtensions.cs b/src/Umbraco.Web/HttpRequestExtensions.cs deleted file mode 100644 index 183de2dbe5..0000000000 --- a/src/Umbraco.Web/HttpRequestExtensions.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core; -using Umbraco.Extensions; - -namespace Umbraco.Web -{ - /// - /// Extension methods for the HttpRequest and HttpRequestBase objects - /// - public static class HttpRequestExtensions - { - /// - /// Extracts the value from the query string and cleans it to prevent xss attacks. - /// - /// - /// - /// - /// - public static string CleanForXss(this HttpRequest request, string key, string valueIfNotFound = "") - { - var item = request.GetItemAsString(key, valueIfNotFound); - return item.CleanForXss(); - } - - /// - /// Safely get a request item as string, if the item does not exist, an empty string is returned. - /// - /// - /// - /// The value to return if the key is not found in the collection - /// - public static string GetItemAsString(this HttpRequest request, string key, string valueIfNotFound = "") - { - return new HttpRequestWrapper(request).GetItemAsString(key, valueIfNotFound); - } - - /// - /// Safely get a request item as string, if the item does not exist, an empty string is returned. - /// - /// - /// - /// The value to return if the key is not found in the collection - /// - public static string GetItemAsString(this HttpRequestBase request, string key, string valueIfNotFound = "") - { - var val = request[key]; - return !val.IsNullOrWhiteSpace() ? val : valueIfNotFound; - } - - /// - /// Safely get the item from the query string and convert it to type 'T', otherwise will return default(T). - /// - /// - /// - /// - /// - public static T GetItemAs(this HttpRequestBase request, string key) - { - var val = request[key]; - var whitespaceCheck = !val.IsNullOrWhiteSpace() ? val : string.Empty; - if (whitespaceCheck.IsNullOrWhiteSpace()) - return (T) typeof (T).GetDefaultValue(); - var attempt = val.TryConvertTo(); - if (attempt.Success) - return attempt.Result; - return (T)typeof(T).GetDefaultValue(); - } - - /// - /// Safely get the item from the query string and convert it to type 'T', otherwise will return default(T). - /// - /// - /// - /// - /// - public static T GetItemAs(this HttpRequest request, string key) - { - return new HttpRequestWrapper(request).GetItemAs(key); - } - - } -} diff --git a/src/Umbraco.Web/IHttpContextAccessor.cs b/src/Umbraco.Web/IHttpContextAccessor.cs deleted file mode 100644 index c83b9425a6..0000000000 --- a/src/Umbraco.Web/IHttpContextAccessor.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Web; - -namespace Umbraco.Web -{ - public interface IHttpContextAccessor - { - HttpContextBase HttpContext { get; set; } - } -} diff --git a/src/Umbraco.Web/ModelStateExtensions.cs b/src/Umbraco.Web/ModelStateExtensions.cs deleted file mode 100644 index ca2974ac4a..0000000000 --- a/src/Umbraco.Web/ModelStateExtensions.cs +++ /dev/null @@ -1,224 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Linq; -using System.Web.Mvc; -using Umbraco.Cms.Core; -using Umbraco.Extensions; - -namespace Umbraco.Web -{ - //Migrated to .NET Core except the Merge method - internal static class ModelStateExtensions - { - - /// - /// Merges ModelState that has names matching the prefix - /// - /// - /// - /// - public static void Merge(this ModelStateDictionary state, ModelStateDictionary dictionary, string prefix) - { - if (dictionary == null) - return; - foreach (var (key, value) in dictionary - //It can either equal the prefix exactly (model level errors) or start with the prefix. (property level errors) - .Where(keyValuePair => keyValuePair.Key == prefix || keyValuePair.Key.StartsWith(prefix + "."))) - { - state[key] = value; - } - } - - /// - /// Checks if there are any model errors on any fields containing the prefix - /// - /// - /// - /// - public static bool IsValid(this ModelStateDictionary state, string prefix) - { - return state.Where(v => v.Key.StartsWith(prefix + ".")).All(v => !v.Value.Errors.Any()); - } - - - - - /// - /// Adds a generic culture error for use in displaying the culture validation error in the save/publish/etc... dialogs - /// - /// - /// - /// - /// - internal static void AddVariantValidationError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, - string culture, string segment, string errMsg) - { - var key = "_content_variant_" + (culture.IsNullOrWhiteSpace() ? "invariant" : culture) + "_" + (segment.IsNullOrWhiteSpace() ? "null" : segment) + "_"; - if (modelState.ContainsKey(key)) return; - modelState.AddModelError(key, errMsg); - } - - /// - /// Returns a list of cultures that have property validation errors - /// - /// - /// - /// The culture to affiliate invariant errors with - /// - /// A list of cultures that have property validation errors. The default culture will be returned for any invariant property errors. - /// - internal static IReadOnlyList<(string culture, string segment)> GetVariantsWithPropertyErrors(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, - string cultureForInvariantErrors) - { - //Add any variant specific errors here - var variantErrors = modelState.Keys - .Where(key => key.StartsWith("_Properties.")) //only choose _Properties errors - .Select(x => x.Split(Constants.CharArrays.Period)) //split into parts - .Where(x => x.Length >= 4 && !x[2].IsNullOrWhiteSpace() && !x[3].IsNullOrWhiteSpace()) - .Select(x => (culture: x[2], segment: x[3])) - //if the culture is marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language - //so errors for those must show up under the default lang. - //if the segment is marked "null" then return an actual null - .Select(x => - { - var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture; - var segment = x.segment == "null" ? null : x.segment; - return (culture, segment); - }) - .Distinct() - .ToList(); - - return variantErrors; - } - - /// - /// Returns a list of cultures that have any validation errors - /// - /// - /// - /// The culture to affiliate invariant errors with - /// - /// A list of cultures that have validation errors. The default culture will be returned for any invariant errors. - /// - internal static IReadOnlyList<(string culture, string segment)> GetVariantsWithErrors(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, string cultureForInvariantErrors) - { - var propertyVariantErrors = modelState.GetVariantsWithPropertyErrors(cultureForInvariantErrors); - - //now check the other special variant errors that are - var genericVariantErrors = modelState.Keys - .Where(x => x.StartsWith("_content_variant_") && x.EndsWith("_")) - .Select(x => x.TrimStart("_content_variant_").TrimEnd("_")) - .Select(x => - { - // Format "_" - var cs = x.Split(Constants.CharArrays.Underscore); - return (culture: cs[0], segment: cs[1]); - }) - .Where(x => !x.culture.IsNullOrWhiteSpace()) - //if it's marked "invariant" than return the default language, this is because we can only edit invariant properties on the default language - //so errors for those must show up under the default lang. - //if the segment is marked "null" then return an actual null - .Select(x => - { - var culture = x.culture == "invariant" ? cultureForInvariantErrors : x.culture; - var segment = x.segment == "null" ? null : x.segment; - return (culture, segment); - }) - .Distinct(); - - return propertyVariantErrors.Union(genericVariantErrors).Distinct().ToList(); - } - - /// - /// Adds the error to model state correctly for a property so we can use it on the client side. - /// - /// - /// - /// - /// Each model state validation error has a name and in most cases this name is made up of parts which are delimited by a '.' - /// - internal static void AddValidationError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, - ValidationResult result, params string[] parts) - { - // if there are assigned member names, we combine the member name with the owner name - // so that we can try to match it up to a real field. otherwise, we assume that the - // validation message is for the overall owner. - // Owner = the component being validated, like a content property but could be just an HTML field on another editor - - var withNames = false; - var delimitedParts = string.Join(".", parts); - foreach (var memberName in result.MemberNames) - { - modelState.TryAddModelError($"{delimitedParts}.{memberName}", result.ToString()); - withNames = true; - } - if (!withNames) - { - modelState.TryAddModelError($"{delimitedParts}", result.ToString()); - } - - } - - /// - /// Will add an error to model state for a key if that key and error don't already exist - /// - /// - /// - /// - /// - private static bool TryAddModelError(this System.Web.Http.ModelBinding.ModelStateDictionary modelState, string key, string errorMsg) - { - if (modelState.TryGetValue(key, out var errs)) - { - foreach(var e in errs.Errors) - if (e.ErrorMessage == errorMsg) return false; //if this same error message exists for the same key, just exit - } - - modelState.AddModelError(key, errorMsg); - return true; - } - - public static IDictionary ToErrorDictionary(this System.Web.Http.ModelBinding.ModelStateDictionary modelState) - { - var modelStateError = new Dictionary(); - foreach (var keyModelStatePair in modelState) - { - var key = keyModelStatePair.Key; - var errors = keyModelStatePair.Value.Errors; - if (errors != null && errors.Count > 0) - { - modelStateError.Add(key, errors.Select(error => error.ErrorMessage)); - } - } - return modelStateError; - } - - /// - /// Serializes the ModelState to JSON for JavaScript to interrogate the errors - /// - /// - /// - public static JsonResult ToJsonErrors(this ModelStateDictionary state) - { - return new JsonResult - { - Data = new - { - success = state.IsValid.ToString().ToLower(), - failureType = "ValidationError", - validationErrors = from e in state - where e.Value.Errors.Count > 0 - select new - { - name = e.Key, - errors = e.Value.Errors.Select(x => x.ErrorMessage) - .Concat( - e.Value.Errors.Where(x => x.Exception != null).Select(x => x.Exception.Message)) - } - } - }; - } - - - } -} diff --git a/src/Umbraco.Web/Mvc/ControllerExtensions.cs b/src/Umbraco.Web/Mvc/ControllerExtensions.cs deleted file mode 100644 index c72a7554f6..0000000000 --- a/src/Umbraco.Web/Mvc/ControllerExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Web.Mvc; -using System.Web.Routing; - -namespace Umbraco.Web.Mvc -{ - internal static class ControllerExtensions - { - /// - /// Return the controller name from the controller type - /// - /// - /// - internal static string GetControllerName(Type controllerType) - { - if (!controllerType.Name.EndsWith("Controller")) - { - throw new InvalidOperationException("The controller type " + controllerType + " does not follow conventions, MVC Controller class names must be suffixed with the term 'Controller'"); - } - return controllerType.Name.Substring(0, controllerType.Name.LastIndexOf("Controller")); - } - - /// - /// Return the controller name from the controller instance - /// - /// - /// - internal static string GetControllerName(this IController controllerInstance) - { - return GetControllerName(controllerInstance.GetType()); - } - - /// - /// Return the controller name from the controller type - /// - /// - /// - /// - internal static string GetControllerName() - { - return GetControllerName(typeof(T)); - } - - } -} diff --git a/src/Umbraco.Web/Mvc/PluginController.cs b/src/Umbraco.Web/Mvc/PluginController.cs deleted file mode 100644 index 0a76e6205d..0000000000 --- a/src/Umbraco.Web/Mvc/PluginController.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Web.Mvc; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Core.Web.Mvc; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Extensions; -using Umbraco.Web.WebApi; -using Current = Umbraco.Web.Composing.Current; - -namespace Umbraco.Web.Mvc -{ - /// - /// Provides a base class for plugin controllers. - /// - public abstract class PluginController : Controller, IDiscoverable - { - private static readonly ConcurrentDictionary MetadataStorage - = new ConcurrentDictionary(); - - // for debugging purposes - internal Guid InstanceId { get; } = Guid.NewGuid(); - - /// - /// Gets the Umbraco context. - /// - public virtual IUmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext; - - /// - /// Gets the database context accessor. - /// - public virtual IUmbracoContextAccessor UmbracoContextAccessor { get; } - - /// - /// Gets the database context. - /// - public IUmbracoDatabaseFactory DatabaseFactory { get; } - - /// - /// Gets or sets the services context. - /// - public ServiceContext Services { get; } - - /// - /// Gets or sets the application cache. - /// - public AppCaches AppCaches { get; } - - /// - /// Gets or sets the profiling logger. - /// - public IProfilingLogger ProfilingLogger { get; } - - /// - /// Gets the Umbraco helper. - /// - public UmbracoHelper Umbraco { get; } - - /// - /// Gets metadata for this instance. - /// - internal PluginControllerMetadata Metadata => GetMetadata(GetType()); - - protected PluginController() - : this( - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService() - ) - { - } - - protected PluginController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger) - { - UmbracoContextAccessor = umbracoContextAccessor; - DatabaseFactory = databaseFactory; - Services = services; - AppCaches = appCaches; - ProfilingLogger = profilingLogger; - } - - /// - /// Gets metadata for a controller type. - /// - /// The controller type. - /// Metadata for the controller type. - internal static PluginControllerMetadata GetMetadata(Type controllerType) - { - return MetadataStorage.GetOrAdd(controllerType, type => - { - // plugin controller? back-office controller? - var pluginAttribute = controllerType.GetCustomAttribute(false); - var backOfficeAttribute = controllerType.GetCustomAttribute(true); - - return new PluginControllerMetadata - { - AreaName = pluginAttribute?.AreaName, - ControllerName = ControllerExtensions.GetControllerName(controllerType), - ControllerNamespace = controllerType.Namespace, - ControllerType = controllerType, - IsBackOffice = backOfficeAttribute != null - }; - }); - } - } -} diff --git a/src/Umbraco.Web/Mvc/PluginControllerAttribute.cs b/src/Umbraco.Web/Mvc/PluginControllerAttribute.cs deleted file mode 100644 index aedc472111..0000000000 --- a/src/Umbraco.Web/Mvc/PluginControllerAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Linq; - -namespace Umbraco.Web.Mvc -{ - // TODO: This has been moved to netcore so can be removed when ready - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] - public class PluginControllerAttribute : Attribute - { - /// - /// Initializes a new instance of the class. - /// - /// - public PluginControllerAttribute(string areaName) - { - // validate this, only letters and digits allowed. - if (areaName.Any(c => !char.IsLetterOrDigit(c))) - throw new FormatException($"Invalid area name \"{areaName}\": the area name can only contains letters and digits."); - - AreaName = areaName; - } - - /// - /// Gets the name of the area. - /// - public string AreaName { get; } - } -} diff --git a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs b/src/Umbraco.Web/Mvc/RenderRouteHandler.cs deleted file mode 100644 index a68d606701..0000000000 --- a/src/Umbraco.Web/Mvc/RenderRouteHandler.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Umbraco.Web.Mvc -{ - // NOTE: already migrated to netcore, just here since the below is referenced still - public class RenderRouteHandler - { - // Define reserved dictionary keys for controller, action and area specified in route additional values data - internal static class ReservedAdditionalKeys - { - internal const string Controller = "c"; - internal const string Action = "a"; - internal const string Area = "ar"; - } - } -} diff --git a/src/Umbraco.Web/Mvc/RouteDefinition.cs b/src/Umbraco.Web/Mvc/RouteDefinition.cs deleted file mode 100644 index ae806f9a4e..0000000000 --- a/src/Umbraco.Web/Mvc/RouteDefinition.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using Umbraco.Cms.Core.Routing; - -namespace Umbraco.Web.Mvc -{ - // TODO: Migrated already to .Net Core - public class RouteDefinition - { - public string ControllerName { get; set; } - public string ActionName { get; set; } - - /// - /// The Controller type found for routing to - /// - public Type ControllerType { get; set; } - - - /// - /// Everything related to the current content request including the requested content - /// - public IPublishedRequest PublishedRequest { get; set; } - - /// - /// Gets/sets whether the current request has a hijacked route/user controller routed for it - /// - public bool HasHijackedRoute { get; set; } - } -} diff --git a/src/Umbraco.Web/Mvc/Strings.Designer.cs b/src/Umbraco.Web/Mvc/Strings.Designer.cs deleted file mode 100644 index 40269241a5..0000000000 --- a/src/Umbraco.Web/Mvc/Strings.Designer.cs +++ /dev/null @@ -1,78 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.Mvc { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Strings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Strings() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Web.Mvc.Strings", typeof(Strings).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized string similar to <?xml version="1.0"?> - /// - ///<configuration> - /// <configSections> - /// <sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> - /// <section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" /> - /// < [rest of string was truncated]";. - /// - internal static string WebConfigTemplate { - get { - return ResourceManager.GetString("WebConfigTemplate", resourceCulture); - } - } - } -} diff --git a/src/Umbraco.Web/Mvc/Strings.resx b/src/Umbraco.Web/Mvc/Strings.resx deleted file mode 100644 index 1d4028f594..0000000000 --- a/src/Umbraco.Web/Mvc/Strings.resx +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - ..\..\umbraco.web.ui\views\web.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252 - - diff --git a/src/Umbraco.Web/Mvc/SurfaceController.cs b/src/Umbraco.Web/Mvc/SurfaceController.cs deleted file mode 100644 index 7cf8300e8d..0000000000 --- a/src/Umbraco.Web/Mvc/SurfaceController.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; - -namespace Umbraco.Web.Mvc -{ - public abstract class SurfaceController : PluginController - { - protected SurfaceController() - { } - - protected SurfaceController(IUmbracoContextAccessor umbracoContextAccessor, IUmbracoDatabaseFactory databaseFactory, ServiceContext services, AppCaches appCaches, IProfilingLogger profilingLogger) - : base(umbracoContextAccessor, databaseFactory, services, appCaches,profilingLogger) - { } - - } -} diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs deleted file mode 100644 index 88495f7999..0000000000 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByIdRouteHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Web.Routing; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web.Mvc -{ - public class UmbracoVirtualNodeByIdRouteHandler : UmbracoVirtualNodeRouteHandler - { - private readonly int _realNodeId; - - public UmbracoVirtualNodeByIdRouteHandler(int realNodeId) - { - _realNodeId = realNodeId; - } - - protected sealed override IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext) - { - var byId = umbracoContext.Content.GetById(_realNodeId); - return byId == null ? null : FindContent(requestContext, umbracoContext, byId); - } - - protected virtual IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext, IPublishedContent baseContent) - { - return baseContent; - } - } -} diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByUdiRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByUdiRouteHandler.cs deleted file mode 100644 index 3ba24945b7..0000000000 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeByUdiRouteHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Web.Routing; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web.Mvc -{ - public class UmbracoVirtualNodeByUdiRouteHandler : UmbracoVirtualNodeRouteHandler - { - private readonly Udi _realNodeUdi; - - public UmbracoVirtualNodeByUdiRouteHandler(GuidUdi realNodeUdi) - { - _realNodeUdi = realNodeUdi; - } - - protected sealed override IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext) - { - var byId = umbracoContext.Content.GetById(_realNodeUdi); - return byId == null ? null : FindContent(requestContext, umbracoContext, byId); - } - - protected virtual IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext, IPublishedContent baseContent) - { - return baseContent; - } - } -} diff --git a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs b/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs deleted file mode 100644 index 5a8de3b55a..0000000000 --- a/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Web; -using System.Web.Routing; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Web; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.Mvc -{ - public abstract class UmbracoVirtualNodeRouteHandler : IRouteHandler - { - // TODO: Need to port this to netcore and figure out if its needed or how this should work (part of a different task) - - // TODO: try lazy property injection? - private IPublishedRouter PublishedRouter => Current.Factory.GetRequiredService(); - - /// - /// Returns the UmbracoContext for this route handler - /// - /// - /// By default this uses the UmbracoContext singleton, this could be overridden to check for null in the case - /// that this handler is used for a request where an UmbracoContext is not created by default see http://issues.umbraco.org/issue/U4-9384 - /// - /// - /// - /// - protected virtual IUmbracoContext GetUmbracoContext(RequestContext requestContext) - { - return Composing.Current.UmbracoContext; - } - - public IHttpHandler GetHttpHandler(RequestContext requestContext) - { - throw new NotImplementedException(); - - // var umbracoContext = GetUmbracoContext(requestContext); - - // var found = FindContent(requestContext, umbracoContext); - // if (found == null) return new NotFoundHandler(); - - // var request = PublishedRouter.CreateRequest(umbracoContext); - // request.PublishedContent = found; - // umbracoContext.PublishedRequest = request; - - // // allows inheritors to change the published content request - // PreparePublishedContentRequest(umbracoContext.PublishedRequest); - - // // create the render model - // var renderModel = new ContentModel(umbracoContext.PublishedRequest.PublishedContent); - - // // assigns the required tokens to the request - // //requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoDataToken, renderModel); - // //requestContext.RouteData.DataTokens.Add(Core.Constants.Web.PublishedDocumentRequestDataToken, umbracoContext.PublishedRequest); - // //requestContext.RouteData.DataTokens.Add(Core.Constants.Web.UmbracoContextDataToken, umbracoContext); - - // //// this is used just for a flag that this is an umbraco custom route - // //requestContext.RouteData.DataTokens.Add(Core.Constants.Web.CustomRouteDataToken, true); - - // // Here we need to detect if a SurfaceController has posted - // var formInfo = RenderRouteHandler.GetFormInfo(requestContext); - // if (formInfo != null) - // { - // var def = new RouteDefinition - // { - // ActionName = requestContext.RouteData.GetRequiredString("action"), - // ControllerName = requestContext.RouteData.GetRequiredString("controller"), - // PublishedRequest = umbracoContext.PublishedRequest - // }; - - // // set the special data token to the current route definition - // requestContext.RouteData.Values[Core.Constants.Web.UmbracoRouteDefinitionDataToken] = def; - - // return RenderRouteHandler.HandlePostedValues(requestContext, formInfo); - // } - - // return new MvcHandler(requestContext); - } - - protected abstract IPublishedContent FindContent(RequestContext requestContext, IUmbracoContext umbracoContext); - - //protected virtual void PreparePublishedContentRequest(IPublishedRequest request) - //{ - // PublishedRouter.PrepareRequest(request); - //} - } -} diff --git a/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs b/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs deleted file mode 100644 index 8178099b3e..0000000000 --- a/src/Umbraco.Web/Mvc/ViewDataDictionaryExtensions.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Web.Mvc; -using Umbraco.Extensions; - -namespace Umbraco.Web.Mvc -{ - internal static class ViewDataDictionaryExtensions - { - /// - /// Merges the source view data into the destination view data - /// - /// - /// - public static void MergeViewDataFrom(this ViewDataDictionary destination, ViewDataDictionary source) - { - destination.MergeLeft(source); - } - } -} diff --git a/src/Umbraco.Web/Properties/AssemblyInfo.cs b/src/Umbraco.Web/Properties/AssemblyInfo.cs deleted file mode 100644 index 0ef8fcf488..0000000000 --- a/src/Umbraco.Web/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Umbraco.Web")] -[assembly: AssemblyDescription("Umbraco Web")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Umbraco CMS")] - -[assembly: ComVisible(false)] -[assembly: Guid("ce9d3539-299e-40d3-b605-42ac423e24fa")] - -// Umbraco Cms -[assembly: InternalsVisibleTo("Umbraco.Web.UI")] -[assembly: InternalsVisibleTo("Umbraco.Tests")] -[assembly: InternalsVisibleTo("Umbraco.Tests.Benchmarks")] - -// Allow this to be mocked in our unit tests -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] - -// Umbraco Deploy -[assembly: InternalsVisibleTo("Umbraco.Deploy")] -[assembly: InternalsVisibleTo("Umbraco.Deploy.UI")] -[assembly: InternalsVisibleTo("Umbraco.Deploy.Cloud")] - -// Umbraco Forms -[assembly: InternalsVisibleTo("Umbraco.Forms.Core")] -[assembly: InternalsVisibleTo("Umbraco.Forms.Core.Providers")] -[assembly: InternalsVisibleTo("Umbraco.Forms.Web")] - -// Umbraco Headless -[assembly: InternalsVisibleTo("Umbraco.Cloud.Headless")] diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializationResult.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializationResult.cs deleted file mode 100644 index cde39eaa3c..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializationResult.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - /// - /// The serialization result from for which the serialized value - /// will be either a string or a byte[] - /// - public struct ContentCacheDataSerializationResult : IEquatable - { - public ContentCacheDataSerializationResult(string stringData, byte[] byteData) - { - StringData = stringData; - ByteData = byteData; - } - - public string StringData { get; } - public byte[] ByteData { get; } - - public override bool Equals(object obj) - { - return obj is ContentCacheDataSerializationResult result && Equals(result); - } - - public bool Equals(ContentCacheDataSerializationResult other) - { - return StringData == other.StringData && - EqualityComparer.Default.Equals(ByteData, other.ByteData); - } - - public override int GetHashCode() - { - var hashCode = 1910544615; - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StringData); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ByteData); - return hashCode; - } - - public static bool operator ==(ContentCacheDataSerializationResult left, ContentCacheDataSerializationResult right) - { - return left.Equals(right); - } - - public static bool operator !=(ContentCacheDataSerializationResult left, ContentCacheDataSerializationResult right) - { - return !(left == right); - } - } - -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializerEntityType.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializerEntityType.cs deleted file mode 100644 index e5b15f8dce..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/ContentCacheDataSerializerEntityType.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - [Flags] - public enum ContentCacheDataSerializerEntityType - { - Document = 1, - Media = 2, - Member = 4 - } - -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs deleted file mode 100644 index 6e68146526..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializer.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; -using Umbraco.Core.Models; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - - /// - /// Serializes/Deserializes document to the SQL Database as a string - /// - /// - /// Resolved from the . This cannot be resolved from DI. - /// - public interface IContentCacheDataSerializer - { - /// - /// Deserialize the data into a - /// - ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData); - - /// - /// Serializes the - /// - ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model); - } - -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializerFactory.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializerFactory.cs deleted file mode 100644 index 14dfd7dc5b..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IContentCacheDataSerializerFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - public interface IContentCacheDataSerializerFactory - { - /// - /// Gets or creates a new instance of - /// - /// - /// - /// This method may return the same instance, however this depends on the state of the application and if any underlying data has changed. - /// This method may also be used to initialize anything before a serialization/deserialization session occurs. - /// - IContentCacheDataSerializer Create(ContentCacheDataSerializerEntityType types); - } - -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs deleted file mode 100644 index e31aec201f..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/IDictionaryOfPropertyDataSerializer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - internal interface IDictionaryOfPropertyDataSerializer - { - IDictionary ReadFrom(Stream stream); - void WriteTo(IDictionary value, Stream stream); - } -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs deleted file mode 100644 index 7a8e038ac9..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializer.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using Umbraco.Cms.Core.Models; -using Umbraco.Cms.Infrastructure.PublishedCache.DataSource; -using Umbraco.Cms.Infrastructure.Serialization; - - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - - public class JsonContentNestedDataSerializer : IContentCacheDataSerializer - { - // by default JsonConvert will deserialize our numeric values as Int64 - // which is bad, because they were Int32 in the database - take care - private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings - { - Converters = new List { new ForceInt32Converter() }, - - // Explicitly specify date handling so that it's consistent and follows the same date handling as MessagePack - DateParseHandling = DateParseHandling.DateTime, - DateFormatHandling = DateFormatHandling.IsoDateFormat, - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - DateFormatString = "o" - }; - private readonly JsonNameTable _propertyNameTable = new DefaultJsonNameTable(); - - public ContentCacheDataModel Deserialize(IReadOnlyContentBase content, string stringData, byte[] byteData) - { - if (stringData == null && byteData != null) - throw new NotSupportedException($"{typeof(JsonContentNestedDataSerializer)} does not support byte[] serialization"); - - JsonSerializer serializer = JsonSerializer.Create(_jsonSerializerSettings); - using (JsonTextReader reader = new JsonTextReader(new StringReader(stringData))) - { - // reader will get buffer from array pool - reader.ArrayPool = JsonArrayPool.Instance; - reader.PropertyNameTable = _propertyNameTable; - return serializer.Deserialize(reader); - } - } - - - public ContentCacheDataSerializationResult Serialize(IReadOnlyContentBase content, ContentCacheDataModel model) - { - // note that numeric values (which are Int32) are serialized without their - // type (eg "value":1234) and JsonConvert by default deserializes them as Int64 - - var json = JsonConvert.SerializeObject(model); - return new ContentCacheDataSerializationResult(json, null); - } - } - public class JsonArrayPool : IArrayPool - { - public static readonly JsonArrayPool Instance = new JsonArrayPool(); - - public char[] Rent(int minimumLength) - { - // get char array from System.Buffers shared pool - return ArrayPool.Shared.Rent(minimumLength); - } - - public void Return(char[] array) - { - // return char array to System.Buffers shared pool - ArrayPool.Shared.Return(array); - } - } - public class AutomaticJsonNameTable : DefaultJsonNameTable - { - int nAutoAdded = 0; - int maxToAutoAdd; - - public AutomaticJsonNameTable(int maxToAdd) - { - this.maxToAutoAdd = maxToAdd; - } - - public override string Get(char[] key, int start, int length) - { - var s = base.Get(key, start, length); - - if (s == null && nAutoAdded < maxToAutoAdd) - { - s = new string(key, start, length); - Add(s); - nAutoAdded++; - } - - return s; - } - } -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs deleted file mode 100644 index e857eb8bf5..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/JsonContentNestedDataSerializerFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - internal class JsonContentNestedDataSerializerFactory : IContentCacheDataSerializerFactory - { - private Lazy _serializer = new Lazy(); - public IContentCacheDataSerializer Create(ContentCacheDataSerializerEntityType types) => _serializer.Value; - } -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/LazyCompressedString.cs b/src/Umbraco.Web/PublishedCache/NuCache/DataSource/LazyCompressedString.cs deleted file mode 100644 index 67248b2cce..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/DataSource/LazyCompressedString.cs +++ /dev/null @@ -1,109 +0,0 @@ -using K4os.Compression.LZ4; -using System; -using System.Diagnostics; -using System.Text; -using Umbraco.Cms.Core.Exceptions; - -namespace Umbraco.Web.PublishedCache.NuCache.DataSource -{ - /// - /// Lazily decompresses a LZ4 Pickler compressed UTF8 string - /// - [DebuggerDisplay("{Display}")] - internal struct LazyCompressedString - { - private byte[] _bytes; - private string _str; - private readonly object _locker; - - /// - /// Constructor - /// - /// LZ4 Pickle compressed UTF8 String - public LazyCompressedString(byte[] bytes) - { - _locker = new object(); - _bytes = bytes; - _str = null; - } - - public byte[] GetBytes() - { - if (_bytes == null) - { - throw new InvalidOperationException("The bytes have already been expanded"); - } - - return _bytes; - } - - /// - /// Returns the decompressed string from the bytes. This methods can only be called once. - /// - /// - /// Throws if this is called more than once - public string DecompressString() - { - if (_str != null) - { - return _str; - } - - lock (_locker) - { - if (_str != null) - { - // double check - return _str; - } - - if (_bytes == null) - { - throw new InvalidOperationException("Bytes have already been cleared"); - } - - _str = Encoding.UTF8.GetString(LZ4Pickler.Unpickle(_bytes)); - _bytes = null; - } - return _str; - } - - /// - /// Used to display debugging output since ToString() can only be called once - /// - private string Display - { - get - { - if (_str != null) - { - return $"Decompressed: {_str}"; - } - - lock (_locker) - { - if (_str != null) - { - // double check - return $"Decompressed: {_str}"; - } - - if (_bytes == null) - { - // This shouldn't happen - throw new PanicException("Bytes have already been cleared"); - } - else - { - return $"Compressed Bytes: {_bytes.Length}"; - } - } - } - } - - public override string ToString() => DecompressString(); - - public static implicit operator string(LazyCompressedString l) => l.ToString(); - } - -} diff --git a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs b/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs deleted file mode 100644 index 0075604e33..0000000000 --- a/src/Umbraco.Web/PublishedCache/NuCache/NuCacheSerializerComponent.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Configuration; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Services; - -namespace Umbraco.Web.PublishedCache.NuCache -{ - /// - /// Rebuilds the database cache if required when the serializer changes - /// - public class NuCacheSerializerComponent : IComponent - { - internal const string Nucache_Serializer_Key = "Umbraco.Web.PublishedCache.NuCache.Serializer"; - private const string JSON_SERIALIZER_VALUE = "JSON"; - private readonly Lazy _service; - private readonly IKeyValueService _keyValueService; - private readonly IProfilingLogger _profilingLogger; - - public NuCacheSerializerComponent(Lazy service, IKeyValueService keyValueService, IProfilingLogger profilingLogger) - { - // We are using lazy here as a work around because the service does quite a lot of initialization in the ctor which - // we want to avoid where possible. Since we only need the service if we are rebuilding, we don't want to eagerly - // initialize anything unless we need to. - _service = service; - _keyValueService = keyValueService; - _profilingLogger = profilingLogger; - } - - public void Initialize() - { - RebuildDatabaseCacheIfSerializerChanged(); - } - - private void RebuildDatabaseCacheIfSerializerChanged() - { - var serializer = ConfigurationManager.AppSettings[Nucache_Serializer_Key]; - var currentSerializer = _keyValueService.GetValue(Nucache_Serializer_Key); - - if (currentSerializer == null) - { - currentSerializer = JSON_SERIALIZER_VALUE; - } - if (serializer == null) - { - serializer = JSON_SERIALIZER_VALUE; - } - - if (serializer != currentSerializer) - { - - using (_profilingLogger.TraceDuration($"Rebuilding NuCache database with {currentSerializer} serializer")) - { - _service.Value.Rebuild(); - _keyValueService.SetValue(Nucache_Serializer_Key, serializer); - } - } - } - - public void Terminate() - { } - } -} diff --git a/src/Umbraco.Web/RouteCollectionExtensions.cs b/src/Umbraco.Web/RouteCollectionExtensions.cs deleted file mode 100644 index 08630820b7..0000000000 --- a/src/Umbraco.Web/RouteCollectionExtensions.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web.Mvc; -using System.Web.Routing; -using System.Web.Http; -using Umbraco.Web.Mvc; - -namespace Umbraco.Web -{ - public static class RouteCollectionExtensions - { - /// - /// Maps an Umbraco route with an UmbracoVirtualNodeRouteHandler - /// - /// - /// - /// - /// - /// - /// - /// - /// - public static Route MapUmbracoRoute(this RouteCollection routes, string name, string url, object defaults, UmbracoVirtualNodeRouteHandler virtualNodeHandler, object constraints = null, string[] namespaces = null) - { - var route = routes.MapRoute(name, url, defaults, constraints, namespaces); - route.RouteHandler = virtualNodeHandler; - return route; - } - - /// - /// Routes a webapi controller with namespaces - /// - /// - /// - /// - /// - /// - /// - public static Route MapHttpRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces) - { - var apiRoute = routes.MapHttpRoute( - name, - url, - defaults); - - //web api routes don't set the data tokens object - if (apiRoute.DataTokens == null) - { - apiRoute.DataTokens = new RouteValueDictionary(); - } - apiRoute.DataTokens.Add("Namespaces", namespaces); //look in this namespace to create the controller - apiRoute.DataTokens.Add("UseNamespaceFallback", false); //Don't look anywhere else except this namespace! - - return apiRoute; - } - - public static void IgnoreStandardExclusions(this RouteCollection routes) - { - // Ignore standard stuff... - using (routes.GetWriteLock()) - { - var exclusions = new Dictionary() - { - {"{resource}.axd/{*pathInfo}", null}, - {"{*allaxd}", new { allaxd = @".*\.axd(/.*)?" }}, - {"{*allashx}", new { allashx = @".*\.ashx(/.*)?" }}, - {"{*allaspx}", new { allaspx = @".*\.aspx(/.*)?" }}, - {"{*favicon}", new { favicon = @"(.*/)?favicon.ico(/.*)?" }}, - }; - //ensure they're not re-added - foreach (var e in exclusions.Where(e => !routes.OfType().Any(x => x.Url == e.Key))) - { - if (e.Value == null) - { - routes.IgnoreRoute(e.Key); - } - else - { - routes.IgnoreRoute(e.Key, e.Value); - } - } - } - } - - /// - /// Extension method to manually register an area - /// - /// - /// - public static void RegisterArea(this RouteCollection routes) - where T : AreaRegistration, new() - { - - // instantiate the area registration - var area = new T(); - - // create a context, which is just the name and routes collection - var context = new AreaRegistrationContext(area.AreaName, routes); - - // register it! - area.RegisterArea(context); - } - - ///// - ///// Extension method to manually register an area from the container - ///// - ///// - ///// - ///// - //public static void RegisterArea(this RouteCollection routes, Framework.DependencyManagement.IDependencyResolver container) - // where T : AreaRegistration - //{ - - // var area = container.Resolve() as AreaRegistration; - // if (area == null) - // { - // throw new InvalidCastException("Could not resolve type " + typeof(T).FullName + " to AreaRegistration"); - // } - - // // create a context, which is just the name and routes collection - // var context = new AreaRegistrationContext(area.AreaName, routes); - - // // register it! - // area.RegisterArea(context); - //} - - - public static void RegisterArea(this RouteCollection routes, T area) where T : AreaRegistration - { - area.RegisterArea(new AreaRegistrationContext(area.AreaName, routes)); - } - - } -} diff --git a/src/Umbraco.Web/Runtime/AspNetUmbracoBootPermissionChecker.cs b/src/Umbraco.Web/Runtime/AspNetUmbracoBootPermissionChecker.cs deleted file mode 100644 index 5cbaf97bb4..0000000000 --- a/src/Umbraco.Web/Runtime/AspNetUmbracoBootPermissionChecker.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Web; -using Umbraco.Cms.Core.Runtime; - -namespace Umbraco.Web.Runtime -{ - public class AspNetUmbracoBootPermissionChecker : IUmbracoBootPermissionChecker - { - public void ThrowIfNotPermissions() - { - new AspNetHostingPermission(AspNetHostingPermissionLevel.Unrestricted).Demand(); - } - } -} diff --git a/src/Umbraco.Web/Runtime/WebFinalComponent.cs b/src/Umbraco.Web/Runtime/WebFinalComponent.cs deleted file mode 100644 index 859e44a50d..0000000000 --- a/src/Umbraco.Web/Runtime/WebFinalComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Web.Http; -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Web.Runtime -{ - public class WebFinalComponent : IComponent - { - - public WebFinalComponent() - { - } - - public void Initialize() - { - // ensure WebAPI is initialized, after everything - GlobalConfiguration.Configuration.EnsureInitialized(); - } - - public void Terminate() - { } - - } -} diff --git a/src/Umbraco.Web/Runtime/WebFinalComposer.cs b/src/Umbraco.Web/Runtime/WebFinalComposer.cs deleted file mode 100644 index 9f2fbb1018..0000000000 --- a/src/Umbraco.Web/Runtime/WebFinalComposer.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Web.Runtime -{ - // web's final composer composes after all user composers - // and *also* after ICoreComposer (in case IUserComposer is disabled) - [ComposeAfter(typeof(IUserComposer))] - public class WebFinalComposer : ComponentComposer - { - } -} diff --git a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs b/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs deleted file mode 100644 index 5ab73bae2d..0000000000 --- a/src/Umbraco.Web/Security/ActiveDirectoryBackOfficeUserPasswordChecker.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.DirectoryServices.AccountManagement; -using System.Threading.Tasks; -using Microsoft.Extensions.Options; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Security; - -namespace Umbraco.Web.Security -{ - // TODO: This relies on an assembly that is not .NET Standard (at least not at the time of implementation) :( - // TODO: This could be ported now, see https://stackoverflow.com/questions/37330705/working-with-directoryservices-in-asp-net-core - public class ActiveDirectoryBackOfficeUserPasswordChecker : IBackOfficeUserPasswordChecker - { - private readonly IOptions _activeDirectorySettings; - - public ActiveDirectoryBackOfficeUserPasswordChecker(IOptions activeDirectorySettings) - { - _activeDirectorySettings = activeDirectorySettings; - } - - public virtual string ActiveDirectoryDomain => _activeDirectorySettings.Value.Domain; - - public Task CheckPasswordAsync(BackOfficeIdentityUser user, string password) - { - bool isValid; - using (var pc = new PrincipalContext(ContextType.Domain, ActiveDirectoryDomain)) - { - isValid = pc.ValidateCredentials(user.UserName, password); - } - - if (isValid && user.HasIdentity == false) - { - // TODO: the user will need to be created locally (i.e. auto-linked) - throw new NotImplementedException("The user " + user.UserName + " does not exist locally and currently the " + typeof(ActiveDirectoryBackOfficeUserPasswordChecker) + " doesn't support auto-linking, see http://issues.umbraco.org/issue/U4-10181"); - } - - var result = isValid - ? BackOfficeUserPasswordCheckerResult.ValidCredentials - : BackOfficeUserPasswordCheckerResult.InvalidCredentials; - - return Task.FromResult(result); - } - } -} diff --git a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs b/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs deleted file mode 100644 index ecddbb7dc2..0000000000 --- a/src/Umbraco.Web/Security/AuthenticationOptionsExtensions.cs +++ /dev/null @@ -1,103 +0,0 @@ -namespace Umbraco.Web.Security -{ - public static class AuthenticationOptionsExtensions - { - // TODO: Migrate this! This will basically be an implementation of sorts for IBackOfficeExternalLoginProviders - - //// these are used for backwards compat - //private const string ExternalSignInAutoLinkOptionsProperty = "ExternalSignInAutoLinkOptions"; - //private const string ChallengeResultCallbackProperty = "ChallengeResultCallback"; - - ///// - ///// Used to specify all back office external login options - ///// - ///// - ///// - //public static void SetBackOfficeExternalLoginProviderOptions(this AuthenticationOptions authOptions, - // BackOfficeExternalLoginProviderOptions externalLoginProviderOptions) - //{ - // authOptions.Description.Properties[Constants.Security.BackOfficeExternalLoginOptionsProperty] = externalLoginProviderOptions; - - // // for backwards compat, we need to add these: - // // TODO: NOOOOOOOOOOO - that is not what we want to do in netcore when we figure out how to migrate this, - // // we'll also need to update angular to reference the correct properties, but that will all be figured out when we start testing - // if (externalLoginProviderOptions.AutoLinkOptions != null) - // authOptions.Description.Properties[ExternalSignInAutoLinkOptionsProperty] = externalLoginProviderOptions.AutoLinkOptions; - // if (externalLoginProviderOptions.OnChallenge != null) - // authOptions.Description.Properties[ChallengeResultCallbackProperty] = externalLoginProviderOptions.OnChallenge; - //} - - //public static AuthenticationProperties GetSignInChallengeResult(this AuthenticationDescription authenticationDescription, IOwinContext ctx) - //{ - // if (authenticationDescription.Properties.ContainsKey(ChallengeResultCallbackProperty) == false) return null; - // var cb = authenticationDescription.Properties[ChallengeResultCallbackProperty] as Func; - // if (cb == null) return null; - // return cb(ctx); - //} - - //public static ExternalSignInAutoLinkOptions GetExternalSignInAutoLinkOptions(this AuthenticationDescription authenticationDescription) - //{ - // if (authenticationDescription.Properties.ContainsKey(ExternalSignInAutoLinkOptionsProperty) == false) return null; - // var options = authenticationDescription.Properties[ExternalSignInAutoLinkOptionsProperty] as ExternalSignInAutoLinkOptions; - // return options; - //} - - ///// - ///// Configures the properties of the authentication description instance for use with Umbraco back office - ///// - ///// - ///// - ///// - ///// - ///// This is important if the identity provider is to be able to authenticate when upgrading Umbraco. We will try to extract this from - ///// any options passed in via reflection since none of the default OWIN providers inherit from a base class but so far all of them have a consistent - ///// name for the 'CallbackPath' property which is of type PathString. So we'll try to extract it if it's not found or supplied. - ///// - ///// If a value is extracted or supplied, this will be added to an internal list which the UmbracoModule will use to allow the request to pass - ///// through without redirecting to the installer. - ///// - //public static void ForUmbracoBackOffice(this AuthenticationOptions options, string style, string icon, string callbackPath = null) - //{ - // if (options == null) throw new ArgumentNullException(nameof(options)); - // if (string.IsNullOrEmpty(options.AuthenticationType)) throw new InvalidOperationException("The authentication type can't be null or empty."); - - // //Ensure the prefix is set - // if (options.AuthenticationType.StartsWith(Constants.Security.BackOfficeExternalAuthenticationTypePrefix) == false) - // { - // options.AuthenticationType = Constants.Security.BackOfficeExternalAuthenticationTypePrefix + options.AuthenticationType; - // } - - // options.Description.Properties["SocialStyle"] = style; - // options.Description.Properties["SocialIcon"] = icon; - - // //flag for use in back office - // options.Description.Properties[Constants.Security.BackOfficeAuthenticationType] = true; - - // if (callbackPath.IsNullOrWhiteSpace()) - // { - // try - // { - // //try to get it with reflection - // var prop = options.GetType().GetProperty("CallbackPath"); - // if (prop != null && TypeHelper.IsTypeAssignableFrom(prop.PropertyType)) - // { - // //get the value - // var path = (PathString) prop.GetValue(options); - // if (path.HasValue) - // { - // RoutableDocumentFilter.ReservedPaths.TryAdd(path.ToString()); - // } - // } - // } - // catch (System.Exception ex) - // { - // Current.Logger.LogError(ex, "Could not read AuthenticationOptions properties"); - // } - // } - // else - // { - // RoutableDocumentFilter.ReservedPaths.TryAdd(callbackPath); - // } - //} - } -} diff --git a/src/Umbraco.Web/Security/BackOfficeUserPasswordCheckerResult.cs b/src/Umbraco.Web/Security/BackOfficeUserPasswordCheckerResult.cs deleted file mode 100644 index 5681bb4766..0000000000 --- a/src/Umbraco.Web/Security/BackOfficeUserPasswordCheckerResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Umbraco.Web.Security -{ - /// - /// The result returned from the IBackOfficeUserPasswordChecker - /// - public enum BackOfficeUserPasswordCheckerResult - { - ValidCredentials, - InvalidCredentials, - FallbackToDefaultChecker - } -} diff --git a/src/Umbraco.Web/Security/BackofficeSecurity.cs b/src/Umbraco.Web/Security/BackofficeSecurity.cs deleted file mode 100644 index ff5d4a6f3f..0000000000 --- a/src/Umbraco.Web/Security/BackofficeSecurity.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Models.Membership; -using Umbraco.Cms.Core.Security; - -namespace Umbraco.Web.Security -{ - // NOTE: Moved to netcore - public class BackOfficeSecurity : IBackOfficeSecurity - { - public IUser CurrentUser => throw new NotImplementedException(); - - public Attempt GetUserId() - { - throw new NotImplementedException(); - } - - public bool IsAuthenticated() - { - throw new NotImplementedException(); - } - - public bool UserHasSectionAccess(string section, IUser user) - { - throw new NotImplementedException(); - } - - } -} diff --git a/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs b/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs deleted file mode 100644 index c88a200572..0000000000 --- a/src/Umbraco.Web/Security/IBackOfficeUserPasswordChecker.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using Umbraco.Cms.Core.Security; - -namespace Umbraco.Web.Security -{ - /// - /// Used by the BackOfficeUserManager to check the username/password which allows for developers to more easily - /// set the logic for this procedure. - /// - public interface IBackOfficeUserPasswordChecker - { - /// - /// Checks a password for a user - /// - /// - /// - /// - /// - /// This will allow a developer to auto-link a local account which is required if the user queried doesn't exist locally. - /// The user parameter will always contain the username, if the user doesn't exist locally, the other properties will not be filled in. - /// A developer can then create a local account by filling in the properties and using UserManager.CreateAsync - /// - Task CheckPasswordAsync(BackOfficeIdentityUser user, string password); - } -} diff --git a/src/Umbraco.Web/StringExtensions.cs b/src/Umbraco.Web/StringExtensions.cs deleted file mode 100644 index a67b0e38c5..0000000000 --- a/src/Umbraco.Web/StringExtensions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Web.Security; - -namespace Umbraco.Web -{ - public static class StringExtensions - { - /// - /// Encrypt the string using the MachineKey in medium trust - /// - /// The string value to be encrypted. - /// The encrypted string. - public static string EncryptWithMachineKey(this string value) - { - if (value == null) - return null; - - string valueToEncrypt = value; - List parts = new List(); - - const int EncrpytBlockSize = 500; - - while (valueToEncrypt.Length > EncrpytBlockSize) - { - parts.Add(valueToEncrypt.Substring(0, EncrpytBlockSize)); - valueToEncrypt = valueToEncrypt.Remove(0, EncrpytBlockSize); - } - - if (valueToEncrypt.Length > 0) - { - parts.Add(valueToEncrypt); - } - - StringBuilder encrpytedValue = new StringBuilder(); - - foreach (var part in parts) - { - var encrpytedBlock = FormsAuthentication.Encrypt(new FormsAuthenticationTicket(0, string.Empty, DateTime.Now, DateTime.MaxValue, false, part)); - encrpytedValue.AppendLine(encrpytedBlock); - } - - return encrpytedValue.ToString().TrimEnd(); - } - - /// - /// Decrypt the encrypted string using the Machine key in medium trust - /// - /// The string value to be decrypted - /// The decrypted string. - public static string DecryptWithMachineKey(this string value) - { - if (value == null) - return null; - - string[] parts = value.Split('\n'); - - StringBuilder decryptedValue = new StringBuilder(); - - foreach (var part in parts) - { - decryptedValue.Append(FormsAuthentication.Decrypt(part.TrimEnd()).UserData); - } - - return decryptedValue.ToString(); - } - } -} diff --git a/src/Umbraco.Web/TypeLoaderExtensions.cs b/src/Umbraco.Web/TypeLoaderExtensions.cs deleted file mode 100644 index 0dcc726afc..0000000000 --- a/src/Umbraco.Web/TypeLoaderExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; -using Umbraco.Cms.Core.Composing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; - - -namespace Umbraco.Web -{ - /// - /// Provides extension methods for the class. - /// - // Migrated to .NET Core - public static class TypeLoaderExtensions - { - /// - /// Gets all types implementing . - /// - internal static IEnumerable GetSurfaceControllers(this TypeLoader typeLoader) - => typeLoader.GetTypes(); - - /// - /// Gets all types implementing . - /// - internal static IEnumerable GetUmbracoApiControllers(this TypeLoader typeLoader) - => typeLoader.GetTypes(); - } -} diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj deleted file mode 100644 index f7f18b52ff..0000000000 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ /dev/null @@ -1,270 +0,0 @@ - - - - - v4.7.2 - false - false - {651E1350-91B6-44B7-BD60-7207006D7003} - Library - Umbraco.Web - Umbraco.Web - ..\ - true - Off - - $(AdditionalFileItemNames);Content - - - true - portable - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - false - latest - - - portable - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\Umbraco.Web.xml - false - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - 2.0.0-alpha.20200128.15 - - - - - - 5.0.376 - - - - - 2.9.1 - - - - - 1.1.11 - - - - - - - - 2.2.85 - - - - - - - - - - - - - - - - 1.0.0 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - 3.5.4 - runtime; build; native; contentfiles; analyzers - all - - - - 1.0.5 - all - - - - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Core - - - {3ae7bf57-966b-45a5-910a-954d7c554441} - Umbraco.Infrastructure - - - {33085570-9bf2-4065-a9b0-a29d920d13ba} - Umbraco.Persistence.SqlCe - - - {f6de8da0-07cc-4ef2-8a59-2bc81dbb3830} - Umbraco.PublishedCache.NuCache - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Strings.resx - - - - - - - - - - - - - - Code - - - - - Component - - - - - - - ResXFileCodeGenerator - Strings.Designer.cs - - - - - Mvc\web.config - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs deleted file mode 100644 index 327537ee00..0000000000 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Runtime.InteropServices; -using System.Web; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Logging; -using Umbraco.Web.Runtime; -using ConnectionStrings = Umbraco.Cms.Core.Configuration.Models.ConnectionStrings; - -namespace Umbraco.Web -{ - /// - /// Represents the Umbraco global.asax class. - /// - public class UmbracoApplication : UmbracoApplicationBase - { - - - - } -} diff --git a/src/Umbraco.Web/UmbracoApplicationBase.cs b/src/Umbraco.Web/UmbracoApplicationBase.cs deleted file mode 100644 index c2008b3c16..0000000000 --- a/src/Umbraco.Web/UmbracoApplicationBase.cs +++ /dev/null @@ -1,328 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Web; -using System.Web.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Serilog.Context; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.IO; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Logging.Serilog; -using Umbraco.Cms.Core.Logging.Serilog.Enrichers; -using Umbraco.Cms.Core.Net; -using Umbraco.Cms.Core.Services; -using Umbraco.Extensions; -using Umbraco.Web.Hosting; -using ConnectionStrings = Umbraco.Cms.Core.Configuration.Models.ConnectionStrings; -using Current = Umbraco.Web.Composing.Current; -using GlobalSettings = Umbraco.Cms.Core.Configuration.Models.GlobalSettings; -using ILogger = Microsoft.Extensions.Logging.ILogger; - -namespace Umbraco.Web -{ - /// - /// Provides an abstract base class for the Umbraco HttpApplication. - /// - public abstract class UmbracoApplicationBase : HttpApplication - { - private readonly ILogger _logger; - private readonly SecuritySettings _securitySettings; - private readonly GlobalSettings _globalSettings; - private readonly ConnectionStrings _connectionStrings; - private readonly IIOHelper _ioHelper; - private IRuntime _runtime; - private IServiceProvider _factory; - private ILoggerFactory _loggerFactory; - - protected UmbracoApplicationBase() - { - HostingSettings hostingSettings = null; - GlobalSettings globalSettings = null; - SecuritySettings securitySettings = null; - WebRoutingSettings webRoutingSettings = null; - - var hostingEnvironment = new AspNetHostingEnvironment(Options.Create(hostingSettings)); - var loggingConfiguration = new LoggingConfiguration( - Path.Combine(hostingEnvironment.ApplicationPhysicalPath, "App_Data\\Logs")); - var ioHelper = new IOHelperWindows(hostingEnvironment); - var logger = SerilogLogger.CreateWithDefaultConfiguration(hostingEnvironment, loggingConfiguration, new ConfigurationRoot(new List())); - - var backOfficeInfo = new AspNetBackOfficeInfo(globalSettings, ioHelper, _loggerFactory.CreateLogger(), Options.Create(webRoutingSettings)); - var profiler = GetWebProfiler(hostingEnvironment); - } - - private IProfiler GetWebProfiler(IHostingEnvironment hostingEnvironment) - { - // create and start asap to profile boot - if (!hostingEnvironment.IsDebugMode) - { - // should let it be null, that's how MiniProfiler is meant to work, - // but our own IProfiler expects an instance so let's get one - return new NoopProfiler(); - } - - return new NoopProfiler(); - } - - protected UmbracoApplicationBase(ILogger logger, ILoggerFactory loggerFactory, SecuritySettings securitySettings, GlobalSettings globalSettings, ConnectionStrings connectionStrings, IIOHelper ioHelper, IProfiler profiler, IHostingEnvironment hostingEnvironment, IBackOfficeInfo backOfficeInfo) - { - _logger = logger; - _securitySettings = securitySettings; - _globalSettings = globalSettings; - _connectionStrings = connectionStrings; - _ioHelper = ioHelper; - _loggerFactory = loggerFactory; - - Logger = logger; - } - - protected ILogger Logger { get; } - - /// - /// Gets a - /// - /// - /// - /// - /// - protected virtual ITypeFinder GetTypeFinder(IHostingEnvironment hostingEnvironment, ILogger logger, IProfiler profiler) - { - // TODO: Currently we are not passing in any TypeFinderConfig (with ITypeFinderSettings) which we should do, however - // this is not critical right now and would require loading in some config before boot time so just leaving this as-is for now. - var runtimeHashPaths = new RuntimeHashPaths(); - // the bin folder and everything in it - runtimeHashPaths.AddFolder(new DirectoryInfo(hostingEnvironment.MapPathContentRoot("~/bin"))); - // the app code folder and everything in it - runtimeHashPaths.AddFile(new FileInfo(hostingEnvironment.MapPathContentRoot("~/App_Code"))); - // global.asax (the app domain also monitors this, if it changes will do a full restart) - runtimeHashPaths.AddFile(new FileInfo(hostingEnvironment.MapPathContentRoot("~/global.asax"))); - var runtimeHash = new RuntimeHash(new ProfilingLogger(_loggerFactory.CreateLogger(), profiler), runtimeHashPaths); - return new TypeFinder(_loggerFactory.CreateLogger(), new DefaultUmbracoAssemblyProvider( - // GetEntryAssembly was actually an exposed API by request of the aspnetcore team which works in aspnet core because a website - // in that case is essentially an exe. However in netframework there is no entry assembly, things don't really work that way since - // the process that is running the site is iisexpress, so this returns null. The best we can do is fallback to GetExecutingAssembly() - // which will just return Umbraco.Infrastructure (currently with netframework) and for our purposes that is OK. - // If you are curious... There is really no way to get the entry assembly in netframework without the hosting website having it's own - // code compiled for the global.asax which is the entry point. Because the default global.asax for umbraco websites is just a file inheriting - // from Umbraco.Web.UmbracoApplication, the global.asax file gets dynamically compiled into a DLL in the dynamic folder (we can get an instance - // of that, but this doesn't really help us) but the actually entry execution is still Umbraco.Web. So that is the 'highest' level entry point - // assembly we can get and we can only get that if we put this code into the WebRuntime since the executing assembly is the 'current' one. - // For this purpose, it doesn't matter if it's Umbraco.Web or Umbraco.Infrastructure since all assemblies are in that same path and we are - // getting rid of netframework. - Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly(), _loggerFactory), runtimeHash); - } - - /// - /// Gets the application register. - /// - protected virtual IServiceCollection GetRegister(GlobalSettings globalSettings) - { - return new ServiceCollection(); - } - - // events - in the order they trigger - - // were part of the BootManager architecture, would trigger only for the initial - // application, so they need not be static, and they would let ppl hook into the - // boot process... but I believe this can be achieved with components as well and - // we don't need these events. - //public event EventHandler ApplicationStarting; - //public event EventHandler ApplicationStarted; - - // this event can only be static since there will be several instances of this class - // triggers for each application instance, ie many times per lifetime of the application - public static event EventHandler ApplicationInit; - - // this event can only be static since there will be several instances of this class - // triggers once per error - public static event EventHandler ApplicationError; - - // this event can only be static since there will be several instances of this class - // triggers once per lifetime of the application, before it is unloaded - public static event EventHandler ApplicationEnd; - - #region Start - - // internal for tests - internal void HandleApplicationStart(object sender, EventArgs evargs) - { - // ******** THIS IS WHERE EVERYTHING BEGINS ******** - - - var globalSettings = _globalSettings; - var umbracoVersion = new UmbracoVersion(); - - // create the register for the application, and boot - // the boot manager is responsible for registrations - var register = GetRegister(globalSettings); - //var boostrapper = GetRuntime( - // _globalSettings, - // _connectionStrings, - // umbracoVersion, - // _ioHelper, - // _logger, - // _loggerFactory, - // null, // TODO get from somewhere that isn't Current. - // null, // TODO get from somewhere that isn't Current. - // null // TODO get from somewhere that isn't Current. - // ); - //_factory = Current.Factory = _runtime.Configure(register); - - - // now we can add our request based logging enrichers (globally, which is what we were doing in netframework before) - LogContext.Push(new HttpSessionIdEnricher(_factory.GetRequiredService())); - LogContext.Push(new HttpRequestNumberEnricher(_factory.GetRequiredService())); - LogContext.Push(new HttpRequestIdEnricher(_factory.GetRequiredService())); - - _runtime = _factory.GetRequiredService(); - } - - // called by ASP.NET (auto event wireup) once per app domain - // do NOT set instance data here - only static (see docs) - // sender is System.Web.HttpApplicationFactory, evargs is EventArgs.Empty - protected void Application_Start(object sender, EventArgs evargs) - { - Thread.CurrentThread.SanitizeThreadCulture(); - HandleApplicationStart(sender, evargs); - } - - #endregion - - #region Init - - private void OnApplicationInit(object sender, EventArgs evargs) - { - TryInvoke(ApplicationInit, "ApplicationInit", sender, evargs); - } - - // called by ASP.NET for every HttpApplication instance after all modules have been created - // which means that this will be called *many* times for different apps when Umbraco runs - public override void Init() - { - // note: base.Init() is what initializes all of the http modules, ties up a bunch of stuff with IIS, etc... - // therefore, since OWIN is an HttpModule when running in IIS/ASP.Net the OWIN startup is not executed - // until this method fires and by that time - Umbraco has booted already - - base.Init(); - OnApplicationInit(this, new EventArgs()); - } - - - #endregion - - #region End - - protected virtual void OnApplicationEnd(object sender, EventArgs evargs) - { - ApplicationEnd?.Invoke(this, EventArgs.Empty); - } - - // internal for tests - internal void HandleApplicationEnd() - { - if (_runtime != null) - { - _runtime.DisposeIfDisposable(); - - _runtime = null; - } - - // try to log the detailed shutdown message (typical asp.net hack: http://weblogs.asp.net/scottgu/433194) - try - { - var runtime = (HttpRuntime) typeof(HttpRuntime).InvokeMember("_theRuntime", - BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField, - null, null, null); - if (runtime == null) - return; - - var shutDownMessage = (string)runtime.GetType().InvokeMember("_shutDownMessage", - BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, - null, runtime, null); - - var shutDownStack = (string)runtime.GetType().InvokeMember("_shutDownStack", - BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField, - null, runtime, null); - - Current.Logger.LogInformation("Application shutdown. Details: {ShutdownReason}\r\n\r\n_shutDownMessage={ShutdownMessage}\r\n\r\n_shutDownStack={ShutdownStack}", - HostingEnvironment.ShutdownReason, - shutDownMessage, - shutDownStack); - } - catch (Exception) - { - //if for some reason that fails, then log the normal output - Current.Logger.LogInformation("Application shutdown. Reason: {ShutdownReason}", HostingEnvironment.ShutdownReason); - } - - Current.Logger.DisposeIfDisposable(); - } - - // called by ASP.NET (auto event wireup) once per app domain - // sender is System.Web.HttpApplicationFactory, evargs is EventArgs.Empty - protected void Application_End(object sender, EventArgs evargs) - { - OnApplicationEnd(sender, evargs); - HandleApplicationEnd(); - } - - #endregion - - #region Error - - protected virtual void OnApplicationError(object sender, EventArgs evargs) - { - ApplicationError?.Invoke(this, EventArgs.Empty); - } - - private void HandleApplicationError() - { - var exception = Server.GetLastError(); - - if (exception == null) return; - - // ignore HTTP errors - if (exception.GetType() == typeof(HttpException)) return; - - Current.Logger.LogError(exception, "An unhandled exception occurred"); - } - - // called by ASP.NET (auto event wireup) at any phase in the application life cycle - protected void Application_Error(object sender, EventArgs e) - { - // when unhandled errors occur - HandleApplicationError(); - OnApplicationError(sender, e); - } - - #endregion - - #region Utilities - - private static void TryInvoke(EventHandler handler, string name, object sender, EventArgs evargs) - { - try - { - handler?.Invoke(sender, evargs); - } - catch (Exception ex) - { - Current.Logger.LogError(ex, "Error in {Name} handler.", name); - throw; - } - } - - #endregion - } -} diff --git a/src/Umbraco.Web/UmbracoContext.cs b/src/Umbraco.Web/UmbracoContext.cs deleted file mode 100644 index 4aae46e44c..0000000000 --- a/src/Umbraco.Web/UmbracoContext.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System; -using System.Web; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Web; - -namespace Umbraco.Web -{ - // NOTE: has all been ported to netcore but exists here just to keep the build working for tests - - public class UmbracoContext : DisposableObjectSlim, IUmbracoContext - { - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly Lazy _publishedSnapshot; - - // initializes a new instance of the UmbracoContext class - // internal for unit tests - // otherwise it's used by EnsureContext above - // warn: does *not* manage setting any IUmbracoContextAccessor - internal UmbracoContext( - IHttpContextAccessor httpContextAccessor, - IPublishedSnapshotService publishedSnapshotService, - IBackOfficeSecurity backofficeSecurity, - GlobalSettings globalSettings, - IHostingEnvironment hostingEnvironment, - IVariationContextAccessor variationContextAccessor, - UriUtility uriUtility, - ICookieManager cookieManager) - { - if (httpContextAccessor == null) throw new ArgumentNullException(nameof(httpContextAccessor)); - if (publishedSnapshotService == null) throw new ArgumentNullException(nameof(publishedSnapshotService)); - if (backofficeSecurity == null) throw new ArgumentNullException(nameof(backofficeSecurity)); - VariationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _httpContextAccessor = httpContextAccessor; - - // ensure that this instance is disposed when the request terminates, though we *also* ensure - // this happens in the Umbraco module since the UmbracoCOntext is added to the HttpContext items. - // - // also, it *can* be returned by the container with a PerRequest lifetime, meaning that the - // container *could* also try to dispose it. - // - // all in all, this context may be disposed more than once, but DisposableObject ensures that - // it is ok and it will be actually disposed only once. - httpContextAccessor.HttpContext?.DisposeOnPipelineCompleted(this); - - ObjectCreated = DateTime.Now; - UmbracoRequestId = Guid.NewGuid(); - Security = backofficeSecurity; - - // beware - we cannot expect a current user here, so detecting preview mode must be a lazy thing - _publishedSnapshot = new Lazy(() => publishedSnapshotService.CreatePublishedSnapshot(PreviewToken)); - - // set the URLs... - // NOTE: The request will not be available during app startup so we can only set this to an absolute URL of localhost, this - // is a work around to being able to access the UmbracoContext during application startup and this will also ensure that people - // 'could' still generate URLs during startup BUT any domain driven URL generation will not work because it is NOT possible to get - // the current domain during application startup. - // see: http://issues.umbraco.org/issue/U4-1890 - OriginalRequestUrl = GetRequestFromContext()?.Url ?? new Uri("http://localhost"); - CleanedUmbracoUrl = uriUtility.UriToUmbraco(OriginalRequestUrl); - } - - /// - /// This is used internally for performance calculations, the ObjectCreated DateTime is set as soon as this - /// object is instantiated which in the web site is created during the BeginRequest phase. - /// We can then determine complete rendering time from that. - /// - public DateTime ObjectCreated { get; } - - /// - /// This is used internally for debugging and also used to define anything required to distinguish this request from another. - /// - public Guid UmbracoRequestId { get; } - - /// - /// Gets the BackofficeSecurity class - /// - public IBackOfficeSecurity Security { get; } - - /// - /// Gets the uri that is handled by ASP.NET after server-side rewriting took place. - /// - public Uri OriginalRequestUrl { get; } - - /// - /// Gets the cleaned up URL that is handled by Umbraco. - /// - /// That is, lowercase, no trailing slash after path, no .aspx... - public Uri CleanedUmbracoUrl { get; } - - /// - /// Gets the published snapshot. - /// - public IPublishedSnapshot PublishedSnapshot => _publishedSnapshot.Value; - - /// - /// Gets the published content cache. - /// - public IPublishedContentCache Content => PublishedSnapshot.Content; - - /// - /// Gets the published media cache. - /// - public IPublishedMediaCache Media => PublishedSnapshot.Media; - - /// - /// Gets the domains cache. - /// - public IDomainCache Domains => PublishedSnapshot.Domains; - - /// - /// Gets/sets the PublishedRequest object - /// - public IPublishedRequest PublishedRequest { get; set; } - - /// - /// Gets the variation context accessor. - /// - public IVariationContextAccessor VariationContextAccessor { get; } - - // NOTE: has been ported to netcore - public bool IsDebug => false; - - // NOTE: has been ported to netcore - public bool InPreviewMode => false; - - // NOTE: has been ported to netcore - public string PreviewToken => null; - - // NOTE: has been ported to netcore - public IDisposable ForcedPreview(bool preview) => null; - - private HttpRequestBase GetRequestFromContext() - { - try - { - return _httpContextAccessor.HttpContext?.Request; - } - catch (HttpException) - { - return null; - } - } - - // NOTE: has been ported to netcore - protected override void DisposeResources() { } - } -} diff --git a/src/Umbraco.Web/UmbracoContextFactory.cs b/src/Umbraco.Web/UmbracoContextFactory.cs deleted file mode 100644 index d310527e78..0000000000 --- a/src/Umbraco.Web/UmbracoContextFactory.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Hosting; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.PublishedCache; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Web.Security; - -namespace Umbraco.Web -{ - // NOTE: This has been migrated to netcore - - /// - /// Creates and manages instances. - /// - public class UmbracoContextFactory : IUmbracoContextFactory - { - private readonly IUmbracoContextAccessor _umbracoContextAccessor; - private readonly IPublishedSnapshotService _publishedSnapshotService; - private readonly IVariationContextAccessor _variationContextAccessor; - private readonly IDefaultCultureAccessor _defaultCultureAccessor; - - private readonly GlobalSettings _globalSettings; - private readonly IUserService _userService; - private readonly IHostingEnvironment _hostingEnvironment; - private readonly IHttpContextAccessor _httpContextAccessor; - private readonly ICookieManager _cookieManager; - private readonly UriUtility _uriUtility; - - /// - /// Initializes a new instance of the class. - /// - public UmbracoContextFactory( - IUmbracoContextAccessor umbracoContextAccessor, - IPublishedSnapshotService publishedSnapshotService, - IVariationContextAccessor variationContextAccessor, - IDefaultCultureAccessor defaultCultureAccessor, - GlobalSettings globalSettings, - IUserService userService, - IHostingEnvironment hostingEnvironment, - UriUtility uriUtility, - IHttpContextAccessor httpContextAccessor, - ICookieManager cookieManager) - { - _umbracoContextAccessor = umbracoContextAccessor ?? throw new ArgumentNullException(nameof(umbracoContextAccessor)); - _publishedSnapshotService = publishedSnapshotService ?? throw new ArgumentNullException(nameof(publishedSnapshotService)); - _variationContextAccessor = variationContextAccessor ?? throw new ArgumentNullException(nameof(variationContextAccessor)); - _defaultCultureAccessor = defaultCultureAccessor ?? throw new ArgumentNullException(nameof(defaultCultureAccessor)); - _globalSettings = globalSettings ?? throw new ArgumentNullException(nameof(globalSettings)); - _userService = userService ?? throw new ArgumentNullException(nameof(userService)); - _hostingEnvironment = hostingEnvironment; - _uriUtility = uriUtility; - _httpContextAccessor = httpContextAccessor; - _cookieManager = cookieManager; - } - - private IUmbracoContext CreateUmbracoContext() - { - // make sure we have a variation context - if (_variationContextAccessor.VariationContext == null) - { - // TODO: By using _defaultCultureAccessor.DefaultCulture this means that the VariationContext will always return a variant culture, it will never - // return an empty string signifying that the culture is invariant. But does this matter? Are we actually expecting this to return an empty string - // for invariant routes? From what i can tell throughout the codebase is that whenever we are checking against the VariationContext.Culture we are - // also checking if the content type varies by culture or not. This is fine, however the code in the ctor of VariationContext is then misleading - // since it's assuming that the Culture can be empty (invariant) when in reality of a website this will never be empty since a real culture is always set here. - _variationContextAccessor.VariationContext = new VariationContext(_defaultCultureAccessor.DefaultCulture); - } - - return new UmbracoContext(_httpContextAccessor, _publishedSnapshotService, new BackOfficeSecurity(), _globalSettings, _hostingEnvironment, _variationContextAccessor, _uriUtility, _cookieManager); - } - - /// - public UmbracoContextReference EnsureUmbracoContext() - { - var currentUmbracoContext = _umbracoContextAccessor.UmbracoContext; - if (currentUmbracoContext != null) - return new UmbracoContextReference(currentUmbracoContext, false, _umbracoContextAccessor); - - - var umbracoContext = CreateUmbracoContext(); - _umbracoContextAccessor.UmbracoContext = umbracoContext; - - return new UmbracoContextReference(umbracoContext, true, _umbracoContextAccessor); - } - } -} diff --git a/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs b/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs deleted file mode 100644 index 34fcc74d27..0000000000 --- a/src/Umbraco.Web/UmbracoDbProviderFactoryCreator.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Data.Common; -using System.Data.SqlServerCe; -using Microsoft.Extensions.Options; -using NPoco; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Infrastructure.Migrations.Install; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax; -using Umbraco.Cms.Persistence.SqlCe; -using Constants = Umbraco.Cms.Core.Constants; - -namespace Umbraco.Web -{ - public class UmbracoDbProviderFactoryCreator : IDbProviderFactoryCreator - { - public UmbracoDbProviderFactoryCreator() - { - } - - - public DbProviderFactory CreateFactory(string providerName) - { - if (string.IsNullOrEmpty(providerName)) return null; - - return DbProviderFactories.GetFactory(providerName); - } - - // gets the sql syntax provider that corresponds, from attribute - public ISqlSyntaxProvider GetSqlSyntaxProvider(string providerName) - { - switch (providerName) - { - case Constants.DbProviderNames.SqlCe: - return new SqlCeSyntaxProvider(Options.Create(new GlobalSettings())); - case Constants.DbProviderNames.SqlServer: - return new SqlServerSyntaxProvider(Options.Create(new GlobalSettings())); - default: - throw new InvalidOperationException($"Unknown provider name \"{providerName}\""); - } - } - - public IBulkSqlInsertProvider CreateBulkSqlInsertProvider(string providerName) - { - switch (providerName) - { - case Constants.DbProviderNames.SqlCe: - return new SqlCeBulkSqlInsertProvider(); - case Constants.DbProviderNames.SqlServer: - return new SqlServerBulkSqlInsertProvider(); - default: - return new BasicBulkSqlInsertProvider(); - } - } - - public void CreateDatabase(string providerName) - { - var engine = new SqlCeEngine(DatabaseBuilder.EmbeddedDatabaseConnectionString); - engine.CreateDatabase(); - } - - public NPocoMapperCollection ProviderSpecificMappers(string providerName) => new NPocoMapperCollection(Array.Empty()); - } -} diff --git a/src/Umbraco.Web/UmbracoHelper.cs b/src/Umbraco.Web/UmbracoHelper.cs deleted file mode 100644 index 194d9ab91d..0000000000 --- a/src/Umbraco.Web/UmbracoHelper.cs +++ /dev/null @@ -1,501 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Web; -using System.Xml.XPath; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Dictionary; -using Umbraco.Cms.Core.Models.PublishedContent; -using Umbraco.Cms.Core.Strings; -using Umbraco.Cms.Core.Templates; -using Umbraco.Cms.Core.Xml; -using Umbraco.Extensions; -using Umbraco.Web.Mvc; -using Umbraco.Web.Security; - -namespace Umbraco.Web -{ - /// - /// A helper class that provides many useful methods and functionality for using Umbraco in templates - /// - /// - /// This object is a request based lifetime - /// - public class UmbracoHelper - { - private readonly IPublishedContentQuery _publishedContentQuery; - private readonly IUmbracoComponentRenderer _componentRenderer; - private readonly ICultureDictionaryFactory _cultureDictionaryFactory; - - private IPublishedContent _currentPage; - private ICultureDictionary _cultureDictionary; - - #region Constructors - - /// - /// Initializes a new instance of . - /// - /// The item assigned to the helper. - /// - /// - /// - /// - /// Sets the current page to the context's published content request's content item. - public UmbracoHelper(IPublishedContent currentPage, - ICultureDictionaryFactory cultureDictionary, - IUmbracoComponentRenderer componentRenderer, - IPublishedContentQuery publishedContentQuery) - { - _cultureDictionaryFactory = cultureDictionary ?? throw new ArgumentNullException(nameof(cultureDictionary)); - _componentRenderer = componentRenderer ?? throw new ArgumentNullException(nameof(componentRenderer)); - _publishedContentQuery = publishedContentQuery ?? throw new ArgumentNullException(nameof(publishedContentQuery)); - _currentPage = currentPage; - } - - /// - /// Initializes a new empty instance of . - /// - /// For tests - nothing is initialized. - internal UmbracoHelper() - { } - - #endregion - - - /// - /// Gets (or sets) the current item assigned to the UmbracoHelper. - /// - /// - /// - /// Note that this is the assigned IPublishedContent item to the - /// UmbracoHelper, this is not necessarily the Current IPublishedContent - /// item being rendered that is assigned to the UmbracoContext. - /// This IPublishedContent object is contextual to the current UmbracoHelper instance. - /// - /// - /// In some cases accessing this property will throw an exception if - /// there is not IPublishedContent assigned to the Helper this will - /// only ever happen if the Helper is constructed via DI during a non front-end request. - /// - /// - /// Thrown if the - /// UmbracoHelper is constructed with an UmbracoContext and it is not a - /// front-end request. - public IPublishedContent AssignedContentItem - { - get - { - if (_currentPage != null) - { - return _currentPage; - } - - throw new InvalidOperationException( - $"Cannot return the {nameof(IPublishedContent)} because the {nameof(UmbracoHelper)} was not constructed with an {nameof(IPublishedContent)}." - ); - - } - set => _currentPage = value; - } - - /// - /// Renders the template for the specified pageId and an optional altTemplateId - /// - /// The content id - /// If not specified, will use the template assigned to the node - public async Task RenderTemplateAsync(int contentId, int? altTemplateId = null) - => await _componentRenderer.RenderTemplateAsync(contentId, altTemplateId); - - #region RenderMacro - - /// - /// Renders the macro with the specified alias. - /// - /// The alias. - /// - public IHtmlEncodedString RenderMacro(string alias) - { - return _componentRenderer.RenderMacroAsync(AssignedContentItem?.Id ?? 0, alias, null).GetAwaiter().GetResult(); - } - - /// - /// Renders the macro with the specified alias, passing in the specified parameters. - /// - /// The alias. - /// The parameters. - /// - public IHtmlEncodedString RenderMacro(string alias, object parameters) - { - return _componentRenderer.RenderMacroAsync(AssignedContentItem?.Id ?? 0, alias, parameters?.ToDictionary()).GetAwaiter().GetResult(); - } - - /// - /// Renders the macro with the specified alias, passing in the specified parameters. - /// - /// The alias. - /// The parameters. - /// - public IHtmlEncodedString RenderMacro(string alias, IDictionary parameters) - { - return _componentRenderer.RenderMacroAsync(AssignedContentItem?.Id ?? 0, alias, parameters).GetAwaiter().GetResult(); - } - - #endregion - - #region Dictionary - - /// - /// Returns the dictionary value for the key specified - /// - /// - /// - public string GetDictionaryValue(string key) - { - return CultureDictionary[key]; - } - - /// - /// Returns the dictionary value for the key specified, and if empty returns the specified default fall back value - /// - /// key of dictionary item - /// fall back text if dictionary item is empty - Name altText to match Umbraco.Field - /// - public string GetDictionaryValue(string key, string altText) - { - var dictionaryValue = GetDictionaryValue(key); - if (String.IsNullOrWhiteSpace(dictionaryValue)) - { - dictionaryValue = altText; - } - return dictionaryValue; - } - - /// - /// Returns the ICultureDictionary for access to dictionary items - /// - public ICultureDictionary CultureDictionary => _cultureDictionary ??= _cultureDictionaryFactory.CreateDictionary(); - - #endregion - - #region Membership - - /// - /// Check if the current user has access to a document - /// - /// The full path of the document object to check - /// True if the current user has access or if the current document isn't protected - public bool MemberHasAccess(string path) => throw new NotImplementedException("Migrated to netcore"); - - /// - /// Whether or not the current member is logged in (based on the membership provider) - /// - /// True is the current user is logged in - public bool MemberIsLoggedOn() => throw new NotImplementedException("Migrated to netcore"); - - #endregion - - - - #region Member/Content/Media from Udi - - #endregion - - #region Content - - /// - /// Gets a content item from the cache. - /// - /// The unique identifier, or the key, of the content item. - /// The content, or null of the content item is not in the cache. - public IPublishedContent Content(object id) - { - return ContentForObject(id); - } - - private IPublishedContent ContentForObject(object id) => _publishedContentQuery.Content(id); - - public IPublishedContent ContentSingleAtXPath(string xpath, params XPathVariable[] vars) - { - return _publishedContentQuery.ContentSingleAtXPath(xpath, vars); - } - - /// - /// Gets a content item from the cache. - /// - /// The unique identifier of the content item. - /// The content, or null of the content item is not in the cache. - public IPublishedContent Content(int id) => _publishedContentQuery.Content(id); - - /// - /// Gets a content item from the cache. - /// - /// The key of the content item. - /// The content, or null of the content item is not in the cache. - public IPublishedContent Content(Guid id) => _publishedContentQuery.Content(id); - - /// - /// Gets a content item from the cache. - /// - /// The unique identifier, or the key, of the content item. - /// The content, or null of the content item is not in the cache. - public IPublishedContent Content(string id) => _publishedContentQuery.Content(id); - - public IPublishedContent Content(Udi id) => _publishedContentQuery.Content(id); - - /// - /// Gets content items from the cache. - /// - /// The unique identifiers, or the keys, of the content items. - /// The content items that were found in the cache. - /// Does not support mixing identifiers and keys. - public IEnumerable Content(params object[] ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(params Udi[] ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(params GuidUdi[] ids) => _publishedContentQuery.Content(ids); - /// - /// Gets content items from the cache. - /// - /// The unique identifiers of the content items. - /// The content items that were found in the cache. - public IEnumerable Content(params int[] ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets content items from the cache. - /// - /// The keys of the content items. - /// The content items that were found in the cache. - public IEnumerable Content(params Guid[] ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets content items from the cache. - /// - /// The unique identifiers, or the keys, of the content items. - /// The content items that were found in the cache. - /// Does not support mixing identifiers and keys. - public IEnumerable Content(params string[] ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); - - /// - /// Gets the contents corresponding to the identifiers. - /// - /// The content identifiers. - /// The existing contents corresponding to the identifiers. - /// If an identifier does not match an existing content, it will be missing in the returned value. - public IEnumerable Content(IEnumerable ids) => _publishedContentQuery.Content(ids); - - public IEnumerable ContentAtXPath(string xpath, params XPathVariable[] vars) - { - return _publishedContentQuery.ContentAtXPath(xpath, vars); - } - - public IEnumerable ContentAtXPath(XPathExpression xpath, params XPathVariable[] vars) - { - return _publishedContentQuery.ContentAtXPath(xpath, vars); - } - - public IEnumerable ContentAtRoot() - { - return _publishedContentQuery.ContentAtRoot(); - } - - - - - #endregion - #region Media - - public IPublishedContent Media(Udi id) => _publishedContentQuery.Media(id); - - public IPublishedContent Media(Guid id) => _publishedContentQuery.Media(id); - - /// - /// Overloaded method accepting an 'object' type - /// - /// - /// - /// - /// We accept an object type because GetPropertyValue now returns an 'object', we still want to allow people to pass - /// this result in to this method. - /// This method will throw an exception if the value is not of type int or string. - /// - public IPublishedContent Media(object id) - { - return MediaForObject(id); - } - - private IPublishedContent MediaForObject(object id) => _publishedContentQuery.Media(id); - - public IPublishedContent Media(int id) => _publishedContentQuery.Media(id); - - public IPublishedContent Media(string id) => _publishedContentQuery.Media(id); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(params object[] ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(params int[] ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(params string[] ids) => _publishedContentQuery.Media(ids); - - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(params Udi[] ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(params GuidUdi[] ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); - - /// - /// Gets the medias corresponding to the identifiers. - /// - /// The media identifiers. - /// The existing medias corresponding to the identifiers. - /// If an identifier does not match an existing media, it will be missing in the returned value. - public IEnumerable Media(IEnumerable ids) => _publishedContentQuery.Media(ids); - - public IEnumerable MediaAtRoot() - { - return _publishedContentQuery.MediaAtRoot(); - } - - #endregion - - - //Migrated to EncryptionHelper - internal static bool DecryptAndValidateEncryptedRouteString(string ufprt, out IDictionary parts) - { - string decryptedString; - try - { - decryptedString = ufprt.DecryptWithMachineKey(); - } - catch (Exception ex) when (ex is FormatException || ex is ArgumentException) - { - StaticApplicationLogging.Logger.LogWarning("A value was detected in the ufprt parameter but Umbraco could not decrypt the string"); - parts = null; - return false; - } - var parsedQueryString = HttpUtility.ParseQueryString(decryptedString); - parts = new Dictionary(); - foreach (var key in parsedQueryString.AllKeys) - { - parts[key] = parsedQueryString[key]; - } - //validate all required keys exist - //the controller - if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Controller)) - return false; - //the action - if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Action)) - return false; - //the area - if (parts.All(x => x.Key != RenderRouteHandler.ReservedAdditionalKeys.Area)) - return false; - - return true; - } - } -} diff --git a/src/Umbraco.Web/UmbracoHttpHandler.cs b/src/Umbraco.Web/UmbracoHttpHandler.cs deleted file mode 100644 index db7227fc32..0000000000 --- a/src/Umbraco.Web/UmbracoHttpHandler.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System.Web; -using System.Web.Mvc; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Web.Composing; - -namespace Umbraco.Web -{ - public abstract class UmbracoHttpHandler : IHttpHandler - { - private UrlHelper _url; - - protected UmbracoHttpHandler() - : this(Current.UmbracoContextAccessor, Current.BackOfficeSecurityAccessor, Current.Services, Current.Logger, Current.ProfilingLogger) - { } - - protected UmbracoHttpHandler(IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backOfficeSecurityAccessor,ServiceContext service, ILogger logger, IProfilingLogger profilingLogger ) - { - UmbracoContextAccessor = umbracoContextAccessor; - BackOfficeSecurityAccessor = backOfficeSecurityAccessor; - Logger = logger; - ProfilingLogger = profilingLogger ; - Services = service; - } - - public abstract void ProcessRequest(HttpContext context); - - public abstract bool IsReusable { get; } - - /// - /// Gets the logger. - /// - public ILogger Logger { get; } - - /// - /// Gets the ProfilingLogger. - /// - public IProfilingLogger ProfilingLogger { get; } - - /// - /// Gets the Umbraco context accessor. - /// - public IUmbracoContextAccessor UmbracoContextAccessor { get; } - - public IBackOfficeSecurityAccessor BackOfficeSecurityAccessor { get; } - - /// - /// Gets the services context. - /// - public ServiceContext Services { get; } - - /// - /// Gets the web security helper. - /// - public IBackOfficeSecurity Security => BackOfficeSecurityAccessor.BackOfficeSecurity; - - /// - /// Gets the URL helper. - /// - /// This URL helper is created without any route data and an empty request context. - public UrlHelper Url => _url ?? (_url = new UrlHelper(HttpContext.Current.Request.RequestContext)); - } -} diff --git a/src/Umbraco.Web/UmbracoWebService.cs b/src/Umbraco.Web/UmbracoWebService.cs deleted file mode 100644 index 9376ae2a41..0000000000 --- a/src/Umbraco.Web/UmbracoWebService.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System.Web.Mvc; -using System.Web.Services; -using Microsoft.Extensions.Logging; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Web.Composing; - -namespace Umbraco.Web -{ - /// - /// An abstract web service class exposing common umbraco objects - /// - public abstract class UmbracoWebService : WebService - { - private UrlHelper _url; - - protected UmbracoWebService(ILogger logger, IProfilingLogger profilingLogger, IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ServiceContext services, GlobalSettings globalSettings) - { - Logger = logger; - ProfilingLogger = profilingLogger; - UmbracoContextAccessor = umbracoContextAccessor; - BackOfficeSecurityAccessor = backOfficeSecurityAccessor; - Services = services; - GlobalSettings = globalSettings; - } - - protected UmbracoWebService() - : this(Current.Logger, Current.ProfilingLogger, Current.UmbracoContextAccessor, Current.BackOfficeSecurityAccessor, Current.Services,new GlobalSettings()) - { - } - - /// - /// Gets the logger. - /// - public ILogger Logger { get; } - - /// - /// Gets the ProfilingLogger. - /// - public IProfilingLogger ProfilingLogger { get; } - - /// - /// Gets the Umbraco context. - /// - public IUmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext; - - /// - /// Gets the Umbraco context accessor. - /// - public IUmbracoContextAccessor UmbracoContextAccessor { get; } - - public IBackOfficeSecurityAccessor BackOfficeSecurityAccessor { get; } - - /// - /// Gets the services context. - /// - public ServiceContext Services { get; } - - /// - /// Gets the global settings. - /// - public GlobalSettings GlobalSettings { get; } - - /// - /// Gets the web security helper. - /// - public IBackOfficeSecurity Security => BackOfficeSecurityAccessor.BackOfficeSecurity; - - /// - /// Gets the URL helper. - /// - /// This URL helper is created without any route data and an empty request context. - public UrlHelper Url => _url ?? (_url = new UrlHelper(Context.Request.RequestContext)); - } -} diff --git a/src/Umbraco.Web/UrlHelperExtensions.cs b/src/Umbraco.Web/UrlHelperExtensions.cs deleted file mode 100644 index 5a336463e3..0000000000 --- a/src/Umbraco.Web/UrlHelperExtensions.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; -using System.Web.Mvc; -using System.Web.Routing; -using Umbraco.Cms.Core; -using Umbraco.Extensions; -using Umbraco.Web.Composing; -using Umbraco.Web.Mvc; -using Umbraco.Web.WebApi; -using ExpressionHelper = Umbraco.Cms.Core.ExpressionHelper; - -namespace Umbraco.Web -{ - /// - /// Extension methods for UrlHelper - /// - public static class UrlHelperExtensions - { - /// - /// Return the URL for a Web Api service - /// - /// - /// - /// - /// - /// - public static string GetUmbracoApiService(this UrlHelper url, string actionName, RouteValueDictionary routeVals = null) - where T : UmbracoApiControllerBase - { - return url.GetUmbracoApiService(actionName, typeof(T), routeVals); - } - - public static string GetUmbracoApiServiceBaseUrl(this UrlHelper url, Expression> methodSelector) - where T : UmbracoApiControllerBase - { - var method = ExpressionHelper.GetMethodInfo(methodSelector); - if (method == null) - { - throw new MissingMethodException("Could not find the method " + methodSelector + " on type " + typeof(T) + " or the result "); - } - return url.GetUmbracoApiService(method.Name).TrimEnd(method.Name); - } - - public static string GetUmbracoApiService(this UrlHelper url, Expression> methodSelector) - where T : UmbracoApiControllerBase - { - var method = ExpressionHelper.GetMethodInfo(methodSelector); - if (method == null) - { - throw new MissingMethodException("Could not find the method " + methodSelector + " on type " + typeof(T) + " or the result "); - } - var parameters = ExpressionHelper.GetMethodParams(methodSelector); - var routeVals = new RouteValueDictionary(parameters); - return url.GetUmbracoApiService(method.Name, routeVals); - } - - /// - /// Return the URL for a Web Api service - /// - /// - /// - /// - /// - /// - public static string GetUmbracoApiService(this UrlHelper url, string actionName, Type apiControllerType, RouteValueDictionary routeVals = null) - { - if (actionName == null) throw new ArgumentNullException(nameof(actionName)); - if (string.IsNullOrEmpty(actionName)) throw new ArgumentException("Value can't be empty.", nameof(actionName)); - if (apiControllerType == null) throw new ArgumentNullException(nameof(apiControllerType)); - - var area = ""; - - var apiController = Current.UmbracoApiControllerTypes - .SingleOrDefault(x => x == apiControllerType); - if (apiController == null) - throw new InvalidOperationException("Could not find the umbraco api controller of type " + apiControllerType.FullName); - var metaData = PluginController.GetMetadata(apiController); - if (!metaData.AreaName.IsNullOrWhiteSpace()) - { - //set the area to the plugin area - area = metaData.AreaName; - } - return url.GetUmbracoApiService(actionName, ControllerExtensions.GetControllerName(apiControllerType), area, routeVals); - } - - /// - /// Return the URL for a Web Api service - /// - /// - /// - /// - /// - /// - /// - public static string GetUmbracoApiService(this UrlHelper url, string actionName, string controllerName, string area, RouteValueDictionary routeVals = null) - { - if (actionName == null) throw new ArgumentNullException(nameof(actionName)); - if (string.IsNullOrEmpty(actionName)) throw new ArgumentException("Value can't be empty.", nameof(actionName)); - if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); - - if (routeVals == null) - { - routeVals = new RouteValueDictionary(new {httproute = "", area = area}); - } - else - { - var requiredRouteVals = new RouteValueDictionary(new { httproute = "", area = area }); - requiredRouteVals.MergeLeft(routeVals); - //copy it back now - routeVals = requiredRouteVals; - } - - return url.Action(actionName, controllerName, routeVals); - } - - - - - - } -} diff --git a/src/Umbraco.Web/UrlHelperRenderExtensions.cs b/src/Umbraco.Web/UrlHelperRenderExtensions.cs deleted file mode 100644 index 70a414ff29..0000000000 --- a/src/Umbraco.Web/UrlHelperRenderExtensions.cs +++ /dev/null @@ -1,425 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Web; -using System.Web.Mvc; -using Umbraco.Cms.Core; -using Umbraco.Core.Models; -using Umbraco.Extensions; -using Umbraco.Web.Composing; - -namespace Umbraco.Web -{ - /// - /// Extension methods for UrlHelper for use in templates - /// - public static class UrlHelperRenderExtensions - { - // #region GetCropUrl - // - // /// - // /// Gets the ImageProcessor URL of a media item by the crop alias (using default media item property alias of "umbracoFile") - // /// - // /// - // /// - // /// The IPublishedContent item. - // /// - // /// - // /// The crop alias e.g. thumbnail - // /// - // /// - // /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be - // /// set to false if using the result of this method for CSS. - // /// - // /// - // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, IPublishedContent mediaItem, IImageUrlGenerator imageUrlGenerator, string cropAlias, bool htmlEncode = true) - // { - // if (mediaItem == null) return EmptyHtmlString; - // - // var url = mediaItem.GetCropUrl(imageUrlGenerator, cropAlias: cropAlias, useCropDimensions: true); - // return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); - // } - // - // /// - // /// Gets the ImageProcessor URL by the crop alias using the specified property containing the image cropper Json data on the IPublishedContent item. - // /// - // /// - // /// - // /// The IPublishedContent item. - // /// - // /// - // /// The property alias of the property containing the Json data e.g. umbracoFile - // /// - // /// - // /// The crop alias e.g. thumbnail - // /// - // /// - // /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be - // /// set to false if using the result of this method for CSS. - // /// - // /// - // /// The ImageProcessor.Web URL. - // /// - // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, IPublishedContent mediaItem, string propertyAlias, string cropAlias, IImageUrlGenerator imageUrlGenerator, bool htmlEncode = true) - // { - // if (mediaItem == null) return EmptyHtmlString; - // - // var url = mediaItem.GetCropUrl(imageUrlGenerator, propertyAlias: propertyAlias, cropAlias: cropAlias, useCropDimensions: true); - // return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); - // } - // - // /// - // /// Gets the ImageProcessor URL from the image path. - // /// - // /// - // /// The IPublishedContent item. - // /// - // /// - // /// The width of the output image. - // /// - // /// - // /// The height of the output image. - // /// - // /// - // /// Property alias of the property containing the Json data. - // /// - // /// - // /// The crop alias. - // /// - // /// - // /// Quality percentage of the output image. - // /// - // /// - // /// The image crop mode. - // /// - // /// - // /// The image crop anchor. - // /// - // /// - // /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - // /// - // /// - // /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - // /// - // /// - // /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - // /// - // /// - // /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: - // /// - // /// - // /// - // /// - // /// - // /// Use a dimension as a ratio - // /// - // /// - // /// If the image should be upscaled to requested dimensions - // /// - // /// - // /// - // /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be - // /// set to false if using the result of this method for CSS. - // /// - // /// - // /// The . - // /// - // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, - // IPublishedContent mediaItem, - // IImageUrlGenerator imageUrlGenerator, - // int? width = null, - // int? height = null, - // string propertyAlias = Umbraco.Core.Constants.Conventions.Media.File, - // string cropAlias = null, - // int? quality = null, - // ImageCropMode? imageCropMode = null, - // ImageCropAnchor? imageCropAnchor = null, - // bool preferFocalPoint = false, - // bool useCropDimensions = false, - // bool cacheBuster = true, - // string furtherOptions = null, - // ImageCropRatioMode? ratioMode = null, - // bool upScale = true, - // bool htmlEncode = true) - // { - // if (mediaItem == null) return EmptyHtmlString; - // - // var url = mediaItem.GetCropUrl(imageUrlGenerator, width, height, propertyAlias, cropAlias, quality, imageCropMode, - // imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBuster, furtherOptions, ratioMode, - // upScale); - // return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); - // } - // - // /// - // /// Gets the ImageProcessor URL from the image path. - // /// - // /// - // /// The image URL. - // /// - // /// - // /// The width of the output image. - // /// - // /// - // /// The height of the output image. - // /// - // /// - // /// The Json data from the Umbraco Core Image Cropper property editor - // /// - // /// - // /// The crop alias. - // /// - // /// - // /// Quality percentage of the output image. - // /// - // /// - // /// The image crop mode. - // /// - // /// - // /// The image crop anchor. - // /// - // /// - // /// Use focal point to generate an output image using the focal point instead of the predefined crop if there is one - // /// - // /// - // /// Use crop dimensions to have the output image sized according to the predefined crop sizes, this will override the width and height parameters - // /// - // /// - // /// Add a serialized date of the last edit of the item to ensure client cache refresh when updated - // /// - // /// - // /// These are any query string parameters (formatted as query strings) that ImageProcessor supports. For example: - // /// - // /// - // /// - // /// - // /// - // /// Use a dimension as a ratio - // /// - // /// - // /// If the image should be upscaled to requested dimensions - // /// - // /// - // /// - // /// Whether to HTML encode this URL - default is true - w3c standards require HTML attributes to be HTML encoded but this can be - // /// set to false if using the result of this method for CSS. - // /// - // /// - // /// The . - // /// - // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, - // string imageUrl, - // IImageUrlGenerator imageUrlGenerator, - // int? width = null, - // int? height = null, - // string imageCropperValue = null, - // string cropAlias = null, - // int? quality = null, - // ImageCropMode? imageCropMode = null, - // ImageCropAnchor? imageCropAnchor = null, - // bool preferFocalPoint = false, - // bool useCropDimensions = false, - // string cacheBusterValue = null, - // string furtherOptions = null, - // ImageCropRatioMode? ratioMode = null, - // bool upScale = true, - // bool htmlEncode = true) - // { - // var url = imageUrl.GetCropUrl(imageUrlGenerator, width, height, imageCropperValue, cropAlias, quality, imageCropMode, - // imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, - // upScale); - // return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); - // } - // - // public static IHtmlString GetCropUrl(this UrlHelper urlHelper, - // ImageCropperValue imageCropperValue, - // IImageUrlGenerator imageUrlGenerator, - // int? width = null, - // int? height = null, - // string cropAlias = null, - // int? quality = null, - // ImageCropMode? imageCropMode = null, - // ImageCropAnchor? imageCropAnchor = null, - // bool preferFocalPoint = false, - // bool useCropDimensions = false, - // string cacheBusterValue = null, - // string furtherOptions = null, - // ImageCropRatioMode? ratioMode = null, - // bool upScale = true, - // bool htmlEncode = true) - // { - // if (imageCropperValue == null) return EmptyHtmlString; - // - // var imageUrl = imageCropperValue.Src; - // var url = imageUrl.GetCropUrl(imageUrlGenerator, imageCropperValue, width, height, cropAlias, quality, imageCropMode, - // imageCropAnchor, preferFocalPoint, useCropDimensions, cacheBusterValue, furtherOptions, ratioMode, - // upScale); - // return htmlEncode ? new HtmlString(HttpUtility.HtmlEncode(url)) : new HtmlString(url); - // } - // - // #endregion - - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // public static string SurfaceAction(this UrlHelper url, string action, string controllerName) - // { - // return url.SurfaceAction(action, controllerName, null); - // } - // - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // /// - // public static string SurfaceAction(this UrlHelper url, string action, string controllerName, object additionalRouteVals) - // { - // return url.SurfaceAction(action, controllerName, "", additionalRouteVals); - // } - // - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // /// - // /// - // public static string SurfaceAction(this UrlHelper url, string action, string controllerName, string area, object additionalRouteVals) - // { - // if (action == null) throw new ArgumentNullException(nameof(action)); - // if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); - // if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - // if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); - // - // var encryptedRoute = CreateEncryptedRouteString(controllerName, action, area, additionalRouteVals); - // - // var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; - // return result; - // } - - //TODO Move to LinkGenerator - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType) - // { - // return url.SurfaceAction(action, surfaceType, null); - // } - - //TODO Move to LinkGenerator - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // /// - // public static string SurfaceAction(this UrlHelper url, string action, Type surfaceType, object additionalRouteVals) - // { - // if (action == null) throw new ArgumentNullException(nameof(action)); - // if (string.IsNullOrEmpty(action)) throw new ArgumentException("Value can't be empty.", nameof(action)); - // if (surfaceType == null) throw new ArgumentNullException(nameof(surfaceType)); - // - // var area = ""; - // - // - // var surfaceController = Current.SurfaceControllerTypes.SingleOrDefault(x => x == surfaceType); - // if (surfaceController == null) - // throw new InvalidOperationException("Could not find the surface controller of type " + surfaceType.FullName); - // var metaData = PluginController.GetMetadata(surfaceController); - // if (metaData.AreaName.IsNullOrWhiteSpace() == false) - // { - // //set the area to the plugin area - // area = metaData.AreaName; - // } - // - // var encryptedRoute = CreateEncryptedRouteString(metaData.ControllerName, action, area, additionalRouteVals); - // - // var result = Current.UmbracoContext.OriginalRequestUrl.AbsolutePath.EnsureEndsWith('?') + "ufprt=" + encryptedRoute; - // return result; - // } - - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // //TODO Move to LinkGenerator - // public static string SurfaceAction(this UrlHelper url, string action) - // where T : SurfaceController - // { - // return url.SurfaceAction(action, typeof (T)); - // } - - // /// - // /// Generates a URL based on the current Umbraco URL with a custom query string that will route to the specified SurfaceController - // /// - // /// - // /// - // /// - // /// - // /// - // //TODO Move to LinkGenerator - // public static string SurfaceAction(this UrlHelper url, string action, object additionalRouteVals) - // where T : SurfaceController - // { - // return url.SurfaceAction(action, typeof (T), additionalRouteVals); - // } - - // /// - // /// This is used in methods like BeginUmbracoForm and SurfaceAction to generate an encrypted string which gets submitted in a request for which - // /// Umbraco can decrypt during the routing process in order to delegate the request to a specific MVC Controller. - // /// - // /// - // /// - // /// - // /// - // /// - // internal static string CreateEncryptedRouteString(string controllerName, string controllerAction, string area, object additionalRouteVals = null) - // { - // if (controllerName == null) throw new ArgumentNullException(nameof(controllerName)); - // if (string.IsNullOrEmpty(controllerName)) throw new ArgumentException("Value can't be empty.", nameof(controllerName)); - // if (controllerAction == null) throw new ArgumentNullException(nameof(controllerAction)); - // if (string.IsNullOrEmpty(controllerAction)) throw new ArgumentException("Value can't be empty.", nameof(controllerAction)); - // if (area == null) throw new ArgumentNullException(nameof(area)); - // - // //need to create a params string as Base64 to put into our hidden field to use during the routes - // var surfaceRouteParams = $"c={HttpUtility.UrlEncode(controllerName)}&a={HttpUtility.UrlEncode(controllerAction)}&ar={area}"; - // - // //checking if the additional route values is already a dictionary and convert to querystring - // string additionalRouteValsAsQuery; - // if (additionalRouteVals != null) - // { - // if (additionalRouteVals is Dictionary additionalRouteValsAsDictionary) - // additionalRouteValsAsQuery = additionalRouteValsAsDictionary.ToQueryString(); - // else - // additionalRouteValsAsQuery = additionalRouteVals.ToDictionary().ToQueryString(); - // } - // else - // additionalRouteValsAsQuery = null; - // - // if (additionalRouteValsAsQuery.IsNullOrWhiteSpace() == false) - // surfaceRouteParams += "&" + additionalRouteValsAsQuery; - // - // return surfaceRouteParams.EncryptWithMachineKey(); - // } - } -} diff --git a/src/Umbraco.Web/WebApi/EnableDetailedErrorsAttribute.cs b/src/Umbraco.Web/WebApi/EnableDetailedErrorsAttribute.cs deleted file mode 100644 index a62df6b5e4..0000000000 --- a/src/Umbraco.Web/WebApi/EnableDetailedErrorsAttribute.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Filters; - -namespace Umbraco.Web.WebApi -{ - /// - /// Ensures controllers have detailed error messages even when debug mode is off - /// - public class EnableDetailedErrorsAttribute : ActionFilterAttribute - { - public override void OnActionExecuting(HttpActionContext actionContext) - { - actionContext.ControllerContext.Configuration.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; - } - } -} diff --git a/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs deleted file mode 100644 index bae3ad05c2..0000000000 --- a/src/Umbraco.Web/WebApi/Filters/FeatureAuthorizeAttribute.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Web.Http; -using System.Web.Http.Controllers; -using Umbraco.Web.Composing; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Features; - -namespace Umbraco.Web.WebApi.Filters -{ - /// - /// Ensures that the controller is an authorized feature. - /// - /// Else returns unauthorized. - public sealed class FeatureAuthorizeAttribute : AuthorizeAttribute - { - private readonly UmbracoFeatures _features; - - /// - /// Initializes a new instance of the class. - /// - public FeatureAuthorizeAttribute() - { - // attributes have to use Current.Container - _features = Current.Factory?.GetService(); - } - - protected override bool IsAuthorized(HttpActionContext actionContext) - { - // if no features resolver has been set then return true, this will occur in unit - // tests and we don't want users to have to set a resolver - //just so their unit tests work. - - if (_features == null) return true; - - var controllerType = actionContext.ControllerContext.ControllerDescriptor.ControllerType; - return _features.IsControllerEnabled(controllerType); - } - } -} diff --git a/src/Umbraco.Web/WebApi/Filters/ValidationFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/ValidationFilterAttribute.cs deleted file mode 100644 index d4e6af93ac..0000000000 --- a/src/Umbraco.Web/WebApi/Filters/ValidationFilterAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Web.Http.Controllers; -using System.Web.Http.Filters; - -namespace Umbraco.Web.WebApi.Filters -{ - - /// - /// An action filter used to do basic validation against the model and return a result - /// straight away if it fails. - /// - /// Migrated to .NET core - internal sealed class ValidationFilterAttribute : ActionFilterAttribute - { - public override void OnActionExecuting(HttpActionContext actionContext) - { - var modelState = actionContext.ModelState; - - if (!modelState.IsValid) - { - actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, modelState); - } - } - } -} diff --git a/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs b/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs deleted file mode 100644 index 2d53393d21..0000000000 --- a/src/Umbraco.Web/WebApi/HttpRequestMessageExtensions.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Net; -using System.Net.Http; -using System.Web; -using Microsoft.Owin; -using Umbraco.Cms.Core; - -namespace Umbraco.Web.WebApi -{ - - public static class HttpRequestMessageExtensions - { - - /// - /// Borrowed from the latest Microsoft.AspNet.WebApi.Owin package which we cannot use because of a later webapi dependency - /// - /// - /// - internal static Attempt TryGetOwinContext(this HttpRequestMessage request) - { - // occurs in unit tests? - if (request.Properties.TryGetValue("MS_OwinContext", out var o) && o is IOwinContext owinContext) - return Attempt.Succeed(owinContext); - - var httpContext = request.TryGetHttpContext(); - try - { - return httpContext - ? Attempt.Succeed(httpContext.Result.GetOwinContext()) - : Attempt.Fail(); - } - catch (InvalidOperationException) - { - //this will occur if there is no OWIN environment which generally would only be in things like unit tests - return Attempt.Fail(); - } - } - - /// - /// Tries to retrieve the current HttpContext if one exists. - /// - /// - public static Attempt TryGetHttpContext(this HttpRequestMessage request) - { - object context; - if (request.Properties.TryGetValue("MS_HttpContext", out context)) - { - var httpContext = context as HttpContextBase; - if (httpContext != null) - { - return Attempt.Succeed(httpContext); - } - } - if (HttpContext.Current != null) - { - return Attempt.Succeed(new HttpContextWrapper(HttpContext.Current)); - } - - return Attempt.Fail(); - } - - - } - -} diff --git a/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs b/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs deleted file mode 100644 index 49b707bf3a..0000000000 --- a/src/Umbraco.Web/WebApi/IsBackOfficeAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Umbraco.Web.WebApi -{ - // TODO: This has been moved to netcore so can be removed when ready - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] - public sealed class IsBackOfficeAttribute : Attribute - { - } -} diff --git a/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs b/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs deleted file mode 100644 index 9269db01c3..0000000000 --- a/src/Umbraco.Web/WebApi/NamespaceHttpControllerSelector.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Web.Http; -using System.Web.Http.Controllers; -using System.Web.Http.Dispatcher; -using static Umbraco.Cms.Core.Constants.Web.Routing; - -namespace Umbraco.Web.WebApi -{ - public class NamespaceHttpControllerSelector : DefaultHttpControllerSelector - { - private const string ControllerKey = ControllerToken; - private readonly HttpConfiguration _configuration; - private readonly Lazy> _duplicateControllerTypes; - - public NamespaceHttpControllerSelector(HttpConfiguration configuration) : base(configuration) - { - _configuration = configuration; - _duplicateControllerTypes = new Lazy>(GetDuplicateControllerTypes); - } - - public override HttpControllerDescriptor SelectController(HttpRequestMessage request) - { - var routeData = request.GetRouteData(); - if (routeData == null - || routeData.Route == null - || routeData.Route.DataTokens == null - || routeData.Route.DataTokens.ContainsKey("Namespaces") == false - || routeData.Route.DataTokens["Namespaces"] == null) - return base.SelectController(request); - - // Look up controller in route data - object controllerName; - routeData.Values.TryGetValue(ControllerKey, out controllerName); - var controllerNameAsString = controllerName as string; - if (controllerNameAsString == null) - return base.SelectController(request); - - //get the currently cached default controllers - this will not contain duplicate controllers found so if - // this controller is found in the underlying cache we don't need to do anything - var map = base.GetControllerMapping(); - if (map.ContainsKey(controllerNameAsString)) - return base.SelectController(request); - - //the cache does not contain this controller because it's most likely a duplicate, - // so we need to sort this out ourselves and we can only do that if the namespace token - // is formatted correctly. - var namespaces = routeData.Route.DataTokens["Namespaces"] as IEnumerable; - if (namespaces == null) - return base.SelectController(request); - - //see if this is in our cache - foreach (var ns in namespaces) - { - HttpControllerDescriptor descriptor; - if (_duplicateControllerTypes.Value.TryGetValue(string.Format("{0}-{1}", controllerNameAsString, ns), out descriptor)) - { - return descriptor; - } - } - - return base.SelectController(request); - } - - private ConcurrentDictionary GetDuplicateControllerTypes() - { - var assembliesResolver = _configuration.Services.GetAssembliesResolver(); - var controllersResolver = _configuration.Services.GetHttpControllerTypeResolver(); - var controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver); - - var groupedByName = controllerTypes.GroupBy( - t => t.Name.Substring(0, t.Name.Length - ControllerSuffix.Length), - StringComparer.OrdinalIgnoreCase).Where(x => x.Count() > 1); - - var duplicateControllers = groupedByName.ToDictionary( - g => g.Key, - g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), - StringComparer.OrdinalIgnoreCase); - - var result = new ConcurrentDictionary(); - - foreach (var controllerTypeGroup in duplicateControllers) - { - foreach (var controllerType in controllerTypeGroup.Value.SelectMany(controllerTypesGrouping => controllerTypesGrouping)) - { - result.TryAdd(string.Format("{0}-{1}", controllerTypeGroup.Key, controllerType.Namespace), - new HttpControllerDescriptor(_configuration, controllerTypeGroup.Key, controllerType)); - } - } - - return result; - } - - } -} diff --git a/src/Umbraco.Web/WebApi/ParameterSwapControllerActionSelector.cs b/src/Umbraco.Web/WebApi/ParameterSwapControllerActionSelector.cs deleted file mode 100644 index 9ae6786ca4..0000000000 --- a/src/Umbraco.Web/WebApi/ParameterSwapControllerActionSelector.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System; -using System.Linq; -using System.Net.Http; -using System.Web; -using System.Web.Http.Controllers; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using Umbraco.Cms.Core; -using Umbraco.Extensions; - -namespace Umbraco.Web.WebApi -{ - /// - /// This is used to auto-select specific actions on controllers that would otherwise be ambiguous based on a single parameter type - /// - /// - /// As an example, lets say we have 2 methods: GetChildren(int id) and GetChildren(Guid id), by default Web Api won't allow this since - /// it won't know what to select, but if this Tuple is passed in new Tuple{string, string}("GetChildren", "id") - /// - /// This supports POST values too however only for JSON values - /// - internal class ParameterSwapControllerActionSelector : ApiControllerActionSelector - { - private readonly ParameterSwapInfo[] _actions; - - /// - /// Constructor accepting a list of action name + parameter name - /// - /// - public ParameterSwapControllerActionSelector(params ParameterSwapInfo[] actions) - { - _actions = actions; - } - - public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext) - { - var found = _actions.FirstOrDefault(x => controllerContext.Request.RequestUri.GetLeftPart(UriPartial.Path).InvariantEndsWith(x.ActionName)); - - if (found != null) - { - HttpActionDescriptor method; - if (TryBindFromUri(controllerContext, found, out method)) - { - return method; - } - - //if it's a post we can try to read from the body and bind from the json value - if (controllerContext.Request.Method == HttpMethod.Post) - { - var requestContent = new HttpMessageContent(controllerContext.Request); - var strJson = requestContent.HttpRequestMessage.Content.ReadAsStringAsync().Result; - var json = JsonConvert.DeserializeObject(strJson); - - if (json == null) - { - return base.SelectAction(controllerContext); - } - - var requestParam = json[found.ParamName]; - - if (requestParam != null) - { - var paramTypes = found.SupportedTypes; - - foreach (var paramType in paramTypes) - { - try - { - var converted = requestParam.ToObject(paramType); - if (converted != null) - { - method = MatchByType(paramType, controllerContext, found); - if (method != null) - return method; - } - } - catch (JsonReaderException) - { - //can't convert - } - catch (JsonSerializationException) - { - //can't convert - } - } - } - } - } - return base.SelectAction(controllerContext); - } - - private bool TryBindFromUri(HttpControllerContext controllerContext, ParameterSwapInfo found, out HttpActionDescriptor method) - { - var requestParam = HttpUtility.ParseQueryString(controllerContext.Request.RequestUri.Query).Get(found.ParamName); - - requestParam = (requestParam == null) ? null : requestParam.Trim(); - var paramTypes = found.SupportedTypes; - - if (requestParam == string.Empty && paramTypes.Length > 0) - { - //if it's empty then in theory we can select any of the actions since they'll all need to deal with empty or null parameters - //so we'll try to use the first one available - method = MatchByType(paramTypes[0], controllerContext, found); - if (method != null) - return true; - } - - if (requestParam != null) - { - foreach (var paramType in paramTypes) - { - //check if this is IEnumerable and if so this will get it's type - //we need to know this since the requestParam will always just be a string - var enumType = paramType.GetEnumeratedType(); - - var converted = requestParam.TryConvertTo(enumType ?? paramType); - if (converted) - { - method = MatchByType(paramType, controllerContext, found); - if (method != null) - return true; - } - } - } - - method = null; - return false; - } - - private static ReflectedHttpActionDescriptor MatchByType(Type idType, HttpControllerContext controllerContext, ParameterSwapInfo found) - { - var controllerType = controllerContext.Controller.GetType(); - var methods = controllerType.GetMethods().Where(info => info.Name == found.ActionName).ToArray(); - if (methods.Length > 1) - { - //choose the one that has the parameter with the T type - var method = methods.FirstOrDefault(x => x.GetParameters().FirstOrDefault(p => p.Name == found.ParamName && p.ParameterType == idType) != null); - - return new ReflectedHttpActionDescriptor(controllerContext.ControllerDescriptor, method); - } - return null; - } - - internal class ParameterSwapInfo - { - public string ActionName { get; private set; } - public string ParamName { get; private set; } - public Type[] SupportedTypes { get; private set; } - - public ParameterSwapInfo(string actionName, string paramName, params Type[] supportedTypes) - { - ActionName = actionName; - ParamName = paramName; - SupportedTypes = supportedTypes; - } - } - - } -} diff --git a/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs b/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs deleted file mode 100644 index ea285434b3..0000000000 --- a/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Newtonsoft.Json.Converters; -using System; -using System.Web.Http.Controllers; - -namespace Umbraco.Web.WebApi -{ - internal class SerializeVersionAttribute : Attribute, IControllerConfiguration - { - public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) - { - var formatter = controllerSettings.Formatters.JsonFormatter; - formatter.SerializerSettings.Converters.Add(new VersionConverter()); - } - } -} diff --git a/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs b/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs deleted file mode 100644 index fac3a633a4..0000000000 --- a/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Web; -using System.Web.Http.WebHost; -using System.Web.Routing; -using System.Web.SessionState; - -namespace Umbraco.Web.WebApi -{ - /// - /// A custom WebApi route handler that enables session on the HttpContext - use with caution! - /// - /// - /// WebApi controllers (and REST in general) shouldn't have session state enabled since it's stateless, - /// enabling session state puts additional locks on requests so only use this when absolutely needed - /// - internal class SessionHttpControllerRouteHandler : HttpControllerRouteHandler - { - protected override IHttpHandler GetHttpHandler(RequestContext requestContext) - { - return new SessionHttpControllerHandler(requestContext.RouteData); - } - - /// - /// A custom WebApi handler that enables session on the HttpContext - /// - private class SessionHttpControllerHandler : HttpControllerHandler, IRequiresSessionState - { - public SessionHttpControllerHandler(RouteData routeData) : base(routeData) - { } - } - } -} diff --git a/src/Umbraco.Web/WebApi/TrimModelBinder.cs b/src/Umbraco.Web/WebApi/TrimModelBinder.cs deleted file mode 100644 index 0361612bb8..0000000000 --- a/src/Umbraco.Web/WebApi/TrimModelBinder.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Web.Http.Controllers; -using System.Web.Http.ModelBinding; - -namespace Umbraco.Web.WebApi -{ - /// - /// A model binder to trim the string - /// - internal class TrimModelBinder : IModelBinder - { - public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) - { - var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); - if (valueResult?.AttemptedValue == null) - { - return false; - } - - bindingContext.Model = (string.IsNullOrWhiteSpace(valueResult.AttemptedValue) ? valueResult.AttemptedValue : valueResult.AttemptedValue.Trim()); - return true; - } - } -} diff --git a/src/Umbraco.Web/WebApi/UmbracoApiController.cs b/src/Umbraco.Web/WebApi/UmbracoApiController.cs deleted file mode 100644 index 6c91060a58..0000000000 --- a/src/Umbraco.Web/WebApi/UmbracoApiController.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Composing; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; - -namespace Umbraco.Web.WebApi -{ - /// - /// Provides a base class for auto-routed Umbraco API controllers. - /// - public abstract class UmbracoApiController : UmbracoApiControllerBase, IDiscoverable - { - protected UmbracoApiController() - { - } - - protected UmbracoApiController(GlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider) - : base(globalSettings, umbracoContextAccessor, backOfficeSecurityAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) - { - } - } -} diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs deleted file mode 100644 index cd1d2f7b63..0000000000 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerBase.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Web.Http; -using Microsoft.Extensions.DependencyInjection; -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Features; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; -using Umbraco.Web.Composing; -using Umbraco.Web.WebApi.Filters; - -namespace Umbraco.Web.WebApi -{ - - - /// - /// Provides a base class for Umbraco API controllers. - /// - /// These controllers are NOT auto-routed. - [FeatureAuthorize] - public abstract class UmbracoApiControllerBase : ApiController, IUmbracoFeature - { - - // note: all Umbraco controllers have two constructors: one with all dependencies, which should be used, - // and one with auto dependencies, ie no dependencies - and then dependencies are automatically obtained - // here from the Current service locator - this is obviously evil, but it allows us to add new dependencies - // without breaking compatibility. - - /// - /// Initializes a new instance of the class with auto dependencies. - /// - /// Dependencies are obtained from the service locator. - protected UmbracoApiControllerBase() - : this( - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService(), - Current.Factory.GetRequiredService() - ) - { } - - /// - /// Initializes a new instance of the class with all its dependencies. - /// - protected UmbracoApiControllerBase(GlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider) - { - UmbracoContextAccessor = umbracoContextAccessor; - BackOfficeSecurityAccessor = backOfficeSecurityAccessor; - SqlContext = sqlContext; - Services = services; - AppCaches = appCaches; - Logger = logger; - RuntimeState = runtimeState; - Mapper = umbracoMapper; - PublishedUrlProvider = publishedUrlProvider; - } - - /// - /// Gets a unique instance identifier. - /// - /// For debugging purposes. - internal Guid InstanceId { get; } = Guid.NewGuid(); - - /// - /// Gets the Umbraco context. - /// - public virtual IUmbracoContext UmbracoContext => UmbracoContextAccessor.UmbracoContext; - - /// - /// Gets the Umbraco context accessor. - /// - public virtual IUmbracoContextAccessor UmbracoContextAccessor { get; } - - public IBackOfficeSecurityAccessor BackOfficeSecurityAccessor { get; } - - - /// - /// Gets the sql context. - /// - public ISqlContext SqlContext { get; } - - /// - /// Gets the services context. - /// - public ServiceContext Services { get; } - - /// - /// Gets the application cache. - /// - public AppCaches AppCaches { get; } - - /// - /// Gets the logger. - /// - public IProfilingLogger Logger { get; } - - /// - /// Gets the runtime state. - /// - internal IRuntimeState RuntimeState { get; } - - /// - /// Gets the mapper. - /// - public UmbracoMapper Mapper { get; } - - protected IPublishedUrlProvider PublishedUrlProvider { get; } - - /// - /// Gets the web security helper. - /// - public IBackOfficeSecurity Security => BackOfficeSecurityAccessor.BackOfficeSecurity; - - - } -} diff --git a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollectionBuilder.cs b/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollectionBuilder.cs deleted file mode 100644 index 0e35f017c6..0000000000 --- a/src/Umbraco.Web/WebApi/UmbracoApiControllerTypeCollectionBuilder.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Composing; - -namespace Umbraco.Web.WebApi -{ - // TODO: This is moved to netcore so can be deleted when possible - - public class UmbracoApiControllerTypeCollectionBuilder : TypeCollectionBuilderBase - { - protected override UmbracoApiControllerTypeCollectionBuilder This => this; - } -} diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizeAttribute.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizeAttribute.cs deleted file mode 100644 index 00ff6cc8f0..0000000000 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizeAttribute.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Web.Http; -using Umbraco.Cms.Core; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Web.Composing; - -namespace Umbraco.Web.WebApi -{ - // TODO: This has been migrated to netcore and can be removed when ready - - public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute - { - private readonly bool _requireApproval; - - /// - /// Can be used by unit tests to enable/disable this filter - /// - internal static bool Enable = true; - - // TODO: inject! - private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor; - private readonly IRuntimeState _runtimeState; - - private IRuntimeState RuntimeState => _runtimeState ?? Current.RuntimeState; - - private IBackOfficeSecurity BackOfficeSecurity => _backOfficeSecurityAccessor.BackOfficeSecurity ?? Current.BackOfficeSecurityAccessor.BackOfficeSecurity; - - /// - /// THIS SHOULD BE ONLY USED FOR UNIT TESTS - /// - /// - /// - public UmbracoAuthorizeAttribute(IBackOfficeSecurityAccessor backofficeSecurityAccessor, IRuntimeState runtimeState) - { - _backOfficeSecurityAccessor = backofficeSecurityAccessor ?? throw new ArgumentNullException(nameof(backofficeSecurityAccessor)); - _runtimeState = runtimeState ?? throw new ArgumentNullException(nameof(runtimeState)); - } - - public UmbracoAuthorizeAttribute() : this(true) - { } - - public UmbracoAuthorizeAttribute(bool requireApproval) - { - _requireApproval = requireApproval; - } - - protected override bool IsAuthorized(System.Web.Http.Controllers.HttpActionContext actionContext) - { - if (Enable == false) - { - return true; - } - - // if not configured (install or upgrade) then we can continue - // otherwise we need to ensure that a user is logged in - - switch (_runtimeState.Level) - { - case RuntimeLevel.Install: - case RuntimeLevel.Upgrade: - return true; - default: - var userApprovalSucceeded = !_requireApproval || (BackOfficeSecurity.CurrentUser?.IsApproved ?? false); - return userApprovalSucceeded; - } - } - } -} diff --git a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs b/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs deleted file mode 100644 index eff9475c57..0000000000 --- a/src/Umbraco.Web/WebApi/UmbracoAuthorizedApiController.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Umbraco.Cms.Core.Cache; -using Umbraco.Cms.Core.Configuration.Models; -using Umbraco.Cms.Core.Logging; -using Umbraco.Cms.Core.Mapping; -using Umbraco.Cms.Core.Routing; -using Umbraco.Cms.Core.Security; -using Umbraco.Cms.Core.Services; -using Umbraco.Cms.Core.Web; -using Umbraco.Cms.Infrastructure.Persistence; - -namespace Umbraco.Web.WebApi -{ - /// - /// Provides a base class for authorized auto-routed Umbraco API controllers. - /// - /// - /// This controller will also append a custom header to the response if the user - /// is logged in using forms authentication which indicates the seconds remaining - /// before their timeout expires. - /// - [IsBackOffice] - // [UmbracoUserTimeoutFilter] has been migrated to netcore - [UmbracoAuthorize] - // [DisableBrowserCache] has been migrated to netcore - // [UmbracoWebApiRequireHttps] - // [CheckIfUserTicketDataIsStale] - // [UnhandedExceptionLoggerConfiguration] - [EnableDetailedErrors] - public abstract class UmbracoAuthorizedApiController : UmbracoApiController - { - protected UmbracoAuthorizedApiController() - { - } - - protected UmbracoAuthorizedApiController(GlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, IBackOfficeSecurityAccessor backOfficeSecurityAccessor, ISqlContext sqlContext, ServiceContext services, AppCaches appCaches, IProfilingLogger logger, IRuntimeState runtimeState, UmbracoMapper umbracoMapper, IPublishedUrlProvider publishedUrlProvider) - : base(globalSettings, umbracoContextAccessor, backOfficeSecurityAccessor, sqlContext, services, appCaches, logger, runtimeState, umbracoMapper, publishedUrlProvider) - { - } - - } -} From bb87c0156d6862b366600cbec34d3d5334999f8e Mon Sep 17 00:00:00 2001 From: Shannon Date: Thu, 15 Jul 2021 14:27:15 -0600 Subject: [PATCH 075/102] removes Umbraco.Web.UI --- src/Umbraco.Web.UI/Content/bootstrap.css | 6757 ----------------- src/Umbraco.Web.UI/Global.asax | 1 - src/Umbraco.Web.UI/Media/Web.config | 9 - src/Umbraco.Web.UI/Properties/AssemblyInfo.cs | 10 - .../PublishProfiles/File export.pubxml | 17 - .../Properties/Settings.Designer.cs | 35 - .../Properties/Settings.settings | 9 - src/Umbraco.Web.UI/Umbraco.Web.UI.csproj | 327 - src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml | 2781 ------- .../Umbraco/js/UmbracoSpeechBubbleBackEnd.js | 68 - .../Umbraco/js/dualSelectBox.js | 48 - src/Umbraco.Web.UI/Umbraco/js/guiFunctions.js | 88 - .../Umbraco/js/installer.app.js | 7 - .../Umbraco/js/umbracoCheckKeys.js | 102 - src/Umbraco.Web.UI/Umbraco/js/web.config | 8 - src/Umbraco.Web.UI/Views/Web.config | 74 - .../config/ClientDependency.Release.config | 66 - .../config/HealthChecks.Release.config | 28 - src/Umbraco.Web.UI/config/HealthChecks.config | 26 - .../config/imageprocessor/cache.config | 10 - .../config/imageprocessor/processing.config | 71 - .../config/imageprocessor/security.config | 24 - .../config/serilog.Release.config | 41 - .../config/serilog.user.Release.config | 39 - src/Umbraco.Web.UI/favicon.ico | Bin 15406 -> 0 bytes src/Umbraco.Web.UI/web.Template.Debug.config | 113 - .../web.Template.Release.config | 16 - src/Umbraco.Web.UI/web.Template.config | 295 - src/Umbraco.Web.UI/web.config.temp-build | 297 - src/umbraco.sln | 20 +- 30 files changed, 6 insertions(+), 11381 deletions(-) delete mode 100644 src/Umbraco.Web.UI/Content/bootstrap.css delete mode 100644 src/Umbraco.Web.UI/Global.asax delete mode 100644 src/Umbraco.Web.UI/Media/Web.config delete mode 100644 src/Umbraco.Web.UI/Properties/AssemblyInfo.cs delete mode 100644 src/Umbraco.Web.UI/Properties/PublishProfiles/File export.pubxml delete mode 100644 src/Umbraco.Web.UI/Properties/Settings.Designer.cs delete mode 100644 src/Umbraco.Web.UI/Properties/Settings.settings delete mode 100644 src/Umbraco.Web.UI/Umbraco.Web.UI.csproj delete mode 100644 src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/UmbracoSpeechBubbleBackEnd.js delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/dualSelectBox.js delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/guiFunctions.js delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/installer.app.js delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/umbracoCheckKeys.js delete mode 100644 src/Umbraco.Web.UI/Umbraco/js/web.config delete mode 100644 src/Umbraco.Web.UI/Views/Web.config delete mode 100644 src/Umbraco.Web.UI/config/ClientDependency.Release.config delete mode 100644 src/Umbraco.Web.UI/config/HealthChecks.Release.config delete mode 100644 src/Umbraco.Web.UI/config/HealthChecks.config delete mode 100644 src/Umbraco.Web.UI/config/imageprocessor/cache.config delete mode 100644 src/Umbraco.Web.UI/config/imageprocessor/processing.config delete mode 100644 src/Umbraco.Web.UI/config/imageprocessor/security.config delete mode 100644 src/Umbraco.Web.UI/config/serilog.Release.config delete mode 100644 src/Umbraco.Web.UI/config/serilog.user.Release.config delete mode 100644 src/Umbraco.Web.UI/favicon.ico delete mode 100644 src/Umbraco.Web.UI/web.Template.Debug.config delete mode 100644 src/Umbraco.Web.UI/web.Template.Release.config delete mode 100644 src/Umbraco.Web.UI/web.Template.config delete mode 100644 src/Umbraco.Web.UI/web.config.temp-build diff --git a/src/Umbraco.Web.UI/Content/bootstrap.css b/src/Umbraco.Web.UI/Content/bootstrap.css deleted file mode 100644 index 6167622cec..0000000000 --- a/src/Umbraco.Web.UI/Content/bootstrap.css +++ /dev/null @@ -1,6757 +0,0 @@ -/*! - * Bootstrap v3.3.7 (http://getbootstrap.com) - * Copyright 2011-2016 Twitter, Inc. - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) - */ -/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */ -html { - font-family: sans-serif; - -webkit-text-size-adjust: 100%; - -ms-text-size-adjust: 100%; -} -body { - margin: 0; -} -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -main, -menu, -nav, -section, -summary { - display: block; -} -audio, -canvas, -progress, -video { - display: inline-block; - vertical-align: baseline; -} -audio:not([controls]) { - display: none; - height: 0; -} -[hidden], -template { - display: none; -} -a { - background-color: transparent; -} -a:active, -a:hover { - outline: 0; -} -abbr[title] { - border-bottom: 1px dotted; -} -b, -strong { - font-weight: bold; -} -dfn { - font-style: italic; -} -h1 { - margin: .67em 0; - font-size: 2em; -} -mark { - color: #000; - background: #ff0; -} -small { - font-size: 80%; -} -sub, -sup { - position: relative; - font-size: 75%; - line-height: 0; - vertical-align: baseline; -} -sup { - top: -.5em; -} -sub { - bottom: -.25em; -} -img { - border: 0; -} -svg:not(:root) { - overflow: hidden; -} -figure { - margin: 1em 40px; -} -hr { - height: 0; - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; -} -pre { - overflow: auto; -} -code, -kbd, -pre, -samp { - font-family: monospace, monospace; - font-size: 1em; -} -button, -input, -optgroup, -select, -textarea { - margin: 0; - font: inherit; - color: inherit; -} -button { - overflow: visible; -} -button, -select { - text-transform: none; -} -button, -html input[type="button"], -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; - cursor: pointer; -} -button[disabled], -html input[disabled] { - cursor: default; -} -button::-moz-focus-inner, -input::-moz-focus-inner { - padding: 0; - border: 0; -} -input { - line-height: normal; -} -input[type="checkbox"], -input[type="radio"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - padding: 0; -} -input[type="number"]::-webkit-inner-spin-button, -input[type="number"]::-webkit-outer-spin-button { - height: auto; -} -input[type="search"] { - -webkit-box-sizing: content-box; - -moz-box-sizing: content-box; - box-sizing: content-box; - -webkit-appearance: textfield; -} -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} -fieldset { - padding: .35em .625em .75em; - margin: 0 2px; - border: 1px solid #c0c0c0; -} -legend { - padding: 0; - border: 0; -} -textarea { - overflow: auto; -} -optgroup { - font-weight: bold; -} -table { - border-spacing: 0; - border-collapse: collapse; -} -td, -th { - padding: 0; -} -/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ -@media print { - *, - *:before, - *:after { - color: #000 !important; - text-shadow: none !important; - background: transparent !important; - -webkit-box-shadow: none !important; - box-shadow: none !important; - } - a, - a:visited { - text-decoration: underline; - } - a[href]:after { - content: " (" attr(href) ")"; - } - abbr[title]:after { - content: " (" attr(title) ")"; - } - a[href^="#"]:after, - a[href^="javascript:"]:after { - content: ""; - } - pre, - blockquote { - border: 1px solid #999; - - page-break-inside: avoid; - } - thead { - display: table-header-group; - } - tr, - img { - page-break-inside: avoid; - } - img { - max-width: 100% !important; - } - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - h2, - h3 { - page-break-after: avoid; - } - .navbar { - display: none; - } - .btn > .caret, - .dropup > .btn > .caret { - border-top-color: #000 !important; - } - .label { - border: 1px solid #000; - } - .table { - border-collapse: collapse !important; - } - .table td, - .table th { - background-color: #fff !important; - } - .table-bordered th, - .table-bordered td { - border: 1px solid #ddd !important; - } -} -@font-face { - font-family: 'Glyphicons Halflings'; - - src: url('../fonts/glyphicons-halflings-regular.eot'); - src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg'); -} -.glyphicon { - position: relative; - top: 1px; - display: inline-block; - font-family: 'Glyphicons Halflings'; - font-style: normal; - font-weight: normal; - line-height: 1; - - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -.glyphicon-asterisk:before { - content: "\002a"; -} -.glyphicon-plus:before { - content: "\002b"; -} -.glyphicon-euro:before, -.glyphicon-eur:before { - content: "\20ac"; -} -.glyphicon-minus:before { - content: "\2212"; -} -.glyphicon-cloud:before { - content: "\2601"; -} -.glyphicon-envelope:before { - content: "\2709"; -} -.glyphicon-pencil:before { - content: "\270f"; -} -.glyphicon-glass:before { - content: "\e001"; -} -.glyphicon-music:before { - content: "\e002"; -} -.glyphicon-search:before { - content: "\e003"; -} -.glyphicon-heart:before { - content: "\e005"; -} -.glyphicon-star:before { - content: "\e006"; -} -.glyphicon-star-empty:before { - content: "\e007"; -} -.glyphicon-user:before { - content: "\e008"; -} -.glyphicon-film:before { - content: "\e009"; -} -.glyphicon-th-large:before { - content: "\e010"; -} -.glyphicon-th:before { - content: "\e011"; -} -.glyphicon-th-list:before { - content: "\e012"; -} -.glyphicon-ok:before { - content: "\e013"; -} -.glyphicon-remove:before { - content: "\e014"; -} -.glyphicon-zoom-in:before { - content: "\e015"; -} -.glyphicon-zoom-out:before { - content: "\e016"; -} -.glyphicon-off:before { - content: "\e017"; -} -.glyphicon-signal:before { - content: "\e018"; -} -.glyphicon-cog:before { - content: "\e019"; -} -.glyphicon-trash:before { - content: "\e020"; -} -.glyphicon-home:before { - content: "\e021"; -} -.glyphicon-file:before { - content: "\e022"; -} -.glyphicon-time:before { - content: "\e023"; -} -.glyphicon-road:before { - content: "\e024"; -} -.glyphicon-download-alt:before { - content: "\e025"; -} -.glyphicon-download:before { - content: "\e026"; -} -.glyphicon-upload:before { - content: "\e027"; -} -.glyphicon-inbox:before { - content: "\e028"; -} -.glyphicon-play-circle:before { - content: "\e029"; -} -.glyphicon-repeat:before { - content: "\e030"; -} -.glyphicon-refresh:before { - content: "\e031"; -} -.glyphicon-list-alt:before { - content: "\e032"; -} -.glyphicon-lock:before { - content: "\e033"; -} -.glyphicon-flag:before { - content: "\e034"; -} -.glyphicon-headphones:before { - content: "\e035"; -} -.glyphicon-volume-off:before { - content: "\e036"; -} -.glyphicon-volume-down:before { - content: "\e037"; -} -.glyphicon-volume-up:before { - content: "\e038"; -} -.glyphicon-qrcode:before { - content: "\e039"; -} -.glyphicon-barcode:before { - content: "\e040"; -} -.glyphicon-tag:before { - content: "\e041"; -} -.glyphicon-tags:before { - content: "\e042"; -} -.glyphicon-book:before { - content: "\e043"; -} -.glyphicon-bookmark:before { - content: "\e044"; -} -.glyphicon-print:before { - content: "\e045"; -} -.glyphicon-camera:before { - content: "\e046"; -} -.glyphicon-font:before { - content: "\e047"; -} -.glyphicon-bold:before { - content: "\e048"; -} -.glyphicon-italic:before { - content: "\e049"; -} -.glyphicon-text-height:before { - content: "\e050"; -} -.glyphicon-text-width:before { - content: "\e051"; -} -.glyphicon-align-left:before { - content: "\e052"; -} -.glyphicon-align-center:before { - content: "\e053"; -} -.glyphicon-align-right:before { - content: "\e054"; -} -.glyphicon-align-justify:before { - content: "\e055"; -} -.glyphicon-list:before { - content: "\e056"; -} -.glyphicon-indent-left:before { - content: "\e057"; -} -.glyphicon-indent-right:before { - content: "\e058"; -} -.glyphicon-facetime-video:before { - content: "\e059"; -} -.glyphicon-picture:before { - content: "\e060"; -} -.glyphicon-map-marker:before { - content: "\e062"; -} -.glyphicon-adjust:before { - content: "\e063"; -} -.glyphicon-tint:before { - content: "\e064"; -} -.glyphicon-edit:before { - content: "\e065"; -} -.glyphicon-share:before { - content: "\e066"; -} -.glyphicon-check:before { - content: "\e067"; -} -.glyphicon-move:before { - content: "\e068"; -} -.glyphicon-step-backward:before { - content: "\e069"; -} -.glyphicon-fast-backward:before { - content: "\e070"; -} -.glyphicon-backward:before { - content: "\e071"; -} -.glyphicon-play:before { - content: "\e072"; -} -.glyphicon-pause:before { - content: "\e073"; -} -.glyphicon-stop:before { - content: "\e074"; -} -.glyphicon-forward:before { - content: "\e075"; -} -.glyphicon-fast-forward:before { - content: "\e076"; -} -.glyphicon-step-forward:before { - content: "\e077"; -} -.glyphicon-eject:before { - content: "\e078"; -} -.glyphicon-chevron-left:before { - content: "\e079"; -} -.glyphicon-chevron-right:before { - content: "\e080"; -} -.glyphicon-plus-sign:before { - content: "\e081"; -} -.glyphicon-minus-sign:before { - content: "\e082"; -} -.glyphicon-remove-sign:before { - content: "\e083"; -} -.glyphicon-ok-sign:before { - content: "\e084"; -} -.glyphicon-question-sign:before { - content: "\e085"; -} -.glyphicon-info-sign:before { - content: "\e086"; -} -.glyphicon-screenshot:before { - content: "\e087"; -} -.glyphicon-remove-circle:before { - content: "\e088"; -} -.glyphicon-ok-circle:before { - content: "\e089"; -} -.glyphicon-ban-circle:before { - content: "\e090"; -} -.glyphicon-arrow-left:before { - content: "\e091"; -} -.glyphicon-arrow-right:before { - content: "\e092"; -} -.glyphicon-arrow-up:before { - content: "\e093"; -} -.glyphicon-arrow-down:before { - content: "\e094"; -} -.glyphicon-share-alt:before { - content: "\e095"; -} -.glyphicon-resize-full:before { - content: "\e096"; -} -.glyphicon-resize-small:before { - content: "\e097"; -} -.glyphicon-exclamation-sign:before { - content: "\e101"; -} -.glyphicon-gift:before { - content: "\e102"; -} -.glyphicon-leaf:before { - content: "\e103"; -} -.glyphicon-fire:before { - content: "\e104"; -} -.glyphicon-eye-open:before { - content: "\e105"; -} -.glyphicon-eye-close:before { - content: "\e106"; -} -.glyphicon-warning-sign:before { - content: "\e107"; -} -.glyphicon-plane:before { - content: "\e108"; -} -.glyphicon-calendar:before { - content: "\e109"; -} -.glyphicon-random:before { - content: "\e110"; -} -.glyphicon-comment:before { - content: "\e111"; -} -.glyphicon-magnet:before { - content: "\e112"; -} -.glyphicon-chevron-up:before { - content: "\e113"; -} -.glyphicon-chevron-down:before { - content: "\e114"; -} -.glyphicon-retweet:before { - content: "\e115"; -} -.glyphicon-shopping-cart:before { - content: "\e116"; -} -.glyphicon-folder-close:before { - content: "\e117"; -} -.glyphicon-folder-open:before { - content: "\e118"; -} -.glyphicon-resize-vertical:before { - content: "\e119"; -} -.glyphicon-resize-horizontal:before { - content: "\e120"; -} -.glyphicon-hdd:before { - content: "\e121"; -} -.glyphicon-bullhorn:before { - content: "\e122"; -} -.glyphicon-bell:before { - content: "\e123"; -} -.glyphicon-certificate:before { - content: "\e124"; -} -.glyphicon-thumbs-up:before { - content: "\e125"; -} -.glyphicon-thumbs-down:before { - content: "\e126"; -} -.glyphicon-hand-right:before { - content: "\e127"; -} -.glyphicon-hand-left:before { - content: "\e128"; -} -.glyphicon-hand-up:before { - content: "\e129"; -} -.glyphicon-hand-down:before { - content: "\e130"; -} -.glyphicon-circle-arrow-right:before { - content: "\e131"; -} -.glyphicon-circle-arrow-left:before { - content: "\e132"; -} -.glyphicon-circle-arrow-up:before { - content: "\e133"; -} -.glyphicon-circle-arrow-down:before { - content: "\e134"; -} -.glyphicon-globe:before { - content: "\e135"; -} -.glyphicon-wrench:before { - content: "\e136"; -} -.glyphicon-tasks:before { - content: "\e137"; -} -.glyphicon-filter:before { - content: "\e138"; -} -.glyphicon-briefcase:before { - content: "\e139"; -} -.glyphicon-fullscreen:before { - content: "\e140"; -} -.glyphicon-dashboard:before { - content: "\e141"; -} -.glyphicon-paperclip:before { - content: "\e142"; -} -.glyphicon-heart-empty:before { - content: "\e143"; -} -.glyphicon-link:before { - content: "\e144"; -} -.glyphicon-phone:before { - content: "\e145"; -} -.glyphicon-pushpin:before { - content: "\e146"; -} -.glyphicon-usd:before { - content: "\e148"; -} -.glyphicon-gbp:before { - content: "\e149"; -} -.glyphicon-sort:before { - content: "\e150"; -} -.glyphicon-sort-by-alphabet:before { - content: "\e151"; -} -.glyphicon-sort-by-alphabet-alt:before { - content: "\e152"; -} -.glyphicon-sort-by-order:before { - content: "\e153"; -} -.glyphicon-sort-by-order-alt:before { - content: "\e154"; -} -.glyphicon-sort-by-attributes:before { - content: "\e155"; -} -.glyphicon-sort-by-attributes-alt:before { - content: "\e156"; -} -.glyphicon-unchecked:before { - content: "\e157"; -} -.glyphicon-expand:before { - content: "\e158"; -} -.glyphicon-collapse-down:before { - content: "\e159"; -} -.glyphicon-collapse-up:before { - content: "\e160"; -} -.glyphicon-log-in:before { - content: "\e161"; -} -.glyphicon-flash:before { - content: "\e162"; -} -.glyphicon-log-out:before { - content: "\e163"; -} -.glyphicon-new-window:before { - content: "\e164"; -} -.glyphicon-record:before { - content: "\e165"; -} -.glyphicon-save:before { - content: "\e166"; -} -.glyphicon-open:before { - content: "\e167"; -} -.glyphicon-saved:before { - content: "\e168"; -} -.glyphicon-import:before { - content: "\e169"; -} -.glyphicon-export:before { - content: "\e170"; -} -.glyphicon-send:before { - content: "\e171"; -} -.glyphicon-floppy-disk:before { - content: "\e172"; -} -.glyphicon-floppy-saved:before { - content: "\e173"; -} -.glyphicon-floppy-remove:before { - content: "\e174"; -} -.glyphicon-floppy-save:before { - content: "\e175"; -} -.glyphicon-floppy-open:before { - content: "\e176"; -} -.glyphicon-credit-card:before { - content: "\e177"; -} -.glyphicon-transfer:before { - content: "\e178"; -} -.glyphicon-cutlery:before { - content: "\e179"; -} -.glyphicon-header:before { - content: "\e180"; -} -.glyphicon-compressed:before { - content: "\e181"; -} -.glyphicon-earphone:before { - content: "\e182"; -} -.glyphicon-phone-alt:before { - content: "\e183"; -} -.glyphicon-tower:before { - content: "\e184"; -} -.glyphicon-stats:before { - content: "\e185"; -} -.glyphicon-sd-video:before { - content: "\e186"; -} -.glyphicon-hd-video:before { - content: "\e187"; -} -.glyphicon-subtitles:before { - content: "\e188"; -} -.glyphicon-sound-stereo:before { - content: "\e189"; -} -.glyphicon-sound-dolby:before { - content: "\e190"; -} -.glyphicon-sound-5-1:before { - content: "\e191"; -} -.glyphicon-sound-6-1:before { - content: "\e192"; -} -.glyphicon-sound-7-1:before { - content: "\e193"; -} -.glyphicon-copyright-mark:before { - content: "\e194"; -} -.glyphicon-registration-mark:before { - content: "\e195"; -} -.glyphicon-cloud-download:before { - content: "\e197"; -} -.glyphicon-cloud-upload:before { - content: "\e198"; -} -.glyphicon-tree-conifer:before { - content: "\e199"; -} -.glyphicon-tree-deciduous:before { - content: "\e200"; -} -.glyphicon-cd:before { - content: "\e201"; -} -.glyphicon-save-file:before { - content: "\e202"; -} -.glyphicon-open-file:before { - content: "\e203"; -} -.glyphicon-level-up:before { - content: "\e204"; -} -.glyphicon-copy:before { - content: "\e205"; -} -.glyphicon-paste:before { - content: "\e206"; -} -.glyphicon-alert:before { - content: "\e209"; -} -.glyphicon-equalizer:before { - content: "\e210"; -} -.glyphicon-king:before { - content: "\e211"; -} -.glyphicon-queen:before { - content: "\e212"; -} -.glyphicon-pawn:before { - content: "\e213"; -} -.glyphicon-bishop:before { - content: "\e214"; -} -.glyphicon-knight:before { - content: "\e215"; -} -.glyphicon-baby-formula:before { - content: "\e216"; -} -.glyphicon-tent:before { - content: "\26fa"; -} -.glyphicon-blackboard:before { - content: "\e218"; -} -.glyphicon-bed:before { - content: "\e219"; -} -.glyphicon-apple:before { - content: "\f8ff"; -} -.glyphicon-erase:before { - content: "\e221"; -} -.glyphicon-hourglass:before { - content: "\231b"; -} -.glyphicon-lamp:before { - content: "\e223"; -} -.glyphicon-duplicate:before { - content: "\e224"; -} -.glyphicon-piggy-bank:before { - content: "\e225"; -} -.glyphicon-scissors:before { - content: "\e226"; -} -.glyphicon-bitcoin:before { - content: "\e227"; -} -.glyphicon-btc:before { - content: "\e227"; -} -.glyphicon-xbt:before { - content: "\e227"; -} -.glyphicon-yen:before { - content: "\00a5"; -} -.glyphicon-jpy:before { - content: "\00a5"; -} -.glyphicon-ruble:before { - content: "\20bd"; -} -.glyphicon-rub:before { - content: "\20bd"; -} -.glyphicon-scale:before { - content: "\e230"; -} -.glyphicon-ice-lolly:before { - content: "\e231"; -} -.glyphicon-ice-lolly-tasted:before { - content: "\e232"; -} -.glyphicon-education:before { - content: "\e233"; -} -.glyphicon-option-horizontal:before { - content: "\e234"; -} -.glyphicon-option-vertical:before { - content: "\e235"; -} -.glyphicon-menu-hamburger:before { - content: "\e236"; -} -.glyphicon-modal-window:before { - content: "\e237"; -} -.glyphicon-oil:before { - content: "\e238"; -} -.glyphicon-grain:before { - content: "\e239"; -} -.glyphicon-sunglasses:before { - content: "\e240"; -} -.glyphicon-text-size:before { - content: "\e241"; -} -.glyphicon-text-color:before { - content: "\e242"; -} -.glyphicon-text-background:before { - content: "\e243"; -} -.glyphicon-object-align-top:before { - content: "\e244"; -} -.glyphicon-object-align-bottom:before { - content: "\e245"; -} -.glyphicon-object-align-horizontal:before { - content: "\e246"; -} -.glyphicon-object-align-left:before { - content: "\e247"; -} -.glyphicon-object-align-vertical:before { - content: "\e248"; -} -.glyphicon-object-align-right:before { - content: "\e249"; -} -.glyphicon-triangle-right:before { - content: "\e250"; -} -.glyphicon-triangle-left:before { - content: "\e251"; -} -.glyphicon-triangle-bottom:before { - content: "\e252"; -} -.glyphicon-triangle-top:before { - content: "\e253"; -} -.glyphicon-console:before { - content: "\e254"; -} -.glyphicon-superscript:before { - content: "\e255"; -} -.glyphicon-subscript:before { - content: "\e256"; -} -.glyphicon-menu-left:before { - content: "\e257"; -} -.glyphicon-menu-right:before { - content: "\e258"; -} -.glyphicon-menu-down:before { - content: "\e259"; -} -.glyphicon-menu-up:before { - content: "\e260"; -} -* { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -*:before, -*:after { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -html { - font-size: 10px; - - -webkit-tap-highlight-color: rgba(0, 0, 0, 0); -} -body { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.42857143; - color: #333; - background-color: #fff; -} -input, -button, -select, -textarea { - font-family: inherit; - font-size: inherit; - line-height: inherit; -} -a { - color: #337ab7; - text-decoration: none; -} -a:hover, -a:focus { - color: #23527c; - text-decoration: underline; -} -a:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -figure { - margin: 0; -} -img { - vertical-align: middle; -} -.img-responsive, -.thumbnail > img, -.thumbnail a > img, -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - display: block; - max-width: 100%; - height: auto; -} -.img-rounded { - border-radius: 6px; -} -.img-thumbnail { - display: inline-block; - max-width: 100%; - height: auto; - padding: 4px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: all .2s ease-in-out; - -o-transition: all .2s ease-in-out; - transition: all .2s ease-in-out; -} -.img-circle { - border-radius: 50%; -} -hr { - margin-top: 20px; - margin-bottom: 20px; - border: 0; - border-top: 1px solid #eee; -} -.sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip: rect(0, 0, 0, 0); - border: 0; -} -.sr-only-focusable:active, -.sr-only-focusable:focus { - position: static; - width: auto; - height: auto; - margin: 0; - overflow: visible; - clip: auto; -} -[role="button"] { - cursor: pointer; -} -h1, -h2, -h3, -h4, -h5, -h6, -.h1, -.h2, -.h3, -.h4, -.h5, -.h6 { - font-family: inherit; - font-weight: 500; - line-height: 1.1; - color: inherit; -} -h1 small, -h2 small, -h3 small, -h4 small, -h5 small, -h6 small, -.h1 small, -.h2 small, -.h3 small, -.h4 small, -.h5 small, -.h6 small, -h1 .small, -h2 .small, -h3 .small, -h4 .small, -h5 .small, -h6 .small, -.h1 .small, -.h2 .small, -.h3 .small, -.h4 .small, -.h5 .small, -.h6 .small { - font-weight: normal; - line-height: 1; - color: #777; -} -h1, -.h1, -h2, -.h2, -h3, -.h3 { - margin-top: 20px; - margin-bottom: 10px; -} -h1 small, -.h1 small, -h2 small, -.h2 small, -h3 small, -.h3 small, -h1 .small, -.h1 .small, -h2 .small, -.h2 .small, -h3 .small, -.h3 .small { - font-size: 65%; -} -h4, -.h4, -h5, -.h5, -h6, -.h6 { - margin-top: 10px; - margin-bottom: 10px; -} -h4 small, -.h4 small, -h5 small, -.h5 small, -h6 small, -.h6 small, -h4 .small, -.h4 .small, -h5 .small, -.h5 .small, -h6 .small, -.h6 .small { - font-size: 75%; -} -h1, -.h1 { - font-size: 36px; -} -h2, -.h2 { - font-size: 30px; -} -h3, -.h3 { - font-size: 24px; -} -h4, -.h4 { - font-size: 18px; -} -h5, -.h5 { - font-size: 14px; -} -h6, -.h6 { - font-size: 12px; -} -p { - margin: 0 0 10px; -} -.lead { - margin-bottom: 20px; - font-size: 16px; - font-weight: 300; - line-height: 1.4; -} -@media (min-width: 768px) { - .lead { - font-size: 21px; - } -} -small, -.small { - font-size: 85%; -} -mark, -.mark { - padding: .2em; - background-color: #fcf8e3; -} -.text-left { - text-align: left; -} -.text-right { - text-align: right; -} -.text-center { - text-align: center; -} -.text-justify { - text-align: justify; -} -.text-nowrap { - white-space: nowrap; -} -.text-lowercase { - text-transform: lowercase; -} -.text-uppercase { - text-transform: uppercase; -} -.text-capitalize { - text-transform: capitalize; -} -.text-muted { - color: #777; -} -.text-primary { - color: #337ab7; -} -a.text-primary:hover, -a.text-primary:focus { - color: #286090; -} -.text-success { - color: #3c763d; -} -a.text-success:hover, -a.text-success:focus { - color: #2b542c; -} -.text-info { - color: #31708f; -} -a.text-info:hover, -a.text-info:focus { - color: #245269; -} -.text-warning { - color: #8a6d3b; -} -a.text-warning:hover, -a.text-warning:focus { - color: #66512c; -} -.text-danger { - color: #a94442; -} -a.text-danger:hover, -a.text-danger:focus { - color: #843534; -} -.bg-primary { - color: #fff; - background-color: #337ab7; -} -a.bg-primary:hover, -a.bg-primary:focus { - background-color: #286090; -} -.bg-success { - background-color: #dff0d8; -} -a.bg-success:hover, -a.bg-success:focus { - background-color: #c1e2b3; -} -.bg-info { - background-color: #d9edf7; -} -a.bg-info:hover, -a.bg-info:focus { - background-color: #afd9ee; -} -.bg-warning { - background-color: #fcf8e3; -} -a.bg-warning:hover, -a.bg-warning:focus { - background-color: #f7ecb5; -} -.bg-danger { - background-color: #f2dede; -} -a.bg-danger:hover, -a.bg-danger:focus { - background-color: #e4b9b9; -} -.page-header { - padding-bottom: 9px; - margin: 40px 0 20px; - border-bottom: 1px solid #eee; -} -ul, -ol { - margin-top: 0; - margin-bottom: 10px; -} -ul ul, -ol ul, -ul ol, -ol ol { - margin-bottom: 0; -} -.list-unstyled { - padding-left: 0; - list-style: none; -} -.list-inline { - padding-left: 0; - margin-left: -5px; - list-style: none; -} -.list-inline > li { - display: inline-block; - padding-right: 5px; - padding-left: 5px; -} -dl { - margin-top: 0; - margin-bottom: 20px; -} -dt, -dd { - line-height: 1.42857143; -} -dt { - font-weight: bold; -} -dd { - margin-left: 0; -} -@media (min-width: 768px) { - .dl-horizontal dt { - float: left; - width: 160px; - overflow: hidden; - clear: left; - text-align: right; - text-overflow: ellipsis; - white-space: nowrap; - } - .dl-horizontal dd { - margin-left: 180px; - } -} -abbr[title], -abbr[data-original-title] { - cursor: help; - border-bottom: 1px dotted #777; -} -.initialism { - font-size: 90%; - text-transform: uppercase; -} -blockquote { - padding: 10px 20px; - margin: 0 0 20px; - font-size: 17.5px; - border-left: 5px solid #eee; -} -blockquote p:last-child, -blockquote ul:last-child, -blockquote ol:last-child { - margin-bottom: 0; -} -blockquote footer, -blockquote small, -blockquote .small { - display: block; - font-size: 80%; - line-height: 1.42857143; - color: #777; -} -blockquote footer:before, -blockquote small:before, -blockquote .small:before { - content: '\2014 \00A0'; -} -.blockquote-reverse, -blockquote.pull-right { - padding-right: 15px; - padding-left: 0; - text-align: right; - border-right: 5px solid #eee; - border-left: 0; -} -.blockquote-reverse footer:before, -blockquote.pull-right footer:before, -.blockquote-reverse small:before, -blockquote.pull-right small:before, -.blockquote-reverse .small:before, -blockquote.pull-right .small:before { - content: ''; -} -.blockquote-reverse footer:after, -blockquote.pull-right footer:after, -.blockquote-reverse small:after, -blockquote.pull-right small:after, -.blockquote-reverse .small:after, -blockquote.pull-right .small:after { - content: '\00A0 \2014'; -} -address { - margin-bottom: 20px; - font-style: normal; - line-height: 1.42857143; -} -code, -kbd, -pre, -samp { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; -} -code { - padding: 2px 4px; - font-size: 90%; - color: #c7254e; - background-color: #f9f2f4; - border-radius: 4px; -} -kbd { - padding: 2px 4px; - font-size: 90%; - color: #fff; - background-color: #333; - border-radius: 3px; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25); -} -kbd kbd { - padding: 0; - font-size: 100%; - font-weight: bold; - -webkit-box-shadow: none; - box-shadow: none; -} -pre { - display: block; - padding: 9.5px; - margin: 0 0 10px; - font-size: 13px; - line-height: 1.42857143; - color: #333; - word-break: break-all; - word-wrap: break-word; - background-color: #f5f5f5; - border: 1px solid #ccc; - border-radius: 4px; -} -pre code { - padding: 0; - font-size: inherit; - color: inherit; - white-space: pre-wrap; - background-color: transparent; - border-radius: 0; -} -.pre-scrollable { - max-height: 340px; - overflow-y: scroll; -} -.container { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -@media (min-width: 768px) { - .container { - width: 750px; - } -} -@media (min-width: 992px) { - .container { - width: 970px; - } -} -@media (min-width: 1200px) { - .container { - width: 1170px; - } -} -.container-fluid { - padding-right: 15px; - padding-left: 15px; - margin-right: auto; - margin-left: auto; -} -.row { - margin-right: -15px; - margin-left: -15px; -} -.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { - position: relative; - min-height: 1px; - padding-right: 15px; - padding-left: 15px; -} -.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { - float: left; -} -.col-xs-12 { - width: 100%; -} -.col-xs-11 { - width: 91.66666667%; -} -.col-xs-10 { - width: 83.33333333%; -} -.col-xs-9 { - width: 75%; -} -.col-xs-8 { - width: 66.66666667%; -} -.col-xs-7 { - width: 58.33333333%; -} -.col-xs-6 { - width: 50%; -} -.col-xs-5 { - width: 41.66666667%; -} -.col-xs-4 { - width: 33.33333333%; -} -.col-xs-3 { - width: 25%; -} -.col-xs-2 { - width: 16.66666667%; -} -.col-xs-1 { - width: 8.33333333%; -} -.col-xs-pull-12 { - right: 100%; -} -.col-xs-pull-11 { - right: 91.66666667%; -} -.col-xs-pull-10 { - right: 83.33333333%; -} -.col-xs-pull-9 { - right: 75%; -} -.col-xs-pull-8 { - right: 66.66666667%; -} -.col-xs-pull-7 { - right: 58.33333333%; -} -.col-xs-pull-6 { - right: 50%; -} -.col-xs-pull-5 { - right: 41.66666667%; -} -.col-xs-pull-4 { - right: 33.33333333%; -} -.col-xs-pull-3 { - right: 25%; -} -.col-xs-pull-2 { - right: 16.66666667%; -} -.col-xs-pull-1 { - right: 8.33333333%; -} -.col-xs-pull-0 { - right: auto; -} -.col-xs-push-12 { - left: 100%; -} -.col-xs-push-11 { - left: 91.66666667%; -} -.col-xs-push-10 { - left: 83.33333333%; -} -.col-xs-push-9 { - left: 75%; -} -.col-xs-push-8 { - left: 66.66666667%; -} -.col-xs-push-7 { - left: 58.33333333%; -} -.col-xs-push-6 { - left: 50%; -} -.col-xs-push-5 { - left: 41.66666667%; -} -.col-xs-push-4 { - left: 33.33333333%; -} -.col-xs-push-3 { - left: 25%; -} -.col-xs-push-2 { - left: 16.66666667%; -} -.col-xs-push-1 { - left: 8.33333333%; -} -.col-xs-push-0 { - left: auto; -} -.col-xs-offset-12 { - margin-left: 100%; -} -.col-xs-offset-11 { - margin-left: 91.66666667%; -} -.col-xs-offset-10 { - margin-left: 83.33333333%; -} -.col-xs-offset-9 { - margin-left: 75%; -} -.col-xs-offset-8 { - margin-left: 66.66666667%; -} -.col-xs-offset-7 { - margin-left: 58.33333333%; -} -.col-xs-offset-6 { - margin-left: 50%; -} -.col-xs-offset-5 { - margin-left: 41.66666667%; -} -.col-xs-offset-4 { - margin-left: 33.33333333%; -} -.col-xs-offset-3 { - margin-left: 25%; -} -.col-xs-offset-2 { - margin-left: 16.66666667%; -} -.col-xs-offset-1 { - margin-left: 8.33333333%; -} -.col-xs-offset-0 { - margin-left: 0; -} -@media (min-width: 768px) { - .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { - float: left; - } - .col-sm-12 { - width: 100%; - } - .col-sm-11 { - width: 91.66666667%; - } - .col-sm-10 { - width: 83.33333333%; - } - .col-sm-9 { - width: 75%; - } - .col-sm-8 { - width: 66.66666667%; - } - .col-sm-7 { - width: 58.33333333%; - } - .col-sm-6 { - width: 50%; - } - .col-sm-5 { - width: 41.66666667%; - } - .col-sm-4 { - width: 33.33333333%; - } - .col-sm-3 { - width: 25%; - } - .col-sm-2 { - width: 16.66666667%; - } - .col-sm-1 { - width: 8.33333333%; - } - .col-sm-pull-12 { - right: 100%; - } - .col-sm-pull-11 { - right: 91.66666667%; - } - .col-sm-pull-10 { - right: 83.33333333%; - } - .col-sm-pull-9 { - right: 75%; - } - .col-sm-pull-8 { - right: 66.66666667%; - } - .col-sm-pull-7 { - right: 58.33333333%; - } - .col-sm-pull-6 { - right: 50%; - } - .col-sm-pull-5 { - right: 41.66666667%; - } - .col-sm-pull-4 { - right: 33.33333333%; - } - .col-sm-pull-3 { - right: 25%; - } - .col-sm-pull-2 { - right: 16.66666667%; - } - .col-sm-pull-1 { - right: 8.33333333%; - } - .col-sm-pull-0 { - right: auto; - } - .col-sm-push-12 { - left: 100%; - } - .col-sm-push-11 { - left: 91.66666667%; - } - .col-sm-push-10 { - left: 83.33333333%; - } - .col-sm-push-9 { - left: 75%; - } - .col-sm-push-8 { - left: 66.66666667%; - } - .col-sm-push-7 { - left: 58.33333333%; - } - .col-sm-push-6 { - left: 50%; - } - .col-sm-push-5 { - left: 41.66666667%; - } - .col-sm-push-4 { - left: 33.33333333%; - } - .col-sm-push-3 { - left: 25%; - } - .col-sm-push-2 { - left: 16.66666667%; - } - .col-sm-push-1 { - left: 8.33333333%; - } - .col-sm-push-0 { - left: auto; - } - .col-sm-offset-12 { - margin-left: 100%; - } - .col-sm-offset-11 { - margin-left: 91.66666667%; - } - .col-sm-offset-10 { - margin-left: 83.33333333%; - } - .col-sm-offset-9 { - margin-left: 75%; - } - .col-sm-offset-8 { - margin-left: 66.66666667%; - } - .col-sm-offset-7 { - margin-left: 58.33333333%; - } - .col-sm-offset-6 { - margin-left: 50%; - } - .col-sm-offset-5 { - margin-left: 41.66666667%; - } - .col-sm-offset-4 { - margin-left: 33.33333333%; - } - .col-sm-offset-3 { - margin-left: 25%; - } - .col-sm-offset-2 { - margin-left: 16.66666667%; - } - .col-sm-offset-1 { - margin-left: 8.33333333%; - } - .col-sm-offset-0 { - margin-left: 0; - } -} -@media (min-width: 992px) { - .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { - float: left; - } - .col-md-12 { - width: 100%; - } - .col-md-11 { - width: 91.66666667%; - } - .col-md-10 { - width: 83.33333333%; - } - .col-md-9 { - width: 75%; - } - .col-md-8 { - width: 66.66666667%; - } - .col-md-7 { - width: 58.33333333%; - } - .col-md-6 { - width: 50%; - } - .col-md-5 { - width: 41.66666667%; - } - .col-md-4 { - width: 33.33333333%; - } - .col-md-3 { - width: 25%; - } - .col-md-2 { - width: 16.66666667%; - } - .col-md-1 { - width: 8.33333333%; - } - .col-md-pull-12 { - right: 100%; - } - .col-md-pull-11 { - right: 91.66666667%; - } - .col-md-pull-10 { - right: 83.33333333%; - } - .col-md-pull-9 { - right: 75%; - } - .col-md-pull-8 { - right: 66.66666667%; - } - .col-md-pull-7 { - right: 58.33333333%; - } - .col-md-pull-6 { - right: 50%; - } - .col-md-pull-5 { - right: 41.66666667%; - } - .col-md-pull-4 { - right: 33.33333333%; - } - .col-md-pull-3 { - right: 25%; - } - .col-md-pull-2 { - right: 16.66666667%; - } - .col-md-pull-1 { - right: 8.33333333%; - } - .col-md-pull-0 { - right: auto; - } - .col-md-push-12 { - left: 100%; - } - .col-md-push-11 { - left: 91.66666667%; - } - .col-md-push-10 { - left: 83.33333333%; - } - .col-md-push-9 { - left: 75%; - } - .col-md-push-8 { - left: 66.66666667%; - } - .col-md-push-7 { - left: 58.33333333%; - } - .col-md-push-6 { - left: 50%; - } - .col-md-push-5 { - left: 41.66666667%; - } - .col-md-push-4 { - left: 33.33333333%; - } - .col-md-push-3 { - left: 25%; - } - .col-md-push-2 { - left: 16.66666667%; - } - .col-md-push-1 { - left: 8.33333333%; - } - .col-md-push-0 { - left: auto; - } - .col-md-offset-12 { - margin-left: 100%; - } - .col-md-offset-11 { - margin-left: 91.66666667%; - } - .col-md-offset-10 { - margin-left: 83.33333333%; - } - .col-md-offset-9 { - margin-left: 75%; - } - .col-md-offset-8 { - margin-left: 66.66666667%; - } - .col-md-offset-7 { - margin-left: 58.33333333%; - } - .col-md-offset-6 { - margin-left: 50%; - } - .col-md-offset-5 { - margin-left: 41.66666667%; - } - .col-md-offset-4 { - margin-left: 33.33333333%; - } - .col-md-offset-3 { - margin-left: 25%; - } - .col-md-offset-2 { - margin-left: 16.66666667%; - } - .col-md-offset-1 { - margin-left: 8.33333333%; - } - .col-md-offset-0 { - margin-left: 0; - } -} -@media (min-width: 1200px) { - .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { - float: left; - } - .col-lg-12 { - width: 100%; - } - .col-lg-11 { - width: 91.66666667%; - } - .col-lg-10 { - width: 83.33333333%; - } - .col-lg-9 { - width: 75%; - } - .col-lg-8 { - width: 66.66666667%; - } - .col-lg-7 { - width: 58.33333333%; - } - .col-lg-6 { - width: 50%; - } - .col-lg-5 { - width: 41.66666667%; - } - .col-lg-4 { - width: 33.33333333%; - } - .col-lg-3 { - width: 25%; - } - .col-lg-2 { - width: 16.66666667%; - } - .col-lg-1 { - width: 8.33333333%; - } - .col-lg-pull-12 { - right: 100%; - } - .col-lg-pull-11 { - right: 91.66666667%; - } - .col-lg-pull-10 { - right: 83.33333333%; - } - .col-lg-pull-9 { - right: 75%; - } - .col-lg-pull-8 { - right: 66.66666667%; - } - .col-lg-pull-7 { - right: 58.33333333%; - } - .col-lg-pull-6 { - right: 50%; - } - .col-lg-pull-5 { - right: 41.66666667%; - } - .col-lg-pull-4 { - right: 33.33333333%; - } - .col-lg-pull-3 { - right: 25%; - } - .col-lg-pull-2 { - right: 16.66666667%; - } - .col-lg-pull-1 { - right: 8.33333333%; - } - .col-lg-pull-0 { - right: auto; - } - .col-lg-push-12 { - left: 100%; - } - .col-lg-push-11 { - left: 91.66666667%; - } - .col-lg-push-10 { - left: 83.33333333%; - } - .col-lg-push-9 { - left: 75%; - } - .col-lg-push-8 { - left: 66.66666667%; - } - .col-lg-push-7 { - left: 58.33333333%; - } - .col-lg-push-6 { - left: 50%; - } - .col-lg-push-5 { - left: 41.66666667%; - } - .col-lg-push-4 { - left: 33.33333333%; - } - .col-lg-push-3 { - left: 25%; - } - .col-lg-push-2 { - left: 16.66666667%; - } - .col-lg-push-1 { - left: 8.33333333%; - } - .col-lg-push-0 { - left: auto; - } - .col-lg-offset-12 { - margin-left: 100%; - } - .col-lg-offset-11 { - margin-left: 91.66666667%; - } - .col-lg-offset-10 { - margin-left: 83.33333333%; - } - .col-lg-offset-9 { - margin-left: 75%; - } - .col-lg-offset-8 { - margin-left: 66.66666667%; - } - .col-lg-offset-7 { - margin-left: 58.33333333%; - } - .col-lg-offset-6 { - margin-left: 50%; - } - .col-lg-offset-5 { - margin-left: 41.66666667%; - } - .col-lg-offset-4 { - margin-left: 33.33333333%; - } - .col-lg-offset-3 { - margin-left: 25%; - } - .col-lg-offset-2 { - margin-left: 16.66666667%; - } - .col-lg-offset-1 { - margin-left: 8.33333333%; - } - .col-lg-offset-0 { - margin-left: 0; - } -} -table { - background-color: transparent; -} -caption { - padding-top: 8px; - padding-bottom: 8px; - color: #777; - text-align: left; -} -th { - text-align: left; -} -.table { - width: 100%; - max-width: 100%; - margin-bottom: 20px; -} -.table > thead > tr > th, -.table > tbody > tr > th, -.table > tfoot > tr > th, -.table > thead > tr > td, -.table > tbody > tr > td, -.table > tfoot > tr > td { - padding: 8px; - line-height: 1.42857143; - vertical-align: top; - border-top: 1px solid #ddd; -} -.table > thead > tr > th { - vertical-align: bottom; - border-bottom: 2px solid #ddd; -} -.table > caption + thead > tr:first-child > th, -.table > colgroup + thead > tr:first-child > th, -.table > thead:first-child > tr:first-child > th, -.table > caption + thead > tr:first-child > td, -.table > colgroup + thead > tr:first-child > td, -.table > thead:first-child > tr:first-child > td { - border-top: 0; -} -.table > tbody + tbody { - border-top: 2px solid #ddd; -} -.table .table { - background-color: #fff; -} -.table-condensed > thead > tr > th, -.table-condensed > tbody > tr > th, -.table-condensed > tfoot > tr > th, -.table-condensed > thead > tr > td, -.table-condensed > tbody > tr > td, -.table-condensed > tfoot > tr > td { - padding: 5px; -} -.table-bordered { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > tbody > tr > th, -.table-bordered > tfoot > tr > th, -.table-bordered > thead > tr > td, -.table-bordered > tbody > tr > td, -.table-bordered > tfoot > tr > td { - border: 1px solid #ddd; -} -.table-bordered > thead > tr > th, -.table-bordered > thead > tr > td { - border-bottom-width: 2px; -} -.table-striped > tbody > tr:nth-of-type(odd) { - background-color: #f9f9f9; -} -.table-hover > tbody > tr:hover { - background-color: #f5f5f5; -} -table col[class*="col-"] { - position: static; - display: table-column; - float: none; -} -table td[class*="col-"], -table th[class*="col-"] { - position: static; - display: table-cell; - float: none; -} -.table > thead > tr > td.active, -.table > tbody > tr > td.active, -.table > tfoot > tr > td.active, -.table > thead > tr > th.active, -.table > tbody > tr > th.active, -.table > tfoot > tr > th.active, -.table > thead > tr.active > td, -.table > tbody > tr.active > td, -.table > tfoot > tr.active > td, -.table > thead > tr.active > th, -.table > tbody > tr.active > th, -.table > tfoot > tr.active > th { - background-color: #f5f5f5; -} -.table-hover > tbody > tr > td.active:hover, -.table-hover > tbody > tr > th.active:hover, -.table-hover > tbody > tr.active:hover > td, -.table-hover > tbody > tr:hover > .active, -.table-hover > tbody > tr.active:hover > th { - background-color: #e8e8e8; -} -.table > thead > tr > td.success, -.table > tbody > tr > td.success, -.table > tfoot > tr > td.success, -.table > thead > tr > th.success, -.table > tbody > tr > th.success, -.table > tfoot > tr > th.success, -.table > thead > tr.success > td, -.table > tbody > tr.success > td, -.table > tfoot > tr.success > td, -.table > thead > tr.success > th, -.table > tbody > tr.success > th, -.table > tfoot > tr.success > th { - background-color: #dff0d8; -} -.table-hover > tbody > tr > td.success:hover, -.table-hover > tbody > tr > th.success:hover, -.table-hover > tbody > tr.success:hover > td, -.table-hover > tbody > tr:hover > .success, -.table-hover > tbody > tr.success:hover > th { - background-color: #d0e9c6; -} -.table > thead > tr > td.info, -.table > tbody > tr > td.info, -.table > tfoot > tr > td.info, -.table > thead > tr > th.info, -.table > tbody > tr > th.info, -.table > tfoot > tr > th.info, -.table > thead > tr.info > td, -.table > tbody > tr.info > td, -.table > tfoot > tr.info > td, -.table > thead > tr.info > th, -.table > tbody > tr.info > th, -.table > tfoot > tr.info > th { - background-color: #d9edf7; -} -.table-hover > tbody > tr > td.info:hover, -.table-hover > tbody > tr > th.info:hover, -.table-hover > tbody > tr.info:hover > td, -.table-hover > tbody > tr:hover > .info, -.table-hover > tbody > tr.info:hover > th { - background-color: #c4e3f3; -} -.table > thead > tr > td.warning, -.table > tbody > tr > td.warning, -.table > tfoot > tr > td.warning, -.table > thead > tr > th.warning, -.table > tbody > tr > th.warning, -.table > tfoot > tr > th.warning, -.table > thead > tr.warning > td, -.table > tbody > tr.warning > td, -.table > tfoot > tr.warning > td, -.table > thead > tr.warning > th, -.table > tbody > tr.warning > th, -.table > tfoot > tr.warning > th { - background-color: #fcf8e3; -} -.table-hover > tbody > tr > td.warning:hover, -.table-hover > tbody > tr > th.warning:hover, -.table-hover > tbody > tr.warning:hover > td, -.table-hover > tbody > tr:hover > .warning, -.table-hover > tbody > tr.warning:hover > th { - background-color: #faf2cc; -} -.table > thead > tr > td.danger, -.table > tbody > tr > td.danger, -.table > tfoot > tr > td.danger, -.table > thead > tr > th.danger, -.table > tbody > tr > th.danger, -.table > tfoot > tr > th.danger, -.table > thead > tr.danger > td, -.table > tbody > tr.danger > td, -.table > tfoot > tr.danger > td, -.table > thead > tr.danger > th, -.table > tbody > tr.danger > th, -.table > tfoot > tr.danger > th { - background-color: #f2dede; -} -.table-hover > tbody > tr > td.danger:hover, -.table-hover > tbody > tr > th.danger:hover, -.table-hover > tbody > tr.danger:hover > td, -.table-hover > tbody > tr:hover > .danger, -.table-hover > tbody > tr.danger:hover > th { - background-color: #ebcccc; -} -.table-responsive { - min-height: .01%; - overflow-x: auto; -} -@media screen and (max-width: 767px) { - .table-responsive { - width: 100%; - margin-bottom: 15px; - overflow-y: hidden; - -ms-overflow-style: -ms-autohiding-scrollbar; - border: 1px solid #ddd; - } - .table-responsive > .table { - margin-bottom: 0; - } - .table-responsive > .table > thead > tr > th, - .table-responsive > .table > tbody > tr > th, - .table-responsive > .table > tfoot > tr > th, - .table-responsive > .table > thead > tr > td, - .table-responsive > .table > tbody > tr > td, - .table-responsive > .table > tfoot > tr > td { - white-space: nowrap; - } - .table-responsive > .table-bordered { - border: 0; - } - .table-responsive > .table-bordered > thead > tr > th:first-child, - .table-responsive > .table-bordered > tbody > tr > th:first-child, - .table-responsive > .table-bordered > tfoot > tr > th:first-child, - .table-responsive > .table-bordered > thead > tr > td:first-child, - .table-responsive > .table-bordered > tbody > tr > td:first-child, - .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; - } - .table-responsive > .table-bordered > thead > tr > th:last-child, - .table-responsive > .table-bordered > tbody > tr > th:last-child, - .table-responsive > .table-bordered > tfoot > tr > th:last-child, - .table-responsive > .table-bordered > thead > tr > td:last-child, - .table-responsive > .table-bordered > tbody > tr > td:last-child, - .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; - } - .table-responsive > .table-bordered > tbody > tr:last-child > th, - .table-responsive > .table-bordered > tfoot > tr:last-child > th, - .table-responsive > .table-bordered > tbody > tr:last-child > td, - .table-responsive > .table-bordered > tfoot > tr:last-child > td { - border-bottom: 0; - } -} -fieldset { - min-width: 0; - padding: 0; - margin: 0; - border: 0; -} -legend { - display: block; - width: 100%; - padding: 0; - margin-bottom: 20px; - font-size: 21px; - line-height: inherit; - color: #333; - border: 0; - border-bottom: 1px solid #e5e5e5; -} -label { - display: inline-block; - max-width: 100%; - margin-bottom: 5px; - font-weight: bold; -} -input[type="search"] { - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; -} -input[type="radio"], -input[type="checkbox"] { - margin: 4px 0 0; - margin-top: 1px \9; - line-height: normal; -} -input[type="file"] { - display: block; -} -input[type="range"] { - display: block; - width: 100%; -} -select[multiple], -select[size] { - height: auto; -} -input[type="file"]:focus, -input[type="radio"]:focus, -input[type="checkbox"]:focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -output { - display: block; - padding-top: 7px; - font-size: 14px; - line-height: 1.42857143; - color: #555; -} -.form-control { - display: block; - width: 100%; - height: 34px; - padding: 6px 12px; - font-size: 14px; - line-height: 1.42857143; - color: #555; - background-color: #fff; - background-image: none; - border: 1px solid #ccc; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s; - -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; - transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; -} -.form-control:focus { - border-color: #66afe9; - outline: 0; - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); - box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6); -} -.form-control::-moz-placeholder { - color: #999; - opacity: 1; -} -.form-control:-ms-input-placeholder { - color: #999; -} -.form-control::-webkit-input-placeholder { - color: #999; -} -.form-control::-ms-expand { - background-color: transparent; - border: 0; -} -.form-control[disabled], -.form-control[readonly], -fieldset[disabled] .form-control { - background-color: #eee; - opacity: 1; -} -.form-control[disabled], -fieldset[disabled] .form-control { - cursor: not-allowed; -} -textarea.form-control { - height: auto; -} -input[type="search"] { - -webkit-appearance: none; -} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - input[type="date"].form-control, - input[type="time"].form-control, - input[type="datetime-local"].form-control, - input[type="month"].form-control { - line-height: 34px; - } - input[type="date"].input-sm, - input[type="time"].input-sm, - input[type="datetime-local"].input-sm, - input[type="month"].input-sm, - .input-group-sm input[type="date"], - .input-group-sm input[type="time"], - .input-group-sm input[type="datetime-local"], - .input-group-sm input[type="month"] { - line-height: 30px; - } - input[type="date"].input-lg, - input[type="time"].input-lg, - input[type="datetime-local"].input-lg, - input[type="month"].input-lg, - .input-group-lg input[type="date"], - .input-group-lg input[type="time"], - .input-group-lg input[type="datetime-local"], - .input-group-lg input[type="month"] { - line-height: 46px; - } -} -.form-group { - margin-bottom: 15px; -} -.radio, -.checkbox { - position: relative; - display: block; - margin-top: 10px; - margin-bottom: 10px; -} -.radio label, -.checkbox label { - min-height: 20px; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - cursor: pointer; -} -.radio input[type="radio"], -.radio-inline input[type="radio"], -.checkbox input[type="checkbox"], -.checkbox-inline input[type="checkbox"] { - position: absolute; - margin-top: 4px \9; - margin-left: -20px; -} -.radio + .radio, -.checkbox + .checkbox { - margin-top: -5px; -} -.radio-inline, -.checkbox-inline { - position: relative; - display: inline-block; - padding-left: 20px; - margin-bottom: 0; - font-weight: normal; - vertical-align: middle; - cursor: pointer; -} -.radio-inline + .radio-inline, -.checkbox-inline + .checkbox-inline { - margin-top: 0; - margin-left: 10px; -} -input[type="radio"][disabled], -input[type="checkbox"][disabled], -input[type="radio"].disabled, -input[type="checkbox"].disabled, -fieldset[disabled] input[type="radio"], -fieldset[disabled] input[type="checkbox"] { - cursor: not-allowed; -} -.radio-inline.disabled, -.checkbox-inline.disabled, -fieldset[disabled] .radio-inline, -fieldset[disabled] .checkbox-inline { - cursor: not-allowed; -} -.radio.disabled label, -.checkbox.disabled label, -fieldset[disabled] .radio label, -fieldset[disabled] .checkbox label { - cursor: not-allowed; -} -.form-control-static { - min-height: 34px; - padding-top: 7px; - padding-bottom: 7px; - margin-bottom: 0; -} -.form-control-static.input-lg, -.form-control-static.input-sm { - padding-right: 0; - padding-left: 0; -} -.input-sm { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-sm { - height: 30px; - line-height: 30px; -} -textarea.input-sm, -select[multiple].input-sm { - height: auto; -} -.form-group-sm .form-control { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.form-group-sm select.form-control { - height: 30px; - line-height: 30px; -} -.form-group-sm textarea.form-control, -.form-group-sm select[multiple].form-control { - height: auto; -} -.form-group-sm .form-control-static { - height: 30px; - min-height: 32px; - padding: 6px 10px; - font-size: 12px; - line-height: 1.5; -} -.input-lg { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-lg { - height: 46px; - line-height: 46px; -} -textarea.input-lg, -select[multiple].input-lg { - height: auto; -} -.form-group-lg .form-control { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.form-group-lg select.form-control { - height: 46px; - line-height: 46px; -} -.form-group-lg textarea.form-control, -.form-group-lg select[multiple].form-control { - height: auto; -} -.form-group-lg .form-control-static { - height: 46px; - min-height: 38px; - padding: 11px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.has-feedback { - position: relative; -} -.has-feedback .form-control { - padding-right: 42.5px; -} -.form-control-feedback { - position: absolute; - top: 0; - right: 0; - z-index: 2; - display: block; - width: 34px; - height: 34px; - line-height: 34px; - text-align: center; - pointer-events: none; -} -.input-lg + .form-control-feedback, -.input-group-lg + .form-control-feedback, -.form-group-lg .form-control + .form-control-feedback { - width: 46px; - height: 46px; - line-height: 46px; -} -.input-sm + .form-control-feedback, -.input-group-sm + .form-control-feedback, -.form-group-sm .form-control + .form-control-feedback { - width: 30px; - height: 30px; - line-height: 30px; -} -.has-success .help-block, -.has-success .control-label, -.has-success .radio, -.has-success .checkbox, -.has-success .radio-inline, -.has-success .checkbox-inline, -.has-success.radio label, -.has-success.checkbox label, -.has-success.radio-inline label, -.has-success.checkbox-inline label { - color: #3c763d; -} -.has-success .form-control { - border-color: #3c763d; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-success .form-control:focus { - border-color: #2b542c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #67b168; -} -.has-success .input-group-addon { - color: #3c763d; - background-color: #dff0d8; - border-color: #3c763d; -} -.has-success .form-control-feedback { - color: #3c763d; -} -.has-warning .help-block, -.has-warning .control-label, -.has-warning .radio, -.has-warning .checkbox, -.has-warning .radio-inline, -.has-warning .checkbox-inline, -.has-warning.radio label, -.has-warning.checkbox label, -.has-warning.radio-inline label, -.has-warning.checkbox-inline label { - color: #8a6d3b; -} -.has-warning .form-control { - border-color: #8a6d3b; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-warning .form-control:focus { - border-color: #66512c; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #c0a16b; -} -.has-warning .input-group-addon { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #8a6d3b; -} -.has-warning .form-control-feedback { - color: #8a6d3b; -} -.has-error .help-block, -.has-error .control-label, -.has-error .radio, -.has-error .checkbox, -.has-error .radio-inline, -.has-error .checkbox-inline, -.has-error.radio label, -.has-error.checkbox label, -.has-error.radio-inline label, -.has-error.checkbox-inline label { - color: #a94442; -} -.has-error .form-control { - border-color: #a94442; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075); -} -.has-error .form-control:focus { - border-color: #843534; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 6px #ce8483; -} -.has-error .input-group-addon { - color: #a94442; - background-color: #f2dede; - border-color: #a94442; -} -.has-error .form-control-feedback { - color: #a94442; -} -.has-feedback label ~ .form-control-feedback { - top: 25px; -} -.has-feedback label.sr-only ~ .form-control-feedback { - top: 0; -} -.help-block { - display: block; - margin-top: 5px; - margin-bottom: 10px; - color: #737373; -} -@media (min-width: 768px) { - .form-inline .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .form-inline .form-control-static { - display: inline-block; - } - .form-inline .input-group { - display: inline-table; - vertical-align: middle; - } - .form-inline .input-group .input-group-addon, - .form-inline .input-group .input-group-btn, - .form-inline .input-group .form-control { - width: auto; - } - .form-inline .input-group > .form-control { - width: 100%; - } - .form-inline .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio, - .form-inline .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .form-inline .radio label, - .form-inline .checkbox label { - padding-left: 0; - } - .form-inline .radio input[type="radio"], - .form-inline .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .form-inline .has-feedback .form-control-feedback { - top: 0; - } -} -.form-horizontal .radio, -.form-horizontal .checkbox, -.form-horizontal .radio-inline, -.form-horizontal .checkbox-inline { - padding-top: 7px; - margin-top: 0; - margin-bottom: 0; -} -.form-horizontal .radio, -.form-horizontal .checkbox { - min-height: 27px; -} -.form-horizontal .form-group { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .form-horizontal .control-label { - padding-top: 7px; - margin-bottom: 0; - text-align: right; - } -} -.form-horizontal .has-feedback .form-control-feedback { - right: 15px; -} -@media (min-width: 768px) { - .form-horizontal .form-group-lg .control-label { - padding-top: 11px; - font-size: 18px; - } -} -@media (min-width: 768px) { - .form-horizontal .form-group-sm .control-label { - padding-top: 6px; - font-size: 12px; - } -} -.btn { - display: inline-block; - padding: 6px 12px; - margin-bottom: 0; - font-size: 14px; - font-weight: normal; - line-height: 1.42857143; - text-align: center; - white-space: nowrap; - vertical-align: middle; - -ms-touch-action: manipulation; - touch-action: manipulation; - cursor: pointer; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.btn:focus, -.btn:active:focus, -.btn.active:focus, -.btn.focus, -.btn:active.focus, -.btn.active.focus { - outline: 5px auto -webkit-focus-ring-color; - outline-offset: -2px; -} -.btn:hover, -.btn:focus, -.btn.focus { - color: #333; - text-decoration: none; -} -.btn:active, -.btn.active { - background-image: none; - outline: 0; - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn.disabled, -.btn[disabled], -fieldset[disabled] .btn { - cursor: not-allowed; - filter: alpha(opacity=65); - -webkit-box-shadow: none; - box-shadow: none; - opacity: .65; -} -a.btn.disabled, -fieldset[disabled] a.btn { - pointer-events: none; -} -.btn-default { - color: #333; - background-color: #fff; - border-color: #ccc; -} -.btn-default:focus, -.btn-default.focus { - color: #333; - background-color: #e6e6e6; - border-color: #8c8c8c; -} -.btn-default:hover { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - color: #333; - background-color: #e6e6e6; - border-color: #adadad; -} -.btn-default:active:hover, -.btn-default.active:hover, -.open > .dropdown-toggle.btn-default:hover, -.btn-default:active:focus, -.btn-default.active:focus, -.open > .dropdown-toggle.btn-default:focus, -.btn-default:active.focus, -.btn-default.active.focus, -.open > .dropdown-toggle.btn-default.focus { - color: #333; - background-color: #d4d4d4; - border-color: #8c8c8c; -} -.btn-default:active, -.btn-default.active, -.open > .dropdown-toggle.btn-default { - background-image: none; -} -.btn-default.disabled:hover, -.btn-default[disabled]:hover, -fieldset[disabled] .btn-default:hover, -.btn-default.disabled:focus, -.btn-default[disabled]:focus, -fieldset[disabled] .btn-default:focus, -.btn-default.disabled.focus, -.btn-default[disabled].focus, -fieldset[disabled] .btn-default.focus { - background-color: #fff; - border-color: #ccc; -} -.btn-default .badge { - color: #fff; - background-color: #333; -} -.btn-primary { - color: #fff; - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary:focus, -.btn-primary.focus { - color: #fff; - background-color: #286090; - border-color: #122b40; -} -.btn-primary:hover { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - color: #fff; - background-color: #286090; - border-color: #204d74; -} -.btn-primary:active:hover, -.btn-primary.active:hover, -.open > .dropdown-toggle.btn-primary:hover, -.btn-primary:active:focus, -.btn-primary.active:focus, -.open > .dropdown-toggle.btn-primary:focus, -.btn-primary:active.focus, -.btn-primary.active.focus, -.open > .dropdown-toggle.btn-primary.focus { - color: #fff; - background-color: #204d74; - border-color: #122b40; -} -.btn-primary:active, -.btn-primary.active, -.open > .dropdown-toggle.btn-primary { - background-image: none; -} -.btn-primary.disabled:hover, -.btn-primary[disabled]:hover, -fieldset[disabled] .btn-primary:hover, -.btn-primary.disabled:focus, -.btn-primary[disabled]:focus, -fieldset[disabled] .btn-primary:focus, -.btn-primary.disabled.focus, -.btn-primary[disabled].focus, -fieldset[disabled] .btn-primary.focus { - background-color: #337ab7; - border-color: #2e6da4; -} -.btn-primary .badge { - color: #337ab7; - background-color: #fff; -} -.btn-success { - color: #fff; - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success:focus, -.btn-success.focus { - color: #fff; - background-color: #449d44; - border-color: #255625; -} -.btn-success:hover { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - color: #fff; - background-color: #449d44; - border-color: #398439; -} -.btn-success:active:hover, -.btn-success.active:hover, -.open > .dropdown-toggle.btn-success:hover, -.btn-success:active:focus, -.btn-success.active:focus, -.open > .dropdown-toggle.btn-success:focus, -.btn-success:active.focus, -.btn-success.active.focus, -.open > .dropdown-toggle.btn-success.focus { - color: #fff; - background-color: #398439; - border-color: #255625; -} -.btn-success:active, -.btn-success.active, -.open > .dropdown-toggle.btn-success { - background-image: none; -} -.btn-success.disabled:hover, -.btn-success[disabled]:hover, -fieldset[disabled] .btn-success:hover, -.btn-success.disabled:focus, -.btn-success[disabled]:focus, -fieldset[disabled] .btn-success:focus, -.btn-success.disabled.focus, -.btn-success[disabled].focus, -fieldset[disabled] .btn-success.focus { - background-color: #5cb85c; - border-color: #4cae4c; -} -.btn-success .badge { - color: #5cb85c; - background-color: #fff; -} -.btn-info { - color: #fff; - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info:focus, -.btn-info.focus { - color: #fff; - background-color: #31b0d5; - border-color: #1b6d85; -} -.btn-info:hover { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - color: #fff; - background-color: #31b0d5; - border-color: #269abc; -} -.btn-info:active:hover, -.btn-info.active:hover, -.open > .dropdown-toggle.btn-info:hover, -.btn-info:active:focus, -.btn-info.active:focus, -.open > .dropdown-toggle.btn-info:focus, -.btn-info:active.focus, -.btn-info.active.focus, -.open > .dropdown-toggle.btn-info.focus { - color: #fff; - background-color: #269abc; - border-color: #1b6d85; -} -.btn-info:active, -.btn-info.active, -.open > .dropdown-toggle.btn-info { - background-image: none; -} -.btn-info.disabled:hover, -.btn-info[disabled]:hover, -fieldset[disabled] .btn-info:hover, -.btn-info.disabled:focus, -.btn-info[disabled]:focus, -fieldset[disabled] .btn-info:focus, -.btn-info.disabled.focus, -.btn-info[disabled].focus, -fieldset[disabled] .btn-info.focus { - background-color: #5bc0de; - border-color: #46b8da; -} -.btn-info .badge { - color: #5bc0de; - background-color: #fff; -} -.btn-warning { - color: #fff; - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning:focus, -.btn-warning.focus { - color: #fff; - background-color: #ec971f; - border-color: #985f0d; -} -.btn-warning:hover { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - color: #fff; - background-color: #ec971f; - border-color: #d58512; -} -.btn-warning:active:hover, -.btn-warning.active:hover, -.open > .dropdown-toggle.btn-warning:hover, -.btn-warning:active:focus, -.btn-warning.active:focus, -.open > .dropdown-toggle.btn-warning:focus, -.btn-warning:active.focus, -.btn-warning.active.focus, -.open > .dropdown-toggle.btn-warning.focus { - color: #fff; - background-color: #d58512; - border-color: #985f0d; -} -.btn-warning:active, -.btn-warning.active, -.open > .dropdown-toggle.btn-warning { - background-image: none; -} -.btn-warning.disabled:hover, -.btn-warning[disabled]:hover, -fieldset[disabled] .btn-warning:hover, -.btn-warning.disabled:focus, -.btn-warning[disabled]:focus, -fieldset[disabled] .btn-warning:focus, -.btn-warning.disabled.focus, -.btn-warning[disabled].focus, -fieldset[disabled] .btn-warning.focus { - background-color: #f0ad4e; - border-color: #eea236; -} -.btn-warning .badge { - color: #f0ad4e; - background-color: #fff; -} -.btn-danger { - color: #fff; - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger:focus, -.btn-danger.focus { - color: #fff; - background-color: #c9302c; - border-color: #761c19; -} -.btn-danger:hover { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - color: #fff; - background-color: #c9302c; - border-color: #ac2925; -} -.btn-danger:active:hover, -.btn-danger.active:hover, -.open > .dropdown-toggle.btn-danger:hover, -.btn-danger:active:focus, -.btn-danger.active:focus, -.open > .dropdown-toggle.btn-danger:focus, -.btn-danger:active.focus, -.btn-danger.active.focus, -.open > .dropdown-toggle.btn-danger.focus { - color: #fff; - background-color: #ac2925; - border-color: #761c19; -} -.btn-danger:active, -.btn-danger.active, -.open > .dropdown-toggle.btn-danger { - background-image: none; -} -.btn-danger.disabled:hover, -.btn-danger[disabled]:hover, -fieldset[disabled] .btn-danger:hover, -.btn-danger.disabled:focus, -.btn-danger[disabled]:focus, -fieldset[disabled] .btn-danger:focus, -.btn-danger.disabled.focus, -.btn-danger[disabled].focus, -fieldset[disabled] .btn-danger.focus { - background-color: #d9534f; - border-color: #d43f3a; -} -.btn-danger .badge { - color: #d9534f; - background-color: #fff; -} -.btn-link { - font-weight: normal; - color: #337ab7; - border-radius: 0; -} -.btn-link, -.btn-link:active, -.btn-link.active, -.btn-link[disabled], -fieldset[disabled] .btn-link { - background-color: transparent; - -webkit-box-shadow: none; - box-shadow: none; -} -.btn-link, -.btn-link:hover, -.btn-link:focus, -.btn-link:active { - border-color: transparent; -} -.btn-link:hover, -.btn-link:focus { - color: #23527c; - text-decoration: underline; - background-color: transparent; -} -.btn-link[disabled]:hover, -fieldset[disabled] .btn-link:hover, -.btn-link[disabled]:focus, -fieldset[disabled] .btn-link:focus { - color: #777; - text-decoration: none; -} -.btn-lg, -.btn-group-lg > .btn { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -.btn-sm, -.btn-group-sm > .btn { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-xs, -.btn-group-xs > .btn { - padding: 1px 5px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -.btn-block { - display: block; - width: 100%; -} -.btn-block + .btn-block { - margin-top: 5px; -} -input[type="submit"].btn-block, -input[type="reset"].btn-block, -input[type="button"].btn-block { - width: 100%; -} -.fade { - opacity: 0; - -webkit-transition: opacity .15s linear; - -o-transition: opacity .15s linear; - transition: opacity .15s linear; -} -.fade.in { - opacity: 1; -} -.collapse { - display: none; -} -.collapse.in { - display: block; -} -tr.collapse.in { - display: table-row; -} -tbody.collapse.in { - display: table-row-group; -} -.collapsing { - position: relative; - height: 0; - overflow: hidden; - -webkit-transition-timing-function: ease; - -o-transition-timing-function: ease; - transition-timing-function: ease; - -webkit-transition-duration: .35s; - -o-transition-duration: .35s; - transition-duration: .35s; - -webkit-transition-property: height, visibility; - -o-transition-property: height, visibility; - transition-property: height, visibility; -} -.caret { - display: inline-block; - width: 0; - height: 0; - margin-left: 2px; - vertical-align: middle; - border-top: 4px dashed; - border-top: 4px solid \9; - border-right: 4px solid transparent; - border-left: 4px solid transparent; -} -.dropup, -.dropdown { - position: relative; -} -.dropdown-toggle:focus { - outline: 0; -} -.dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 1000; - display: none; - float: left; - min-width: 160px; - padding: 5px 0; - margin: 2px 0 0; - font-size: 14px; - text-align: left; - list-style: none; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .15); - border-radius: 4px; - -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175); - box-shadow: 0 6px 12px rgba(0, 0, 0, .175); -} -.dropdown-menu.pull-right { - right: 0; - left: auto; -} -.dropdown-menu .divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.dropdown-menu > li > a { - display: block; - padding: 3px 20px; - clear: both; - font-weight: normal; - line-height: 1.42857143; - color: #333; - white-space: nowrap; -} -.dropdown-menu > li > a:hover, -.dropdown-menu > li > a:focus { - color: #262626; - text-decoration: none; - background-color: #f5f5f5; -} -.dropdown-menu > .active > a, -.dropdown-menu > .active > a:hover, -.dropdown-menu > .active > a:focus { - color: #fff; - text-decoration: none; - background-color: #337ab7; - outline: 0; -} -.dropdown-menu > .disabled > a, -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - color: #777; -} -.dropdown-menu > .disabled > a:hover, -.dropdown-menu > .disabled > a:focus { - text-decoration: none; - cursor: not-allowed; - background-color: transparent; - background-image: none; - filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); -} -.open > .dropdown-menu { - display: block; -} -.open > a { - outline: 0; -} -.dropdown-menu-right { - right: 0; - left: auto; -} -.dropdown-menu-left { - right: auto; - left: 0; -} -.dropdown-header { - display: block; - padding: 3px 20px; - font-size: 12px; - line-height: 1.42857143; - color: #777; - white-space: nowrap; -} -.dropdown-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 990; -} -.pull-right > .dropdown-menu { - right: 0; - left: auto; -} -.dropup .caret, -.navbar-fixed-bottom .dropdown .caret { - content: ""; - border-top: 0; - border-bottom: 4px dashed; - border-bottom: 4px solid \9; -} -.dropup .dropdown-menu, -.navbar-fixed-bottom .dropdown .dropdown-menu { - top: auto; - bottom: 100%; - margin-bottom: 2px; -} -@media (min-width: 768px) { - .navbar-right .dropdown-menu { - right: 0; - left: auto; - } - .navbar-right .dropdown-menu-left { - right: auto; - left: 0; - } -} -.btn-group, -.btn-group-vertical { - position: relative; - display: inline-block; - vertical-align: middle; -} -.btn-group > .btn, -.btn-group-vertical > .btn { - position: relative; - float: left; -} -.btn-group > .btn:hover, -.btn-group-vertical > .btn:hover, -.btn-group > .btn:focus, -.btn-group-vertical > .btn:focus, -.btn-group > .btn:active, -.btn-group-vertical > .btn:active, -.btn-group > .btn.active, -.btn-group-vertical > .btn.active { - z-index: 2; -} -.btn-group .btn + .btn, -.btn-group .btn + .btn-group, -.btn-group .btn-group + .btn, -.btn-group .btn-group + .btn-group { - margin-left: -1px; -} -.btn-toolbar { - margin-left: -5px; -} -.btn-toolbar .btn, -.btn-toolbar .btn-group, -.btn-toolbar .input-group { - float: left; -} -.btn-toolbar > .btn, -.btn-toolbar > .btn-group, -.btn-toolbar > .input-group { - margin-left: 5px; -} -.btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { - border-radius: 0; -} -.btn-group > .btn:first-child { - margin-left: 0; -} -.btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn:last-child:not(:first-child), -.btn-group > .dropdown-toggle:not(:first-child) { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group > .btn-group { - float: left; -} -.btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group .dropdown-toggle:active, -.btn-group.open .dropdown-toggle { - outline: 0; -} -.btn-group > .btn + .dropdown-toggle { - padding-right: 8px; - padding-left: 8px; -} -.btn-group > .btn-lg + .dropdown-toggle { - padding-right: 12px; - padding-left: 12px; -} -.btn-group.open .dropdown-toggle { - -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); - box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); -} -.btn-group.open .dropdown-toggle.btn-link { - -webkit-box-shadow: none; - box-shadow: none; -} -.btn .caret { - margin-left: 0; -} -.btn-lg .caret { - border-width: 5px 5px 0; - border-bottom-width: 0; -} -.dropup .btn-lg .caret { - border-width: 0 5px 5px; -} -.btn-group-vertical > .btn, -.btn-group-vertical > .btn-group, -.btn-group-vertical > .btn-group > .btn { - display: block; - float: none; - width: 100%; - max-width: 100%; -} -.btn-group-vertical > .btn-group > .btn { - float: none; -} -.btn-group-vertical > .btn + .btn, -.btn-group-vertical > .btn + .btn-group, -.btn-group-vertical > .btn-group + .btn, -.btn-group-vertical > .btn-group + .btn-group { - margin-top: -1px; - margin-left: 0; -} -.btn-group-vertical > .btn:not(:first-child):not(:last-child) { - border-radius: 0; -} -.btn-group-vertical > .btn:first-child:not(:last-child) { - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn:last-child:not(:first-child) { - border-top-left-radius: 0; - border-top-right-radius: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -.btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { - border-radius: 0; -} -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .btn:last-child, -.btn-group-vertical > .btn-group:first-child:not(:last-child) > .dropdown-toggle { - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.btn-group-vertical > .btn-group:last-child:not(:first-child) > .btn:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.btn-group-justified { - display: table; - width: 100%; - table-layout: fixed; - border-collapse: separate; -} -.btn-group-justified > .btn, -.btn-group-justified > .btn-group { - display: table-cell; - float: none; - width: 1%; -} -.btn-group-justified > .btn-group .btn { - width: 100%; -} -.btn-group-justified > .btn-group .dropdown-menu { - left: auto; -} -[data-toggle="buttons"] > .btn input[type="radio"], -[data-toggle="buttons"] > .btn-group > .btn input[type="radio"], -[data-toggle="buttons"] > .btn input[type="checkbox"], -[data-toggle="buttons"] > .btn-group > .btn input[type="checkbox"] { - position: absolute; - clip: rect(0, 0, 0, 0); - pointer-events: none; -} -.input-group { - position: relative; - display: table; - border-collapse: separate; -} -.input-group[class*="col-"] { - float: none; - padding-right: 0; - padding-left: 0; -} -.input-group .form-control { - position: relative; - z-index: 2; - float: left; - width: 100%; - margin-bottom: 0; -} -.input-group .form-control:focus { - z-index: 3; -} -.input-group-lg > .form-control, -.input-group-lg > .input-group-addon, -.input-group-lg > .input-group-btn > .btn { - height: 46px; - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; - border-radius: 6px; -} -select.input-group-lg > .form-control, -select.input-group-lg > .input-group-addon, -select.input-group-lg > .input-group-btn > .btn { - height: 46px; - line-height: 46px; -} -textarea.input-group-lg > .form-control, -textarea.input-group-lg > .input-group-addon, -textarea.input-group-lg > .input-group-btn > .btn, -select[multiple].input-group-lg > .form-control, -select[multiple].input-group-lg > .input-group-addon, -select[multiple].input-group-lg > .input-group-btn > .btn { - height: auto; -} -.input-group-sm > .form-control, -.input-group-sm > .input-group-addon, -.input-group-sm > .input-group-btn > .btn { - height: 30px; - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; - border-radius: 3px; -} -select.input-group-sm > .form-control, -select.input-group-sm > .input-group-addon, -select.input-group-sm > .input-group-btn > .btn { - height: 30px; - line-height: 30px; -} -textarea.input-group-sm > .form-control, -textarea.input-group-sm > .input-group-addon, -textarea.input-group-sm > .input-group-btn > .btn, -select[multiple].input-group-sm > .form-control, -select[multiple].input-group-sm > .input-group-addon, -select[multiple].input-group-sm > .input-group-btn > .btn { - height: auto; -} -.input-group-addon, -.input-group-btn, -.input-group .form-control { - display: table-cell; -} -.input-group-addon:not(:first-child):not(:last-child), -.input-group-btn:not(:first-child):not(:last-child), -.input-group .form-control:not(:first-child):not(:last-child) { - border-radius: 0; -} -.input-group-addon, -.input-group-btn { - width: 1%; - white-space: nowrap; - vertical-align: middle; -} -.input-group-addon { - padding: 6px 12px; - font-size: 14px; - font-weight: normal; - line-height: 1; - color: #555; - text-align: center; - background-color: #eee; - border: 1px solid #ccc; - border-radius: 4px; -} -.input-group-addon.input-sm { - padding: 5px 10px; - font-size: 12px; - border-radius: 3px; -} -.input-group-addon.input-lg { - padding: 10px 16px; - font-size: 18px; - border-radius: 6px; -} -.input-group-addon input[type="radio"], -.input-group-addon input[type="checkbox"] { - margin-top: 0; -} -.input-group .form-control:first-child, -.input-group-addon:first-child, -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group > .btn, -.input-group-btn:first-child > .dropdown-toggle, -.input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle), -.input-group-btn:last-child > .btn-group:not(:last-child) > .btn { - border-top-right-radius: 0; - border-bottom-right-radius: 0; -} -.input-group-addon:first-child { - border-right: 0; -} -.input-group .form-control:last-child, -.input-group-addon:last-child, -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group > .btn, -.input-group-btn:last-child > .dropdown-toggle, -.input-group-btn:first-child > .btn:not(:first-child), -.input-group-btn:first-child > .btn-group:not(:first-child) > .btn { - border-top-left-radius: 0; - border-bottom-left-radius: 0; -} -.input-group-addon:last-child { - border-left: 0; -} -.input-group-btn { - position: relative; - font-size: 0; - white-space: nowrap; -} -.input-group-btn > .btn { - position: relative; -} -.input-group-btn > .btn + .btn { - margin-left: -1px; -} -.input-group-btn > .btn:hover, -.input-group-btn > .btn:focus, -.input-group-btn > .btn:active { - z-index: 2; -} -.input-group-btn:first-child > .btn, -.input-group-btn:first-child > .btn-group { - margin-right: -1px; -} -.input-group-btn:last-child > .btn, -.input-group-btn:last-child > .btn-group { - z-index: 2; - margin-left: -1px; -} -.nav { - padding-left: 0; - margin-bottom: 0; - list-style: none; -} -.nav > li { - position: relative; - display: block; -} -.nav > li > a { - position: relative; - display: block; - padding: 10px 15px; -} -.nav > li > a:hover, -.nav > li > a:focus { - text-decoration: none; - background-color: #eee; -} -.nav > li.disabled > a { - color: #777; -} -.nav > li.disabled > a:hover, -.nav > li.disabled > a:focus { - color: #777; - text-decoration: none; - cursor: not-allowed; - background-color: transparent; -} -.nav .open > a, -.nav .open > a:hover, -.nav .open > a:focus { - background-color: #eee; - border-color: #337ab7; -} -.nav .nav-divider { - height: 1px; - margin: 9px 0; - overflow: hidden; - background-color: #e5e5e5; -} -.nav > li > a > img { - max-width: none; -} -.nav-tabs { - border-bottom: 1px solid #ddd; -} -.nav-tabs > li { - float: left; - margin-bottom: -1px; -} -.nav-tabs > li > a { - margin-right: 2px; - line-height: 1.42857143; - border: 1px solid transparent; - border-radius: 4px 4px 0 0; -} -.nav-tabs > li > a:hover { - border-color: #eee #eee #ddd; -} -.nav-tabs > li.active > a, -.nav-tabs > li.active > a:hover, -.nav-tabs > li.active > a:focus { - color: #555; - cursor: default; - background-color: #fff; - border: 1px solid #ddd; - border-bottom-color: transparent; -} -.nav-tabs.nav-justified { - width: 100%; - border-bottom: 0; -} -.nav-tabs.nav-justified > li { - float: none; -} -.nav-tabs.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-tabs.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-tabs.nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs.nav-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs.nav-justified > .active > a, -.nav-tabs.nav-justified > .active > a:hover, -.nav-tabs.nav-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs.nav-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs.nav-justified > .active > a, - .nav-tabs.nav-justified > .active > a:hover, - .nav-tabs.nav-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.nav-pills > li { - float: left; -} -.nav-pills > li > a { - border-radius: 4px; -} -.nav-pills > li + li { - margin-left: 2px; -} -.nav-pills > li.active > a, -.nav-pills > li.active > a:hover, -.nav-pills > li.active > a:focus { - color: #fff; - background-color: #337ab7; -} -.nav-stacked > li { - float: none; -} -.nav-stacked > li + li { - margin-top: 2px; - margin-left: 0; -} -.nav-justified { - width: 100%; -} -.nav-justified > li { - float: none; -} -.nav-justified > li > a { - margin-bottom: 5px; - text-align: center; -} -.nav-justified > .dropdown .dropdown-menu { - top: auto; - left: auto; -} -@media (min-width: 768px) { - .nav-justified > li { - display: table-cell; - width: 1%; - } - .nav-justified > li > a { - margin-bottom: 0; - } -} -.nav-tabs-justified { - border-bottom: 0; -} -.nav-tabs-justified > li > a { - margin-right: 0; - border-radius: 4px; -} -.nav-tabs-justified > .active > a, -.nav-tabs-justified > .active > a:hover, -.nav-tabs-justified > .active > a:focus { - border: 1px solid #ddd; -} -@media (min-width: 768px) { - .nav-tabs-justified > li > a { - border-bottom: 1px solid #ddd; - border-radius: 4px 4px 0 0; - } - .nav-tabs-justified > .active > a, - .nav-tabs-justified > .active > a:hover, - .nav-tabs-justified > .active > a:focus { - border-bottom-color: #fff; - } -} -.tab-content > .tab-pane { - display: none; -} -.tab-content > .active { - display: block; -} -.nav-tabs .dropdown-menu { - margin-top: -1px; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar { - position: relative; - min-height: 50px; - margin-bottom: 20px; - border: 1px solid transparent; -} -@media (min-width: 768px) { - .navbar { - border-radius: 4px; - } -} -@media (min-width: 768px) { - .navbar-header { - float: left; - } -} -.navbar-collapse { - padding-right: 15px; - padding-left: 15px; - overflow-x: visible; - -webkit-overflow-scrolling: touch; - border-top: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1); -} -.navbar-collapse.in { - overflow-y: auto; -} -@media (min-width: 768px) { - .navbar-collapse { - width: auto; - border-top: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-collapse.collapse { - display: block !important; - height: auto !important; - padding-bottom: 0; - overflow: visible !important; - } - .navbar-collapse.in { - overflow-y: visible; - } - .navbar-fixed-top .navbar-collapse, - .navbar-static-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - padding-right: 0; - padding-left: 0; - } -} -.navbar-fixed-top .navbar-collapse, -.navbar-fixed-bottom .navbar-collapse { - max-height: 340px; -} -@media (max-device-width: 480px) and (orientation: landscape) { - .navbar-fixed-top .navbar-collapse, - .navbar-fixed-bottom .navbar-collapse { - max-height: 200px; - } -} -.container > .navbar-header, -.container-fluid > .navbar-header, -.container > .navbar-collapse, -.container-fluid > .navbar-collapse { - margin-right: -15px; - margin-left: -15px; -} -@media (min-width: 768px) { - .container > .navbar-header, - .container-fluid > .navbar-header, - .container > .navbar-collapse, - .container-fluid > .navbar-collapse { - margin-right: 0; - margin-left: 0; - } -} -.navbar-static-top { - z-index: 1000; - border-width: 0 0 1px; -} -@media (min-width: 768px) { - .navbar-static-top { - border-radius: 0; - } -} -.navbar-fixed-top, -.navbar-fixed-bottom { - position: fixed; - right: 0; - left: 0; - z-index: 1030; -} -@media (min-width: 768px) { - .navbar-fixed-top, - .navbar-fixed-bottom { - border-radius: 0; - } -} -.navbar-fixed-top { - top: 0; - border-width: 0 0 1px; -} -.navbar-fixed-bottom { - bottom: 0; - margin-bottom: 0; - border-width: 1px 0 0; -} -.navbar-brand { - float: left; - height: 50px; - padding: 15px 15px; - font-size: 18px; - line-height: 20px; -} -.navbar-brand:hover, -.navbar-brand:focus { - text-decoration: none; -} -.navbar-brand > img { - display: block; -} -@media (min-width: 768px) { - .navbar > .container .navbar-brand, - .navbar > .container-fluid .navbar-brand { - margin-left: -15px; - } -} -.navbar-toggle { - position: relative; - float: right; - padding: 9px 10px; - margin-top: 8px; - margin-right: 15px; - margin-bottom: 8px; - background-color: transparent; - background-image: none; - border: 1px solid transparent; - border-radius: 4px; -} -.navbar-toggle:focus { - outline: 0; -} -.navbar-toggle .icon-bar { - display: block; - width: 22px; - height: 2px; - border-radius: 1px; -} -.navbar-toggle .icon-bar + .icon-bar { - margin-top: 4px; -} -@media (min-width: 768px) { - .navbar-toggle { - display: none; - } -} -.navbar-nav { - margin: 7.5px -15px; -} -.navbar-nav > li > a { - padding-top: 10px; - padding-bottom: 10px; - line-height: 20px; -} -@media (max-width: 767px) { - .navbar-nav .open .dropdown-menu { - position: static; - float: none; - width: auto; - margin-top: 0; - background-color: transparent; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } - .navbar-nav .open .dropdown-menu > li > a, - .navbar-nav .open .dropdown-menu .dropdown-header { - padding: 5px 15px 5px 25px; - } - .navbar-nav .open .dropdown-menu > li > a { - line-height: 20px; - } - .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-nav .open .dropdown-menu > li > a:focus { - background-image: none; - } -} -@media (min-width: 768px) { - .navbar-nav { - float: left; - margin: 0; - } - .navbar-nav > li { - float: left; - } - .navbar-nav > li > a { - padding-top: 15px; - padding-bottom: 15px; - } -} -.navbar-form { - padding: 10px 15px; - margin-top: 8px; - margin-right: -15px; - margin-bottom: 8px; - margin-left: -15px; - border-top: 1px solid transparent; - border-bottom: 1px solid transparent; - -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); - box-shadow: inset 0 1px 0 rgba(255, 255, 255, .1), 0 1px 0 rgba(255, 255, 255, .1); -} -@media (min-width: 768px) { - .navbar-form .form-group { - display: inline-block; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .form-control { - display: inline-block; - width: auto; - vertical-align: middle; - } - .navbar-form .form-control-static { - display: inline-block; - } - .navbar-form .input-group { - display: inline-table; - vertical-align: middle; - } - .navbar-form .input-group .input-group-addon, - .navbar-form .input-group .input-group-btn, - .navbar-form .input-group .form-control { - width: auto; - } - .navbar-form .input-group > .form-control { - width: 100%; - } - .navbar-form .control-label { - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio, - .navbar-form .checkbox { - display: inline-block; - margin-top: 0; - margin-bottom: 0; - vertical-align: middle; - } - .navbar-form .radio label, - .navbar-form .checkbox label { - padding-left: 0; - } - .navbar-form .radio input[type="radio"], - .navbar-form .checkbox input[type="checkbox"] { - position: relative; - margin-left: 0; - } - .navbar-form .has-feedback .form-control-feedback { - top: 0; - } -} -@media (max-width: 767px) { - .navbar-form .form-group { - margin-bottom: 5px; - } - .navbar-form .form-group:last-child { - margin-bottom: 0; - } -} -@media (min-width: 768px) { - .navbar-form { - width: auto; - padding-top: 0; - padding-bottom: 0; - margin-right: 0; - margin-left: 0; - border: 0; - -webkit-box-shadow: none; - box-shadow: none; - } -} -.navbar-nav > li > .dropdown-menu { - margin-top: 0; - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { - margin-bottom: 0; - border-top-left-radius: 4px; - border-top-right-radius: 4px; - border-bottom-right-radius: 0; - border-bottom-left-radius: 0; -} -.navbar-btn { - margin-top: 8px; - margin-bottom: 8px; -} -.navbar-btn.btn-sm { - margin-top: 10px; - margin-bottom: 10px; -} -.navbar-btn.btn-xs { - margin-top: 14px; - margin-bottom: 14px; -} -.navbar-text { - margin-top: 15px; - margin-bottom: 15px; -} -@media (min-width: 768px) { - .navbar-text { - float: left; - margin-right: 15px; - margin-left: 15px; - } -} -@media (min-width: 768px) { - .navbar-left { - float: left !important; - } - .navbar-right { - float: right !important; - margin-right: -15px; - } - .navbar-right ~ .navbar-right { - margin-right: 0; - } -} -.navbar-default { - background-color: #f8f8f8; - border-color: #e7e7e7; -} -.navbar-default .navbar-brand { - color: #777; -} -.navbar-default .navbar-brand:hover, -.navbar-default .navbar-brand:focus { - color: #5e5e5e; - background-color: transparent; -} -.navbar-default .navbar-text { - color: #777; -} -.navbar-default .navbar-nav > li > a { - color: #777; -} -.navbar-default .navbar-nav > li > a:hover, -.navbar-default .navbar-nav > li > a:focus { - color: #333; - background-color: transparent; -} -.navbar-default .navbar-nav > .active > a, -.navbar-default .navbar-nav > .active > a:hover, -.navbar-default .navbar-nav > .active > a:focus { - color: #555; - background-color: #e7e7e7; -} -.navbar-default .navbar-nav > .disabled > a, -.navbar-default .navbar-nav > .disabled > a:hover, -.navbar-default .navbar-nav > .disabled > a:focus { - color: #ccc; - background-color: transparent; -} -.navbar-default .navbar-toggle { - border-color: #ddd; -} -.navbar-default .navbar-toggle:hover, -.navbar-default .navbar-toggle:focus { - background-color: #ddd; -} -.navbar-default .navbar-toggle .icon-bar { - background-color: #888; -} -.navbar-default .navbar-collapse, -.navbar-default .navbar-form { - border-color: #e7e7e7; -} -.navbar-default .navbar-nav > .open > a, -.navbar-default .navbar-nav > .open > a:hover, -.navbar-default .navbar-nav > .open > a:focus { - color: #555; - background-color: #e7e7e7; -} -@media (max-width: 767px) { - .navbar-default .navbar-nav .open .dropdown-menu > li > a { - color: #777; - } - .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { - color: #333; - background-color: transparent; - } - .navbar-default .navbar-nav .open .dropdown-menu > .active > a, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #555; - background-color: #e7e7e7; - } - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #ccc; - background-color: transparent; - } -} -.navbar-default .navbar-link { - color: #777; -} -.navbar-default .navbar-link:hover { - color: #333; -} -.navbar-default .btn-link { - color: #777; -} -.navbar-default .btn-link:hover, -.navbar-default .btn-link:focus { - color: #333; -} -.navbar-default .btn-link[disabled]:hover, -fieldset[disabled] .navbar-default .btn-link:hover, -.navbar-default .btn-link[disabled]:focus, -fieldset[disabled] .navbar-default .btn-link:focus { - color: #ccc; -} -.navbar-inverse { - background-color: #222; - border-color: #080808; -} -.navbar-inverse .navbar-brand { - color: #9d9d9d; -} -.navbar-inverse .navbar-brand:hover, -.navbar-inverse .navbar-brand:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-text { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a { - color: #9d9d9d; -} -.navbar-inverse .navbar-nav > li > a:hover, -.navbar-inverse .navbar-nav > li > a:focus { - color: #fff; - background-color: transparent; -} -.navbar-inverse .navbar-nav > .active > a, -.navbar-inverse .navbar-nav > .active > a:hover, -.navbar-inverse .navbar-nav > .active > a:focus { - color: #fff; - background-color: #080808; -} -.navbar-inverse .navbar-nav > .disabled > a, -.navbar-inverse .navbar-nav > .disabled > a:hover, -.navbar-inverse .navbar-nav > .disabled > a:focus { - color: #444; - background-color: transparent; -} -.navbar-inverse .navbar-toggle { - border-color: #333; -} -.navbar-inverse .navbar-toggle:hover, -.navbar-inverse .navbar-toggle:focus { - background-color: #333; -} -.navbar-inverse .navbar-toggle .icon-bar { - background-color: #fff; -} -.navbar-inverse .navbar-collapse, -.navbar-inverse .navbar-form { - border-color: #101010; -} -.navbar-inverse .navbar-nav > .open > a, -.navbar-inverse .navbar-nav > .open > a:hover, -.navbar-inverse .navbar-nav > .open > a:focus { - color: #fff; - background-color: #080808; -} -@media (max-width: 767px) { - .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { - border-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu .divider { - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { - color: #9d9d9d; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { - color: #fff; - background-color: transparent; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { - color: #fff; - background-color: #080808; - } - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, - .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { - color: #444; - background-color: transparent; - } -} -.navbar-inverse .navbar-link { - color: #9d9d9d; -} -.navbar-inverse .navbar-link:hover { - color: #fff; -} -.navbar-inverse .btn-link { - color: #9d9d9d; -} -.navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link:focus { - color: #fff; -} -.navbar-inverse .btn-link[disabled]:hover, -fieldset[disabled] .navbar-inverse .btn-link:hover, -.navbar-inverse .btn-link[disabled]:focus, -fieldset[disabled] .navbar-inverse .btn-link:focus { - color: #444; -} -.breadcrumb { - padding: 8px 15px; - margin-bottom: 20px; - list-style: none; - background-color: #f5f5f5; - border-radius: 4px; -} -.breadcrumb > li { - display: inline-block; -} -.breadcrumb > li + li:before { - padding: 0 5px; - color: #ccc; - content: "/\00a0"; -} -.breadcrumb > .active { - color: #777; -} -.pagination { - display: inline-block; - padding-left: 0; - margin: 20px 0; - border-radius: 4px; -} -.pagination > li { - display: inline; -} -.pagination > li > a, -.pagination > li > span { - position: relative; - float: left; - padding: 6px 12px; - margin-left: -1px; - line-height: 1.42857143; - color: #337ab7; - text-decoration: none; - background-color: #fff; - border: 1px solid #ddd; -} -.pagination > li:first-child > a, -.pagination > li:first-child > span { - margin-left: 0; - border-top-left-radius: 4px; - border-bottom-left-radius: 4px; -} -.pagination > li:last-child > a, -.pagination > li:last-child > span { - border-top-right-radius: 4px; - border-bottom-right-radius: 4px; -} -.pagination > li > a:hover, -.pagination > li > span:hover, -.pagination > li > a:focus, -.pagination > li > span:focus { - z-index: 2; - color: #23527c; - background-color: #eee; - border-color: #ddd; -} -.pagination > .active > a, -.pagination > .active > span, -.pagination > .active > a:hover, -.pagination > .active > span:hover, -.pagination > .active > a:focus, -.pagination > .active > span:focus { - z-index: 3; - color: #fff; - cursor: default; - background-color: #337ab7; - border-color: #337ab7; -} -.pagination > .disabled > span, -.pagination > .disabled > span:hover, -.pagination > .disabled > span:focus, -.pagination > .disabled > a, -.pagination > .disabled > a:hover, -.pagination > .disabled > a:focus { - color: #777; - cursor: not-allowed; - background-color: #fff; - border-color: #ddd; -} -.pagination-lg > li > a, -.pagination-lg > li > span { - padding: 10px 16px; - font-size: 18px; - line-height: 1.3333333; -} -.pagination-lg > li:first-child > a, -.pagination-lg > li:first-child > span { - border-top-left-radius: 6px; - border-bottom-left-radius: 6px; -} -.pagination-lg > li:last-child > a, -.pagination-lg > li:last-child > span { - border-top-right-radius: 6px; - border-bottom-right-radius: 6px; -} -.pagination-sm > li > a, -.pagination-sm > li > span { - padding: 5px 10px; - font-size: 12px; - line-height: 1.5; -} -.pagination-sm > li:first-child > a, -.pagination-sm > li:first-child > span { - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; -} -.pagination-sm > li:last-child > a, -.pagination-sm > li:last-child > span { - border-top-right-radius: 3px; - border-bottom-right-radius: 3px; -} -.pager { - padding-left: 0; - margin: 20px 0; - text-align: center; - list-style: none; -} -.pager li { - display: inline; -} -.pager li > a, -.pager li > span { - display: inline-block; - padding: 5px 14px; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 15px; -} -.pager li > a:hover, -.pager li > a:focus { - text-decoration: none; - background-color: #eee; -} -.pager .next > a, -.pager .next > span { - float: right; -} -.pager .previous > a, -.pager .previous > span { - float: left; -} -.pager .disabled > a, -.pager .disabled > a:hover, -.pager .disabled > a:focus, -.pager .disabled > span { - color: #777; - cursor: not-allowed; - background-color: #fff; -} -.label { - display: inline; - padding: .2em .6em .3em; - font-size: 75%; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: baseline; - border-radius: .25em; -} -a.label:hover, -a.label:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.label:empty { - display: none; -} -.btn .label { - position: relative; - top: -1px; -} -.label-default { - background-color: #777; -} -.label-default[href]:hover, -.label-default[href]:focus { - background-color: #5e5e5e; -} -.label-primary { - background-color: #337ab7; -} -.label-primary[href]:hover, -.label-primary[href]:focus { - background-color: #286090; -} -.label-success { - background-color: #5cb85c; -} -.label-success[href]:hover, -.label-success[href]:focus { - background-color: #449d44; -} -.label-info { - background-color: #5bc0de; -} -.label-info[href]:hover, -.label-info[href]:focus { - background-color: #31b0d5; -} -.label-warning { - background-color: #f0ad4e; -} -.label-warning[href]:hover, -.label-warning[href]:focus { - background-color: #ec971f; -} -.label-danger { - background-color: #d9534f; -} -.label-danger[href]:hover, -.label-danger[href]:focus { - background-color: #c9302c; -} -.badge { - display: inline-block; - min-width: 10px; - padding: 3px 7px; - font-size: 12px; - font-weight: bold; - line-height: 1; - color: #fff; - text-align: center; - white-space: nowrap; - vertical-align: middle; - background-color: #777; - border-radius: 10px; -} -.badge:empty { - display: none; -} -.btn .badge { - position: relative; - top: -1px; -} -.btn-xs .badge, -.btn-group-xs > .btn .badge { - top: 0; - padding: 1px 5px; -} -a.badge:hover, -a.badge:focus { - color: #fff; - text-decoration: none; - cursor: pointer; -} -.list-group-item.active > .badge, -.nav-pills > .active > a > .badge { - color: #337ab7; - background-color: #fff; -} -.list-group-item > .badge { - float: right; -} -.list-group-item > .badge + .badge { - margin-right: 5px; -} -.nav-pills > li > a > .badge { - margin-left: 3px; -} -.jumbotron { - padding-top: 30px; - padding-bottom: 30px; - margin-bottom: 30px; - color: inherit; - background-color: #eee; -} -.jumbotron h1, -.jumbotron .h1 { - color: inherit; -} -.jumbotron p { - margin-bottom: 15px; - font-size: 21px; - font-weight: 200; -} -.jumbotron > hr { - border-top-color: #d5d5d5; -} -.container .jumbotron, -.container-fluid .jumbotron { - padding-right: 15px; - padding-left: 15px; - border-radius: 6px; -} -.jumbotron .container { - max-width: 100%; -} -@media screen and (min-width: 768px) { - .jumbotron { - padding-top: 48px; - padding-bottom: 48px; - } - .container .jumbotron, - .container-fluid .jumbotron { - padding-right: 60px; - padding-left: 60px; - } - .jumbotron h1, - .jumbotron .h1 { - font-size: 63px; - } -} -.thumbnail { - display: block; - padding: 4px; - margin-bottom: 20px; - line-height: 1.42857143; - background-color: #fff; - border: 1px solid #ddd; - border-radius: 4px; - -webkit-transition: border .2s ease-in-out; - -o-transition: border .2s ease-in-out; - transition: border .2s ease-in-out; -} -.thumbnail > img, -.thumbnail a > img { - margin-right: auto; - margin-left: auto; -} -a.thumbnail:hover, -a.thumbnail:focus, -a.thumbnail.active { - border-color: #337ab7; -} -.thumbnail .caption { - padding: 9px; - color: #333; -} -.alert { - padding: 15px; - margin-bottom: 20px; - border: 1px solid transparent; - border-radius: 4px; -} -.alert h4 { - margin-top: 0; - color: inherit; -} -.alert .alert-link { - font-weight: bold; -} -.alert > p, -.alert > ul { - margin-bottom: 0; -} -.alert > p + p { - margin-top: 5px; -} -.alert-dismissable, -.alert-dismissible { - padding-right: 35px; -} -.alert-dismissable .close, -.alert-dismissible .close { - position: relative; - top: -2px; - right: -21px; - color: inherit; -} -.alert-success { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.alert-success hr { - border-top-color: #c9e2b3; -} -.alert-success .alert-link { - color: #2b542c; -} -.alert-info { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.alert-info hr { - border-top-color: #a6e1ec; -} -.alert-info .alert-link { - color: #245269; -} -.alert-warning { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.alert-warning hr { - border-top-color: #f7e1b5; -} -.alert-warning .alert-link { - color: #66512c; -} -.alert-danger { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.alert-danger hr { - border-top-color: #e4b9c0; -} -.alert-danger .alert-link { - color: #843534; -} -@-webkit-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@-o-keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -@keyframes progress-bar-stripes { - from { - background-position: 40px 0; - } - to { - background-position: 0 0; - } -} -.progress { - height: 20px; - margin-bottom: 20px; - overflow: hidden; - background-color: #f5f5f5; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, .1); -} -.progress-bar { - float: left; - width: 0; - height: 100%; - font-size: 12px; - line-height: 20px; - color: #fff; - text-align: center; - background-color: #337ab7; - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .15); - -webkit-transition: width .6s ease; - -o-transition: width .6s ease; - transition: width .6s ease; -} -.progress-striped .progress-bar, -.progress-bar-striped { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - -webkit-background-size: 40px 40px; - background-size: 40px 40px; -} -.progress.active .progress-bar, -.progress-bar.active { - -webkit-animation: progress-bar-stripes 2s linear infinite; - -o-animation: progress-bar-stripes 2s linear infinite; - animation: progress-bar-stripes 2s linear infinite; -} -.progress-bar-success { - background-color: #5cb85c; -} -.progress-striped .progress-bar-success { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-info { - background-color: #5bc0de; -} -.progress-striped .progress-bar-info { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-warning { - background-color: #f0ad4e; -} -.progress-striped .progress-bar-warning { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.progress-bar-danger { - background-color: #d9534f; -} -.progress-striped .progress-bar-danger { - background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); - background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -} -.media { - margin-top: 15px; -} -.media:first-child { - margin-top: 0; -} -.media, -.media-body { - overflow: hidden; - zoom: 1; -} -.media-body { - width: 10000px; -} -.media-object { - display: block; -} -.media-object.img-thumbnail { - max-width: none; -} -.media-right, -.media > .pull-right { - padding-left: 10px; -} -.media-left, -.media > .pull-left { - padding-right: 10px; -} -.media-left, -.media-right, -.media-body { - display: table-cell; - vertical-align: top; -} -.media-middle { - vertical-align: middle; -} -.media-bottom { - vertical-align: bottom; -} -.media-heading { - margin-top: 0; - margin-bottom: 5px; -} -.media-list { - padding-left: 0; - list-style: none; -} -.list-group { - padding-left: 0; - margin-bottom: 20px; -} -.list-group-item { - position: relative; - display: block; - padding: 10px 15px; - margin-bottom: -1px; - background-color: #fff; - border: 1px solid #ddd; -} -.list-group-item:first-child { - border-top-left-radius: 4px; - border-top-right-radius: 4px; -} -.list-group-item:last-child { - margin-bottom: 0; - border-bottom-right-radius: 4px; - border-bottom-left-radius: 4px; -} -a.list-group-item, -button.list-group-item { - color: #555; -} -a.list-group-item .list-group-item-heading, -button.list-group-item .list-group-item-heading { - color: #333; -} -a.list-group-item:hover, -button.list-group-item:hover, -a.list-group-item:focus, -button.list-group-item:focus { - color: #555; - text-decoration: none; - background-color: #f5f5f5; -} -button.list-group-item { - width: 100%; - text-align: left; -} -.list-group-item.disabled, -.list-group-item.disabled:hover, -.list-group-item.disabled:focus { - color: #777; - cursor: not-allowed; - background-color: #eee; -} -.list-group-item.disabled .list-group-item-heading, -.list-group-item.disabled:hover .list-group-item-heading, -.list-group-item.disabled:focus .list-group-item-heading { - color: inherit; -} -.list-group-item.disabled .list-group-item-text, -.list-group-item.disabled:hover .list-group-item-text, -.list-group-item.disabled:focus .list-group-item-text { - color: #777; -} -.list-group-item.active, -.list-group-item.active:hover, -.list-group-item.active:focus { - z-index: 2; - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.list-group-item.active .list-group-item-heading, -.list-group-item.active:hover .list-group-item-heading, -.list-group-item.active:focus .list-group-item-heading, -.list-group-item.active .list-group-item-heading > small, -.list-group-item.active:hover .list-group-item-heading > small, -.list-group-item.active:focus .list-group-item-heading > small, -.list-group-item.active .list-group-item-heading > .small, -.list-group-item.active:hover .list-group-item-heading > .small, -.list-group-item.active:focus .list-group-item-heading > .small { - color: inherit; -} -.list-group-item.active .list-group-item-text, -.list-group-item.active:hover .list-group-item-text, -.list-group-item.active:focus .list-group-item-text { - color: #c7ddef; -} -.list-group-item-success { - color: #3c763d; - background-color: #dff0d8; -} -a.list-group-item-success, -button.list-group-item-success { - color: #3c763d; -} -a.list-group-item-success .list-group-item-heading, -button.list-group-item-success .list-group-item-heading { - color: inherit; -} -a.list-group-item-success:hover, -button.list-group-item-success:hover, -a.list-group-item-success:focus, -button.list-group-item-success:focus { - color: #3c763d; - background-color: #d0e9c6; -} -a.list-group-item-success.active, -button.list-group-item-success.active, -a.list-group-item-success.active:hover, -button.list-group-item-success.active:hover, -a.list-group-item-success.active:focus, -button.list-group-item-success.active:focus { - color: #fff; - background-color: #3c763d; - border-color: #3c763d; -} -.list-group-item-info { - color: #31708f; - background-color: #d9edf7; -} -a.list-group-item-info, -button.list-group-item-info { - color: #31708f; -} -a.list-group-item-info .list-group-item-heading, -button.list-group-item-info .list-group-item-heading { - color: inherit; -} -a.list-group-item-info:hover, -button.list-group-item-info:hover, -a.list-group-item-info:focus, -button.list-group-item-info:focus { - color: #31708f; - background-color: #c4e3f3; -} -a.list-group-item-info.active, -button.list-group-item-info.active, -a.list-group-item-info.active:hover, -button.list-group-item-info.active:hover, -a.list-group-item-info.active:focus, -button.list-group-item-info.active:focus { - color: #fff; - background-color: #31708f; - border-color: #31708f; -} -.list-group-item-warning { - color: #8a6d3b; - background-color: #fcf8e3; -} -a.list-group-item-warning, -button.list-group-item-warning { - color: #8a6d3b; -} -a.list-group-item-warning .list-group-item-heading, -button.list-group-item-warning .list-group-item-heading { - color: inherit; -} -a.list-group-item-warning:hover, -button.list-group-item-warning:hover, -a.list-group-item-warning:focus, -button.list-group-item-warning:focus { - color: #8a6d3b; - background-color: #faf2cc; -} -a.list-group-item-warning.active, -button.list-group-item-warning.active, -a.list-group-item-warning.active:hover, -button.list-group-item-warning.active:hover, -a.list-group-item-warning.active:focus, -button.list-group-item-warning.active:focus { - color: #fff; - background-color: #8a6d3b; - border-color: #8a6d3b; -} -.list-group-item-danger { - color: #a94442; - background-color: #f2dede; -} -a.list-group-item-danger, -button.list-group-item-danger { - color: #a94442; -} -a.list-group-item-danger .list-group-item-heading, -button.list-group-item-danger .list-group-item-heading { - color: inherit; -} -a.list-group-item-danger:hover, -button.list-group-item-danger:hover, -a.list-group-item-danger:focus, -button.list-group-item-danger:focus { - color: #a94442; - background-color: #ebcccc; -} -a.list-group-item-danger.active, -button.list-group-item-danger.active, -a.list-group-item-danger.active:hover, -button.list-group-item-danger.active:hover, -a.list-group-item-danger.active:focus, -button.list-group-item-danger.active:focus { - color: #fff; - background-color: #a94442; - border-color: #a94442; -} -.list-group-item-heading { - margin-top: 0; - margin-bottom: 5px; -} -.list-group-item-text { - margin-bottom: 0; - line-height: 1.3; -} -.panel { - margin-bottom: 20px; - background-color: #fff; - border: 1px solid transparent; - border-radius: 4px; - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: 0 1px 1px rgba(0, 0, 0, .05); -} -.panel-body { - padding: 15px; -} -.panel-heading { - padding: 10px 15px; - border-bottom: 1px solid transparent; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel-heading > .dropdown .dropdown-toggle { - color: inherit; -} -.panel-title { - margin-top: 0; - margin-bottom: 0; - font-size: 16px; - color: inherit; -} -.panel-title > a, -.panel-title > small, -.panel-title > .small, -.panel-title > small > a, -.panel-title > .small > a { - color: inherit; -} -.panel-footer { - padding: 10px 15px; - background-color: #f5f5f5; - border-top: 1px solid #ddd; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .list-group, -.panel > .panel-collapse > .list-group { - margin-bottom: 0; -} -.panel > .list-group .list-group-item, -.panel > .panel-collapse > .list-group .list-group-item { - border-width: 1px 0; - border-radius: 0; -} -.panel > .list-group:first-child .list-group-item:first-child, -.panel > .panel-collapse > .list-group:first-child .list-group-item:first-child { - border-top: 0; - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .list-group:last-child .list-group-item:last-child, -.panel > .panel-collapse > .list-group:last-child .list-group-item:last-child { - border-bottom: 0; - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .panel-heading + .panel-collapse > .list-group .list-group-item:first-child { - border-top-left-radius: 0; - border-top-right-radius: 0; -} -.panel-heading + .list-group .list-group-item:first-child { - border-top-width: 0; -} -.list-group + .panel-footer { - border-top-width: 0; -} -.panel > .table, -.panel > .table-responsive > .table, -.panel > .panel-collapse > .table { - margin-bottom: 0; -} -.panel > .table caption, -.panel > .table-responsive > .table caption, -.panel > .panel-collapse > .table caption { - padding-right: 15px; - padding-left: 15px; -} -.panel > .table:first-child, -.panel > .table-responsive:first-child > .table:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child { - border-top-left-radius: 3px; - border-top-right-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:first-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:first-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:first-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:first-child { - border-top-left-radius: 3px; -} -.panel > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child td:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child td:last-child, -.panel > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > thead:first-child > tr:first-child th:last-child, -.panel > .table:first-child > tbody:first-child > tr:first-child th:last-child, -.panel > .table-responsive:first-child > .table:first-child > tbody:first-child > tr:first-child th:last-child { - border-top-right-radius: 3px; -} -.panel > .table:last-child, -.panel > .table-responsive:last-child > .table:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child { - border-bottom-right-radius: 3px; - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:first-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:first-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:first-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:first-child { - border-bottom-left-radius: 3px; -} -.panel > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child td:last-child, -.panel > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tbody:last-child > tr:last-child th:last-child, -.panel > .table:last-child > tfoot:last-child > tr:last-child th:last-child, -.panel > .table-responsive:last-child > .table:last-child > tfoot:last-child > tr:last-child th:last-child { - border-bottom-right-radius: 3px; -} -.panel > .panel-body + .table, -.panel > .panel-body + .table-responsive, -.panel > .table + .panel-body, -.panel > .table-responsive + .panel-body { - border-top: 1px solid #ddd; -} -.panel > .table > tbody:first-child > tr:first-child th, -.panel > .table > tbody:first-child > tr:first-child td { - border-top: 0; -} -.panel > .table-bordered, -.panel > .table-responsive > .table-bordered { - border: 0; -} -.panel > .table-bordered > thead > tr > th:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:first-child, -.panel > .table-bordered > tbody > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, -.panel > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, -.panel > .table-bordered > thead > tr > td:first-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:first-child, -.panel > .table-bordered > tbody > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, -.panel > .table-bordered > tfoot > tr > td:first-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { - border-left: 0; -} -.panel > .table-bordered > thead > tr > th:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > th:last-child, -.panel > .table-bordered > tbody > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, -.panel > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, -.panel > .table-bordered > thead > tr > td:last-child, -.panel > .table-responsive > .table-bordered > thead > tr > td:last-child, -.panel > .table-bordered > tbody > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, -.panel > .table-bordered > tfoot > tr > td:last-child, -.panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { - border-right: 0; -} -.panel > .table-bordered > thead > tr:first-child > td, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > td, -.panel > .table-bordered > tbody > tr:first-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > td, -.panel > .table-bordered > thead > tr:first-child > th, -.panel > .table-responsive > .table-bordered > thead > tr:first-child > th, -.panel > .table-bordered > tbody > tr:first-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:first-child > th { - border-bottom: 0; -} -.panel > .table-bordered > tbody > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, -.panel > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td, -.panel > .table-bordered > tbody > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, -.panel > .table-bordered > tfoot > tr:last-child > th, -.panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th { - border-bottom: 0; -} -.panel > .table-responsive { - margin-bottom: 0; - border: 0; -} -.panel-group { - margin-bottom: 20px; -} -.panel-group .panel { - margin-bottom: 0; - border-radius: 4px; -} -.panel-group .panel + .panel { - margin-top: 5px; -} -.panel-group .panel-heading { - border-bottom: 0; -} -.panel-group .panel-heading + .panel-collapse > .panel-body, -.panel-group .panel-heading + .panel-collapse > .list-group { - border-top: 1px solid #ddd; -} -.panel-group .panel-footer { - border-top: 0; -} -.panel-group .panel-footer + .panel-collapse .panel-body { - border-bottom: 1px solid #ddd; -} -.panel-default { - border-color: #ddd; -} -.panel-default > .panel-heading { - color: #333; - background-color: #f5f5f5; - border-color: #ddd; -} -.panel-default > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ddd; -} -.panel-default > .panel-heading .badge { - color: #f5f5f5; - background-color: #333; -} -.panel-default > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ddd; -} -.panel-primary { - border-color: #337ab7; -} -.panel-primary > .panel-heading { - color: #fff; - background-color: #337ab7; - border-color: #337ab7; -} -.panel-primary > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #337ab7; -} -.panel-primary > .panel-heading .badge { - color: #337ab7; - background-color: #fff; -} -.panel-primary > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #337ab7; -} -.panel-success { - border-color: #d6e9c6; -} -.panel-success > .panel-heading { - color: #3c763d; - background-color: #dff0d8; - border-color: #d6e9c6; -} -.panel-success > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #d6e9c6; -} -.panel-success > .panel-heading .badge { - color: #dff0d8; - background-color: #3c763d; -} -.panel-success > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #d6e9c6; -} -.panel-info { - border-color: #bce8f1; -} -.panel-info > .panel-heading { - color: #31708f; - background-color: #d9edf7; - border-color: #bce8f1; -} -.panel-info > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #bce8f1; -} -.panel-info > .panel-heading .badge { - color: #d9edf7; - background-color: #31708f; -} -.panel-info > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #bce8f1; -} -.panel-warning { - border-color: #faebcc; -} -.panel-warning > .panel-heading { - color: #8a6d3b; - background-color: #fcf8e3; - border-color: #faebcc; -} -.panel-warning > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #faebcc; -} -.panel-warning > .panel-heading .badge { - color: #fcf8e3; - background-color: #8a6d3b; -} -.panel-warning > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #faebcc; -} -.panel-danger { - border-color: #ebccd1; -} -.panel-danger > .panel-heading { - color: #a94442; - background-color: #f2dede; - border-color: #ebccd1; -} -.panel-danger > .panel-heading + .panel-collapse > .panel-body { - border-top-color: #ebccd1; -} -.panel-danger > .panel-heading .badge { - color: #f2dede; - background-color: #a94442; -} -.panel-danger > .panel-footer + .panel-collapse > .panel-body { - border-bottom-color: #ebccd1; -} -.embed-responsive { - position: relative; - display: block; - height: 0; - padding: 0; - overflow: hidden; -} -.embed-responsive .embed-responsive-item, -.embed-responsive iframe, -.embed-responsive embed, -.embed-responsive object, -.embed-responsive video { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 100%; - height: 100%; - border: 0; -} -.embed-responsive-16by9 { - padding-bottom: 56.25%; -} -.embed-responsive-4by3 { - padding-bottom: 75%; -} -.well { - min-height: 20px; - padding: 19px; - margin-bottom: 20px; - background-color: #f5f5f5; - border: 1px solid #e3e3e3; - border-radius: 4px; - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); - box-shadow: inset 0 1px 1px rgba(0, 0, 0, .05); -} -.well blockquote { - border-color: #ddd; - border-color: rgba(0, 0, 0, .15); -} -.well-lg { - padding: 24px; - border-radius: 6px; -} -.well-sm { - padding: 9px; - border-radius: 3px; -} -.close { - float: right; - font-size: 21px; - font-weight: bold; - line-height: 1; - color: #000; - text-shadow: 0 1px 0 #fff; - filter: alpha(opacity=20); - opacity: .2; -} -.close:hover, -.close:focus { - color: #000; - text-decoration: none; - cursor: pointer; - filter: alpha(opacity=50); - opacity: .5; -} -button.close { - -webkit-appearance: none; - padding: 0; - cursor: pointer; - background: transparent; - border: 0; -} -.modal-open { - overflow: hidden; -} -.modal { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1050; - display: none; - overflow: hidden; - -webkit-overflow-scrolling: touch; - outline: 0; -} -.modal.fade .modal-dialog { - -webkit-transition: -webkit-transform .3s ease-out; - -o-transition: -o-transform .3s ease-out; - transition: transform .3s ease-out; - -webkit-transform: translate(0, -25%); - -ms-transform: translate(0, -25%); - -o-transform: translate(0, -25%); - transform: translate(0, -25%); -} -.modal.in .modal-dialog { - -webkit-transform: translate(0, 0); - -ms-transform: translate(0, 0); - -o-transform: translate(0, 0); - transform: translate(0, 0); -} -.modal-open .modal { - overflow-x: hidden; - overflow-y: auto; -} -.modal-dialog { - position: relative; - width: auto; - margin: 10px; -} -.modal-content { - position: relative; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #999; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - outline: 0; - -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, .5); - box-shadow: 0 3px 9px rgba(0, 0, 0, .5); -} -.modal-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 1040; - background-color: #000; -} -.modal-backdrop.fade { - filter: alpha(opacity=0); - opacity: 0; -} -.modal-backdrop.in { - filter: alpha(opacity=50); - opacity: .5; -} -.modal-header { - padding: 15px; - border-bottom: 1px solid #e5e5e5; -} -.modal-header .close { - margin-top: -2px; -} -.modal-title { - margin: 0; - line-height: 1.42857143; -} -.modal-body { - position: relative; - padding: 15px; -} -.modal-footer { - padding: 15px; - text-align: right; - border-top: 1px solid #e5e5e5; -} -.modal-footer .btn + .btn { - margin-bottom: 0; - margin-left: 5px; -} -.modal-footer .btn-group .btn + .btn { - margin-left: -1px; -} -.modal-footer .btn-block + .btn-block { - margin-left: 0; -} -.modal-scrollbar-measure { - position: absolute; - top: -9999px; - width: 50px; - height: 50px; - overflow: scroll; -} -@media (min-width: 768px) { - .modal-dialog { - width: 600px; - margin: 30px auto; - } - .modal-content { - -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - box-shadow: 0 5px 15px rgba(0, 0, 0, .5); - } - .modal-sm { - width: 300px; - } -} -@media (min-width: 992px) { - .modal-lg { - width: 900px; - } -} -.tooltip { - position: absolute; - z-index: 1070; - display: block; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 12px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - filter: alpha(opacity=0); - opacity: 0; - - line-break: auto; -} -.tooltip.in { - filter: alpha(opacity=90); - opacity: .9; -} -.tooltip.top { - padding: 5px 0; - margin-top: -3px; -} -.tooltip.right { - padding: 0 5px; - margin-left: 3px; -} -.tooltip.bottom { - padding: 5px 0; - margin-top: 3px; -} -.tooltip.left { - padding: 0 5px; - margin-left: -3px; -} -.tooltip-inner { - max-width: 200px; - padding: 3px 8px; - color: #fff; - text-align: center; - background-color: #000; - border-radius: 4px; -} -.tooltip-arrow { - position: absolute; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.tooltip.top .tooltip-arrow { - bottom: 0; - left: 50%; - margin-left: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-left .tooltip-arrow { - right: 5px; - bottom: 0; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.top-right .tooltip-arrow { - bottom: 0; - left: 5px; - margin-bottom: -5px; - border-width: 5px 5px 0; - border-top-color: #000; -} -.tooltip.right .tooltip-arrow { - top: 50%; - left: 0; - margin-top: -5px; - border-width: 5px 5px 5px 0; - border-right-color: #000; -} -.tooltip.left .tooltip-arrow { - top: 50%; - right: 0; - margin-top: -5px; - border-width: 5px 0 5px 5px; - border-left-color: #000; -} -.tooltip.bottom .tooltip-arrow { - top: 0; - left: 50%; - margin-left: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-left .tooltip-arrow { - top: 0; - right: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.tooltip.bottom-right .tooltip-arrow { - top: 0; - left: 5px; - margin-top: -5px; - border-width: 0 5px 5px; - border-bottom-color: #000; -} -.popover { - position: absolute; - top: 0; - left: 0; - z-index: 1060; - display: none; - max-width: 276px; - padding: 1px; - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; - font-size: 14px; - font-style: normal; - font-weight: normal; - line-height: 1.42857143; - text-align: left; - text-align: start; - text-decoration: none; - text-shadow: none; - text-transform: none; - letter-spacing: normal; - word-break: normal; - word-spacing: normal; - word-wrap: normal; - white-space: normal; - background-color: #fff; - -webkit-background-clip: padding-box; - background-clip: padding-box; - border: 1px solid #ccc; - border: 1px solid rgba(0, 0, 0, .2); - border-radius: 6px; - -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - box-shadow: 0 5px 10px rgba(0, 0, 0, .2); - - line-break: auto; -} -.popover.top { - margin-top: -10px; -} -.popover.right { - margin-left: 10px; -} -.popover.bottom { - margin-top: 10px; -} -.popover.left { - margin-left: -10px; -} -.popover-title { - padding: 8px 14px; - margin: 0; - font-size: 14px; - background-color: #f7f7f7; - border-bottom: 1px solid #ebebeb; - border-radius: 5px 5px 0 0; -} -.popover-content { - padding: 9px 14px; -} -.popover > .arrow, -.popover > .arrow:after { - position: absolute; - display: block; - width: 0; - height: 0; - border-color: transparent; - border-style: solid; -} -.popover > .arrow { - border-width: 11px; -} -.popover > .arrow:after { - content: ""; - border-width: 10px; -} -.popover.top > .arrow { - bottom: -11px; - left: 50%; - margin-left: -11px; - border-top-color: #999; - border-top-color: rgba(0, 0, 0, .25); - border-bottom-width: 0; -} -.popover.top > .arrow:after { - bottom: 1px; - margin-left: -10px; - content: " "; - border-top-color: #fff; - border-bottom-width: 0; -} -.popover.right > .arrow { - top: 50%; - left: -11px; - margin-top: -11px; - border-right-color: #999; - border-right-color: rgba(0, 0, 0, .25); - border-left-width: 0; -} -.popover.right > .arrow:after { - bottom: -10px; - left: 1px; - content: " "; - border-right-color: #fff; - border-left-width: 0; -} -.popover.bottom > .arrow { - top: -11px; - left: 50%; - margin-left: -11px; - border-top-width: 0; - border-bottom-color: #999; - border-bottom-color: rgba(0, 0, 0, .25); -} -.popover.bottom > .arrow:after { - top: 1px; - margin-left: -10px; - content: " "; - border-top-width: 0; - border-bottom-color: #fff; -} -.popover.left > .arrow { - top: 50%; - right: -11px; - margin-top: -11px; - border-right-width: 0; - border-left-color: #999; - border-left-color: rgba(0, 0, 0, .25); -} -.popover.left > .arrow:after { - right: 1px; - bottom: -10px; - content: " "; - border-right-width: 0; - border-left-color: #fff; -} -.carousel { - position: relative; -} -.carousel-inner { - position: relative; - width: 100%; - overflow: hidden; -} -.carousel-inner > .item { - position: relative; - display: none; - -webkit-transition: .6s ease-in-out left; - -o-transition: .6s ease-in-out left; - transition: .6s ease-in-out left; -} -.carousel-inner > .item > img, -.carousel-inner > .item > a > img { - line-height: 1; -} -@media all and (transform-3d), (-webkit-transform-3d) { - .carousel-inner > .item { - -webkit-transition: -webkit-transform .6s ease-in-out; - -o-transition: -o-transform .6s ease-in-out; - transition: transform .6s ease-in-out; - - -webkit-backface-visibility: hidden; - backface-visibility: hidden; - -webkit-perspective: 1000px; - perspective: 1000px; - } - .carousel-inner > .item.next, - .carousel-inner > .item.active.right { - left: 0; - -webkit-transform: translate3d(100%, 0, 0); - transform: translate3d(100%, 0, 0); - } - .carousel-inner > .item.prev, - .carousel-inner > .item.active.left { - left: 0; - -webkit-transform: translate3d(-100%, 0, 0); - transform: translate3d(-100%, 0, 0); - } - .carousel-inner > .item.next.left, - .carousel-inner > .item.prev.right, - .carousel-inner > .item.active { - left: 0; - -webkit-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); - } -} -.carousel-inner > .active, -.carousel-inner > .next, -.carousel-inner > .prev { - display: block; -} -.carousel-inner > .active { - left: 0; -} -.carousel-inner > .next, -.carousel-inner > .prev { - position: absolute; - top: 0; - width: 100%; -} -.carousel-inner > .next { - left: 100%; -} -.carousel-inner > .prev { - left: -100%; -} -.carousel-inner > .next.left, -.carousel-inner > .prev.right { - left: 0; -} -.carousel-inner > .active.left { - left: -100%; -} -.carousel-inner > .active.right { - left: 100%; -} -.carousel-control { - position: absolute; - top: 0; - bottom: 0; - left: 0; - width: 15%; - font-size: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); - background-color: rgba(0, 0, 0, 0); - filter: alpha(opacity=50); - opacity: .5; -} -.carousel-control.left { - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, .0001))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .5) 0%, rgba(0, 0, 0, .0001) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control.right { - right: 0; - left: auto; - background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -o-linear-gradient(left, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .0001)), to(rgba(0, 0, 0, .5))); - background-image: linear-gradient(to right, rgba(0, 0, 0, .0001) 0%, rgba(0, 0, 0, .5) 100%); - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); - background-repeat: repeat-x; -} -.carousel-control:hover, -.carousel-control:focus { - color: #fff; - text-decoration: none; - filter: alpha(opacity=90); - outline: 0; - opacity: .9; -} -.carousel-control .icon-prev, -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-left, -.carousel-control .glyphicon-chevron-right { - position: absolute; - top: 50%; - z-index: 5; - display: inline-block; - margin-top: -10px; -} -.carousel-control .icon-prev, -.carousel-control .glyphicon-chevron-left { - left: 50%; - margin-left: -10px; -} -.carousel-control .icon-next, -.carousel-control .glyphicon-chevron-right { - right: 50%; - margin-right: -10px; -} -.carousel-control .icon-prev, -.carousel-control .icon-next { - width: 20px; - height: 20px; - font-family: serif; - line-height: 1; -} -.carousel-control .icon-prev:before { - content: '\2039'; -} -.carousel-control .icon-next:before { - content: '\203a'; -} -.carousel-indicators { - position: absolute; - bottom: 10px; - left: 50%; - z-index: 15; - width: 60%; - padding-left: 0; - margin-left: -30%; - text-align: center; - list-style: none; -} -.carousel-indicators li { - display: inline-block; - width: 10px; - height: 10px; - margin: 1px; - text-indent: -999px; - cursor: pointer; - background-color: #000 \9; - background-color: rgba(0, 0, 0, 0); - border: 1px solid #fff; - border-radius: 10px; -} -.carousel-indicators .active { - width: 12px; - height: 12px; - margin: 0; - background-color: #fff; -} -.carousel-caption { - position: absolute; - right: 15%; - bottom: 20px; - left: 15%; - z-index: 10; - padding-top: 20px; - padding-bottom: 20px; - color: #fff; - text-align: center; - text-shadow: 0 1px 2px rgba(0, 0, 0, .6); -} -.carousel-caption .btn { - text-shadow: none; -} -@media screen and (min-width: 768px) { - .carousel-control .glyphicon-chevron-left, - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-prev, - .carousel-control .icon-next { - width: 30px; - height: 30px; - margin-top: -10px; - font-size: 30px; - } - .carousel-control .glyphicon-chevron-left, - .carousel-control .icon-prev { - margin-left: -10px; - } - .carousel-control .glyphicon-chevron-right, - .carousel-control .icon-next { - margin-right: -10px; - } - .carousel-caption { - right: 20%; - left: 20%; - padding-bottom: 30px; - } - .carousel-indicators { - bottom: 20px; - } -} -.clearfix:before, -.clearfix:after, -.dl-horizontal dd:before, -.dl-horizontal dd:after, -.container:before, -.container:after, -.container-fluid:before, -.container-fluid:after, -.row:before, -.row:after, -.form-horizontal .form-group:before, -.form-horizontal .form-group:after, -.btn-toolbar:before, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:before, -.btn-group-vertical > .btn-group:after, -.nav:before, -.nav:after, -.navbar:before, -.navbar:after, -.navbar-header:before, -.navbar-header:after, -.navbar-collapse:before, -.navbar-collapse:after, -.pager:before, -.pager:after, -.panel-body:before, -.panel-body:after, -.modal-header:before, -.modal-header:after, -.modal-footer:before, -.modal-footer:after { - display: table; - content: " "; -} -.clearfix:after, -.dl-horizontal dd:after, -.container:after, -.container-fluid:after, -.row:after, -.form-horizontal .form-group:after, -.btn-toolbar:after, -.btn-group-vertical > .btn-group:after, -.nav:after, -.navbar:after, -.navbar-header:after, -.navbar-collapse:after, -.pager:after, -.panel-body:after, -.modal-header:after, -.modal-footer:after { - clear: both; -} -.center-block { - display: block; - margin-right: auto; - margin-left: auto; -} -.pull-right { - float: right !important; -} -.pull-left { - float: left !important; -} -.hide { - display: none !important; -} -.show { - display: block !important; -} -.invisible { - visibility: hidden; -} -.text-hide { - font: 0/0 a; - color: transparent; - text-shadow: none; - background-color: transparent; - border: 0; -} -.hidden { - display: none !important; -} -.affix { - position: fixed; -} -@-ms-viewport { - width: device-width; -} -.visible-xs, -.visible-sm, -.visible-md, -.visible-lg { - display: none !important; -} -.visible-xs-block, -.visible-xs-inline, -.visible-xs-inline-block, -.visible-sm-block, -.visible-sm-inline, -.visible-sm-inline-block, -.visible-md-block, -.visible-md-inline, -.visible-md-inline-block, -.visible-lg-block, -.visible-lg-inline, -.visible-lg-inline-block { - display: none !important; -} -@media (max-width: 767px) { - .visible-xs { - display: block !important; - } - table.visible-xs { - display: table !important; - } - tr.visible-xs { - display: table-row !important; - } - th.visible-xs, - td.visible-xs { - display: table-cell !important; - } -} -@media (max-width: 767px) { - .visible-xs-block { - display: block !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline { - display: inline !important; - } -} -@media (max-width: 767px) { - .visible-xs-inline-block { - display: inline-block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm { - display: block !important; - } - table.visible-sm { - display: table !important; - } - tr.visible-sm { - display: table-row !important; - } - th.visible-sm, - td.visible-sm { - display: table-cell !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-block { - display: block !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline { - display: inline !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .visible-sm-inline-block { - display: inline-block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md { - display: block !important; - } - table.visible-md { - display: table !important; - } - tr.visible-md { - display: table-row !important; - } - th.visible-md, - td.visible-md { - display: table-cell !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-block { - display: block !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline { - display: inline !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .visible-md-inline-block { - display: inline-block !important; - } -} -@media (min-width: 1200px) { - .visible-lg { - display: block !important; - } - table.visible-lg { - display: table !important; - } - tr.visible-lg { - display: table-row !important; - } - th.visible-lg, - td.visible-lg { - display: table-cell !important; - } -} -@media (min-width: 1200px) { - .visible-lg-block { - display: block !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline { - display: inline !important; - } -} -@media (min-width: 1200px) { - .visible-lg-inline-block { - display: inline-block !important; - } -} -@media (max-width: 767px) { - .hidden-xs { - display: none !important; - } -} -@media (min-width: 768px) and (max-width: 991px) { - .hidden-sm { - display: none !important; - } -} -@media (min-width: 992px) and (max-width: 1199px) { - .hidden-md { - display: none !important; - } -} -@media (min-width: 1200px) { - .hidden-lg { - display: none !important; - } -} -.visible-print { - display: none !important; -} -@media print { - .visible-print { - display: block !important; - } - table.visible-print { - display: table !important; - } - tr.visible-print { - display: table-row !important; - } - th.visible-print, - td.visible-print { - display: table-cell !important; - } -} -.visible-print-block { - display: none !important; -} -@media print { - .visible-print-block { - display: block !important; - } -} -.visible-print-inline { - display: none !important; -} -@media print { - .visible-print-inline { - display: inline !important; - } -} -.visible-print-inline-block { - display: none !important; -} -@media print { - .visible-print-inline-block { - display: inline-block !important; - } -} -@media print { - .hidden-print { - display: none !important; - } -} -/*# sourceMappingURL=bootstrap.css.map */ diff --git a/src/Umbraco.Web.UI/Global.asax b/src/Umbraco.Web.UI/Global.asax deleted file mode 100644 index 08312748b9..0000000000 --- a/src/Umbraco.Web.UI/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Inherits="Umbraco.Web.UmbracoApplication" Language="C#" %> diff --git a/src/Umbraco.Web.UI/Media/Web.config b/src/Umbraco.Web.UI/Media/Web.config deleted file mode 100644 index cd48da3840..0000000000 --- a/src/Umbraco.Web.UI/Media/Web.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/src/Umbraco.Web.UI/Properties/AssemblyInfo.cs b/src/Umbraco.Web.UI/Properties/AssemblyInfo.cs deleted file mode 100644 index 53f2769d0e..0000000000 --- a/src/Umbraco.Web.UI/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("Umbraco.Web.UI")] -[assembly: AssemblyDescription("Umbraco Web UI")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Umbraco CMS")] - -[assembly: ComVisible(false)] -[assembly: Guid("A87ADD48-9DB5-4068-BCC6-377CC54E7AE4")] diff --git a/src/Umbraco.Web.UI/Properties/PublishProfiles/File export.pubxml b/src/Umbraco.Web.UI/Properties/PublishProfiles/File export.pubxml deleted file mode 100644 index c594b3a591..0000000000 --- a/src/Umbraco.Web.UI/Properties/PublishProfiles/File export.pubxml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - FileSystem - Debug - Any CPU - - True - True - C:\Users\Shannon\SkyDrive\Documents\Umbraco\Keynote\RestDemo - False - - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Properties/Settings.Designer.cs b/src/Umbraco.Web.UI/Properties/Settings.Designer.cs deleted file mode 100644 index 2598a8d66c..0000000000 --- a/src/Umbraco.Web.UI/Properties/Settings.Designer.cs +++ /dev/null @@ -1,35 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.ApplicationScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("https://our.umbraco.com/umbraco/webservices/api/repository.asmx")] - public string Umbraco_Web_UI_org_umbraco_our_Repository { - get { - return ((string)(this["Umbraco_Web_UI_org_umbraco_our_Repository"])); - } - } - } -} diff --git a/src/Umbraco.Web.UI/Properties/Settings.settings b/src/Umbraco.Web.UI/Properties/Settings.settings deleted file mode 100644 index d491844807..0000000000 --- a/src/Umbraco.Web.UI/Properties/Settings.settings +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - https://our.umbraco.com/umbraco/webservices/api/repository.asmx - - - \ No newline at end of file diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj deleted file mode 100644 index a9940193f7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ /dev/null @@ -1,327 +0,0 @@ - - - - - v4.7.2 - false - {4C4C194C-B5E4-4991-8F87-4373E24CC19F} - Library - Umbraco.Web.UI - Umbraco.Web.UI - ..\ - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - OnBuildSuccess - true - 44331 - enabled - disabled - false - - - - - - - Web.config - $(AdditionalFileItemNames);Content - - - true - full - false - bin\ - TRACE;DEBUG - prompt - 4 - false - ..\bin\Debug\ - true - ..\Package\Umbraco.Web.UI.zip - latest - - - true - pdbonly - true - bin\ - TRACE - prompt - 4 - bin\Umbraco.Web.UI.xml - false - ..\bin\Release\ - latest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1.0.0 - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - 3.5.3 - runtime; build; native; contentfiles; analyzers - all - - - - - - - - {29aa69d9-b597-4395-8d42-43b1263c240a} - Umbraco.Core - - - {0fad7d2a-d7dd-45b1-91fd-488bb6cdacea} - Umbraco.Examine.Lucene - - - {f6de8da0-07cc-4ef2-8a59-2bc81dbb3830} - Umbraco.PublishedCache.NuCache - - - {651e1350-91b6-44b7-bd60-7207006d7003} - Umbraco.Web - - - - - - True - True - Settings.settings - - - - ClientDependency.config - Designer - - - - - Designer - - - Designer - - - HealthChecks.config - Designer - - - Designer - - - serilog.user.config - Designer - - - Designer - - - serilog.config - Designer - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - Designer - - - Designer - - - Designer - - - Web.Template.config - Designer - - - Web.Template.config - Designer - - - - Designer - - - - - 11.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v14.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v16.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v17.0 - - - - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v14.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v15.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v16.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v17.0\Web\Microsoft.Web.Publishing.Tasks.dll - $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v17.0\Web\Microsoft.Web.Publishing.Tasks.dll - - - - - - $(NuGetPackageFolders.Split(';')[0]) - - - - - - - - - - False - True - 8121 - / - http://localhost:8121 - 8130 - 8140 - 8150 - / - http://localhost:8150 - 8131 - / - http://localhost:8131 - False - False - - - False - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @(ConfigFiles) - - $(OriginalFileName.Replace("%(ConfigFiles.Extension)",".$(Configuration)%(ConfigFiles.Extension)")) - - $(OriginalFileName.Replace("$(ProjectDir)", "$(WebProjectOutputDir)")) - - - - - - diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml deleted file mode 100644 index c915f42fd3..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/cy.xml +++ /dev/null @@ -1,2781 +0,0 @@ - - - - Method4 Ltd - https://www.method4.co.uk/ - - - Diwylliannau ac Enwau Gwesteia - Trywydd Archwilio - Dewis Nod - Newid Math o Ddogfen - Newid Math o Data - Copïo - Creu - Allforio - Creu Pecyn - Creu grŵp - Dileu - Analluogi - Golygu gosodiadau - Gwagu bin ailgylchu - Galluogi - Allforio Math o Ddogfen - Mewnforio Math o Ddogfen - Mewnforio Pecyn - Golygu mewn Cynfas - Gadael - Symud - Hysbysiadau - Cyrchiad cyhoeddus - Cyhoeddi - Dadgyhoeddi - Ail-lwytho - Ail-gyhoeddi yr holl safle - Dileu - Ailenwi - Adfer - Gosod hawliau ar gyfer y dudalen %0% - Dewis ble i copïo - Dewis ble i symud - Yn y strwythyr goeden isod - Dewis ble i gopïo'r eitem(au) a ddewiswyd - Dewis ble i symud yr eitem(au) a ddewiswyd - wedi symud i - wedi copïo i - wedi dileu - Hawliau - Rolio yn ôl - Anfon I Gyhoeddi - Anfon I Gyfieithu - Gosod grŵp - Trefnu - Cyfieithu - Diweddaru - Gosod Hawliau - Datgloi - Creu Templed Gynnwys - Ail-anfon Gwahoddiad - - - Cynnwys - Gweinyddu - Strwythyr - Arall - - - Caniatáu hawl i osod to assign diwylliannau ac enwau gwesteia - Caniatáu hawl i weld cofnod hanes nod - Caniatáu hawl i weld nod - Caniatáu hawl i newid math o ddogfen ar gyfer nod - Caniatáu hawl i gopïo nod - Caniatáu hawl i greu nodau - Caniatáu hawl i ddileu nodau - Caniatáu hawl i symud nodau - Caniatáu hawl i osod a newid cyrchiad cyhoeddus ar gyfer nod - Caniatáu hawl i gyhoeddi nod - Caniatáu hawl i dadgyhoeddi nod - Caniatáu hawl i newid hawliau ar gyfer nod - Caniatáu hawl i rolio nod yn ôl at gyflwr blaenorol - Caniatáu hawl i anfon nod am gymeradwyo cyn cyhoeddi - Caniatáu hawl i anfon nod am gyfieithiad - Caniatáu hawl i newid trefn nodau - Caniatáu hawl i gyfiethu nod - Caniatáu hawl i achub nod - Caniatáu hawl i greu Templed Cynnwys - - - Cynnwys - Gwybodaeth - - - Dim hawl. - Ychwanegu Parth newydd - dileu - Nod annilys. - Fformat parth annilys. - Parth wedi'i neilltuo eisoes. - Iaith - Parth - Parth newydd '%0%' wedi'i greu - Parth '%0%' wedi dileu - Parth '%0%' wedi neilltuo eisoes - Parth '%0%' wedi diweddaru - Golygu Parthau Presennol - - - - Etifeddu - Diwylliant - - neu etifeddu diwylliant o nodau rhiant. Bydd hyn hefyd
- yn berthnasol i'r nod bresennol, oni bai fod parth isod yn berthnasol hefyd.]]> -
- Parthau - - - Clirio dewisiad - Dewis - Gwneud rhywbeth arall - Trwm - Canslo Mewnoliad Paragraff - Mewnosod maes ffurflen - Mewnosod pennawd graffig - Golygu Html - Mewnoli Paragraff - Italig - Canoli - Unioni Chwith - Unioni Dde - Mewnosod Dolen - Mewnosod dolen leol (angor) - Rhestr Bwled - Rhestr rhifol - Mewnosod macro - Mewnosod llun - Chyhoeddi a cau - Cyhoeddi efo disgynnydd - Golygu perthnasau - Dychwelyd i'r rhestr - Achub - Achub a cau - Achub a chyhoeddi - Achub ac amserlenni - Achub ac anfon am gymeradwyo - Achub gwedd rhestr - Amserlenni - Rhagolwg - Save and preview - Rhagolwg wedi analluogi gan nad oes templed wedi'i neilltuo - Dewis arddull - Dangos arddulliau - Mewnosod tabl - Cynhyrchu modelau a cau - Achub a chynhyrchu modelau - Dadwneud - Ail-wneud - Rolio yn ôl - Dileu tag - Canslo - Cadarnhau - Mwy opsiynau cyhoeddi - Submit - Submit and close - - - Dangos am - Cynnwys wedi'i dileu - Cynnwys wedi'i dadgyhoeddi - Cynnwys wedi'i dadgyhoeddi am y ieithoedd: %0% - Cynnwys wedi'i Achub a Chyhoeddi - Cynnwys wedi'i Achub a Chyhoeddi am y ieithoedd: %0% - Cynnwys wedi'i achub - Cynnwys wedi'i achub am y ieithoedd: %0% - Cynnwys wedi'i symud - Cynnwys wedi'i copïo - Cynnwys wedi'i rolio yn ôl - Cynnwys wedi'i anfon i Gyhoeddi - Cynnwys wedi'i anfon i gyhoeddi am y ieithoedd: %0% - Cynnwys wedi'i anfon i gyfieithu - Trefnu eitemau blant cyflawnwyd gan ddefnyddiwr - %0% - Copïo - Cyhoeddi - Cyhoeddi - Symud - Achub - Achub - Dileu - Dadgyhoeddi - Dadgyhoeddi - Rolio yn ôl - Anfon i Gyhoeddi - Anfon i Gyhoeddi - Anfon i Gyfieithu - Tefnu - Arferu - Hanes (pob amrywiad) - - - Er mwyn newid y math o ddogfen ar gyfer y cynnwys dewiswyd, yn gyntaf dewiswch o'r rhestr o fathau dilys ar gyfer y lleoliad yma. - Wedyn cadarnhewch a/neu newid y mapiau priodweddau o'r math bresennol at yr un newydd, yna cliciwch Achub. - Mae'r cynnwys wedi'i ail-gyhoeddi. - Priodwedd Bresennol - Math bresennol - Ni ellir newid y fath o ddogfen gan nad oes dewisiadau amgen ar gyfer y lleoliad yma. Bydd dewis amgen yn ddilys os caniateir i'r dewis fod o dan rhiant yr eitem gynnwys sydd wedi'i ddewis ac fod caniatâd i bob eitem gynnwys blentyn sy'n bodoli eisoes i gael eu creu oddi'i tan. - Math o Ddogfen Wedi'i Newid - Mapio Priodweddau - Mapio i Briodwedd - Templed Newydd - Math Newydd - dim - Cynnwys - Dewis Math o Ddogfen Newydd - Mae'r math o ddogfen ar gyfer y cynnwys dewiswyd wedi cael ei newid yn llwyddiannus i [new type] ac mae'r priodweddau canlynol wedi'u mapio: - i - Ni ellir cwblhau mapio'r priodweddau gan fod gan un neu fwy o'r priodweddau fwy nag un map wedi'i ddiffinio. - Dim ond mathau amgen sy'n ddilys ar gyfer y lleoliad bresennol sydd yn ymddangos. - - - Methwyd creu ffolder o dan id rhiant %0% - Methwyd creu ffolder o dan rhiant efo enw %0% - Mae'r enw'r ffolder methu cynnwys nodau anghyfreithlon. - Methwyd dileu eitem: %0% - - - Wedi Cyhoeddi - Am y dudlaen yma - Enw arall - (sut fyddwch chi'n disgrifio'r llun dros y ffôn) - Dolenni Amgen - Cliwich i olygu'r eitem yma - Creuwyd gan - Awdur gwreiddiol - Diweddarwyd gan - Creuwyd - Dyddiad/amser creuwyd y ddogfen yma - Math o Ddogfen - Yn golygu - Dileu am - Mae'r eitem yma wedi cael ei newid ar ôl cyhoeddi - Nid yw'r eitem yma wedi cael ei gyhoeddi - Cyhoeddiad ddiwethaf - Nid oes unrhyw eitemau i ddangos - Nid oes unrhyw eitemau i ddangos yn y rhestr. - Nid oes unrhyw gynnwys wedi'i ychwanegu - Nid oes unrhyw aelodau wedi'u ychwanegu - Math o Gyfrwng - Dolen i eitem gyfrwng(au) - Grŵp Aelod - Rôl - Math o Aelod - Dim newidiadau wedi'u gwneud - Dim dyddiad wedi'i ddewis - Teitl tudalen - Does dim dolen gan yr eitem gyfrwng yma - Ni all unrhyw gynnwys cael ei hychwanegu am eitem hon - Priodweddau - Mae'r ddogfen yma wedi'i gyhoeddi ond nid yw'n weladwy gan nad yw'r rhiant '%0%' wedi'i gyhoeddi - Mae'r diwylliant yma yn cyhoeddedig ond ddim yn weladwy oherwydd mae'n anghyhoeddedig ar rhiant '%0%' - Mae'r ddogfen yma wedi'i gyhoeddi ond nid yw'n bodoli yn y storfa - Ni ellir nôl y url - Mae'r ddogfen yma wedi'i gyhoeddi ond byddai'r url yn gwrthdaro gyda chynnwys %0% - Mae'r ddogfen yma wedi'i gyhoeddi ond mae'r url methu cael ei cyfeirio - Cyhoeddi - Wedi cyhoeddi - Wedi cyhoeddi (newidiadau nes arddodiad) - Statws Cyhoeddi - Cyhoeddi efo disgynnyddion i cyhoeddi %0% ac yr holl eitemau cynnwys o dan ac a thrwy hynny wneud eu cynnwys ar gael i'r cyhoedd.]]> - Cyhoeddi efo disgynnyddion i cyhoeddi y ieithoedd a ddewiswyd ac yr un ieithoedd o'r eitemau o dan a thrwy hynny wneud eu cynnwys ar gael i'r cyhoedd.]]> - Cyhoeddi am - Dadgyhoeddi am - Clirio Dyddiad - Gosod dyddiad - Trefn wedi diweddaru - Er mwyn trefnu'r nodau, llusgwch y nodau neu cliciwch un o benynnau'r colofnau. Gallwch ddewis nifer o nodau gan ddal y botwm "shift" neu "control" wrth ddewis - Ystadegau - Teitl (dewisol) - Testyn amgen (dewisol) - Math - Dadgyhoeddi - Wedi dadgyhoeddi - Heb ei greu - Golygwyd ddiwethaf - Dyddiad/amser golygwyd y ddogfen yma - Dileu ffeil(iau) - Cliciwch yma i dileu'r llun oddi wrth y eitem cyfrwng - Cliciwch yma i dileu'r ffeil oddi wrth y eitem cyfrwng - Dolen i ddogfen - Aeold o grŵp(iau) - Ddim yn aelod o'r grŵp(iau) - Eitemau blentyn - Targed - Mae hyn yn trawsnewid at yr amser ganlynol ar y gweinydd: - Beth mae hyn yn golygu?]]> - Ydych chi'n sicr eich bod eisiau dileu'r eitem yma? - Mae'r priodwedd %0% yn defnyddio'r golygydd %1% sydd ddim yn cyd-fynd â Chynnwys Amnyth. - Wyt ti'n siŵr fod ti eisiau dileu pob eitem? - Nid oes unrhyw fathau o gynnwys wedi'u ffurfweddu ar gyfer yr eiddo hwn. - Ychwanegu teip elfen - Dewis teip elfen - Dewis y grŵp dylid arddangos ei briodweddau. Os caiff ei adael yn wag, bydd y grŵp cyntaf ar yr elfen yn cael ei defnyddio. - Rhowch fynegiad angular i werthuso yn erbyn pib eitem am ei enw. Defnyddiwch - i ddangos y mynegai'r eitem - Ychwanegu blwch testun arall - Dileu'r blwch testun yma - Gwraidd cynnwys - Cynnwys eitemau cynnwys heb eu cyhoeddi. - Mae'r gwerth yma'n gudd. Os ydych chi angen hawl i weld y gwerth yma, cysylltwch â gweinyddwr eich gwefan. - Mae'r gwerth yma'n gudd. - Pa ieithoedd yr hoffech chi eu cyhoeddi? Mae pob iaith sydd â chynnwys wei cael ei arbed! - Pa ieithoedd yr hoffech chi eu cyhoeddi? - Pa ieithoedd yr hoffech chi eu arbed? - Mae pob iaith sydd â chynnwys yn cael ei arbed wrth greu! - Pa ieithoedd hoffech chi anfon am gymeradwyaeth? - Pa ieithoedd yr hoffech chi eu hamserlennu? - Dewiswch yr ieithoedd i'w anghyhoeddi. Bydd anghyhoeddi iaith orfodol yn anghyhoeddi pob iaith. - Ieithoedd Cyhoeddedig - Ieithoedd heb ei gyhoeddi - Ieithoedd heb eu haddasu - Nid yw'r ieithoedd hyn wedi'u creu - Bydd pob amrywiad newydd yn cael ei arbed. - P'un amrywiadau wyt ti eisiau cyhoeddi? - Dewiswch pa amrywiadau wyt ti eisiau arbed. - Dewiswch pa amrywiadau i anfon am gymeradwyaeth. - Gosod cyhoeddi rhestredig... - Dewiswch yr amrywiadau i'w anghyhoeddi. Bydd anghyhoeddi iaith orfodol yn anghyhoeddi pob amrywiad. - Mae'r amrywiadau canlynol yn ofynnol er mwyn i gyhoeddi: - Ni ddim yn barod i Gyhoeddi - Barod i Gyhoeddi? - Barod i Arbed? - Anfonwch am gymeradwyaeth - Dewiswch y dyddiad a'r amser i gyhoeddi a / neu anghyhoeddi'r eitem gynnwys. - Creu newydd - Gludo o'r clipfwrdd - Mae'r eitem yma yn y Bin Ailgylchu - - - Creu Templed Cynnwys newydd o '%0%' - Gwag - Dewis Templed Cynnwys - Templed Cynnwys wedi'i greu - Creuwyd Templed Cynnwys o '%0%' - Mae Templed Cynnwys gyda'r un enw yn bodoli eisoes - Mae Templed Cynnwys yn gynnwys sydd wedi'i ddiffinio o flaen llaw y gellir ei ddewis gan olygwr i'w ddefnyddio fel sail ar gyfer creu cynnwys newydd - - - Cliciwch i lanlwytho - Gollyngwch eich ffeiliau yma... - Dolen i gyfrwng - neu cliciwch yma i ddewis ffeiliau - Gallwch lusgo ffeiliau yma i lanlwtho. - Dim ond mathau caniatol o ffeil sydd - Ni ellir lanlwytho'r ffeil yma, nid yw math y ffeil yn wedi'i gymeradwyo - Maint ffeil uchaf - Gwraidd gyfrwng - Methwyd symud cyfrwng - Ni all y ffolderi rhiant a chyrchfan fod yr un peth - Methwyd copïo cyfrwng - Methwyd creu ffolder o dan id rhiant %0% - Methwyd ailenwi'r ffolder gyda id %0% - Llusgo a gollwng eich ffeil(iau) i mewn i'r ardal - Ni chaniateir llwytho i fyny yn y lleoliad hwn. - - - Creu aelod newydd - Pob Aelod - Nid oes gan grwpiau aelodau unrhyw eiddo ychwanegol ar gyfer golygu. - - - Ble hoffwch greu eitem newydd %0% - Creu eitem o dan - Dewiswch y fath o ddogfen hoffwch greu templed dogfen ar ei gyfer - Rhoi enw ffolder i mewn - Dewiswch fath a theitl - Mathau o Ddogfennau o fewn y adran Gosodiadau, gan olygu y opsiwn Mathau o nod blentyn caniataol o dan Caniatadau]]> - Mathau o Ddogfennau tu fewn y adran Gosodiadau.]]> - Nid yw'r dudalen a ddewiswyd yn y goeden gynnwys yn caniatáu i unrhyw dudalennau gael eu creu oddi tani. - Golygu caniatâd ar gyfer y math hwn o ddogfen - Creu Math o Ddogfen newydd - Mathau o Ddogfennau o fewn y adran Gosodiadau, gan olygu y opsiwn Caniatáu fel gwraidd o dan Caniatadau]]> - "mathau o gyfrwng".]]> - Nid yw'r cyfryngau a ddewiswyd yn y goeden yn caniatáu i unrhyw gyfryngau eraill gael eu creu oddi tano. - Golygu caniatâd ar gyfer y math hwn o gyfryngau - Math o Ddogfen heb dempled - Ffolder newydd - Math o ddata newydd - Ffeil JavaScript newydd - Rhan-wedd wag newydd - Macro rhan-wedd newydd - Rhan-wedd newydd o damaid - Macro rhan-wedd wag newydd o damaid - Macro rhan-wedd newydd (heb macro) - Ffeil ddalen arddull newydd - Ffeil ddalen arddull Golygydd Testun Cyfoethog newydd - Macro rhan-wedd wag newydd - - - Pori eich gwefan - - Cuddio - Os nad yw Umbraco yn agor, efallai byddwch angen galluogi popups o'r safle yma - wedi agor mewn ffenestr newydd - Ailgychwyn - Ymweld â - Croeso - - - Aros - Hepgor newidiadau - Mae gennych chi newidiadau sydd heb eu achub - Ydych chi'n sicr eich bod eisiau llywio i ffwrdd o'r dudalen yma? - mae gennych chi newidiadau sydd heb eu achub - Bydd cyhoeddi yn gwneud yr eitemau a ddewiswyd yn weladwy ar y wefan. - Bydd anghyhoeddi yn tynnu'r eitemau a ddewiswyd a'u holl ddisgynyddion o'r safle. - Bydd dadgyhoeddi yn dileu'r dudalen yma a phob un o'i phlant o'r safle. - Mae gennych chi newidiadau heb eu cadw. Bydd gwneud newidiadau i'r Math o Ddogfen yn taflu'r newidiadau i ffwrdd. - - - Wedi gwneud - Wedi dileu eitem %0% - Wedi dileu %0% eitem - Wedi dileu %0% allan o %1% eitem - Wedi dileu %0% allan o %1% o eitemau - Wedi cyhoeddi eitem %0% - Wedi cyhoeddi %0% o eitemau - Wedi cyhoeddi %0% allan o %1% eitem - Wedi cyhoeddi %0% allan o %1% eitemau - Wedi dadgyhoeddi eitem %0% - Wedi dadgyhoeddi %0% o eitemau - Wedi dadgyhoeddi %0% allan o %1% eitem - Wedi dadgyhoeddi %0% allan o %1% o eitemau - Wedi symud eitem %0% - Wedi symud %0% o eitemau - Wedi symud %0% allan o %1% eitem - Wedi symud %0% allan o %1% o eitemau - Wedi copïo eitem %0% - Wedi copïo %0% o eitemau - Wedi copïo %0% allan o %1% eitem - Wedi copïo %0% allan o %1% eitemau - - - Teitl y ddolen - Dolen - Angor / llinyn ymholi - Enw - Gweinyddu enwau gwesteia - Cau'r ffenestr yma - Ydych chi'n sicr eich bod eisiau dileu - Wyt ti'n siŵr fod ti eisiau dileu %0% yn seiliedig ar %1% - - Ydych chi'n sicr eich bod eisiau analluogi - - Wyt ti'n siŵr fod ti eisiau dileu - %0%]]> - %0%]]> - - Ydych chi'n sicr? - Ydych chi'n sicr? - Torri - Golygu Eitem Geiriadur - Golygu Iaith - Golygu cyfrwng a dewiswyd - Mewnosod dolen leol - Mewnosod nod - Mewnosod pennawd graffig - Mewnosod llun - Mewnosod dolen - Cliciwch i ychwanegu Macro - Mewnosod tabl - Bydd hyn yn dileu'r iaith - Gall newid y diwylliant ar gyfer iaith fod yn weithrediad drud a bydd yn arwain at ailadeiladu'r storfa cynnwys a'r mynegeion - Golygwyd ddiwethaf - Dolen - Dolen fewnol: - Wrth ddefnyddio dolenni leol, defnyddiwch "#" o flaen y ddolen - Agor mewn ffenestr newydd? - Gosodiadau Macro - Nid yw'r macro yma yn cynnwys unrhyw briodweddau gallwch chi olygu - Gludo - Golygu hawliau ar gyfer - Gosod hawliau ar gyfer - Gosod hawliau ar gyfer %0% ar gyfer y grŵp defnyddwyr %1% - Dewiswch y grŵpiau defnyddwyr yr ydych eisiau gosod hwaliau ar eu cyfer - Mae'r eitemau yn y bin ailgylchu yn cael eu dileu. Peidiwch â chau'r ffenestr yma wrth i'r gweithrediad gymryd lle - Mae'r bin ailgylchu yn awr yn wag - Pan gaiff eitemau eu dileu o'r bin ailgylchu, byddent yn diflannu am byth - regexlib.com ar hyn o bryd, nid oes gennym reolaeth dros hyn. Mae'n ddrwg iawn gennym ni am yr anghyfleustra.]]> - Chwiliwch am fynegiad cyson er mwyn ychwanegu dilysiad i faes ffurflen. Enghraifft: 'email, 'zip-code' 'url' - Dileu Macro - Maes Gofynnol - Safle wedi'i ail-fynegi - Mae storfa'r wefan wedi'i ddiweddaru. Mae holl gynnwys cyhoeddi wedi'i ddiweddaru, ac mae'r holl gynnwys sydd heb ei gyhoeddi yn dal i fod heb ei gyhoeddi - Bydd storfa'r wefan yn cael ei adnewyddu. Bydd holl gynnwys cyhoeddi yn cael ei ddiweddaru, ac bydd holl gynnwys sydd heb ei gyhoeddi yn dal i fod heb ei gyhoeddi. - Nifer o golofnau - Nifer o resi - - Gosodwch id dalfan wrth osod ID ar eich dalfan gallwch chwistrellu cynnwys i mewn i'r templed yma o dempledi blentyn, - wrth gyfeirio at yr ID yma gan ddefnyddio elfen <asp:content />.]]> - - - Dewiswch id dalfan o'r rhestr isod. Gallwch ddim ond - ddewis Id (neu sawl) o feistr y dempled bresennol.]]> - - Cliciwch ar y llun i weld y maint llawn - Dewis eitem - Gweld Eitem Storfa - Creu ffolder... - Perthnasu at y gwreiddiol - Cynnwys disgynyddion - Y gymuned fwyaf cyfeillgar - Dolen i dudalen - Agor y ddolen ddogfen mewn ffenestr neu tab newydd - Dolen i gyfrwng - Dolen i ffeil - Dewis nod cychwyn cynnwys - Dewis cyfrwng - Dewis y math o gyfrwng - Dewis eicon - Dewis eitem - Dewis dolen - Dewis macro - Dewis cynnwys - Dewiswch y math o gynnwys - Dewis nod cychwyn cyfrwng - Dewis aelod - Dewis grŵp aelod - Dewiswch fath aelod - Dewis nod - Dewis adran - Dewis defnyddiwr - Dewis defnyddwyr - Dim eiconau wedi'u darganfod - Does dim paramedrau ar gyfer y macro yma - Does dim macro ar gael i fewnosod - Darparwyr mewngofnodi allanol - Manylion Eithriad - Trywydd stac - Eithriad Fewnol - Dolenni eich - Dad-ddolenni eich - cyfrif - Dewiswch olygwr - Dewiswch ffurfweddiad - Dewiswch damaid - Bydd hyn yn dileu'r nod a'i holl ieithoedd. Os mai dim ond un iaith yr ydych am ei dileu, ewch i'w anghyhoedd yn lle. - %0%.]]> - %0% o'r grŵp %1%]]> - Ydw, dileu - - - Nid oes unrhyw eitemau geiriadur. - - - - %0%' islaw
Gallwch ychwanegu ieithoedd ychwanegol o dan 'ieithoedd' yn y ddewislen ar y chwith - ]]> -
- Enw Diwylliant - Golygu allwedd yr eitem geiriadur. - - - - Trosolwg Geiriadur - - - Chwilwyr wedi'u Ffurfweddu - Yn dangos priodweddau ac offer ar gyfer unrhyw Chwiliwr wedi'i ffurfweddu (h.y. fel chwiliwr aml-fynegai) - Gwerthoedd maes - Statws iechyd - Statws iechyd y mynegai ac os gellir ei ddarllen - Mynegewyr - Gwybodaeth mynegai - Yn rhestru priodweddau'r mynegai - Rheoli mynegeion Examine - Yn caniatáu ichi weld manylion pob mynegai ac yn darparu rhai offer ar gyfer rheoli'r mynegeion - Ailadeiladu mynegai - - - Yn dibynnu ar faint o gynnwys sydd yn eich gwefan, gallai hyn gymryd cryn amser.
- Ni argymhellir ailadeiladu mynegai ar adegau o draffig gwefan uchel neu pan fydd golygyddion yn golygu cynnwys. - ]]> -
- Chwilwyr - Chwiliwch y mynegai a gweld y canlyniadau - Offer - Offer i reoli'r mynegai - meysydd - Ni ellir darllen yr mynegai a bydd angen ei ailadeiladu - Mae'r broses yn cymryd mwy o amser na'r disgwyl, gwiriwch y log Umbraco i weld os mae wedi bod unrhyw wall yn ystod y gweithrediad hwn - Ni ellir ailadeiladu'r mynegai hwn oherwydd nad yw wedi'i aseinio - IIndexPopulator - - - Darparwch eich enw defnyddiwr - Darparwch eich cyfrinair - Cadarnhewch eich cyfrinair - Enwch y %0%... - Darparwch enw... - Darparwch ebost... - Darparwch enw defnyddiwr... - Label... - Darparwch ddisgrifiad... - Teipiwch i chwilio... - Teipiwch i hidlo... - Teipiwch i ychwanegu tagiau (gwasgwch enter ar ôl pob tag)... - Darparwch eich ebost - Darparwch neges... - Mae eich enw defnyddiwr fel arfer eich cyfeiriad ebost - #gwerth neu ?allwedd=gwerth - Darparwch enw arall... - Yn generadu enw arall... - Creu eitem - Creu - Golygu - Enw - - - Caniatáu ar y gwraidd - Dim ond Mathau o Gynnwys gyda hwn wedi ticio all gael eu creu ar lefel wraidd coed Cynnwys a Chyfrwng - Mathau o nod blentyn caniataol - Cyfansoddiadau Mathau o Ddogfen - Creu - Dileu tab - Disgrifiad - Tab newydd - Tab - Ciplun bach - Galluogi gwedd rhestr - Ffurfweddu'r eitem gynnwysi ddangos rhestr trefnadwy & a chwiladwy o'i phlant, ni fydd y plant yn cael eu dangos yn y goeden - Gwedd rhestr bresennol - Y fath o ddata gwedd rhestr gweithredol - Creu gwedd rhestr pwrpasol - Dileu gwedd rhestr pwrpasol - Mae math o gynnwys, math o gyfrwng neu math o aeold gyda'r enw arall yma'n bodoli eisoes - - - Wedi ailenwi - Darparwch enw ffolder newydd yma - %0% wedi ailenwi i %1% - - - Ychwanegu cyn-werth - Math o ddata cronfa ddata - GUID golygydd priodwedd - Golygydd priodwedd - Botymau - Galluogi gosodiadau datblygedig ar gyfer - Galluogi dewislen cyd-destun - Maint fwyaf diofyn llun wedi'i fewnosod - Taflenni arddull perthnasol - Dangos label - Lled ac uchder - Holl fathau o briodweddau & data priodwedd - yn defnyddio'r fath yma o ddata yn cael eu dileu yn barhaol, cadarnhewch eich bod eisiau dileu'r rhain hefyd - Iawn, dileu - a holl fathau o briodwedd & data priodwedd sy'n defnyddio'r math o ddata yma - Dewiswch y ffolder i symud - i'r strwythyr goeden isod - wedi symud o dan - - %0% yn dileu'r briodweddau a'i data o'r eitemau canlynol]]> - Rwy'n deall y weithred hon yn dileu'r holl briodweddau a data sy'n seiliedig ar Fath o Ddata hon - - - Mae eich data wedi'i achub, ond cyn i chi allu cyhoeddi'r dudalen yma, mae yna wallau yr ydych angen eu gwirio yn gyntaf: - Nid yw'r darparwr aeoldaeth bresennol yn cefnogi newid cyfrinair (EnablePasswordRetrieval angen cael ei osod i true) - %0% yn bodoli eisoes - Roedd yna wallau: - Roedd yna wallau: - Dylai'r cyfrinair fod o leiaf %0% nod o hyd a chynnwys o leiaf %1% nod(au) sydd ddim yn llythyren na rhif - %0% angen bod yn gyfanrif - Mae'r maes %0% yn y tab %1% yn ofynnol - %0% yn faes ofynnol - %0% yn %1% mewn fformat annilys - %0% mewn fformat annilys - - - Derbynwyd gwall o'r gweinydd - Mae'r math o ffeil yma wedi'i wahardd gan y gweinyddwr - NODYN! Er bod CodeMirror wedi'i alluogi gan y ffurfwedd, mae o wedi'i analluogi mewn Internet Explorer gan nad yw'n ddigon cadarn. - Darparwch yr enw ac yr enw arall ar y math o briodwedd newydd! - Mae yna broblem gyda hawliau darllen/ysgrifennu i ffeil neu ffolder penodol - Gwall yn llwytho sgript Rhan-Wedd (ffeil: %0%) - Gwall yn llwytho Rheolydd Defnyddiwr '%0%' - Gwall yn llwytho Rheolydd Pwrpasol (Gwasaneth: %0%, Math: '%1%') - Gwall yn llwytho sgript MacroEngine (ffeil: %0%) - "Gwall yn dosbarthu'r ffeil XSLT: %0% - "Gwall yn darllen y ffeil XSLT: %0% - Darparwch deitl - Dewiswch fath - Rydych ar fîn gwneud y llun yn fwy 'na'r maint gwreiddiol. Ydych chi'n sicr eich bod eisiau parhau? - Gwall yn y sgript python - Nid yw'r sgript python wedi'i achub gan ei fod yn cynnwys gwall(au) - Nod gychwynnol wedi'i ddileu, cysylltwch â'ch gweinyddwr - Marciwch gynnwys cyn newid arddull - Dim arddulliau gweithredol ar gael - Symudwch y cyrchwr ar ochr chwith y ddwy gell yr ydych eisiau cyfuno - Ni allwch hollti cell sydd heb ei gyfuno. - Mae gan briodwedd hon gwallau - Gwall yn y ffynhonnell XSLT - Nid yw'r XSLTwedi'i achub gan ei fod yn cynnwys gwall(au) - Mae gwall ffurfwedd gyda'r math o ddata sy'n cael ei ddefnyddio ar gyfer y priodwedd yma, gwiriwch y fath o ddata - - - Dewisiadau - Amdano - Gweithred - Gweithredoedd - Ychwanegu - Enw arall - Holl - Ydych chi'n sicr? - Yn ôl - Yn ôl i'r trosolwg - Ffin - wrth - Canslo - Ymyl cell - Dewis - Clirio - Cau - Cau Ffenest - Sylw - Cadarnhau - Gorfodi - Gorfodi cyfraneddau - Cynnwys - Bwrw ymlaen - Copïo - Creu - Adran tocio - Cronfa ddata - Dyddiad - Diofyn - Dileu - Wedi dileu - Yn dileu... - Cynllun - Geiriadur - Dimensiynau - Gwaredu - I lawr - Lawrlwytho - Golygu - Golygwyd - Elfennau - Ebost - Gwall - Maes - Canfod - Cyntaf - Canolbwynt - Cyffredinol - Grŵpiau - Grŵp - Uchder - Help - Cuddio - Hanes - Eicon - Id - Mewnforio - Cynnwys is-ffolderi wrth chwilio - Search only this folder Chwilio yn ffolder hwn yn unig - Gwybodaeth - Ymyl mewnol - Mewnosod - Gosod - Annilys - Unioni - Label - Iaith - Olaf - Gosodiad - Dolenni - Yn llwytho - Wedi cloi - Mewngofnodi - Allgofnodi - Allgofnodi - Macro - Gofynnol - Neges - Symud - Mwy - Enw - Newydd - Nesaf - Na - o - I ffwrdd - Iawn - Agor - Opsiynau - Ymlaen - neu - Trefnu wrth - Cyfrinair - Llwybr - ID Dalfan - Un eiliad os gwelwch yn dda... - Blaenorol - Priodweddau - Ailadeiladu - Ebost i dderbyn data ffurflen - Bin Ailgylchu - Mae eich bin ailgylchu yn wag - Ail-lwytho - Ar ôl - Dileu - Ailenwi - Adnewyddu - Gofynnol - Adfer - Ceisio eto - Hawliau - Cyhoeddi ar amserlen - Chwilio - Mae'n ddrwg gennym ni, ni all ganfod beth roeddwch chi'n chwilio amdano. - Dim eitemau wedi'u hychwanegu - Gweinydd - Gosodiadau - Dangos - Dangos y dudlaen wrth Anfon - Maint - Trefnu - Statws - Cyflwyno - Llwyddiant - Math - Math i chwilio... - o dan - I fyny - Diweddaru - Uwchraddio - Lanlwytho - Url - Defnyddiwr - Enw defnyddiwr - Gwerth - Gwedd - Croeso... - Lled - Ie - Ffolder - Canlyniadau chwilio - Ail-drefnu - Rydw i wedi gorffen ail-drefnu - Rhagolwg - Newid cyfrinair - i - Gwedd rhestr - Yn achub... - presennol - Mewnblannu - Adfer - dewiswyd - Arall - Erthyglau - Fideos - Clirio - Arsefydlu - - - Du - Gwyrdd - Melyn - Oren - Glas - Llwyd Las - Llwyd - Brown - Glas Golau - Gwyrddlas - Gwyrdd Golau - Leim - Melyngoch - Oren Ddwfn - Coch - Pinc - Piws - Piws Ddwfn - Dulas - - - Ychwanegu tab - Ychwanegu grŵp - Ychwanegu priodwedd - Ychwanegu golygydd - Ychwanegu templed - Ychwanegu nod blentyn - Ychwanegu plentyn - Golygu math o ddata - Llywio adrannau - Llwybrau byr - dangos llwybrau byr - Toglo gwedd rhestr - Toglo caniatáu fel gwraidd - Sylwi/Dad-sylwi llinellau - Dileu llinell - Copïo llinellau i fyny - Copïo llinellau i lawr - Symud Llinellau I Fyny - Symud Llinellau I Lawr - Cyffredinol - Golygydd - Toglo caniatáu amrywiadau diwylliant - Toglo caniatáu segmentiad - - - Lliw cefndir - Trwm - Lliw ffont - Ffont - Testun - - - Tudalen - - - Ni all y gosodydd gysylltu â'r gronfa ddata. - Methwyd achub y ffeil web.config. Ceisiwch newid y llinyn gyswllt yn uniongyrchol. - Canfwyd eich cronfa ddata ac mae'n cael ei adnabod fel - Ffurfwedd gronfa ddata - - gosod i osod y gronfa ddata %0% Umbraco - ]]> - - Nesaf i fwrw ymlaen.]]> - - Cronfa ddata heb ei ganfod! Gwiriwch fod y gwybodaeth yn y "llinyn gyswllt" o'r ffeil "web.config" yn gywir.

-

Er mwyn parhau, newidiwch y ffeil "web.config" (gan ddefnyddio Visual Studio neu eich hoff olygydd testun), rholiwch at y gwaelod, ychwanegwch y llinyn gyswllt ar gyfer eich cronfa ddata yn yr allwedd o'r enw "UmbracoDbDSN" ac achub y ffeil.

-

- Cliciwch y botwm ceisio eto pan rydych wedi - gorffen.
- Mwy o wybodaeth am newid y ffeil web.config yma.

]]> -
- - - Cysylltwch â'ch darparwr gwe (ISP) os oes angen. - Os ydych chi'n gosod ar beiriant leol neu weinydd, efallai bydd angen gwybodaeth o'ch gweinyddwr system arnoch.]]> - - - - Gwasgwch y botwm uwchraddio i uwchraddio eoch gronfa ddata i Umbraco %0%

-

- Peidiwch â phoeni - ni fydd unrhyw gynnwys yn cael ei ddileu a bydd popeth yn parhau i weithio wedyn! -

- ]]> -
- - Gwasgwch Nesaf i - barhau. ]]> - - nesaf i barhau gyda'r dewin ffurfwedd]]> - Mae angen newid cyfrinair y defnyddiwr Diofyn!]]> - Mae'r defnyddiwr Diofyn wedi'u analluogi neu does dim hawliau i Umbraco!

Does dim angen unrhyw weithredoedd pellach. Cliciwch Nesaf i barhau.]]> - Mae cyfrinair y defnyddiwr Diofyn wedi'i newid yn llwyddiannus ers y gosodiad!

Does dim angen unrhyw weithredoedd pellach. Cliciwch Nesaf i barhau.]]> - Mae'r cyfrinair wedi'i newid! - Cewch gychwyn gwych, gwyliwch ein fideos rhaglith - Wrth glicio'r botwm nesaf (neu newid y umbracoConfigurationStatus yn web.config), rydych yn derbyn y trwydded ar gyfer y meddalwedd yma fel y nodir yn y blwch isod. Sylwch fod y dosbarthiad Umbraco yma yn cynnwys 2 drwydded gwahanol, y trwydded cod agored MIT ar gyfer y fframwaith ac y trwydded Umbraco rhadwedd sy'n ymdrin â'r Rhyngwyneb Defnyddiwr. - Heb osod eto. - Ffeiliau a ffolderi wedi'u effeithio - Mwy o wybodaeth am osod hawliau ar gyfer Umbraco yma - Rydych angen caniatáu i ASP.NET newid hawliau ar y ffeiliau/ffolderi canlynol - - Mae eich gosodiadau hawliau bron a bod yn berffaith!

- Gallwch redeg Umbraco heb broblemau, ond ni fydd yn bosibl i chi osod pecynnau sydd wedi'u hargymell er mwyn cymryd mantais llawn o Umbraco.]]> -
- Sut i Gywiro - Cliciwch yma i ddarllen y ferswin destun - fideo tiwtorial ar osod hawliau ffolder ar gyfer Umbraco neu darllenwch y fersiwn destun.]]> - - Gall eich gosodiadau hawliau fod yn broblem! -

- Gallwch redeg Umbraco heb broblemau, ond ni fydd yn bosibl i chi greu ffolderi neu gosod pecynnau sydd wedi'u hargymell er mwyn cymryd mantais llawn o Umbraco.]]> -
- - Nid yw eich gosodiadau hawliau yn barod ar gyfer Umbraco! -

- Er mwyn rhedeg Umbraco, bydd angen i chi ddiweddaru eich gosodiadau hawliau.]]> -
- - Mae eich gosodiadau hawliau yn berffaith!

- Rydych yn barod i redeg Umbraco a gosod pecynnau!]]> -
- Yn datrys y broblem ffolder - Dilynwch y ddolen yma ar gyfer mwy o wybodaethar broblemau gyda ASP.NET a chreu ffolderi - Gosod hawliau ffolderi - - - - Rydw i eisiau ail-gychwyn - - dysgwch sut) - Gallwch ddewis i osod Runway yn hwyrach os hoffwch chi. Ewch at yr adran Datblygwr a dewiswch Pecynnau. - ]]> - - Rydych newydd osod platfform glân Umbraco. Beth hoffwch chi wneud nesaf? - Mae Runway wedi'i osod - - - Dyma ein rhestr o fodylau yr ydym yn argymell, ticiwch y rhai hoffwch chi'u gosod, neu gwelwch yr holl restr o fodylau - ]]> - - Dim ond wedi'i argymell ar gyfer defnyddwyr brofiadol - Hoffwn i gychwyn gyda gwefan syml - - - Mae "Runway" yn wefan syml sy'n darparu mathau o ddogfennau a thempledi syml. Gall y gosodwr osod Runway i chi yn awtomatig, - ond gallwch olygu, estyn neu ei ddileu yn hawdd. Nid yw'n angenrheidiol a gallwch ddefnyddio Umbraco yn berffaith heb. Ond, - mae Runwayyn cynnig sylfaen hawdd wedi'i seilio ar arferion gorau er mwyn i chi gychwyn yn gyflymach nag erioed. - Os rydych chi'n dewis gosod Runway, gallwch ddewis blociau adeiliadu syml o'r enw Modylau Runway er mwyn mwyhau eich tudalennau Runway. -

- - Wedi cynnwys gyda Runway: Tudalen Hafan, Tudalen Cychwyn Allan, Tudalen Gosod Modylau.
- Modylau Dewisol: Llywio Dop, Map o'r wefan, Cysylltu, Oriel. -
- ]]> -
- Beth yw Runway - Cam 1/5 Derbyn trwydded - Cam 2/5: Ffurfwedd Gronfa Ddata - Cam 3/5: Dilysu Hawliau Ffeiliau - Cam 4/5: Gwirio Diogelwch Umbraco - Cam 5/5: Mae Umbraco yn barod i chi gychwyn - Diolch am ddewis Umbraco - - Porwch eich safle newydd -Rydych wedi gosod Runway, felly beth am weld sut mae eich gwefan newydd yn edrych.]]> - - - Cymorth a gwyboaeth bellach -Cewch gymorth o'n cymuned gwobrwyol, porwch drwy ein dogfennaeth neu gwyliwch fideos yn rhad ac am ddim ar sut i adeiladu gwefan syml, sut i ddefnyddio pecynnau a chanllaw cyflym i dermeg Umbraco]]> - - Mae Umbraco wedi'i osod %0% ac mae'n barod i'w ddefnyddio - - /web.config a diweddaru'r allwedd AppSetting UmbracoConfigurationStatus yng ngwaelod y gwerth o '%0%'.]]> - - - yn syth wrth glicio ar y botwm "Cychwyn Umbraco" isod.
Os ydych yn newydd i Umbraco, -gallwch ddarganfod digonedd o adnoddau ar ein tudalennau cychwyn allan.]]> -
- - Cychwyn Umbraco -Er mwyn gweinyddu eich gwefan, agorwch swyddfa gefn Umbraco a dechreuwch ychwangeu cynnwys, diweddaru'r templedi a thaflenni arddull neu ychwanegu nodweddion newydd]]> - - Methwyd cysylltu â'r gronfa ddata. - Umbraco Fersiwn 3 - Umbraco Fersiwn 4 - Gwylio - - Umbraco %0% ar gyfer gosodiad ffres neu uwchraddio o ferswin 3.0. -

- Gwasgwch "nesaf" i gychwyn y dewin.]]> -
- - - Côd Diwylliant - Enw Diwylliant - - - Rydych wedi segura a bydd allgofnodi awtomatig yn digwydd mewn - Adnewyddwch rwan er mwyn achub eich gwaith - - - Dydd Sul Swmpus - Dydd Llun Llwyddiannus - Dydd Mawrth Moethus - Dydd Mercher Melys - Dydd Iau Iachus - Dydd Gwener Gwych - Dydd Sadwrn Syfrdannus - Mewngofnodwch isod - Mewngofnodwch gyda - Sesiwn wedi cyrraedd terfyn amser - © 2001 - %0%
Umbraco.com

]]>
- Wedi anghofio eich cyfrinair? - Bydd ebost yn cael ei anfon i'r cyfeiriad darparwyd gyda dolen i ailosod eich cyfrinair - Bydd ebost gyda chyfarwyddiadau ailosod cyfrinair yn cael ei anfon at y cyfeiriad darparwyd os yw'n cyfateb â'n cofnodion - Dangos cyfrinair - Cuddio cyfrinair - Dychwelyd i'r ffurflen mewngofnodi - Darparwch gyfrinair newydd - Mae eich cyfrinair wedi'i ddiweddaru - Mae'r ddolen rydych wedi clicio arno naill ai yn annilys neu wedi dod i ben - Umbraco: Ailosod Cyfrinair - - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Ailosod cyfrinair wedi dymuno -

-

- Eich enw defnyddiwr ar gyfer swyddfa gefn Umbraco yw: %0% -

-

- - - - - - -
- - Cliciwch y ddolen yma er mwyn ailosod eich cyfrinair - -
-

-

Os na allwch glicio ar y ddolen yma, copïwch a gludwch y URL i mewn i'ch porwr:

- - - - -
- - %1% - -
-

-
-
-


-
-
- - - ]]> -
- - - Dashfwrdd - Adrannau - Cynnwys - - - Dewis tudalen uwchben... - %0% wedi copïo i %1% - Dewiswch ble ddylai'r ddogfen %0% gael ei gopïo i isod - %0% wedi ei symud i %1% - Dewiswch ble ddylai'r ddogfen %0% gael ei symud i isod - wedi ei ddewis fel gwraidd eich cynnwys newydd, cliciwch 'iawn' isod. - Dim nod wedi'i ddewis eto, dewiswch nod yn y rhestr uchod yn gyntaf cyn clicio 'iawn' - Nid yw'r nod bresennol yn cael ei ganiatáu o dan y nod ddewiswyd oherwydd ei fath - Ni all y nod bresennol gael ei symud i un o'i is-dudalennau - Ni all y nod bresennol fodoli ar y gwraidd - Nid yw'r gweithred wedi'i ganiatáu gan nad oes gennych ddigon o hawliau ar gyfer 1 neu fwy o ddogfennau blentyn. - Perthnasu eitemau wedi'u copïo at y rhai gwreiddiol - - - %0%]]> - Gosodiad hysbysiadau wedi cadw am - - - - Mae'r ieithoedd canlynol wedi'u haddasu %0% - - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Helo %0%, -

-

- Mae hyn yn ebost awtomatig i'ch hysbysu fod y dasg '%1%' wedi'i berfformio ar y dudalen '%2%' gan y defnyddiwr '%3%' -

- - - - - - -
- -
- GOLYGU
-
-

-

Crynodeb diweddariad:

- - %6% -
-

-

- Mwynhewch eich diwrnod!

- Hwyl fawr oddi wrth y robot Umbraco -

-
-
-


-
-
- - - ]]> -
- - Mae'r ieithoedd canlynol wedi'u haddasu:

- %0% - ]]> -
- [%0%] Hysbysiad am %1% wedi perfformio am %2% - Hysbysiadau - - - Gweithredoedd - Creu - Creu pecyn - - - Pori a darganfod y pecyn. Fel arfer, mae gen becynnau Umbraco estyniadau ".umb" neu ".zip". - ]]> - - Bydd hwn yn dileu'r pecyn - Gollwng i lanlwytho - Cynhwyswch yr holl nodau plentyn - neu cliciwch yma i ddewis ffeil pecyn - Lanlwytho pecyn - Gosod pecyn leol wrth ddewis o'ch peiriant. Dylwch ddim ond osod pecynnau o ffynonellau yr ydych yn adnabod a bod gennych hyder ynddynt - Lanlwytho pecyn arall - Canslo a lanlwytho pecyn arall - Trwydded - Rydw i'n derbyn - termau defnydd - Llwybr i'r ffeil - Llwybr llwyr i'r ffeil (ie: /bin/umbraco.bin) - Wedi'i osod - Gosod yn lleol - Gosod pecyn - Gorffen - Pecynnau wedi'u gosod - Nid oes gennych unrhyw becynnau wedi'u gosod - 'Pecynnau' yng nghornel dop, dde eich sgrîn]]> - Nid oes gan y pecyn hwn unrhyw olwg cyfluniad - Nid oes unrhyw becynnau wedi'u creu eto - URL y Awdur - Cynnwys y Pecyn - Ffeiliau y Pecyn - URL Eicon - Gosod pecyn - Trwydded - URL Trwydded - Priodweddau Pecyn - Chwilio am becynnau - Canlyniadau ar gyfer - Ni allwn ddarganfod unrhyw beth ar gyfer - Ceisiwch chwilio am becyn arall neu porwch drwy'r categorïau - Poblogaidd - Pecynnau newydd - yn cynnwys - pwyntiau karma - Gwybodaeth - Perchennog - Cyfranwyr - Creuwyd - Fersiwn bresennol - Fersiwn .NET - Lawrlwythiadau - Hoffi - Cydweddoldeb - Mae'r pecyn yma yn gydnaws â'r fersiynau canlynol o Umbraco, fel y mae aelodau'r gymued yn adrodd yn ôl. Ni all warantu cydweddoldeb cyflawn ar gyfer fersiynau sydd wedi'u hadrodd o dan 100% - Ffynonellau allanol - Awdur - Arddangosiad - Dogfennaeth - Meta ddata pecynnau - Enw pecyn - Does dim eitemau o fewn y pecyn - -
- Gallwch ddileu hyn yn ddiogel o'r system wrth glicio "dadosod pecyn" isod.]]> -
- Dim uwchraddiadau ar gael - Dewisiadau pecyn - Readme pecyn - Ystorfa pecyn - Cadarnhau dadosod pecyn - Pecyn wedi dadosod - Cafodd y pecyn ei ddadosod yn llwyddiannus - Dadosod pecyn - - - Rhybudd: bydd unrhyw ddogfennau, cyfrwng ayyb sy'n dibynnu ar yr eitemau yr ydych am ddileu yn torri, a gall arwain at system ansefydlog, - felly dadosodwch gyda gofal. Os oes unrhyw amheuaeth, cysylltwch ag awdur y pecyn.]]> - - Lawrlwytho diweddariad o'r ystorfa - Uwchraddio pecyn - Cyfarwyddiadau uwchraddio - Mae yna uwchraddiad ar gael ar gyfer y pecyn yma. Gallwch lawrlwytho'n uniongyrchol o'r ystorfa pecynnau Umbraco. - Fersiwn pecyn - Uwchraddio o ferswin - Hanes ferswin pecyn - Gweld gwefan pecyn - Pecyn wedi'i osod eisoes - Ni all y pecyn yma gael ei osod, mae angen fersiwn Umrbaco o leiaf - Dadosod... - Lawrlwytho... - Mewnforio... - Gosod... - Ailgychwyn, arhoswch... - Wedi cwblhau, bydd eich porwr yn adnewyddu, arhoswch... - Cliciwch 'Cwblhau' i orffen y gosodiad ac adnewyddu'r dudalen. - Lanlwytho pecyn... - - - Gludo gyda fformatio llawn (Heb ei argymell) - Mae'r testun yr ydych yn ceisio gludo yn cynnwys nodauneu fformatio arbennig. Gall hyn gael ei achosi gan ludo testun o Microsoft Word. Gall Umbraco ddileu nodau neu fformatio arbennig yn awtomatig, fel bod y cynnwys sy'n cael ei ludo yn fwy addas ar gyfer y we. - Gludo fel testun crai heb unrhyw fformatio - Gludo, ond dileu fformatio (Wedi'i hargymell) - - - Amddiffyniad yn seiliedig grŵp - Os ydych chi am ganiatáu mynediad i bob aelod o grwpiau aelodau penodol - Mae angen i chi greu grŵp aelod cyn y gallwch ddefnyddio dilysiad grŵp - Amddiffyn ar sail rôl - Os hoffwch reoli cyrchiad i'r dudalen wrth ddefnyddio dilysu ar sail rôl, gan ddefnyddio grwpiau aelodaeth Umbraco. - Mae angen i chi greu grŵp aeloadeth cyn i chi allu defnyddio dilysu ar sail rôl - Tudalen Wall - Wedi'i ddefnyddio pan mae defnyddwyr wedi mewngofnodi, ond nid oes ganddynt hawliau - Dewiswch sut i gyfyngu hawliau at y dudalen yma - %0% wedi amddiffyn rwan - Amddiffyniad wedi dileu o %0% - Tudalen Mewngofnodi - Dewiswch y dudalen sy'n cynnwys y ffurflen mewngofnodi - Dileu Amddiffyniad - Dewiswch y tudalennau sy'n cynnwys ffurflenni mewngofnodi a negeseuon gwall - Dewiswch y rolau sydd a hawliau i'r dudlaen yma - Gosodwch yr enw defnyddiwr a chyfrinair ar gyfer y dudalen yma - Amddiffyniad defnyddiwr unigol - Os hoffwch osod amddifyniad syml wrth ddefnyddio enw defnyddiwr a chyfrinair sengl - %0%?]]> - %0%]]> - %0%]]> - Amddiffyn aelodau penodol - Os ydych am ganiatáu mynediad i aelodau penodol - - - Caniatâd annigonol gan ddefnyddwyr i gyhoeddi'r holl ddogfennau disgynyddion - - - - - - - - - - - - - - - - - - - - Methodd y dilysiad ar gyfer yr iaith ofynnol '%0%'. Roedd yr iaith wedi cael ei arbed ond nid ei chyhoeddi. - Cynnwys is-dudalennau heb eu cyhoeddi - Cyhoeddi ar waith - arhoswch... - %0% allan o %1% o dudalennau wedi eu cyhoeddi... - %0% wedi ei gyhoeddi - %0% ac eu is-dudalennau wedi'u cyhoeddi - Cyhoeddi %0% ac ei holl is-dudalennau - - Cyhoeddi er mwyn cyhoeddi %0% a felly yn gwneud i'r cynnwys berthnasol fod ar gael i'r cyhoedd.

- Gallwch gyhoeddi'r dudalen yma ac ei holl is-dudalennau wrth dicio Cynnwys tudalennau heb eu cyhoeddi isod. - ]]> -
- - - Nid ydych chi wedi ffurfweddu unrhyw liwiau sydd wedi'u cymeradwyo - - - Gallwch ond ddewis eitemau o'r math(au): %0% - Rydych wedi dewis eitem gynnwys sydd naill ai wedi'i ddileu neu yn y bin ailgylchu - Rydych wedi dewis eitemau gynnwys sydd naill ai wedi'u dileu neu yn y bin ailgylchu - - - Rydych wedi dewis eitem gyfrwng sydd naill ai wedi'i ddileu neu yn y bin ailgylchu - Rydych wedi dewis eitemau gyfrwng sydd naill ai wedi'u dileu neu yn y bin ailgylchu - Eitem wedi'i ddileu - Yn sbwriel - - - Darparwch ddolen allanol - Dewiswch dudalen fewnol - Capsiwn - Dolen - Agor mewn ffenestr newydd - Darparwch y capsiwn arddangos - Darparwch y ddolen - - - Ailosod tocio - Achub tocio - Ychwanegu tocio newydd - Wedi gwneud - Dadwneud golygion - Diffiniad defnyddiwr - - - Dewis fersiwn i gymharu efo fersiwn bresennol - Newidiadau - Creuwyd - Fersiwn bresennol - Ni fydd testun coch yn cael ei ddangos yn y fersiwn dewiswyd. , mae gwyrdd yn golygu wedi'i ychwanegu]]> - Dogfen wedi'i rolio yn ôl - Mae hyn yn dangos y fersiwn dewiswyd ar ffurf HTML, os hoffwch weld y gwahaniaeth rhwng 2 fersiwn ar yr un pryd, defnyddiwch y wedd gwahaniaethol - Rolio yn ôl at - Dewis fersiwn - Gwedd - - - Golygu ffeil sgript - - - Gwas - Cynnwys - Tywyswr - Datblygwr - Ffurflenni - Cymorth - Dewin Ffurfweddu Umbraco - Cyfrwng - Aelodau - Cylchlythyrau - Pecynnau - Gosodiadau - Ystadegau - Cyfieithiad - Defnyddwyr - Dadansoddeg - - - ewch i - Pynciau cymorth ar gyfer - Penodau fideo ar gyfer - Teithiau - Y fideos tiwtorial Umbraco gorau - Ymweld â our.umbraco.com - Ymweld â umbraco.tv - - - Templed diofyn - Allwedd Geiriadur - Er mwyn mewnforio math o ddogfen, darganfyddwch y ffeil ".udt" ar ecih cyfrifiadur wrth glicio ar y botwn "Pori" a cliciwch "Mewnforio" (byddwch yn cael eich gofyn i gadarnhau ar y sgrîn nesaf) - Teitl Tab Newydd - Math o nod - Math - Taflen arddull - Sgript - Priodwedd taflen arddull - Tab - Teitl Tab - Tabiau - Math o Gynnwys Meistr wedi'i alluogi - Mae'r Math o Gynnwys yma yn defnyddio - Dim priodweddau wedi'u diffinio ar y tab yma. Cliciwch ar y ddolen "ychwanegu priodwedd newydd" ar y topi greu priodwedd newydd. - Math o Ddogfen Feistr - Creu templedi cydweddol - Ychwanegu eicon - - - Trefn - Dyddiad creu - Trefnu wedi'i gwblhau. - Llusgwch yr eitemau gwahanol i fyny neu i lawr isod er mwyn gosod sut dylen nhw gael eu trefnu. Neu cliciwch ar beniadau'r golofnau i drefnu'r holl gasgliad o eitemau - - Nid oes gan y nod hwn nodau plentyn i trefnu - - - Dilysiad - Rhaid i wallau dilysu gael eu trwsio cyn gall yr eitem gael ei achub - Wedi methu - Wedi achub - Diffyg hawliau defnyddiwr, ni ellir cwblhau'r gweithred - Wedi canslo - Gweithred wedi'i ganslo gan ymestyniad 3-ydd parti - Cyhoeddi wedi'i ganslo gan ymestyniad 3-ydd parti - Math o briodwedd yn bodoli eisoes - Math o briodwedd wedi'i greu - Math o ddata: %1%]]> - math o briodwedd wedi'i ddileu - Math o Ddogfen wedi'u achub - Tab wedi'i greu - Tab wedi'i ddileu - Tab gyda id: %0% wedi'i ddileu - Taflen arddull heb ei achub - Taflen arddull wedi'i achub - Taflen arddull wedi'i achub heb unrhyw wallau - Math o ddata wedi'i achub - Eitem geiriadur wedi'i achub - Cyhoeddi wedi methu gan nad yw'r dudalen rhiant wedi'i gyhoeddi - Cynnwys wedi'i gyhoeddi - ac yn weladwy ar y wefan - %0% dogfennau wedi'i gyhoeddi ac yn gweledig ar y wefan - %0% gyhoeddi ac yn gweledig ar y wefan - %0% dogfennau wedi'i gyhoeddi am yr ieithoedd %1% ac yn gweledig ar y wefan - ac yn weladwy ar y wefan tan %0% at %1% - Cynnwys wedi'i achub - Cofiwch gyhoeddi er mwyn i'r newidiadau fod yn weladwy - Mae amserlen ar gyfer cyhoeddi wedi'i diweddaru - %0% wedi arbed - Bydd newidiadau yn cael ei gymerdwyo ar %0% at %1% - Wedi'i anfon am gymeradwyo - Newidiadau wedi'u hanfon am gymeradwyo - %0% newidiadau wedi'u hanfon am gymeradwyo - Cyfrwng wedi'i achub - Grŵp aeloadeth wedi'i achub - Cyfrwng wedi'i achub heb unrhyw wallau - Aelod wedi'i achub - Priodwedd taflen arddull wedi'i achub - Taflen arddull wedi'i achub - Templed wedi'i achub - Gwall yn achub y defnyddiwr (gwiriwch y log) - Defnyddiwr wedi'i achub - math o ddefnyddiwr wedi'i achub - Grŵp defnyddwyr wedi'i achub - Diwylliannau ac enwau gwesteia wedi'i achub - Gwall wrth achub diwylliannau ac enwau gwesteia - Ffeil heb ei achub - Ni ellir achub y ffeil. Gwiriwch hawliau'r ffeil - Ffeil wedi'i achub - Ffeil wedi'i achub heb unrhyw wallau - Iaith wedi'i achub - Math o Gyfrwng wedi'i achub - Math o Aelod wedi'i achub - Grŵp Aelod wedi'i achub - Sgript Python heb ei achub - Ni ellir achub y sgript Python oherwydd gwall - Sgript Python wedi'i achub - Dim gwallau yn y sgript Python - Templed heb ei achub - Sicrhewch nad oes gennych 2 dempled gyda'r un enw arall - Templed wedi'i achub - Templed wedi'i achub heb unrhyw wallau! - XSLT heb ei achub - XSLT yn cynnwys gwall - Ni ellir achub y ffeil XSLT, gwiriwch hawliau ffeil - XSLT wedi'i achub - Dim gwallau yn yr XSLT - Cynnwys wedi'i ddadgyhoeddi - amrywiad cynnwys %0% wedi'i dadgyhoeddi - Roedd yr iaith orfodol '%0%' wedi'i dadgyhoeddi. Mae'r holl ieithoedd ar gyfer yr eitem gynnwys hon bellach wedi'i dadgyhoeddi. - Rhan-wedd wedi'i achub - Rhan-wedd wedi'i achub heb unrhyw wallau! - Rhan-wedd heb ei achub - Bu gwall yn ystod achub y ffeil. - Hawliau wedi'u hachub ar gyfer - Gwedd sgript wedi'i achub - Gwedd sgript wedi'i achub heb unrhyw wallau! - Gwedd sgript heb ei achub - Bu gwall yn ystod achub y ffeil. - Bu gwall yn ystod achub y ffeil. - Wedi dileu %0% o rwpiau defnwyddwr - %0% wedi'i ddileu - %0% o ddefnyddwyr wedi'u galluogi - Bu gwall yn ystod galluogi'r defnyddwyr - Wedi analluogi %0% o ddefnyddwyr - Bu gwall yn ystod analluogi'r defnyddwyr - %0% yn awr wedi galluogi - Bu gwall yn ystod galluogi'r defnyddiwr - %0% yn awr wedi analluogi - Bu gwall yn ystod analluogi'r defnyddiwr - Grwpiau defnyddiwr wedi'u gosod - Wedi dileu %0% o rwpiau defnyddwyr - %0% wedi dileu - Wedi datgloi %0% o ddefnyddwyr - Bu gwall yn ystod datgloi'r defnyddwyr - %0% yn awr wedi datgloi - Bu gwall yn ystod datgloi'r defnyddiwr - Allforwyd yr aelod at ffeil - Bu gwall yn ystod allforio'r aelod - Defnyddiwr %0% wedi'i ddileu - Gawhodd defnyddiwr - Gwahoddiad wedi'i ail-anfon at %0% - Methu cyhoeddi'r ddogfen gan nad yw'r gofynnol '%0%' wedi cael ei gyhoeddi - Methodd dilysiad ar gyfer iaith '%0%' - Mae'r math dogfen wedi ei allforio i ffeil - Digwyddodd gwall wrth allforio'r math dogfen - Ni all y dyddiad rhyddhau fod yn y gorffennol - Ni all drefnu'r ddogfen i'w chyhoeddi gan nad yw'r gofynnol '%0%' wedi cael ei gyhoeddi - Ni all drefnu'r ddogfen i'w chyhoeddi oherwydd mae ganddo'r gofynnol '%0%' ddyddiad cyhoeddi yn hwyrach nag iaith nad yw'n orfodol - Ni all y dyddiad terfyn fod yn y gorffennol - Ni all y dyddiad terfyn fod cyn y dyddiad rhyddhau - - - Yn defnyddio cystrawen CSS e.e: h1, .coch, .glas - Ychwanegu ardull - Golygu ardull - Ardull golygydd testun cyfoethog - Diffiniwch yr arddulliau a ddylai fod ar gael yn y golygydd testun cyfoethog ar gyfer y daflen arddull hon - Golygu taflen arddull - Golygu priodwedd taflen arddull - Enw ar gyfer adnabod y priodwedd arddull yn y golygydd testun gyfoethog - Rhagolwg - Sut fydd y testun yn edrych yn y golygydd testun cyfoethog. - Dewisydd - Yn defnyddio cystrawen CSS e.e: h1, .coch, .glas - Arddulliau - Dyled y CSS ei gymhwyso yn y golygydd testun cyfoethog, e.g. "color:red;" - Côd - Golygydd - - - - Methwyd dileu templed efo'r ID %0% - Golygu templed - Adrannau - Mewnosod ardal cynnwys - Mewnosod dalfan ar gyfer ardal cynnwys - Mewnosod - Dewiswch beth i fewnosod i mewn i'ch templed - Eitem geiriadaur - Mae eitem geiriadur yn ddalfan ar gyfer darn o destun y gall gael ei gyfieithu, sy'n ei wneud yn hawdd i greu dyluniadau ar gyfer gwefannau aml-ieithog. - Macro - - Mae Macro yn gydran ffurfweddol sy'n wych ar gyfer - darnau o'ch dyluniad sy'n cael eu ail-ddefnyddio, ble mae angen y dewis i ddarparu paramedrau, - er enghraifft orielau, ffurflenni a rhestri. - - Gwerth - Yn dangos gwerth maes penodol o'r dudalen bresennol, gyda'r dewisiadau i newid y gwerth neu syrthio'n ôl at werthoedd eraill. - Rhan-wedd - - Mae rhan-wedd yn ffeil templed ar wahân y gall gael ei ddatganu o fewn templed arall, - mae'n wych ar gyfer ail-ddefnyddio côd neu ar gyfer gwahanu templedi cymhleth i mewn i ffeiliau gwahanol. - - Templed Meistr - Dim templed meistr - Dim meistr - - Datganu templed blentyn - - @RenderBody(). - ]]> - - Diffiniwch adran benodol - - @section { ... }. Gall hyn gael ei ddatganu mewn adran - benodol o rhiant y templed yma, wrth ddefnyddio @RenderSection. - ]]> - - Datganu adran benodol - - @RenderSection(name). - mae hyn yn datganu adran o dempled blentyn sydd wedi'i lapio mewn diffiniad berthnasol o @section [name]{ ... }. - ]]> - - Enw Adran - Mae Adran yn ofynnol - @section, fel arall bydd gwall yn cael ei ddangos. - ]]> - Adeiladwr ymholiad - Adeiladu ymholiad - o eitemau wedi dychwelyd, mewn - Copi i'r clipfwrdd - Rydw i eisiau - holl gynnwys - cynnwys o'r fath "%0%" - o - fy wefan - ble - ac - yn - ddim yn - cyn - cyn (gan gynnwys y dyddiad dewiswyd) - ar ôl - ar ôl (gan gynnwys y dyddiad dewiswyd) - yn gyfartal i - ddim yn gyfartal i - yn cynnwys - ddim yn cynnwys - yn fwy na - yn fwy na neu yn gyfartal i - llai na - llai na neu yn gyfartal i - Id - Enw - Dyddiad Creu - Dyddiad Diweddariad Ddiwethaf - trefnu wrth - esgynnol - disgynnol - Templed - - - Golygydd Testun Gyfoethog - Llun - Macro - Mewnosod - Pennawd - Dyfyniad - Dewis math o gynnwys - Dewis cynllun - Ychwanegu rhes - Ychwanegu cynnwys - Gollwng cynnwys - Gosodiadau wedi'u hymgeisio - Nid yw'r cynnwys yma wedi'i ganiatáu yma - Caniateir y cynnwys yma - Cliciwch i fewnblannu - Cliciwch i fewnosod llun - Capsiwn llun... - Ysgrifennwch yma... - Cynlluniau Grid - Cynlluniau yw'r holl ardal weithio gyfan ar gyfer y golygydd grid, fel arfer rydych ddim ond angen un neu ddau gynllun gwahanol - Ychwanegu Cynllun Grid - Golygu Cynllun Grid - Newid y cynllun wrth osod lledau colofnau ac ychwanegu adrannau ychwanegol - Ffurfweddau rhes - Mae rhesi yn gelloedd sydd wedi'u trefnu yn llorweddol - Ychwanegu Ffurfwedd rhes - Golygu Ffurfwedd rhes - Newidiwch y rhes wrth osod lledau colofn ac ychwanegu adrannau ychwanegol - Nid oes ffurfwedd pellach ar gael - Colofnau - Cyfanswm y nifer o golofnau yn y cynllun grid - Gosodiadau - Ffurfweddu pa osodiadau gall olygyddion eu newid - Ardduliau - Ffurfweddu pa arddulliau gall olygyddion eu newid - Bydd gosodiadau dim ond yn newid os mae'r ffurfwedd json yn ddilys - Caniatáu pob golygydd - Caniatáu holl ffurfweddi rhes - Uchafswm o eitemau - Gadewch yn wag neu gosod i 0 ar gyfer diderfyn - Gosod fel diofyn - Dewis ychwanegol - Dewis diofyn - wedi'u hychwanegu - Rhybudd - Rydych chi'n dileu'r ffurfwedd rhes - Bydd dileu enw ffurfwedd rhes yn arwain at golli data ar gyfer unrhyw gynnwys cynfodol sy'n seiliedig ar ffurfwedd hwn. - - - Cyfansoddiadau - Nid ydych wedi ychwanegu unrhyw dabiau - Ychwanegu tab newydd - Ychwanegu tab arall - Grŵp - Nid ydych wedi ychwanegu unrhyw grwpiau - Ychwanegu grŵp - Wedi etifeddu o - Ychwanegu priodwedd - Label gofynnol - Caniatáu gwedd rhestr - Ffurfweddi yr eitem gynnwys i ddangos rhestr trefnadwy a chwiladwy o'i phlant, ni fydd y plant yn cael eu dangos yn y goeden - Templedi Caniateir - Dewiswch pa olygoddion templedi sy'n cael defnyddio cynnwys o'r fath yma - Caniatáu fel gwraidd - Caniatáu golygyddion i greu cynnwys o'r fath yma yng ngwraidd y goeden gynnwys - Iawn - caniatáu cynnwys o'r fath yma yn y gwraidd - Mathau o nod blentyn caniateir - Caniatáu cynnwys o'r mathau benodol i gael eu creu o dan cynnwys o'r fath yma - Dewis nod blentyn - Etifeddu tabiau a phriodweddau o fath o ddogfen sy'n bodoli eisoes. Bydd tabiau newydd yn cael eu ychwanegu at y fath o ddogfen bresennol neu eu cyfuno os mae tab gyda enw yr union yr un fath yn bodoli eisoes. - Mae'r math o gynnwys yma wedi'i ddefnyddio mewn cyfansoddiad, felly ni ellir ei gyfansoddi ei hunan. - Nid oes unrhyw fathau o gynnwys ar gael i'w defnyddio fel cyfansoddiad. - Bydd dileu cyfansoddiad yn dileu'r holl ddata eiddo priodwedd gysylltiedig. Ar ôl i chi arbed y math o ddogfen, bydd ddim ffordd nôl. - Golygyddion ar gael - Ail-ddefnyddio - Gosodiadau golygydd - Ffurfweddau sydd ar gael - Creu ffurfwedd newydd - Ffurfwedd - Iawn, dileu - wedi symud islaw - wedi copïo islaw - Dewiswch y ffolder i symud - Dewiswch y ffolder i gopïo - i yn y strwythyr goeden isod - Holl Fathau o Ddogfennau - Holl Ddogfennau - Holl eitemau gyfrwng - sy'n defnyddio'r fath o ddogfen yma fydd yn cael eu dileu yn barhaol, cadarnhewch os hoffwch ddileu'r rhain hefyd. - sy'n defnyddio'r fath o gyfrwng yma fydd yn cael eu dileu yn barhaol, cadarnhewch os hoffwch ddileu'r rhain hefyd. - sy'n defnyddio'r fath o aelod yma fydd yn cael eu dileu yn barhaol, cadarnhewch os hoffwch ddileu'r rhain hefyd. - a phob dogfen sy'n defnyddio'r fath yma - a phob eitem gyfrwng sy'n defnyddio'r fath yma - a phob aelod sy'n defnyddio'r fath yma - sy'n defnyddio'r golygydd yma fydd yn cael eu diweddaru gyda'r gosodiadau newydd - Aeloed yn gallu golygu - Caniatáu i'r gwerth briodwedd yma gael ei olygu gan yr aelod ar eu tudalen broffil - Yn ddata sensitif - Cuddio'r priodwedd yma o'r golygyddion cynnwys sydd heb hawliau i weld gwybodaeth sensitif - Dangos ar broffil aelod - Caniatáu i'r gwerth briodwedd yma gael ei ddangos ar y dudalen broffil aelod - does dim rhif trefnu gan y tab - Ble mae'r cyfansoddiad yma'n cael ei ddefnyddio? - Mae'r cyfansoddiad yma yn cael ei ddefnyddio'n bresennol yng nghyfansoddiad o'r mathau o gynnwys ganlynol: - Caniatáu amrywiadau - Caniatáu amrywiad yn ôl ddiwylliant - Caniatáu segmentiad - Amrywio gan ddiwylliant - Amrywio gan segmentiad - Caniatáu i olygyddion greu cynnwys o'r math hwn mewn gwahanol ieithoedd - Caniatáu golygyddion i greu cynnwys o ieithoedd gwahanol - Caniatáu golygyddion i greu segmentiadau o'r cynnwys hwn - Caniatáu amrywio yn ôl diwylliant - Caniatáu segmentiad - Math o elfen - Yn fath Elfen - Mae math Elfen i fod i gael ei ddefnyddio er enghraifft mewn Cynnwys Nythu, ac nid yn y goeden - Ni ellir newid math o ddogfen i fath Elfen ar ôl mae'n cael ei defnyddio i greu un neu fwy o eitemau cynnwys. - Nid yw hyn yn berthnasol ar gyfer math Elfen - Rydych wedi gwneud newidiadau i'r eiddo hwn. Ydych chi'n siŵr eich bod chi am eu taflu? - - - Ychwanegu iaith - Iath gorfodol - Rhaid llenwi eiddo ar yr iaith hon cyn y gellir cyhoeddi'r nod. - Iaith diofyn - Gall wefan Umbraco ddim ond cael un iaith ddiofyn. - Gall newid iaith ddiofyn arwain at golli cynnwys diofyn. - Syrthio yn ôl i - Dim iaith cwympo yn ôl - Er mwyn caniatáu i gynnwys amlieithog ddisgyn yn ôl i iaith arall os nad yw'n bresennol yn yr iaith y gofynnwyd amdani, dewiswch hi yma. - Iaith cwympo yn ôl - dim - - - Ychwanegu paramedr - Golygu paramedr - Rhowch enw macro - Paramedrau - Diffiniwch y paramedrau a ddylai fod ar gael wrth ddefnyddio'r macro hwn. - Dewiswch ffeil macro golwg rhannol - - - Adeiladu modelau - gall hyn gymryd amser, peidiwch â phoeni - Modelau wedi'u generadu - Methwyd generadu modelau - Methwyd generadu modelau, gweler yr eithriadau yn y log Umbraco - - - Ychwanegu maes rolio yn ôl - Maes rolio yn ôl - Ychwanegu gwerth diofyn - Gwerth diofyn - Maes rolio yn ôl - Gwerth diofyn - Cyflwr - Amgodiad - Dewis maes - Trawsnewid torriadau llinellau - Iawn, trawsnewid torriadau llinellau - Cyfnewid torriadau llinellau gyda tag html 'br' - Meysydd bersonol - Dyddiad yn unig - Fformat ac amgodiad - Fformatio ar ffurf dyddiad - Fformatio'r gwerth ar ffurf dyddiad, neu dyddiad gyda amser, yn ôl y diwylliant gweithredol - Amgodi HTML - Bydd yn cyfnewid nodau arbennig gyda'u nodau HTML cyfatebol. - Bydd yn cael ei fewnosod ar ôl y gwerth maes - Bydd yn cael ei fewnosod cyn y gwerth maes - Llythrennau bach - Newid allbwn - Dim - Sampl allbwn - Mewnosod ar ôl maes - Mewnosod cyn maes - Ailadroddus - Iawn, gwnewch yn ailadroddus - Gwahanwr - Meysydd Safonol - Llythrennau bras - Amgodi URL - Bydd yn fformatio nodau arbennig o fewn URL - Bydd ddim ond yn cael ei ddefnyddio pan mae'r gwerthoedd maes uchod yn wag - Bydd y maes yma ddim ond yn cael ei ddefnyddio os mae'r maes gynradd yn wag - Dyddiad ac amser - - - Tasgau wedi'u neilltuo i chi - - wedi'u neilltuo i chi. Er mwyn gweld gwedd fanwl gan gynnwys sylwadau, cliciwch ar "Manylion" neu enw'r dudalen. - Gallwch hefyd lawrlwytho'r dudalen ar ffurf XML yn uniongyrchol gan glicio'r ddolen "Lawrlwytho Xml".
- Er mwyn cau tasg cyfieithu, ewch at y wedd fanylion a cliciwch ar y botwm "Cau". - ]]> -
- cau tasg - Manylion cyfieithiad - Lawrlwytho pob tasg cyfieithu ar ffurf XML - Lawrlwytho XML - Lawrlwytho XML DTD - Meysydd - Cynnwys is-dudalennau - - - - [%0%] Tasg cyfieithu ar gyfer %1% - Dim defnyddwyr cyfieithu wedi'u darganfod. Creuwch ddefnyddiwr cyfieithu cyn i chi gychwyn anfon cynnwys am gyfieithiadau - Tasgau wedi'u creu gennych chi - - wedi'u creu gennych chi. Er mwyn gweld gwedd fanwl sy'n cynnwys sylwadau, - cliciwch ar "Manylion" neu enw'r dudalen. Gallwch hefyd lawrlwytho'r dudalen ar ffurf XML yn uniongyrchol gan glicio ar y ddolen "Lawrlwytho Xml". - Er mwyn cau tasgau cyfieithu, ewch at y wedd fanylion a cliciwch y botwm "Cau". - ]]> - - Mae'r dudalen '%0%' wedi cael ei anfon am gyfieithiad - Dewiswch yr iaith y dylai'r cynnwys gael ei gyfieithu i - Anfon y dudalen '%0%' am gyfieithiad - Wedi'i neilltuo gan - Tasg wedi'i hagor - Cyfanswm o eiriau - Cyfieithu i - Cyfieithiad wedi'i gwblhau. - Gallwch ragolygu'r tudalennau yr ydych newydd gyfieithu gan glicio isod. Os mae'r dudalen gwreiddiol wedi'i ganfod, byddwch yn cael cymhariaeth o'r 2 dudalen. - Cyfieithiad wedi methu, mae'n bosib fod y ffeil XML wedi llygru - Dewisiadau cyfieithu - Cyfieithydd - Lanlwytho cyfieithiad XML - - - Cynnwys - Templedi Cynnwys - Cyfrwng - Porwr Storfa - Bin Ailgylchu - Pecynnau wedi'u creu - Mathau o Ddata - Geiriadur - Pecynnau wedi'u gosod - Gosod croen - Gosod cit gychwynol - Ieithoedd - Gosod pecyn leol - Macros - Mathau o Gyfrwng - Aelodau - Grwpiau Aelodau - Grwpiau Rolau - Mathau o Aelod - Mathau o Ddogfen - Math o Berthynas - Pecynnydd - Pecynnau - Rhan-weddi - Ffeiliau Rhan-wedd Macro - Ffeiliau Python - Gosod o ystorfa - Gosod Runway - Modylau Runway - Ffeiliau Sgriptio - Sgriptiau - Taflenni arddull - Templedi - Ffeiliau XSLT - Dadansoddeg - Gwyliwr Log - Defnyddwyr - Gosodiadau - Templedi - Trydydd parti - - - Diweddariad newydd yn barod - %0% yn barod, cliciwch yma i lawrlwytho - Dim cysylltiad at y gweinydd - Gwall yn chwilio am ddiweddariad. Ceisiwch wirio'r trywydd stac am fwy o wybodaeth - - - Mynediad - Ar sail y grwpiau aelodaeth ac y nodau cychwyn, mae gan y defnyddiwr hawliau at y nodau ganlynol - Neilltuo hawl - Gweinyddwr - Maes categori - Defnyddiwr wedi'i greu - Newidiwch Eich Cyfrinair - Newidiwch lun - Cyfrinair newydd - ddim wedi cloi allan - Nid yw'r cyfrinair wedi'i newid - Cadarnhau cyfrinair newydd - Gallwch newid eich cyfrinair i gyrchu Swyddfa Gefn Umbracogan lenwi allan y ffurflen isod a chlicio'r botwm 'Newid Cyfrinair' - Sianel Gynnwys - Creu defnyddiwr arall - Creu defnyddwyr newydd i roi hawliau iddynt gyrchu Umbraco. Pan mae defnyddiwr newydd yn cael ei greu, bydd cyfrinair yn cael ei generadu y gallwch chi rannu gyda'r defnyddiwr. - Maes disgrifiad - Analluogi Defnyddiwr - Math o Ddogfen - Golygydd - Maes dyfyniad - Nifer o fethiannau ceisio mewngofnodi - Ewch at broffil defnyddiwr - Ychwanegu grwpiau i neilltuo mynediad a hawliau - Gwahodd defnyddiwr arall - Gwahodd defnyddwyr newydd i roi hawliau iddynt gyrchu Umbraco. Bydd gwahoddiad ebost yn cael ei anfon at y defnyddiwr gyda gwybodaeth ar sut i fewngofnodi i Umbraco. Mae gwahoddiadau yn para am 72 awr. - Iaith - Gosod yr iaith fyddwch chi'n gweld yn y dewislenni a'r deialogau - Dyddiad cloi allan diweddaraf - Mewngofnodi diweddaraf - Cyfrinair wedi'i newid ddiwethaf - Enw defnyddiwr - Nod gychwynol gyfrwng - Cyfyngu'r llyfrgell gyfrwng at nod gychwynol benodol - Nodau gychwynol gyfrwng - Cyfyngu'r llyfrgell gyfrwng at nodau gychwynol benodol - Adrannau - Analluogi Mynediad Umbraco - ddim wedi mewngofnodi eto - Hen gyfrinair - Cyfrinair - Ailosod cyfrinair - Mae eich cyfrinair wedi'i newid! - Cyfrinair wedi'i newid - Cadarnhewch y cyfrinair newydd - Darparwch eich cyfrinair newydd - Ni all eich cyfrinair newydd fod yn wag! - Cyfrinair bresennol - Cyfrinair bresennol annilys - Roedd gwahaniaeth rhwng y cyfrinair newydd ac y cyfrinair i gadarnhau. Ceisiwch eto! - Nid yw'r cyfrinair cadarnhau yn cyfateb â'r cyfrinair newydd! - Cyfnewid hawliau nod blentyn - Rydych ar hyn o bryd yn newid hawliau ar gyfer y tudalennau: - Dewis tudalennau i newid eu hawliau - Dileu llun - Hawliau diofyn - Hawliau gronynnog - Gosod hawliau ar gyfer nodau penodol - Proffil - Chwilio holl blant - Ychwanegu adrannau i roi hawliau i ddefnyddwyr - Dewis grwpiau defnyddwir - Dim nod gychwynol wedi'i ddewis - Dim nodau cychwynol wedi'u dewis - Nod gynnwys gychwynol - Cyfyngu'r goeden gynnwys i nod gychwynol benodol - Nodau cynnwys gychwynol - Cyfyngu'r goeden gynnwys i nodau gychwynol benodol - Defnyddiwr wedi diweddaru ddiwethaf - wedi ei greu - Mae'r defnyddiwr newydd wedi'i greu. Er mwyn mewngofnodi i Umbraco defnyddiwch y cyfrinair isod. - Rheoli defnyddwyr - Enw - Hawliau defnyddiwr - Hawliau grwpiau defnyddiwr - Grŵp defnyddiwr - Grwpiau defnyddiwr - wedi'i wahodd - Mae gwahoddiad wedi cael ei anfon at y defnyddiwr newydd gyda manylion ar sut i fewngofnodi i Umbraco. - Helo a chroeso i Umbraco! Mewn 1 munud yn unig, byddech chi'n barod i fynd, rydym dim ond angen gosod cyfrinair a llun ar gyfer eich avatar. - Croeso i Umbraco! Yn anffodus, mae eich gwahoddiad wedi terfynu. Cysylltwch â'ch gweinyddwr a gofynnwch iddynt ail-anfon. - Lanlwythwch lun i wneud o'n haws i boble eich adnabod chi. - Ysgrifennydd - Cyfieithydd - Newid - Eich proffil - Eich hanes diweddar - Sesiwn yn terfynu mewn - Gwahodd defnyddiwr - Creu defnyddiwr - Anfon gwahoddiad - Yn ôl at ddefnyddwyr - Umbraco: Gwahoddiad - - - - - - - - - - - - -
- - - - - -
- -
- -
-
- - - - - - -
-
-
- - - - -
- - - - -
-

- Helo %0%, -

-

- Rydych wedi cael eich gwahodd gan %1% i'r Swyddfa Gefn Umbraco. -

-

- Neges oddi wrth %1%: -
- %2% -

- - - - - - -
- - - - - - -
- - Cliciwch y ddolen yma i dderbyn y gwahoddiad - -
-
-

Os na allwch chi glicio ar y ddolen, copiwch a gludwch y URL i mewn i'ch porwr:

- - - - -
- - %3% - -
-

-
-
-


-
-
- - ]]> -
- Gwahoddiad - Yn ail-anfon y gwahoddiad... - Dileu Defnyddiwr - Ydych chi'n sicr eich bod eisiau dileu'r cyfrif defnyddiwr yma? - Pob - Gweithredol - Wedi analluogi - Wedi cloi allan - Wedi gwahodd - Anactif - Enw (A-Y) - Enw (Y-A) - Hynaf - Diweddaraf - Mewngofnodi diweddaraf - No user groups have been added - - - Dilysiad - Dim dilysiad - Dilysu fel cyfeiriad ebost - Dilysu fel rhif - Dilysu fel URL - ...neu darparwch ddilysiad bersonol - Maes yn ofynnol - Darparwch neges gwall dilysiad arferu (opsiynol) - Darparwch fynegiad rheoliadd - Darparwch neges gwall dilysiad arferu (opsiynol) - Mae angen i chi ychwanegu o leiaf - gallwch ddim ond gael - Adio lan i - o eitemau - url(s) - url(s) wedi'i ddewis - o eitemau wedi'u dewis - Dyddiad annilys - Ddim yn rif - Ebost annilys - Ni all y gwerth fod yn null - Ni all y gwerth fod yn gwag - Mae'r gwerth yn annilys, nid yw'n cyfateb i'r patrwm cywir - Dilysiad arferu - %1% mwy.]]> - %1% gormod.]]> - - - - Gwerth wedi'i osod at y gwerth argymhellwyd: '%0%'. - Gwerth wedi'i osod at '%1%' ar gyfer XPath '%2%' yn y ffeil ffurfweddu '%3%'. - Yn disgwyl y gwerth '%1%' ar gyfer '%2%' yn y ffeil ffurfweddu '%3%', ond darganfyddwyd '%0%'. - Darganfyddwyd gwerth annisgwyl '%0%' ar gyfer '%2%' yn y ffeil ffurfweddu '%3%'. - - Gwallau bersonol wedi gosod at '%0%'. - Gwallau bersonol wedi gosod at '%0%' yn bresennol. Argymhellwyd i osod hyn i '%1%' cyn mynd yn fyw. - Gwallau bersonol wedi gosod at '%0%' yn llwyddiannus. - - Gwallau Macro wedi gosod at '%0%'. - Gwallau Macro wedi gosod at '%0%' a fydd yn atal rhai neu holl dudalennau yn eich safle rhag llwytho'n gyfan gwbl os oes unrhyw wallau o fewn macros. Bydd cywiro hyn yn gosod y gwerth at '%1%'. - Gwallau Macro wedi gosod at '%0%' yn llwyddiannus. - - Ceisio sgipio Gwallau IIS Bersonol wedi'i osod at '%0%' ac rydych yn defnyddio fersiwn IIS '%1%'. - Ceisio sgipio Gwallau IIS Bersonol wedi'i osod at '%0%'. Argymhellwyd gosod hyn at '%1%' ar gyfer eich fersiwn IIS (%2%). - Ceisio sgipio Gwallau IIS Bersonol wedi'i osod at '%0%' yn llwyddiannus. - - Ffeil ddim yn bodoli: '%0%'. - '%0%' yn y ffeil ffurfweddu '%1%'.]]> - Bu gwall, gwiriwch y log ar gyfer y gwall cyflawn: %0%. - Aelodau - Cyfanswm XML: %0%, Cyfanswm: %1%, Cyfanswm annilys: %2% - Cyfrwng - Cyfanswm XML: %0%, Cyfanswm: %1%, Cyfanswm annilys: %2% - Cynnwys - Cyfanswm XML: %0%, Cyfanswm wedi cyhoeddi: %1%, Cyfanswm annilys: %2% - Cronfa ddata - Mae'r sgema gronfa ddata yn gywir ar gyfer y fersiwn yma o Umbraco - %0% o broblemau wedi'u canfod gyda'ch sgema gronfa ddata (Gwiriwch y log am fanylion) - Darganfyddwyd gwallau wrth ddilysu'r sgema gronfa ddata yn erbyn y fersiwn bresennol o Umbraco. - Mae tystysgrif eich gwefan yn ddilys. - Gwall dilysu tystysgrif: '%0%' - Mae tystysgrif SSL eich gwefan wedi terfynu. - Mae tystysgrif SSL eich gwefan am derfynu mewn %0% diwrnod. - Gwall yn pingio'r URL %0% - '%1%' - Rydych yn bresennol %0% yn gweld y wefan yn defnyddio'r cynllun HTTPS. - Mae'r appSetting 'umbracoUseSSL' wedi'i osod at 'false' yn eich ffeil web.config. Unwaith rydych yn ymweld â'r safle gan ddefnyddio'r cynllun HTTPS, dylai hynny gael ei osod i 'true'. - Mae'r appSetting 'umbracoUseSSL' wedi'i osod at '%0%' yn eich ffeil web.config, mae eich cwcis %1% marcio yn ddiogel. - Ni ellir diweddaru'r gosodiad 'umbracoUseSSL' yn eich ffeil web.config. Gwall: %0% - - Galluogi HTTPS - Yn gosod umbracoSSL i true yn yr appSettings yn y ffeil web.config. - Mae'r appSetting 'umbracoUseSSL' yn awr wedi'i osod at 'true' yn eich ffeil web.config, bydd eich cwcis wedi eu marcio yn ddiogel. - Trwsio - Ni ellir trwsio gwiriad gyda math chymhariaeth gwerth o 'ShouldNotEqual'. - Ni ellir trwsio gwiriad gyda math chymhariaeth gwerth o 'ShouldEqual' gyda gwerth a ddarparwyd. - Gwerth i drwrsio gwiriad heb ei ddarparu. - Modd casgliad dadfygio wedi'i analluogi. - Modd casgliad dadfygio wedi'i alluogi. Argymhellwyd analluogi'r gosodiad yma cyn mynd yn fyw. - Modd casgliad dadfygio wedi'i analluogi yn llwyddiannus. - Modd olrhain wedi'i analluogi. - Modd olrhain wedi'i alluogi. Argymhellwyd analluogi'r gosodiad yma cyn mynd yn fyw. - Modd olrhain wedi'i analluogi yn llwyddiannus. - Mae gan pob ffolder yr hawliau cywir wedi'u gosod. - - %0%.]]> - %0%. Os nad ydyn nhw'n cael eu ysgrifennu atynt, does dim angen unrhyw weithred.]]> - Mae gan pob ffeil yr hawliau cywir wedi'u gosod. - - %0%.]]> - %0%. Os nad ydyn nhw'n cael eu ysgrifennu atynt, does dim angen unrhyw weithred.]]> - X-Frame-Options sy'n cael ei ddefnyddio i reoli os mae safle'n gallu cael ei osod o fewn IFRAME gan safle arall wedi'i ganfod.]]> - X-Frame-Options sy'n cael ei ddefnyddio i reoli os mae safle'n gallu cael ei osod o fewn IFRAME gan safle arall wedi'i ganfod.]]> - Gosod Peniad o fewn Ffurfwedd - Ychwanegu gwerth at yr adran httpProtocol/customHeaders o'r ffeil web.config er mwyn atal y safle rhag cael ei ddangos o fewn IFRAME gan safleoedd eraill. - Gosodiad ar gyfer creu peniad sy'n atal y wefan rhag cael ei ddangos o fewn IFRAME ar safle arall wedi'i ychwanegu at eich ffeil web.config. - Ni ellir diweddaru'r ffeil web.config. Gwall: %0% - X-Content-Type-Options sy'n cael ei ddefnyddio i amddiffyn yn erbyn gwendidau sniffio MIME wedi'i ganfod.]]> - X-Content-Type-Options sy'n cael ei ddefnyddio i amddiffyn yn erbyn gwendidau sniffio MIME wedi'i ganfod.]]> - Ychwanegu gwerth at yr adran httpProtocol/customHeaders o'r ffeil web.config er mwyn amddiffyn yn erbyn gwendidau sniffio MIME. - Gosodiad ar gyfer creu peniad sy'n amddiffyn yn erbyn gwendidau sniffio MIME wedi'i ychwanegu at eich ffeil web.config. - Strict-Transport-Security, hefyd wedi'i adnabod fel HSTS-header, wedi'i ganfod.]]> - Strict-Transport-Security wedi'i ganfod.]]> - Ychwanegu'r peniad 'Strict-Transport-Security' gyda'r gwerth 'max-age=10886400; preload' i'r adran httpProtocol/customHeaders o'r ffeil web.config. Defnyddiwch y trwsiad hyn dim ond os bydd gennych chi eich parthau yn rhedeg gyda https am yr 18 wythnos nesaf (o leiaf). - Mae'r peniad HSTS wedi'i ychwanegu at y ffeil web.config. - X-XSS-Protection wedi'i ganfod.]]> - X-XSS-Protection wedi'i ganfod.]]> - Ychwanegu'r peniad 'X-XSS-Protection' gyda'r gwerth '1; mode=block' at yr adran httpProtocol/customHeaders yn y ffeil web.config. - Mae'r peniad X-XSS-Protection wedi'i ychwanegu at y ffeil web.config. - - %0%.]]> - Dim peniadau sy'n datgelu gwynodaeth am dechnoleg eich gwefan wedi'u canfod. - Ni ellir darganfod system.net/mailsettings yn y ffeil Web.config. - Yn yr adran system.net/mailsettings o'r ffeil Web.config, nid yw'r "host" wedi ffurfweddu. - Gosodiadau SMTP wedi ffurfweddu'n gywir ac mae'r gwasanaeth yn gweithio fel y disgwylir. - Ni ellir cysylltu â gweinydd SMTP sydd wedi ffurfweddu gyda "host" '%0%' a phorth '%1%'. Gwiriwch fod y gosodiadau SMTP yn y ffeil Web.config, system.net/mailsettings yn gywir. - %0%.]]> - %0%.]]> -

Canlyniadau'r gwiriad Statws Iechyd Umbraco ar amserlen rhedwyd ar %0% am %1% fel y ganlyn:

%2%]]>
- Statws Iechyd Umbraco: %0% - Gwiriwch Pob Grŵp - Gwiriwch y grŵp - - Mae'r gwiriwr iechyd yn gwerthuso gwahanol rannau o'ch gwefan ar gyfer gosodiadau arfer gorau, cyfluniad, problemau posibl, ac ati. Gallwch chi drwsio problemau yn hawdd trwy wasgu botwm. - Gallwch chi ychwanegu eich gwiriadau iechyd eich hun, edrych ar y ddogfennaeth i gael mwy o wybodaeth am wiriadau iechyd arferu.

- ]]> -
- Eich wefan gallu defnyddio y protocol gwarchodaeth TLS 1.2 wrth wneud cysylltiadau allanol i endpoints HTTPS - Nid yw'ch gwefan wedi'i ffurfweddu i ganiatáu protocol diogelwch TLS 1.2 wrth wneud cysylltiadau allanol: efallai na fydd modd cyrchu rhai endpoints HTTPS gan ddefnyddio protocol llai diogel. - - - Analluogi olinydd URL - Galluogi olinydd URL - Diwylliant - URL gwreiddiol - Ailgyfeirwyd I - Gweinyddu Ailgyfeirio URLs - Mae'r URLs ganlynol yn ailgyfeirio at yr eitem gynnwys yma: - Dim ailgyfeiriadau wedi'u gwneud - Pan mae tudalen wedi'i gyhoeddi yn cael ei ailenwi neu symud bydd ailgyfeiriad yn cael ei greu yn awtomatig at y dudalen newydd. - Dileu - Ydych chi'n sicr eich bod eisiau dileu'r ailgyfeiriad o '%0%' at '%1%'? - URL ailgyfeirio wedi'i ddileu. - Gwall yn dileu'r URL. - Bydd hyn yn dileu'r ailgyfeiriad - Ydych chi'n sicr eich bod eisiau analluogi'r olinydd URL? - Mae'r olinydd URL wedi cael ei analluogi. - Gwall yn ystod analluogi'r olinydd URL, gall fwy o wybodaeth gael ei ddarganfod yn eich ffeil log. - Mae'r olinydd URL wedi cael ei alluogi. - Gwall yn ystod galluogi'r olinydd URL, gall fwy o wybodaeth gael ei ddarganfod yn eich ffeil log. - - - Dim eitemau Geiriadur i ddewis ohonynt - - - o nodau ar ôl - %1% gormod.]]> - - - Wedi chwalu cynnwys gyda Id: {0} yn berthnasol i gynnwys rhiant gwreiddiol gyda Id: {1} - Wedi chwalu cyfrwng gyda Id: {0} yn berthnasol i gyfrwng rhiant gwreiddiol gyda Id: {1} - Ni ellir adfer yr eitem yma yn awtomatig - Nid oes unrhyw leoliad lle gellir adfer yr eitem hon yn awtomatig. Gallwch chi symud yr eitem â llaw gan ddefnyddio'r goeden isod. - oedd adferwyd o dan - Does dim perthynas 'adfer' ar gael ar gyfer y nod yma. Defnyddiwch y ddewislen Symud i'w symud â llaw. - Mae'r eitem yr ydych eisiau adfer yr item oddi tan ('%0%') yn y bin ailgylchu. Defnyddiwch y ddewislen Symud i'w symud â llaw. - - - Cyfeiriad - Rhiant i plentyn - Deugyfeiriadol - Rhiant - Plentyn - Cyfrif - Cysylltiadau - Creu - Sylw - Enw - Dim cysylltiadau ar gyfer y math hwn o berthynas. - Math o Berthynas - Cysylltiadau - - - Dechrau Arni - Rheolaeth Ailgyfeirio URL - Cynnwys - Croeso - Rheolaeth Examine - Statws Cyhoeddedig - Adeiladwr Modelau - Gwiriad Iechyd - Proffilio - Dechrau Arni - Gosod Ffurflenni Umbraco - - - Mynd yn ôl - Cynllun gweithredol: - Neidio i - grŵp - pasio - rhybudd - methu - awgrym - Gwiriad wedi'i basio - Gwiriad wedi'i methu - Agor chwiliad swyddfa gefn - Agor/Cau cymorth swyddfa gefn - Agor/Cau eich opsiynau proffil - Sefydli Diwylliannau ac Enwau Gwesteia am %0% - Creu nod newydd o dan %0% - Sefydli Mynediad Cyhoeddus ar %0% - Sefydli Caniataid ar %0% - Newid y trefniad am %0% - Creu templed cynnwys yn seiliedig ar %0% - Agor dewislen cyd-destun ar gyfer - Iaith gyfredol - Newid iaith i - Creu ffolder newydd - Golwg Rhannol - Macro Golwg Rhannol - Aelod - Math o ddata - Chwilio'r dangosfwrdd ailgyfeirio - Chwilio'r adran grŵp defnyddwyr - Chwilio'r adran defnyddwyr - Creu eitem - Creu - Golygu - Enw - Ychwanegu rhes newydd - Gweld mwy o opsiynau - Wedi cyfieithu - Cyfieithiad ar goll - Eitemau geiriadur - - - Cyfeiriadau - This Data Type has no references. Nid oes gan y Math o Ddata hwn unrhyw gyferiadau. - Defnyddir mewn Mathau o Ddogfennau - Ddim cyfeiriadau i Fathau o Ddogfennau. - Defnyddir mewm Mathau o Gyfrwng - Ddim cyfeiriadau i Fathau o Gyfrwng. - Defnyddir mewn Mathau o Aelod - Ddim cyfeiriadau i Fathau o Aelod. - Defnyddir gan - A ddefnyddir yn Ddogfennau - A ddefnyddir yn Aelodau - A ddefnyddir yn Cyfryngau - - - Dileu Chwiliad Cadwedig - Lefelau Log - Chwiliadau Cadwedig - Arbed Chwiliad - Rhoi enw cyfeillgar am eich ymholiad chwilio - Hidlo Chwiliad - Cyfanswm o Eitemau - Stamp Amser - Lefel - Peiriant - Neges - Eithriad - Priodweddau - Chwilio efo Google - Chwiliwch y neges hon efo Google - Chwilio efo Bing - Chwiliwch y neges hon efo Bing - Chwilio Our Umbraco - Chwiliwch y neges hon arno Our Umbraco fforymau a dogfennau - Chwilio Our Umbraco efo Google - Chwilio Our Umbraco fforymau efo Google - Chwilio'r cod gwreiddiol Umbraco - Chwilio tu fewn y cod gwreiddiol Umbraco ar Github - Chwilio Problemau Umbraco - Chwilio Problemau Umbraco ar Github - Dileu chwiliad hon - Darganfod logiau efo ID y Cais - Darganfod logiau efo Namespace - Darganfod logiau efo Enw Peiriant - Agor - - - Copi %0% - %0% o %1% - Dileu pob eitem - Clirio y clipfwrdd - - - Agor Gweithredoedd Priodweddau - Cau Gweithredoedd Priodweddau - - - Aros - Adnewyddu statws - Cuddstôr Cof - - - - Ail-lwytho - Cuddstôr Cronfa Ddata - - Gall ailadeiladu fod yn ddrud. - Defnyddio fo pan mae ail-lwytho ddim yn ddigon, a ti'n feddwl mai'r stôr cronfa ddata heb gael ei - chynhyrchu'n iawn—a fyddai'n arwydd o broblem gritigol efo Umbraco. - ]]> - - Ailadeiladu - Mewnol - - nad oes angeni chi ei defnyddio. - ]]> - - Casglu - Statws Cuddstôr Cyhoeddedig - Cuddstorau - - - Proffilio perfformiad - - - Mae Umbraco yn rhedeg mewn modd dadfygio. Mae hyn yn golygu y gallwch chi ddefnyddio'r proffiliwr perfformiad adeiledig i asesu'r perfformiad wrth rendro tudalennau. -

-

- OS ti eisiau actifadu'r proffiliwr am rendro tudalen penodol, bydd angen ychwanegu umbDebug=true i'r ymholiad wrth geisio am y tudalen -

-

- Os ydych chi am i'r proffiliwr gael ei actifadu yn ddiofyn am bob rendrad tudalen, gallwch chi ddefnyddio'r togl isod. - Bydd e'n gosod cwci yn eich porwr, sydd wedyn yn actifadu'r proffiliwr yn awtomatig. - Mewn geiriau eraill, bydd y proffiliwr dim ond yn actif yn ddiofyn yn eich porwr chi - nid porwr pawb eraill. -

- ]]> -
- Actifadu y proffiliwr yn ddiofyn - Nodyn atgoffa cyfeillgar - - - Ni ddylech chi fyth adael i safle cynhyrchu redeg yn y modd dadfygio. Mae'r modd dadfygio yn gallu cael ei diffodd trwy ychwanegu'r gosodiad debug="false" ar yr elfen <grynhoi /> yn web.config. -

- ]]> -
- - - Mae Umbraco ddim yn rhedeg mewn modd dadfygio ar hyn o bryd, felly nid allwch chi ddefnyddio'r proffiliwer adeiledig. Dyma sut y dylai fod ar gyfer safle cynhyrchu. -

-

- Mae'r modd dadfygio yn gallu cael ei throi arno gan ychwanegu'r gosodiad debug="true" ar yr elfen <grynhoi /> yn web.config. -

- ]]> -
- - - Oriau o fideos hyfforddiant Umbraco ddim ond un clic i fwrdd - - Eisiau meistroli Umbraco? Treuliwch gwpl o funudau yn dysgu rhai o'r arferion gorau gan wylio un o'r fideos hyn am sut i ddefnyddio Umbraco. Ac ymweld â umbraco.tv am fwy o fideos am Umbraco

- ]]> -
- I roi cychwyn i chi - - - Dechrau yma - Mae'r adran hon yn cynnwys y blociau adeiladu am eich safle Umbraco. Dilyn y dolenni isod i ddarganfod fwy am weithio gyda'r eitemau yn yr adran Gosodiadau - Ddarganfod fwy - - fewn yr adran Dogfennaeth o Our Umbraco - ]]> - - - Fforwm Cymunedol - ]]> - - - fideos tiwtorial (mae rhai am ddim, ond bydd angen tanysgrifiad am rhai eraill) - ]]> - - - hoffer hybu cynhyrchiant a chefnogaeth fasnachol - ]]> - - - hyfforddi ac ardystio - ]]> - - - - Croeso i'r SRC cyfeillgar - Diolch am ddewis Umbraco - rydyn ni'n credu y gallai hyn fod dechreuad i rywbeth prydferth. Er y gallai deilo'n llethol ar y dechrau, rydym wedi gwneud llawer i wneud y gromlin ddysgu mor llyfn a chyflym a phosib. - - - Ffurflenni Umbraco - Creu ffurflenni gan ddefnyddio rhyngwyneb llusgo a gollwng sythweledol. O ffurflenni cyswllt syml sy'n anfon e-byst, i holiaduron mwy datblygedig sy'n integreiddio efo systemau CRM. Bydd eich cleientiaid wrth ei modd! - - - Creu bloc newydd - Atodwch adran gosodiadau - Dewis golygfa - Dewis taflen arddull - Dewis delwedd bawd - Creu newydd - Taflen arddull arferu - Ychwanegu taflen arddull - Ymddangosiad y golygydd - Modelau data - Ymddangosiad y catalog - Lliw cefndir - Lliw eicon - Model Cynnwys - Label - Golygfa arferu - Ddangos disgrifiad golygfa arferu - Trosysgrifo sut mae'r bloc hwn yn ymddangos yn yr UI y swyddfa gefn. Dewis ffeil .html sy'n cynnwys eich cyflwyniad. - Model gosodiadau - Maint y golygydd troshaen - Ychwanegu golygfa arferu - Ychwanegu gosodiadau - Trosysgrifo templed label - %0%.]]> - %0%.]]> - Bydd cynnwys y bloc hwn yn dal i fod yn bresennol, ni fydd golygu'r cynnwys hwn ar gael mwyach a bydd yn cael ei ddangos fel cynnwys heb gefnogaeth. - - Delwedd bawd - Ychwanegu delwedd bawd - Creu gwag - Clipfwrdd - Gosodiadau - Datblygedig - Gorfodi cuddio'r golygydd cynnwys - Rydych chi wedi gwneud newidiadau i'r cynnwys hwn. Wyt ti'n siŵr eich bod chi am eu taflu ei fwrdd? - Gwaredu cread? - - Priodwedd '%0%' yn defnyddio'r golygydd '%1%' sydd ddim yn cael ei gefnogi mewn blociau. - - - Beth yw Templedi Gynnwys - Mae Templedi Gynnwys yn gynnwys cyn-diffiniedig sydd yn gallu cael ei ddewis wrth greu nod cynnwys newydd. - Sut ydw i'n creu Templed Gynnwys? - - Mae yna ddwy ffordd i greu Templed Gynnwys:

-
    -
  • Gliciwch-de ar nod cynnwys a dewis "Creu Templed Gynnwys" i greu Templed Gynnwys newydd.
  • -
  • Gliciwch-de ar y goeden Templedi Gynnwys yn yr adran Gosodiadau a dewis y Math of Dogfen ti eisiau creu Templed Gynnwys am.
  • -
-

Unwaith y rhoddir enw, gall golygyddion ddechrau defnyddio'r Templed Gynnwys fel sylfaen am ei thudalen newydd.

- ]]> -
- Sut ydw i'n rheoli Templedi Gynnwys - Gallwch chi olygu a dileu Templedi Gynnwys o'r goeden "Templedi Gynnwys" yn yr adran Gosodiadau. Ehangwch y Math o Ddogfen mae'r Templed Gynnwys yn seiliedig arno a chlicio fo i'w golygu neu ddileu. - -
diff --git a/src/Umbraco.Web.UI/Umbraco/js/UmbracoSpeechBubbleBackEnd.js b/src/Umbraco.Web.UI/Umbraco/js/UmbracoSpeechBubbleBackEnd.js deleted file mode 100644 index f738538f90..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/js/UmbracoSpeechBubbleBackEnd.js +++ /dev/null @@ -1,68 +0,0 @@ -// Umbraco SpeechBubble Javascript - -// Dependency Loader Constructor -function UmbracoSpeechBubble(id) { - this.id = id; - this.ie = document.all ? true : false; - - this.GenerateSpeechBubble(); -} - -UmbracoSpeechBubble.prototype.GenerateSpeechBubble = function () { - - var sbHtml = document.getElementById(this.id); - - sbHtml.innerHTML = '' + - '
' + - '
' + - '' + - ' Close' + - '

The header!

' + - '

Default Text Container!


' + - '
' + - '
'; -}; - -UmbracoSpeechBubble.prototype.ShowMessage = function (icon, header, message, dontAutoHide) { - var speechBubble = jQuery("#" + this.id); - jQuery("#" + this.id + "Header").html(header); - jQuery("#" + this.id + "Message").html(message); - jQuery("#" + this.id + "Icon").attr('src', 'images/speechBubble/' + icon + '.png'); - - if (!this.ie) { - if (!dontAutoHide) { - jQuery("#" + this.id).fadeIn("slow").animate({ opacity: 1.0 }, 5000).fadeOut("fast"); - } else { - jQuery(".speechClose").show(); - jQuery("#" + this.id).fadeIn("slow"); - } - } else { - // this is special for IE as it handles fades with pngs very ugly - jQuery("#" + this.id).show(); - if (!dontAutoHide) { - setTimeout('UmbSpeechBubble.Hide();', 5000); - } else { - jQuery(".speechClose").show(); - } - } -}; - -UmbracoSpeechBubble.prototype.Hide = function () { - if (!this.ie) { - jQuery("#" + this.id).fadeOut("slow"); - } else { - jQuery("#" + this.id).hide(); - } -}; - -// Initialize -var UmbSpeechBubble = null -function InitUmbracoSpeechBubble() { - if (UmbSpeechBubble == null) - UmbSpeechBubble = new UmbracoSpeechBubble("defaultSpeechbubble"); -} - -jQuery(document).ready(function() { - InitUmbracoSpeechBubble(); -}); diff --git a/src/Umbraco.Web.UI/Umbraco/js/dualSelectBox.js b/src/Umbraco.Web.UI/Umbraco/js/dualSelectBox.js deleted file mode 100644 index 5409e662e2..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/js/dualSelectBox.js +++ /dev/null @@ -1,48 +0,0 @@ - -function dualSelectBoxShift(id) { - var posVal = document.getElementById(id + "_posVals"); - var selVal = document.getElementById(id + "_selVals"); - - // First check the possible items - for (var i=0;i